瀏覽代碼

名称翻译器代码提交

yucheng 4 年之前
父節點
當前提交
d4a082fd41
共有 26 個文件被更改,包括 1992 次插入0 次删除
  1. 35 0
      fm-translate/pom.xml
  2. 112 0
      fm-translate/src/main/java/com/persagy/fm/translate/aop/TranslateAopAspect.java
  3. 33 0
      fm-translate/src/main/java/com/persagy/fm/translate/dao/ITranslateDao.java
  4. 59 0
      fm-translate/src/main/java/com/persagy/fm/translate/extension/BeanTransWrapper.java
  5. 91 0
      fm-translate/src/main/java/com/persagy/fm/translate/extension/BeanTransWrapperBuilder.java
  6. 32 0
      fm-translate/src/main/java/com/persagy/fm/translate/extension/ITransWrapperBuilder.java
  7. 71 0
      fm-translate/src/main/java/com/persagy/fm/translate/extension/MapPairResultHandler.java
  8. 65 0
      fm-translate/src/main/java/com/persagy/fm/translate/extension/MapResultHandler.java
  9. 45 0
      fm-translate/src/main/java/com/persagy/fm/translate/extension/MapTransWrapper.java
  10. 34 0
      fm-translate/src/main/java/com/persagy/fm/translate/helper/EmojiCharsTranslator.java
  11. 213 0
      fm-translate/src/main/java/com/persagy/fm/translate/helper/TranslatorHelper.java
  12. 145 0
      fm-translate/src/main/java/com/persagy/fm/translate/impl/DBTranslateHandleImpl.java
  13. 94 0
      fm-translate/src/main/java/com/persagy/fm/translate/impl/ServiceTranslateHandleImpl.java
  14. 250 0
      fm-translate/src/main/java/com/persagy/fm/translate/impl/TranslateServiceImpl.java
  15. 36 0
      fm-translate/src/main/java/com/persagy/fm/translate/model/ITransWrapper.java
  16. 34 0
      fm-translate/src/main/java/com/persagy/fm/translate/model/ITransable.java
  17. 65 0
      fm-translate/src/main/java/com/persagy/fm/translate/model/Trans.java
  18. 392 0
      fm-translate/src/main/java/com/persagy/fm/translate/model/TransMeta.java
  19. 20 0
      fm-translate/src/main/java/com/persagy/fm/translate/model/TransType.java
  20. 19 0
      fm-translate/src/main/java/com/persagy/fm/translate/model/Transes.java
  21. 19 0
      fm-translate/src/main/java/com/persagy/fm/translate/service/ITranslateBatchHandle.java
  22. 31 0
      fm-translate/src/main/java/com/persagy/fm/translate/service/ITranslateHandle.java
  23. 21 0
      fm-translate/src/main/java/com/persagy/fm/translate/service/ITranslateService.java
  24. 29 0
      fm-translate/src/main/java/com/persagy/fm/translate/service/ITranslator.java
  25. 31 0
      fm-translate/src/main/java/com/persagy/fm/translate/web/MappingJacksonHttpMessageConverter.java
  26. 16 0
      fm-translate/src/main/resources/mapper/adder/translate/ITranslateDao.xml

+ 35 - 0
fm-translate/pom.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>fm-parent</artifactId>
+        <groupId>com.persagy</groupId>
+        <version>3.0.0</version>
+        <relativePath>../fm-parent</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>fm-translate</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>integrated-common-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>fm-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-tx</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.data</groupId>
+            <artifactId>spring-data-commons</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 112 - 0
fm-translate/src/main/java/com/persagy/fm/translate/aop/TranslateAopAspect.java

@@ -0,0 +1,112 @@
+package com.persagy.fm.translate.aop;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.persagy.fm.common.helper.SpringHelper;
+import com.persagy.fm.translate.helper.TranslatorHelper;
+import com.persagy.fm.translate.model.ITransable;
+import com.persagy.fm.translate.model.Trans;
+import com.persagy.fm.translate.model.TransMeta;
+import com.persagy.fm.translate.model.Transes;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.core.Ordered;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * 翻译器Aspect
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+@Component
+@Aspect
+@Slf4j
+public class TranslateAopAspect implements Ordered {
+
+    /**
+     * 翻译描述信息缓存
+     */
+    private Cache<String,List<TransMeta>> transCach;
+
+    /**
+     * 构造方法,初始化缓存
+     */
+    public TranslateAopAspect(){
+        int count = SpringHelper.getInt("tranlate.cache.size",100);
+        transCach = CacheBuilder.newBuilder().initialCapacity(count/2).maximumSize(count).build();
+    }
+
+    @Pointcut("@annotation(com.persagy.fm.translate.model.Transes)||@annotation(com.persagy.fm.translate.model.Trans)")
+    private void pointCutMethod() {
+    }
+
+    /**
+     * 取得方法缓存的Key
+     * @param signature 方法标签
+     * @return 缓存Key
+     */
+    private String getMethodKey(MethodSignature signature){
+        return signature.toString();
+    }
+
+    /**
+     * 获取翻译元数据
+     * @param signature 方法标示
+     * @throws Exception
+     */
+    public List<TransMeta> buildMeta(MethodSignature signature) throws Exception {
+        String key = getMethodKey(signature);
+        List<TransMeta> metas = transCach.getIfPresent(key);
+        if(metas!=null) {
+            return metas;
+        }
+        Method method = signature.getMethod();
+        Transes transes = method.getAnnotation(Transes.class);
+        if(transes != null && transes.value().length > 0) {
+            metas = TranslatorHelper.parseMetas(transes.value());
+        }else{
+            Trans tran = method.getAnnotation(Trans.class);
+            if(tran != null) {
+                metas = TranslatorHelper.parseMetas(new Trans[]{tran});
+            }
+        }
+        if(metas != null) {
+            transCach.put(key,metas);
+        }
+        return metas;
+    }
+
+    @Around("pointCutMethod()")
+    public Object process(ProceedingJoinPoint point) throws Throwable {
+        try {
+            log.info("Around translate---"+point.getSignature().toLongString());
+            List<TransMeta> metas = buildMeta((MethodSignature) point.getSignature());
+            Object rt = point.proceed();
+
+            if (metas == null || rt == null) {
+                return rt;
+            }
+            if (rt instanceof ITransable) {
+                rt = TranslatorHelper.translate((ITransable) rt, metas);
+            } else {
+                rt = TranslatorHelper.translate(rt,metas);
+            }
+            log.info("Around translate-complete---"+point.getSignature().toLongString());
+            return rt;
+        } finally {
+            TranslatorHelper.clearMetas();
+        }
+    }
+
+    @Override
+    public int getOrder() {
+        return 2000;
+    }
+}

+ 33 - 0
fm-translate/src/main/java/com/persagy/fm/translate/dao/ITranslateDao.java

@@ -0,0 +1,33 @@
+package com.persagy.fm.translate.dao;
+
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.session.ResultHandler;
+
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 基于数据库翻译的dao
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+public interface ITranslateDao{
+
+	/**
+	 * 根据sql语句查询
+	 * @param sql  查询sql
+	 * @param parameter 参数
+	 * @return 结果集
+	 */
+	Map<String,Object> findTransBySql(@Param("sql") String sql, @Param("parameter") String parameter);
+
+	/**
+	 * 批量翻译查询
+	 * @param sql       查询sql
+	 * @param parameter 参数
+	 * @param handler 结果处理
+	 */
+	void findBatchTransBySql(@Param("sql") String sql, @Param("parameter") List<String> parameter, ResultHandler handler);
+
+}

+ 59 - 0
fm-translate/src/main/java/com/persagy/fm/translate/extension/BeanTransWrapper.java

@@ -0,0 +1,59 @@
+package com.persagy.fm.translate.extension;
+
+import com.persagy.fm.translate.model.ITransWrapper;
+import org.springframework.cglib.beans.BeanMap;
+
+/**
+ * map翻译包装类
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+public class BeanTransWrapper implements ITransWrapper{
+
+    /**
+     * 源对象
+     */
+    private Object source;
+    /**
+     * 目标对象
+     */
+    private Object target;
+
+    /**
+     * 目标对象
+     */
+    private BeanMap targetMap;
+
+    /**
+     * 构造方法
+     * @param source 源对象
+     * @param target 目标对象
+     */
+    public BeanTransWrapper(Object source,Object target){
+        this.source = source;
+        if(target == null){
+            target = source;
+        }
+        this.target = target;
+        targetMap = BeanMap.create(target);
+    }
+    @Override
+    public Object getValue(String name) {
+        return targetMap.get(name);
+    }
+
+    @Override
+    public void setValue(String name, Object object) {
+        targetMap.put(name,object);
+    }
+
+    @Override
+    public Object getSource() {
+        return source;
+    }
+
+    @Override
+    public Object getResult() {
+        return target;
+    }
+}

+ 91 - 0
fm-translate/src/main/java/com/persagy/fm/translate/extension/BeanTransWrapperBuilder.java

@@ -0,0 +1,91 @@
+package com.persagy.fm.translate.extension;
+
+import com.persagy.fm.common.utils.ProxyBeanUtil;
+import com.persagy.fm.translate.model.ITransWrapper;
+import org.springframework.cglib.beans.BeanCopier;
+import org.springframework.cglib.beans.BeanGenerator;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * 基于对象的包装
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+public class BeanTransWrapperBuilder implements ITransWrapperBuilder{
+
+    /**
+     * 是否需要替换原始对象
+     */
+    private boolean isMap;
+
+    /**
+     * 扩展项
+     */
+    private Collection<String> extendProps = null;
+
+    /**
+     * 对象类
+     */
+    private Class<?> clazz = null;
+
+    /**
+     * 新的扩展类
+     */
+    private Class<?> newClass = null;
+
+    /**
+     * 生成器
+     */
+    private BeanGenerator gen;
+
+    /**
+     * 是否存在扩展
+     */
+    private boolean extend = false;
+    /**
+     * 构造方法
+     * @param clazz 对象类
+     */
+    public BeanTransWrapperBuilder(Class clazz){
+        this.isMap = Map.class.isAssignableFrom(clazz);
+        this.clazz = clazz;
+    }
+    @Override
+    public ITransWrapper createWrapper(Object origion) {
+        if(isMap) {
+            return new MapTransWrapper((Map<String, Object>) origion);
+        }
+        if(extend && gen == null){
+            gen = ProxyBeanUtil.createBeanGenerator(origion, extendProps.toArray(new String[0]));
+            if(gen == null) {
+                extend = false;
+            } else {
+                newClass = (Class) gen.createClass();
+            }
+        }
+        Object target = null;
+        if(extend){
+            BeanCopier copier = BeanCopier.create(this.clazz,newClass,false);
+            target =  gen.create();
+            copier.copy(origion,target,null);
+        }
+        return new BeanTransWrapper(origion, target);
+    }
+
+    @Override
+    public boolean isNeedReplaceOrigin() {
+        return !isMap;
+    }
+
+    @Override
+    public void setExtendProps(Collection<String> extendProps) {
+        if(extendProps != null && extendProps.size() > 0) {
+            this.extendProps = extendProps;
+            extend = true;
+        }else {
+            extend = false;
+        }
+    }
+}

+ 32 - 0
fm-translate/src/main/java/com/persagy/fm/translate/extension/ITransWrapperBuilder.java

@@ -0,0 +1,32 @@
+package com.persagy.fm.translate.extension;
+
+import com.persagy.fm.translate.model.ITransWrapper;
+
+import java.util.Collection;
+
+/**
+ * 翻译包装对象工厂类
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+public interface ITransWrapperBuilder {
+
+    /**
+     * 创建包装对象
+     * @param obj 原始对象
+     * @return 包装对象
+     */
+    ITransWrapper createWrapper(Object obj);
+
+    /**
+     * 是否需要替换原始对象
+     * @return 布尔值
+     */
+    boolean isNeedReplaceOrigin();
+
+    /**
+     * 设置目标字段集合
+     * @param target 目标字段集合
+     */
+    void setExtendProps(Collection<String> target);
+}

+ 71 - 0
fm-translate/src/main/java/com/persagy/fm/translate/extension/MapPairResultHandler.java

@@ -0,0 +1,71 @@
+package com.persagy.fm.translate.extension;
+
+import org.apache.ibatis.session.ResultContext;
+import org.apache.ibatis.session.ResultHandler;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 基于两列生成map的结果处理,会把其中一列设置为key,另一列设置为value
+ * @author Sun
+ * @version 1.0 2015-7-23
+ * @since 1.6
+ */
+public class MapPairResultHandler implements ResultHandler {
+
+    /**
+     * 键值字段
+     */
+    private String mapKey = null;
+
+    /**
+     * 映射值字段
+     */
+    private String mapValue = null;
+
+    /**
+     * 结果map
+     */
+    private final Map<String, Object> mappedResults;
+
+
+    /**
+     * 构造方法
+     * @param mapKey 作为键值的字段名
+     */
+    public MapPairResultHandler(String mapKey, String mapValue){
+        this.mapKey = mapKey;
+        this.mapValue = mapValue;
+        mappedResults = new HashMap<>();
+    }
+
+    /**
+     * 构造方法
+     * @param mapKey 作为键值的字段名
+     * @param map 已有的map对象
+     */
+    public MapPairResultHandler(String mapKey, String mapValue, Map<String,Object> map){
+        this.mapKey = mapKey;
+        this.mapValue = mapValue;
+        mappedResults = map;
+    }
+
+    @Override
+    public void handleResult(ResultContext context) {
+        @SuppressWarnings("unchecked")
+        Map<String,String> row =(Map)context.getResultObject();
+        String key = row.get(mapKey);
+        mappedResults.put(key,row.get(mapValue));
+    }
+
+    /**
+     * 取得结果map对象
+     * @return 结果map对象
+     */
+    public Map<String,Object> getResult(){
+        return mappedResults;
+    }
+
+
+}

+ 65 - 0
fm-translate/src/main/java/com/persagy/fm/translate/extension/MapResultHandler.java

@@ -0,0 +1,65 @@
+package com.persagy.fm.translate.extension;
+
+import org.apache.ibatis.session.ResultContext;
+import org.apache.ibatis.session.ResultHandler;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 基于map结果处理这里因为mapkey为动态的所以自行处理
+ * @author Sun
+ * @version 1.0 2015-7-23
+ * @since 1.6
+ */
+public class MapResultHandler<T> implements ResultHandler {
+
+    /**
+     * 键值字段
+     */
+    private String mapKey = null;
+
+
+    /**
+     * 结果map
+     */
+    private final Map<String, Object> mappedResults;
+
+
+    /**
+     * 构造方法
+     * @param mapKey 作为键值的字段名
+     */
+    public MapResultHandler(String mapKey){
+        this.mapKey = mapKey;
+        mappedResults = new HashMap<>();
+    }
+
+    /**
+     * 构造方法
+     * @param mapKey 作为键值的字段名
+     * @param map 已有的map对象
+     */
+    public MapResultHandler(String mapKey, String mapValue, Map<String,Object> map){
+        this.mapKey = mapKey;
+        mappedResults = map;
+    }
+
+    @Override
+    public void handleResult(ResultContext context) {
+        @SuppressWarnings("unchecked")
+        Map<String,Object> row =(Map)context.getResultObject();
+        String key = (String) row.get(mapKey);
+        mappedResults.put(key,row);
+    }
+
+    /**
+     * 取得结果map对象
+     * @return 结果map对象
+     */
+    public Map<String,Object> getResult(){
+        return mappedResults;
+    }
+
+
+}

+ 45 - 0
fm-translate/src/main/java/com/persagy/fm/translate/extension/MapTransWrapper.java

@@ -0,0 +1,45 @@
+package com.persagy.fm.translate.extension;
+
+import com.persagy.fm.translate.model.ITransWrapper;
+
+import java.util.Map;
+
+/**
+ * map翻译包装类
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+public class MapTransWrapper implements ITransWrapper{
+
+    /**
+     * 源对象
+     */
+    private Map<String,Object> source;
+
+    /**
+     * 构造方法
+     * @param source 源对象
+     */
+    public MapTransWrapper(Map<String,Object> source){
+        this.source = source;
+    }
+    @Override
+    public Object getValue(String name) {
+        return source.get(name);
+    }
+
+    @Override
+    public void setValue(String name, Object object) {
+        source.put(name,object);
+    }
+
+    @Override
+    public Object getSource() {
+        return source;
+    }
+
+    @Override
+    public Object getResult() {
+        return source;
+    }
+}

+ 34 - 0
fm-translate/src/main/java/com/persagy/fm/translate/helper/EmojiCharsTranslator.java

@@ -0,0 +1,34 @@
+package com.persagy.fm.translate.helper;
+
+import cn.hutool.extra.emoji.EmojiUtil;
+import com.persagy.fm.translate.service.ITranslator;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * emoji翻译
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+@Service
+public class EmojiCharsTranslator implements ITranslator {
+	@Override
+	public String trans(String parameter) {
+		return EmojiUtil.toHtml(parameter);
+	}
+
+	@Override
+	public Map<String, Object> transBatch(List<String> parameter) {
+		if (parameter == null || parameter.size() == 0) {
+			return null;
+		}
+		Map result = new HashMap(16);
+		for (Object obj : parameter) {
+			result.put(obj, EmojiUtil.toHtml((String) obj));
+		}
+		return result;
+	}
+}

+ 213 - 0
fm-translate/src/main/java/com/persagy/fm/translate/helper/TranslatorHelper.java

@@ -0,0 +1,213 @@
+package com.persagy.fm.translate.helper;
+
+import com.persagy.fm.common.helper.SpringHelper;
+import com.persagy.fm.translate.extension.BeanTransWrapperBuilder;
+import com.persagy.fm.translate.extension.ITransWrapperBuilder;
+import com.persagy.fm.translate.model.ITransable;
+import com.persagy.fm.translate.model.Trans;
+import com.persagy.fm.translate.model.TransMeta;
+import com.persagy.fm.translate.service.ITranslateService;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 数据翻译助手
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+public final class TranslatorHelper {
+
+	/**
+	 * 前台传到后台的翻译元数据
+	 */
+	public static final String TRANSOR_PARAM = "__transor";
+
+	/**
+	 * 线程级缓存
+	 */
+	private static ThreadLocal<List<TransMeta>> threaMetas = new ThreadLocal<>();
+
+	/**
+	 * 取得请求的翻译元数据
+	 * @return 翻译元数据列表
+	 */
+	public static List<TransMeta> getMetas(){
+		return threaMetas.get();
+	}
+
+    /**
+     * 根据实体类创建翻译包装构建器
+     * @param clazz 实体类
+     * @return 翻译包装构建器
+     */
+	public static ITransWrapperBuilder createTransWrapperBuilder(Class<?> clazz){
+		// 预留此方法,后续可增加扩展点
+		return new BeanTransWrapperBuilder(clazz);
+	}
+
+	/**
+	 * 设置请求的翻译元数据串
+	 * @param transor 翻译描述信息
+	 */
+	public static void setTransor(String transor){
+		if(StringUtils.isEmpty(transor)) {
+            return;
+        }
+		List<TransMeta> metas = TransMeta.parseMetas(transor);
+		if(metas != null) {
+            threaMetas.set(metas);
+        }
+	}
+
+	/**
+	 * 通过注解放置翻译描述信息
+	 * @param transes 注解数组
+	 * @return 描述信息列表
+	 */
+	public static List<TransMeta> parseMetas(Trans[] transes) {
+		List<TransMeta> metas = new ArrayList<>(transes.length);
+		for(Trans trans: transes){
+			TransMeta meta = new TransMeta();
+			meta.setType(trans.type());
+			meta.setParameter(trans.parameter());
+			meta.setCondition(trans.condition());
+			meta.setMapKey(trans.mapKey());
+			meta.setBatch(trans.batch());
+			meta.setResult(trans.result());
+			meta.setTarget(trans.target());
+			meta.setSource(trans.source());
+			metas.add(meta);
+		}
+		return metas;
+	}
+
+	/**
+	 * 通过注解放置翻译描述信息
+	 * @param transes 注解数组
+	 * @return 描述信息列表
+	 */
+	public static List<TransMeta> putMetas(Trans[] transes) {
+		List<TransMeta> metas = parseMetas(transes);
+		putMetas(metas);
+		return metas;
+	}
+
+	/**
+	 * 通过字符串取得翻译描述信息
+	 * @param transor 翻译描述串json形式
+	 * @return 描述信息列表
+	 */
+	public static List<TransMeta> putMetas(String transor) {
+		if(StringUtils.isEmpty(transor)){
+			return null;
+		}
+		List<TransMeta> metas =TransMeta.parseMetas(transor);
+		if(metas != null) {
+            putMetas(metas);
+        }
+		return metas;
+	}
+	/**
+	 * 将翻译元数据放入线程变量中
+	 * @param metas    翻译元数据列表
+	 */
+	public static void putMetas(List<TransMeta> metas){
+		threaMetas.set(metas);
+	}
+	/**
+	 * 清除缓存
+	 */
+	public static void clearMetas(){
+		threaMetas.remove();
+	}
+
+	/**
+	 * 翻译数据
+	 * @param tansable 可翻译对象
+	 * @param metas 元数据
+	 * @return 翻译后数据
+	 */
+	@SuppressWarnings("unchecked")
+	public static Object translate(ITransable tansable, List<TransMeta> metas){
+		if(tansable == null || metas == null) {
+            return tansable;
+        }
+		if(!tansable.isNeedTranslate()) {
+            return tansable;
+        }
+		Object data = tansable.getTranslator();
+		if(data == null) {
+            return tansable;
+        }
+		if(tansable.isMapPackage() && data instanceof Map){
+			Map<String,Object> map = (Map<String, Object>) data;
+			for(Map.Entry<String, Object> en:map.entrySet()){
+				Object child = translate(en.getValue(),metas);
+				map.put(en.getKey(), child);
+			}
+		}else{
+			data = translate(data,metas);
+		}
+		tansable.setTranslator(data);
+		return tansable;
+	}
+
+	/**
+	 * 翻译数据
+	 * @param object 实体,实体列表,map或map列表
+	 * @return 翻译后数据
+	 */
+	public static Object translate(Object object, List<TransMeta> transMeta) {
+		if(object == null || transMeta == null || transMeta.size() == 0) {
+            return object;
+        }
+		ITranslateService tranlate = SpringHelper.getBean(ITranslateService.class);
+		return tranlate.translate(object, transMeta);
+	}
+	
+	/**
+	 * 翻译数据
+	 * @param tansable 可翻译对象
+	 * @return 翻译后数据
+	 */
+	public static Object translate(ITransable tansable){
+		return translate(tansable,getMetas());
+	}
+
+	/**
+	 * 翻译数据
+	 * @param object 实体,实体列表,map或map列表
+	 * @return 翻译后数据
+	 */
+	public static Object translate(Object object) {
+		return translate(object,getMetas());
+	}
+	/**
+	 * 翻译数据
+	 * @param tansable 可翻译对象
+	 * @param transor  翻译描述信息
+	 * @return 翻译后数据
+	 */
+	public static Object translate(ITransable tansable,String transor){
+		if(StringUtils.isEmpty(transor)){
+			return tansable;
+		}
+		return translate(tansable,TransMeta.parseMetas(transor));
+	}
+
+	/**
+	 * 翻译数据
+	 * @param object 实体,实体列表,map或map列表
+	 * @param transor    翻译描述信息
+	 * @return 翻译后数据
+	 */
+	public static Object translate(Object object,String transor) {
+		if(StringUtils.isEmpty(transor)){
+			return object;
+		}
+		return translate(object,TransMeta.parseMetas(transor));
+	}
+}

+ 145 - 0
fm-translate/src/main/java/com/persagy/fm/translate/impl/DBTranslateHandleImpl.java

@@ -0,0 +1,145 @@
+package com.persagy.fm.translate.impl;
+
+import com.persagy.fm.common.helper.SpringHelper;
+import com.persagy.fm.translate.dao.ITranslateDao;
+import com.persagy.fm.translate.extension.MapPairResultHandler;
+import com.persagy.fm.translate.extension.MapResultHandler;
+import com.persagy.fm.translate.model.ITransWrapper;
+import com.persagy.fm.translate.model.TransMeta;
+import com.persagy.fm.translate.model.TransType;
+import com.persagy.fm.translate.service.ITranslateBatchHandle;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Service;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 基于db的数据翻译
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+@Service(TransType.DB)
+@Scope("prototype")
+public class DBTranslateHandleImpl implements ITranslateBatchHandle {
+
+    /**
+     * 翻译元数据
+     */
+    private TransMeta meta;
+
+    /**
+     * 数据库dao
+     */
+    private ITranslateDao dao = null;
+
+    /**
+     * 查询语句
+     */
+    private String sql = null;
+
+    /**
+     * 目标字段
+     */
+    private String[] targetFields = null;
+
+    /**
+     * 结果字段
+     */
+    private String[] resultFields = null;
+
+    /**
+     * 取得翻译元数据
+     *
+     * @return 翻译元数据
+     */
+    @Override
+    public TransMeta getMeta() {
+        return meta;
+    }
+
+    /**
+     * 设置翻译元数据
+     *
+     * @param meta 翻译元数据
+     */
+    @Override
+    public void setMeta(TransMeta meta) {
+        this.meta = meta;
+    }
+
+    @Override
+    public ITransWrapper trans(ITransWrapper data) {
+        String parameter = (String) data.getValue(meta.getParameter());
+        if (StringUtils.isEmpty(parameter)) {
+            //参数值为空,则目标值也为空
+            targetFields = meta.getTarget().split(",");
+            for (int i = 0; i < targetFields.length; i++) {
+                data.setValue(targetFields[i], null);
+            }
+            return data;
+        }
+        if (dao == null) {
+            dao = SpringHelper.getBean(ITranslateDao.class);
+        }
+        if (sql == null) {
+            String table = meta.getSource();
+            String column = meta.getResult();
+            String con = meta.getCondition();
+            targetFields = meta.getTarget().split(",");
+            resultFields = meta.getResultFields();
+            sql = "select " + column + " from " + table + " where " + con + "=#{parameter}";
+        }
+        Map<String, Object> map = dao.findTransBySql(sql, parameter);
+        for (int i = 0; i < resultFields.length; i++) {
+            Object r = map == null ? null : map.get(resultFields[i]);
+            if (r == null && map != null) {
+                r = map.get(resultFields[i].toUpperCase());
+            }
+            if (r != null) {
+                data.setValue(targetFields[i], r.toString());
+            } else {
+                data.setValue(targetFields[i], r);
+            }
+
+        }
+        return data;
+    }
+
+    @Override
+    public Map<String, Object> batchTrans(List<String> parameter) {
+        if (parameter == null || parameter.size() == 0) {
+            return null;
+        }
+        if (dao == null) {
+            dao = SpringHelper.getBean(ITranslateDao.class);
+        }
+        String table = meta.getSource();
+        String column = meta.getResult();
+        String con = meta.getCondition();
+        String key = meta.getMapKey();
+        int idx = key.indexOf(" as ");
+        if (idx > 0) {
+            key = key.substring(idx + 4).trim();
+        }
+        String selectPart = meta.getMapKey() + "," + column;
+        String sql = "select " + selectPart + " from " + table + " where " + con + " in";
+        String[] fields = meta.getResultFields();
+        String[] ids = parameter.stream().distinct().toArray(String[]::new);
+        if (fields == null || fields.length == 0) {
+            return null;
+        } else if (fields.length > 1) {
+            MapResultHandler handler = new MapResultHandler(key);
+            dao.findBatchTransBySql(sql, Arrays.asList(ids), handler);
+            return handler.getResult();
+        } else {
+            MapPairResultHandler handler = new MapPairResultHandler(key, fields[0]);
+            dao.findBatchTransBySql(sql, Arrays.asList(ids), handler);
+            return handler.getResult();
+        }
+    }
+
+}

+ 94 - 0
fm-translate/src/main/java/com/persagy/fm/translate/impl/ServiceTranslateHandleImpl.java

@@ -0,0 +1,94 @@
+package com.persagy.fm.translate.impl;
+
+import com.persagy.common.exception.BusinessException;
+import com.persagy.fm.common.helper.SpringHelper;
+import com.persagy.fm.translate.model.ITransWrapper;
+import com.persagy.fm.translate.model.TransMeta;
+import com.persagy.fm.translate.model.TransType;
+import com.persagy.fm.translate.service.ITranslateBatchHandle;
+import com.persagy.fm.translate.service.ITranslator;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 基于指定服务接口的数据翻译
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+@SuppressWarnings("ALL")
+@Service(TransType.SERVICE)
+@Scope("prototype")
+public class ServiceTranslateHandleImpl implements ITranslateBatchHandle {
+
+	/**
+	 * 翻译服务对象
+	 */
+	private ITranslator translator;
+	
+	/**
+	 * 翻译元数据
+	 */
+	private TransMeta meta;
+	
+	/**
+	 * 目标字段
+	 */
+	private String[] targetFields = null;
+
+    @Override
+	public TransMeta getMeta() {
+		return meta;
+	}
+
+    @Override
+	public void setMeta(TransMeta meta) {
+		this.meta = meta;
+	}
+
+	@Override
+	public ITransWrapper trans(ITransWrapper data) {
+		if(translator == null){
+			translator = SpringHelper.getBean(meta.getSource(), ITranslator.class);
+			if(translator == null){
+				throw new BusinessException("翻译服务配置出错");
+			}
+			String target = meta.getTarget();
+			if(StringUtils.isEmpty(target)){
+				throw new BusinessException("翻译服务配置出错");
+			}
+			targetFields = meta.getTarget().split(",");
+		}
+		String parameter = String.valueOf(data.getValue(
+				meta.getParameter()));
+		String rsStr = translator.trans(parameter);
+		String[] rs = null;
+		if(StringUtils.isNotEmpty(rsStr)){
+			rs = rsStr.split(",",targetFields.length);
+		}
+		for(int i = 0; i < targetFields.length; i++){
+			if(rs == null || rs.length <= i){
+				data.setValue(targetFields[i], null);
+			}else{
+				data.setValue(targetFields[i], rs[i]);
+			}
+		}
+		return data;
+	}
+
+	@Override
+	public Map<String, Object> batchTrans(List<String> parameter) {
+		if(translator == null){
+			translator = SpringHelper.getBean(meta.getSource(),ITranslator.class);
+			if(translator == null){
+				throw new BusinessException("翻译服务配置出错");
+			}
+		}
+		return translator.transBatch(parameter);
+	}
+
+}

+ 250 - 0
fm-translate/src/main/java/com/persagy/fm/translate/impl/TranslateServiceImpl.java

@@ -0,0 +1,250 @@
+package com.persagy.fm.translate.impl;
+
+import com.persagy.fm.common.helper.SpringHelper;
+import com.persagy.fm.translate.extension.ITransWrapperBuilder;
+import com.persagy.fm.translate.helper.TranslatorHelper;
+import com.persagy.fm.translate.model.ITransWrapper;
+import com.persagy.fm.translate.model.TransMeta;
+import com.persagy.fm.translate.service.ITranslateBatchHandle;
+import com.persagy.fm.translate.service.ITranslateHandle;
+import com.persagy.fm.translate.service.ITranslateService;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+
+/**
+ * 翻译接口实现
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+@Service
+@Transactional
+public class TranslateServiceImpl implements ITranslateService {
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Object translate(Object origions, List<TransMeta> metas) {
+        if (origions == null || metas == null || metas.size() == 0) {
+            return origions;
+        }
+        Object orignObj = origions;
+        List<Object> objs = null;
+        boolean islist = false;
+        //对分页和列表进行处理获取单个对象,并设置是否为列表标志
+        if (origions instanceof Page) {
+            try {
+                Field field = PageImpl.class.getSuperclass().getDeclaredField("content");
+                field.setAccessible(true);
+                objs = (List<Object>) field.get(origions);
+            } catch (NoSuchFieldException | IllegalAccessException e) {
+                objs = null;
+            }
+            if (CollectionUtils.isEmpty(objs)) {
+                return origions;
+            }
+            orignObj = objs.get(0);
+            islist = true;
+        } else if (origions instanceof List) {
+            objs = (List<Object>) origions;
+            if (objs.size() == 0) {
+                return origions;
+            }
+            orignObj = objs.get(0);
+            islist = true;
+        }
+        //按翻译元数据生成翻译处理器
+        List<ITranslateHandle> handles = new ArrayList<>();
+        HashSet<String> properties = new HashSet<>();
+        ITransWrapperBuilder builder = TranslatorHelper.createTransWrapperBuilder(orignObj.getClass());
+        for (TransMeta meta : metas) {
+            ITranslateHandle handle = createHandle(meta);
+            handles.add(handle);
+            meta.setBatch(meta.isBatch() && handle instanceof ITranslateBatchHandle);
+            if (!builder.isNeedReplaceOrigin()) {
+                continue;
+            }
+            //生成vo的扩展属性
+            String target = meta.getTarget();
+            if (StringUtils.isEmpty(target)) {
+                continue;
+            }
+            if (target.indexOf(",") > -1) {
+                String[] ps = target.split(",");
+                for (String p : ps) {
+                    properties.add(p);
+                }
+            } else {
+                properties.add(target);
+            }
+        }
+        builder.setExtendProps(properties);
+        if (islist) {
+
+            transMultiRows(objs, handles, builder);
+            return origions;
+        } else {
+            //单条数据翻译
+            return transSingleRow(orignObj, handles, builder);
+        }
+    }
+
+    /**
+     * 单个vo数据翻译
+     *
+     * @param data    单个数据
+     * @param handles 翻译处理集合
+     */
+    protected Object transSingleRow(Object data, List<ITranslateHandle> handles, ITransWrapperBuilder builder) {
+        ITransWrapper wrapper = builder.createWrapper(data);
+        for (ITranslateHandle handle : handles) {
+            handle.trans(wrapper);
+        }
+        return wrapper.getResult();
+    }
+
+    /**
+     * 批量翻译
+     *
+     * @param datas   多行VO数据
+     * @param handles 翻译处理集合
+     * @return 翻译后的结果
+     */
+    protected List<Object> transMultiRows(List<Object> datas, List<ITranslateHandle> handles, ITransWrapperBuilder builder) {
+        List<ITransWrapper> wrappers = new ArrayList<>();
+        for (ITranslateHandle handle : handles) {
+            TransMeta meta = handle.getMeta();
+            if (meta.isBatch()) {
+                batchTransMultiRows(datas, wrappers, (ITranslateBatchHandle) handle, builder);
+            } else {
+                transMultiRowLineByLine(datas, wrappers, handle, builder);
+            }
+        }
+        return datas;
+    }
+
+    /**
+     * 批量翻译vo数据
+     *
+     * @param datas    原始数据
+     * @param wrappers 数据包装对象列表
+     * @param handle   翻译处理
+     * @param builder   数据包装对象构建器
+     */
+    protected void batchTransMultiRows(List<Object> datas, List<ITransWrapper> wrappers, ITranslateBatchHandle handle, ITransWrapperBuilder builder) {
+        List<String> parameters = new ArrayList<>();
+        TransMeta meta = handle.getMeta();
+        if (wrappers.size() == 0) {
+            for (int i = 0; i < datas.size(); i++) {
+                ITransWrapper wrapper = builder.createWrapper(datas.get(i));
+                wrappers.add(wrapper);
+                if (builder.isNeedReplaceOrigin()) {
+                    datas.set(i, wrapper.getResult());
+                }
+                Object parameter = wrapper.getValue(meta.getParameter());
+                if (parameter == null) {
+                    continue;
+                }
+                parameters.add(parameter.toString());
+            }
+        } else {
+            for (int i = 0; i < wrappers.size(); i++) {
+                ITransWrapper wrapper = wrappers.get(i);
+                Object parameter = wrapper.getValue(meta.getParameter());
+                if (parameter == null) {
+                    continue;
+                }
+                parameters.add(parameter.toString());
+            }
+        }
+        parameters = parameters.stream().distinct().collect(Collectors.toList());
+        Map<String, Object> batchRs = handle.batchTrans(parameters);
+        if (batchRs == null) {
+            return;
+        }
+        for (int i = 0; i < wrappers.size(); i++) {
+            ITransWrapper wrapper = wrappers.get(i);
+            Object parameter = wrapper.getValue(meta.getParameter());
+            if (parameter == null) {
+                continue;
+            }
+            Object rs = batchRs.get(parameter.toString());
+            setRowTransResult(rs, handle.getMeta(), wrapper);
+        }
+    }
+
+    /**
+     * 逐行翻译vo数据
+     *
+     * @param datas    原始数据
+     * @param wrappers 包装列表
+     * @param handle   翻译处理
+     * @param builder   构建器
+     */
+    protected void transMultiRowLineByLine(List<Object> datas, List<ITransWrapper> wrappers, ITranslateHandle handle, ITransWrapperBuilder builder) {
+        if (wrappers.size() == 0) {
+            for (int i = 0; i < datas.size(); i++) {
+                ITransWrapper wrapper = builder.createWrapper(datas.get(i));
+                wrappers.add(wrapper);
+                if (builder.isNeedReplaceOrigin()) {
+                    datas.set(i, wrapper.getResult());
+                }
+            }
+        }
+
+        for (int j = 0; j < wrappers.size(); j++) {
+            ITransWrapper wrapper = wrappers.get(j);
+            handle.trans(wrapper);
+        }
+    }
+
+
+    /**
+     * 批量翻译时设置单行数据单个翻译结果
+     *
+     * @param rs      翻译结果
+     * @param meta    翻译元数据
+     * @param wrapper 包装对象
+     */
+    protected void setRowTransResult(Object rs, TransMeta meta, ITransWrapper wrapper) {
+        if (rs == null) {
+            return;
+        }
+        if (rs instanceof Map) {
+            String[] rsFields = meta.getResultFields();
+            String[] targetFields = meta.getTargetFields();
+            Map<String, Object> resultMap = (Map<String, Object>) rs;
+            int len = rsFields.length > targetFields.length ? targetFields.length : rsFields.length;
+            for (int j = 0; j < len; j++) {
+                Object rsr = resultMap.get(rsFields[j]);
+                if (rsr != null) {
+                    wrapper.setValue(targetFields[j], rsr.toString());
+                }
+            }
+        } else {
+            wrapper.setValue(meta.getTarget(), rs.toString());
+        }
+    }
+
+    /**
+     * 根据元数据生成处理器
+     *
+     * @param meta 元数据
+     * @return 处理器
+     */
+    private ITranslateHandle createHandle(TransMeta meta) {
+        ITranslateHandle handle = SpringHelper.getBean(meta.getType(), ITranslateHandle.class);
+        handle.setMeta(meta);
+        return handle;
+    }
+}

+ 36 - 0
fm-translate/src/main/java/com/persagy/fm/translate/model/ITransWrapper.java

@@ -0,0 +1,36 @@
+package com.persagy.fm.translate.model;
+
+/**
+ * 翻译对象包装对象
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+public interface ITransWrapper {
+
+
+	/**
+	 * 取得属性值
+	 * @param name 属性名
+	 * @return 属性值
+	 */
+	Object getValue(String name);
+
+	/**
+	 * 设置属性值
+	 * @param name   属性名
+	 * @param object 属性值
+	 */
+	void setValue(String name,Object object);
+
+	/**
+	 * 取得原始对象
+	 * @return 原始对象
+	 */
+	Object getSource();
+
+	/**
+	 * 取得原始对象
+	 * @return 原始对象
+	 */
+	Object getResult();
+}

+ 34 - 0
fm-translate/src/main/java/com/persagy/fm/translate/model/ITransable.java

@@ -0,0 +1,34 @@
+package com.persagy.fm.translate.model;
+
+/**
+ * 翻译接口
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+public interface ITransable {
+
+	/**
+	 * 取得翻译对象
+	 * @return 需要翻译对象
+	 */
+	Object getTranslator();
+	
+	/**
+	 * 设置翻译数据
+	 * @param object 翻译后数据
+	 */
+	void setTranslator(Object object);
+
+	/**
+	 * 取得的数据是否是map包装的数据,如果是map是数据实体返回false,如果包含多个需要翻译的对象则返回true;
+	 * @return 布尔值
+	 */
+	boolean isMapPackage();
+
+	/**
+	 * 判断当前数据对象是否需要翻译
+	 * @return 布尔值
+	 */
+	boolean isNeedTranslate();
+
+}

+ 65 - 0
fm-translate/src/main/java/com/persagy/fm/translate/model/Trans.java

@@ -0,0 +1,65 @@
+package com.persagy.fm.translate.model;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.lang.annotation.*;
+
+/**
+ * 翻译描述信息注解
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})
+@Inherited
+@Documented
+public @interface Trans {
+
+
+    /**
+     * 实体名称或表名或服务名,TransMeta中的source
+     * @return 实体标识或表名或服务名
+     */
+    String source();
+
+
+    /**
+     * 翻译类型,涉及服务处理
+     * @return 翻译类型
+     */
+    String type() default TransType.DB;
+
+    /**
+     * 参数字段(原始对象的使用的外键)
+     */
+    String parameter();
+
+
+    /**
+     * 查询结果字段名
+     * @return 翻译结果标识
+     */
+    String result() default TransMeta.DEFAULT_RESULTFIELD;
+    /**
+     * 目标字段(原始对象的对应的翻译结果字段)
+     * @return 翻译的目标字段
+     */
+    String target() default StringUtils.EMPTY;
+    /**
+     * 查询条件字段名
+     */
+    String condition() default TransMeta.DEFAULT_CONDITIONFIELD;
+
+
+    /**
+     * 多条结果时映射的字段,不是必须使用,批量查询结果映射键值字段
+     * @return 批量查询结果映射键值字段
+     */
+    String mapKey() default StringUtils.EMPTY;
+
+    /**
+     * 是否为批量翻译,默认根据类型决定
+     * @return 是否为批量查询
+     */
+    boolean batch() default false;
+}

+ 392 - 0
fm-translate/src/main/java/com/persagy/fm/translate/model/TransMeta.java

@@ -0,0 +1,392 @@
+package com.persagy.fm.translate.model;
+
+import cn.hutool.json.JSONUtil;
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 翻译描述信息
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+@Slf4j
+public class TransMeta {
+	
+
+	/**
+	 * 默认参数字段
+	 */
+	public final static String DEFAULT_CONDITIONFIELD = "id";
+
+	/**
+	 * 默认结果字段
+	 */
+	public final static String DEFAULT_RESULTFIELD = "name";
+	
+	/**
+	 * 配置属性-翻译类型
+	 */
+	public final static String OP_TYPE = "type";
+	
+	/**
+	 * 配置属性-翻译参数
+	 */
+	public final static String OP_PARAMETER = "parameter";
+	
+	/**
+	 * 配置属性-翻译数据来源
+	 */
+	public final static String OP_SOURCE = "source";
+	
+	/**
+	 * 配置属性-翻译数据来源
+	 */
+	public final static String OP_RESULT = "result";
+	
+	/**
+	 * 配置属性-翻译条件
+	 */
+	public final static String OP_CONDITION = "con";
+
+	/**
+	 * 配置属性-批量翻译的结果映射键值一般为参数值,这里主要为数据库翻译使用
+	 */
+	public final static String OP_MAPKEY = "batchKey";
+
+	/**
+	 * 配置属性-是否为批量翻译
+	 */
+	public final static String OP_BATCH = "batch";
+
+	/**
+	 * 配置属性-目标属性
+	 */
+	public final static String OP_TARGET = "target";
+
+	/** 匹配符-前缀 */
+	private static final char BRACKET_BEGIN = '(';
+	/** 匹配符-后缀 */
+	private static final char BRACKET_END = ')';
+	/** 匹配符-分隔 */
+	private static final char COMMA = ',';
+
+	/**
+	 * 翻译类型
+	 */
+	private String type = TransType.DB;
+	
+	/**
+	 * 参数字段(原始对象的使用的外键)
+	 */
+	private String parameter = null;
+	
+	/**
+	 * 数据来源(服务名,实体名,表名,公式)
+	 */
+	private String source = null;
+	
+	/**
+	 * 查询结果字段名
+	 */
+	private String result = DEFAULT_RESULTFIELD;
+	
+	/**
+	 * 查询条件字段名
+	 */
+	private String condition = DEFAULT_CONDITIONFIELD;
+
+	/**
+	 * 多条结果时映射的字段,不是必须使用
+	 */
+	private String mapKey = condition;
+	
+	/**
+	 * 目标字段(原始对象的对应的翻译结果字段)
+	 */
+	private String target = null;
+
+	/**
+	 * 是否为批量翻译,默认根据类型决定
+	 */
+	private boolean isBatch = false;
+
+	/**
+	 * 目标字段
+	 */
+	private String[] targetFields = null;
+
+	/**
+	 * 结果字段
+	 */
+	private String[] resultFields = null;
+
+	/**
+	 * 解析json串,生成翻译元数据对象
+	 * @param transMetas json串
+	 * @return 翻译元数据对象
+	 */
+	public static List<TransMeta> parseMetas(String transMetas){
+		if(StringUtils.isEmpty(transMetas)) {
+			return null;
+		}
+		return JSONUtil.parseArray(transMetas).toList(TransMeta.class);
+	}
+	/**
+	 * 解析json串,生成翻译元数据对象
+	 * @param transors json串
+	 * @return 翻译元数据对象
+	 */
+	public static Map<String,List<TransMeta>> parseMetasMap(String transors){
+		if(StringUtils.isEmpty(transors)) {
+			return null;
+		}
+
+//		try {
+//			JsonNode node = Json2ObjectMapper.getInstance().readTree(transors);
+//			Iterator<Map.Entry<String, JsonNode>> it = node.fields();
+//			Map<String,List<TransMeta>> metas = new HashMap<>(16);
+//			while(it.hasNext()){
+//				Map.Entry<String, JsonNode> en = it.next();
+//				metas.put(en.getKey(), parseJsonMetas(en.getValue()));
+//			}
+//			return metas;
+//		} catch (JsonProcessingException e) {
+//			log.error("翻译配置出错",e);
+//		} catch (IOException e) {
+//			log.error("翻译配置出错",e);
+//		}
+		return null;
+	}
+	/**
+	 * 解析json串,生成翻译元数据对象
+	 * @param node
+	 * @return 翻译元数据对象
+	 */
+	public static List<TransMeta> parseJsonMetas(JsonNode node){
+		if(!node.isArray()) {
+			return null;
+		}
+		int len = node.size();
+		List<TransMeta> metas = new ArrayList<TransMeta>();
+		for(int i = 0; i < len; i++){
+			TransMeta meta = parseJsonMeta(node.get(i));
+			if(meta != null){
+				metas.add(meta);
+			}
+		}
+		return metas;
+	}
+	/**
+	 * 解析json串,生成翻译元数据对象
+	 * @param transMeta json串
+	 * @return 翻译元数据对象
+	 */
+	public static TransMeta parseMeta(String transMeta){
+		if(StringUtils.isEmpty(transMeta)) {
+			return null;
+		}
+		return JSONUtil.toBean(transMeta, TransMeta.class);
+	}
+
+	/**
+	 * 解析json对象,生成翻译元数据对象
+	 * @param node    json节点
+	 * @return 翻译元数据对象
+	 */
+	public static TransMeta parseJsonMeta(JsonNode node){
+		if(node == null) {
+			return null;
+		}
+		TransMeta meta = new TransMeta();
+		String type = null;
+		if(node.has(OP_TYPE)){
+			JsonNode typeNode = node.get(OP_TYPE);
+			if(!typeNode.isNull()){
+				type = typeNode.asText();
+			}
+		}
+		if(type != null) {
+			meta.setType(type);
+		}
+		
+		if(node.has(OP_PARAMETER)){
+			JsonNode paramNode = node.get(OP_PARAMETER);
+			if(!paramNode.isNull()) {
+				meta.setParameter(paramNode.asText());
+			}
+		}
+		if(node.has(OP_CONDITION)){
+			JsonNode conNode = node.get(OP_CONDITION);
+			if(!conNode.isNull()) {
+				meta.setCondition(conNode.asText());
+			}
+		}
+		if(node.has(OP_MAPKEY)){
+			JsonNode conNode = node.get(OP_MAPKEY);
+			if(!conNode.isNull()) {
+				meta.setMapKey(conNode.asText());
+			}
+		}
+		if(node.has(OP_BATCH)){
+			JsonNode conNode = node.get(OP_BATCH);
+			if(!conNode.isNull()) {
+				meta.setBatch(Boolean.parseBoolean(conNode.asText()));
+			}
+		}
+		if(node.has(OP_RESULT)){
+			JsonNode resultNode = node.get(OP_RESULT);
+			if(!resultNode.isNull()) {
+				meta.setResult(resultNode.asText());
+			}
+		}
+		if(node.has(OP_SOURCE)){
+			JsonNode sourceNode = node.get(OP_SOURCE);
+			if(!sourceNode.isNull()) {
+				meta.setSource(sourceNode.asText());
+			}
+		}
+		if(node.has(OP_TARGET)){
+			JsonNode targetNode = node.get(OP_TARGET);
+			if(!targetNode.isNull()) {
+				meta.setTarget(targetNode.asText());
+			}
+		}
+		return meta;
+
+	}
+
+	
+	public String getType() {
+		return type;
+	}
+
+
+	public void setType(String type) {
+		this.type = type;
+	}
+
+
+	public String getParameter() {
+		return parameter;
+	}
+
+
+	public void setParameter(String parameter) {
+		this.parameter = parameter;
+	}
+
+
+	public String getSource() {
+		return source;
+	}
+
+
+	public void setSource(String source) {
+		this.source = source;
+	}
+
+
+	public String getResult() {
+		return result;
+	}
+
+	public void setResult(String result) {
+		this.result = result;
+	}
+
+	public String getTarget() {
+		if (StringUtils.isEmpty(target)) {
+			return parameter+"_showname";
+		}
+		return target;
+	}
+
+	public void setTarget(String target) {
+		this.target = target;
+	}
+
+	public String getCondition() {
+		return condition;
+	}
+
+
+	public void setCondition(String condition) {
+		this.condition = condition;
+	}
+
+	public String getMapKey() {
+		if (StringUtils.isEmpty(mapKey)) {
+			return getCondition();
+		}
+		return mapKey;
+	}
+
+	public void setMapKey(String mapKey) {
+		this.mapKey = mapKey;
+	}
+
+	public boolean isBatch() {
+		if(type == TransType.DB) {
+			return true;
+		}
+		return isBatch;
+	}
+
+	public void setBatch(boolean batch) {
+		isBatch = batch;
+	}
+
+	/**
+	 * 取得目标字段
+	 * @return 取得目标字段
+	 */
+	public String[] getTargetFields(){
+		if(targetFields == null){
+			targetFields = getTarget().split(",");
+		}
+		return targetFields;
+	}
+
+	/**
+	 * 取得结果字段
+	 * @return 结果字段
+	 */
+	public String[] getResultFields(){
+		if(resultFields == null){
+			List<String> rs = new ArrayList<>();
+			char[] cs = result.toCharArray();
+			int bracketCount = 0;
+			StringBuilder section = new StringBuilder();
+			for(int i = 0; i < cs.length; i++){
+				char c = cs[i];
+				if(c == BRACKET_BEGIN){
+					bracketCount++;
+				}else if(c==COMMA && bracketCount == 0){
+					if(section.length() > 0){
+						rs.add(section.toString().trim());
+						section = new StringBuilder();
+					}
+					continue;
+				}else if(c == BRACKET_END){
+					bracketCount--;
+				}else if(c=='a' && i >0 && i+3 < cs.length && cs[i-1] == ' ' && cs[i+1] == 's' && cs[i+2] == ' '){
+					section = new StringBuilder();
+					i+=2;
+					continue;
+				}
+				section.append(c);
+			}
+			if(section.length()>0) {
+				rs.add(section.toString().trim());
+			}
+			resultFields = rs.toArray(new String[rs.size()]);
+		}
+		return resultFields;
+	}
+
+}

+ 20 - 0
fm-translate/src/main/java/com/persagy/fm/translate/model/TransType.java

@@ -0,0 +1,20 @@
+package com.persagy.fm.translate.model;
+
+/**
+ * 翻译转换类型
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+public class TransType {
+
+    /**
+     * 数据方式
+     */
+    public static final String DB = "DBTransHandler";
+
+    /**
+     * 服务调用方式
+     */
+    public static final String SERVICE = "ServiceTransHandler";
+
+}

+ 19 - 0
fm-translate/src/main/java/com/persagy/fm/translate/model/Transes.java

@@ -0,0 +1,19 @@
+package com.persagy.fm.translate.model;
+
+import java.lang.annotation.*;
+
+/**
+ * 多个翻译描述集合
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Inherited
+public @interface Transes{
+    /**
+     * 包含的多个翻译结果
+     * @return 翻译描述数组
+     */
+    Trans[] value() default {};
+}

+ 19 - 0
fm-translate/src/main/java/com/persagy/fm/translate/service/ITranslateBatchHandle.java

@@ -0,0 +1,19 @@
+package com.persagy.fm.translate.service;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 翻译接口
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+public interface ITranslateBatchHandle extends ITranslateHandle{
+
+	/**
+	 * 翻译Map数据
+	 * @param parameter 需要翻译的对象
+	 * @return 结果数据
+	 */
+	Map<String,Object> batchTrans(List<String> parameter);
+}

+ 31 - 0
fm-translate/src/main/java/com/persagy/fm/translate/service/ITranslateHandle.java

@@ -0,0 +1,31 @@
+package com.persagy.fm.translate.service;
+
+import com.persagy.fm.translate.model.ITransWrapper;
+import com.persagy.fm.translate.model.TransMeta;
+
+/**
+ * 翻译接口
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+public interface ITranslateHandle {
+
+	/**
+	 * 翻译Map数据
+	 * @param wrapper 需要翻译的对象
+	 * @return 结果数据
+	 */
+	ITransWrapper trans(ITransWrapper wrapper);
+
+	/**
+	 * 取得翻译元数据
+	 * @return 翻译元数据
+	 */
+	TransMeta getMeta();
+
+	/**
+	 * 设置翻译元数据
+	 * @param meta 翻译元数据
+	 */
+	void setMeta(TransMeta meta);
+}

+ 21 - 0
fm-translate/src/main/java/com/persagy/fm/translate/service/ITranslateService.java

@@ -0,0 +1,21 @@
+package com.persagy.fm.translate.service;
+
+import com.persagy.fm.translate.model.TransMeta;
+
+import java.util.List;
+
+
+/**
+ * 数据翻译接口
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+public interface ITranslateService {
+	/**
+	 * 翻译数据
+	 * @param origion 原始数据
+	 * @param metas 翻译元数据
+	 * @return 翻译后数据
+	 */
+	Object translate(Object origion, List<TransMeta> metas);
+}

+ 29 - 0
fm-translate/src/main/java/com/persagy/fm/translate/service/ITranslator.java

@@ -0,0 +1,29 @@
+package com.persagy.fm.translate.service;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 翻译服务接口
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+public interface ITranslator {
+
+	/**
+	 * 基于服务的翻译接口
+	 * 
+	 * @param parameter 请求参数
+	 * @return 翻译结果
+	 */
+	String trans(String parameter);
+
+	/**
+	 * 基于服务的批量翻译接口
+	 * 
+	 * @param parameter 请求参数
+	 * @return 翻译结果
+	 */
+	Map<String, Object> transBatch(List<String> parameter);
+
+}

+ 31 - 0
fm-translate/src/main/java/com/persagy/fm/translate/web/MappingJacksonHttpMessageConverter.java

@@ -0,0 +1,31 @@
+package com.persagy.fm.translate.web;
+
+import com.persagy.fm.translate.helper.TranslatorHelper;
+import com.persagy.fm.translate.model.ITransable;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.converter.HttpMessageNotWritableException;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+
+/**
+ * 支持翻译扩展原有的JsonConvert
+ * @author Charlie Yu
+ * @Date 2021-03-03
+ */
+public class MappingJacksonHttpMessageConverter extends MappingJackson2HttpMessageConverter {
+
+	@Override
+	public void writeInternal(Object object, Type type, HttpOutputMessage outputMessage)
+			throws IOException, HttpMessageNotWritableException {
+		if(object instanceof ITransable){
+			object = TranslatorHelper.translate((ITransable)object);
+		}else {
+			object = TranslatorHelper.translate(object);
+		}
+		super.writeInternal(object,type, outputMessage);
+	}
+
+}

+ 16 - 0
fm-translate/src/main/resources/mapper/adder/translate/ITranslateDao.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC
+	"-//mybatis.org//DTD Mapper 3.0//EN"
+	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="cn.crcc.ccit.chassis.translate.dao.ITranslateDao">
+	<select id="findTransBySql" resultType="map">
+		${sql}
+	</select>
+	<select id="findBatchTransBySql" resultType="map">
+		${sql}
+		<foreach collection="parameter" index="index" item="item" open="(" separator="," close=")">
+			${item}
+		</foreach>
+	</select>
+</mapper>