Explorar o código

项目初始化

lixing %!s(int64=4) %!d(string=hai) anos
pai
achega
cc5a5730ca
Modificáronse 45 ficheiros con 4600 adicións e 165 borrados
  1. 6 0
      fm-common/pom.xml
  2. 108 0
      fm-common/src/main/java/com/persagy/fm/common/context/AppContext.java
  3. 38 0
      fm-common/src/main/java/com/persagy/fm/common/context/DefaultAppContext.java
  4. 27 0
      fm-common/src/main/java/com/persagy/fm/common/context/DefaultAppContextFactory.java
  5. 21 0
      fm-common/src/main/java/com/persagy/fm/common/context/IAppContextFactory.java
  6. 995 0
      fm-common/src/main/java/com/persagy/fm/common/helper/RedisHelper.java
  7. 228 0
      fm-common/src/main/java/com/persagy/fm/common/model/data/DataType.java
  8. 0 2
      fm-common/src/main/java/com/persagy/fm/common/model/entity/AuditableEntity.java
  9. 106 0
      fm-common/src/main/java/com/persagy/fm/common/model/query/ChainLogicExpression.java
  10. 238 0
      fm-common/src/main/java/com/persagy/fm/common/model/query/Condition.java
  11. 29 0
      fm-common/src/main/java/com/persagy/fm/common/model/query/ExpressionType.java
  12. 19 0
      fm-common/src/main/java/com/persagy/fm/common/model/query/IExpression.java
  13. 109 0
      fm-common/src/main/java/com/persagy/fm/common/model/query/LogicExpression.java
  14. 161 0
      fm-common/src/main/java/com/persagy/fm/common/model/query/Operator.java
  15. 64 0
      fm-common/src/main/java/com/persagy/fm/common/model/query/SqlExpression.java
  16. 255 0
      fm-common/src/main/java/com/persagy/fm/common/model/query/ValueExpression.java
  17. 43 35
      fm-common/src/main/java/com/persagy/fm/common/utils/TypeConvertUtil.java
  18. 160 125
      fm-parent/pom.xml
  19. 1 3
      fm-server/src/main/java/com/persagy/fm/server/ServerApplication.java
  20. 35 0
      fm-translate/pom.xml
  21. 112 0
      fm-translate/src/main/java/com/persagy/fm/translate/aop/TranslateAopAspect.java
  22. 33 0
      fm-translate/src/main/java/com/persagy/fm/translate/dao/ITranslateDao.java
  23. 59 0
      fm-translate/src/main/java/com/persagy/fm/translate/extension/BeanTransWrapper.java
  24. 91 0
      fm-translate/src/main/java/com/persagy/fm/translate/extension/BeanTransWrapperBuilder.java
  25. 32 0
      fm-translate/src/main/java/com/persagy/fm/translate/extension/ITransWrapperBuilder.java
  26. 71 0
      fm-translate/src/main/java/com/persagy/fm/translate/extension/MapPairResultHandler.java
  27. 65 0
      fm-translate/src/main/java/com/persagy/fm/translate/extension/MapResultHandler.java
  28. 45 0
      fm-translate/src/main/java/com/persagy/fm/translate/extension/MapTransWrapper.java
  29. 34 0
      fm-translate/src/main/java/com/persagy/fm/translate/helper/EmojiCharsTranslator.java
  30. 213 0
      fm-translate/src/main/java/com/persagy/fm/translate/helper/TranslatorHelper.java
  31. 145 0
      fm-translate/src/main/java/com/persagy/fm/translate/impl/DBTranslateHandleImpl.java
  32. 94 0
      fm-translate/src/main/java/com/persagy/fm/translate/impl/ServiceTranslateHandleImpl.java
  33. 250 0
      fm-translate/src/main/java/com/persagy/fm/translate/impl/TranslateServiceImpl.java
  34. 36 0
      fm-translate/src/main/java/com/persagy/fm/translate/model/ITransWrapper.java
  35. 34 0
      fm-translate/src/main/java/com/persagy/fm/translate/model/ITransable.java
  36. 65 0
      fm-translate/src/main/java/com/persagy/fm/translate/model/Trans.java
  37. 392 0
      fm-translate/src/main/java/com/persagy/fm/translate/model/TransMeta.java
  38. 20 0
      fm-translate/src/main/java/com/persagy/fm/translate/model/TransType.java
  39. 19 0
      fm-translate/src/main/java/com/persagy/fm/translate/model/Transes.java
  40. 19 0
      fm-translate/src/main/java/com/persagy/fm/translate/service/ITranslateBatchHandle.java
  41. 31 0
      fm-translate/src/main/java/com/persagy/fm/translate/service/ITranslateHandle.java
  42. 21 0
      fm-translate/src/main/java/com/persagy/fm/translate/service/ITranslateService.java
  43. 29 0
      fm-translate/src/main/java/com/persagy/fm/translate/service/ITranslator.java
  44. 31 0
      fm-translate/src/main/java/com/persagy/fm/translate/web/MappingJacksonHttpMessageConverter.java
  45. 16 0
      fm-translate/src/main/resources/mapper/adder/translate/ITranslateDao.xml

+ 6 - 0
fm-common/pom.xml

@@ -72,5 +72,11 @@
             <version>${mapstruct.version}</version>
         </dependency>
         <!-- mybatisPlus相关的依赖 end -->
+
+        <!-- redis -->
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>integrated-redis-spring-boot-starter</artifactId>
+        </dependency>
     </dependencies>
 </project>

+ 108 - 0
fm-common/src/main/java/com/persagy/fm/common/context/AppContext.java

@@ -0,0 +1,108 @@
+package com.persagy.fm.common.context;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serializable;
+
+
+/**
+ * 应用访问上下文类,主要负责上下文的属性和参数的操作
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+@Slf4j
+public abstract class AppContext implements Serializable {
+
+    /** 序列id */
+    private static final long serialVersionUID = 697592850956445675L;
+
+    /** 工厂对象 */
+    private static IAppContextFactory factory = null;
+
+    /**
+     * 取得当前线程中的上下文对象
+     * @return 上下文对象
+     */
+    public static AppContext getContext() {
+        return getFactory().getContext();
+    }
+
+    /**
+     * 反注册当前上下文对象
+     */
+    public static void unload() {
+        getFactory().unloadContext();
+    }
+
+    /**
+     * 取得上下文工厂类
+     * @return 上下文工厂类
+     */
+    public static IAppContextFactory getFactory() {
+        if (factory == null) {
+            factory = new DefaultAppContextFactory();
+        }
+        return factory;
+    }
+
+    /**
+     * 取得操作用户主键
+     * @return 用户主键
+     */
+    public abstract String getUserId();
+
+    /**
+     * 设置操作用户主键
+     * @param userId 用户主键
+     */
+    public abstract void setUserId(String userId);
+
+    /**
+     * 取得操作用户名
+     * @return 用户名
+     */
+    public abstract String getUserName();
+
+    /**
+     * 设置操作用户名
+     * @param userName 用户名
+     */
+    public abstract void setUserName(String userName);
+
+    /**
+     * 取得租户
+     * @return 租户
+     */
+    public abstract String getTenant();
+
+    /**
+     * 设置租户
+     * @param tenant 租户
+     */
+    public abstract void setTenant(String tenant);
+
+    /**
+     * 取得产品线
+     * @return 产品线
+     */
+    public abstract String getProductLine();
+
+    /**
+     * 设置产品线
+     * @param productLine 产品线
+     */
+    public abstract void setProductLine(String productLine);
+
+    /**
+     * 动态数据源支持
+     * @return java.lang.String
+     **/
+    public abstract String getDataSourceName();
+
+    /**
+     * 设置动态数据源
+     * @param dataSourceName 动态数据源
+     * @return void
+     **/
+    public abstract void setDataSourceName(String dataSourceName);
+}

+ 38 - 0
fm-common/src/main/java/com/persagy/fm/common/context/DefaultAppContext.java

@@ -0,0 +1,38 @@
+package com.persagy.fm.common.context;
+
+import lombok.Data;
+
+/**
+ * 默认应用上下文
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+@Data
+public class DefaultAppContext extends AppContext {
+
+    /**
+     * 用户主键
+     */
+    private String userId;
+
+    /**
+     * 用户名
+     */
+    private String userName;
+
+    /**
+     * 租户
+     */
+    private String tenant;
+
+    /**
+     * 所属产品线
+     */
+    private String productLine;
+
+    /**
+     * 数据源名称
+     */
+    private String dataSourceName;
+
+}

+ 27 - 0
fm-common/src/main/java/com/persagy/fm/common/context/DefaultAppContextFactory.java

@@ -0,0 +1,27 @@
+package com.persagy.fm.common.context;
+
+/**
+ * 默认的appContext工厂
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+public class DefaultAppContextFactory implements IAppContextFactory {
+
+    /** 线程级的上下文 */
+    private ThreadLocal<AppContext> context = new ThreadLocal<>();
+
+    @Override
+    public AppContext getContext() {
+        AppContext c = context.get();
+        if(c == null){
+            c = new DefaultAppContext();
+            context.set(c);
+        }
+        return c;
+    }
+
+    @Override
+    public void unloadContext() {
+        context.remove();
+    }
+}

+ 21 - 0
fm-common/src/main/java/com/persagy/fm/common/context/IAppContextFactory.java

@@ -0,0 +1,21 @@
+package com.persagy.fm.common.context;
+
+/**
+ * app上下问工厂类
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+public interface IAppContextFactory {
+
+    /**
+     * 创建应用上下文对象
+     * @return 应用上下文对象
+     */
+    AppContext getContext();
+
+    /**
+     * 卸载应用上下文对象
+     * @return 应用上下文对象
+     */
+    void unloadContext();
+}

+ 995 - 0
fm-common/src/main/java/com/persagy/fm/common/helper/RedisHelper.java

@@ -0,0 +1,995 @@
+package com.persagy.fm.common.helper;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.dao.DataAccessException;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.SessionCallback;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.data.redis.serializer.GenericToStringSerializer;
+import org.springframework.data.redis.serializer.RedisSerializer;
+
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * Redis工具类
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+@Slf4j
+public final class RedisHelper {
+
+    /** jedis模板 */
+    private static RedisTemplate redisTemplate = null;
+
+    /** 助手类,私有构造方法 */
+    private RedisHelper() {
+    }
+
+    /**
+     * 获取缓存模板
+     * @return 获取缓存模板
+     */
+    public static RedisTemplate getTemplate() {
+        if (redisTemplate == null) {
+            redisTemplate = SpringHelper.getBean("redisTemplate", RedisTemplate.class);
+            if (redisTemplate == null) {
+                Map<String, RedisTemplate> beans = SpringHelper.getBeansOfType(RedisTemplate.class);
+                if (!beans.isEmpty()) {
+                    redisTemplate = beans.values().iterator().next();
+                }
+            }
+        }
+        if (redisTemplate == null) {
+            log.error("redis config is error! please init RedisTemplate bean!");
+            throw new RuntimeException("redis config is error! please init RedisTemplate bean!");
+        }
+        return redisTemplate;
+    }
+
+    /**
+     * 按键值模式删除指定键值
+     * @param keyMode 键值匹配模式
+     * @return 清楚记录数组
+     */
+    public static long clearKeys(String keyMode) {
+        String clearScript = "local keys = redis.call('keys', ARGV[1]) for i=1,#keys,5000 do redis.call('del', unpack(keys, i, math.min(i+4999, #keys))) end return #keys";
+        return (long) getTemplate().execute(new DefaultRedisScript<>(clearScript, Long.class), getKeySerializer(), getValueSerializer(), new ArrayList(), keyMode);
+    }
+
+    /**
+     * 按键值模式删除指定键值
+     * @param keys 键值列表
+     * @return 清楚记录数
+     */
+    public static long clearKeys(List<String> keys) {
+        String clearScript = "return redis.call('del', unpack(KEY))";
+        return (long) getTemplate().execute(new DefaultRedisScript<>(clearScript, Long.class), getKeySerializer(), getValueSerializer(), keys, new Object[0]);
+    }
+
+    /**
+     * 指定键值累加操作
+     * @param key 键值
+     * @return 累加结果
+     */
+    public static Long incr(final String key) {
+        return (Long) getTemplate().execute((RedisCallback<Long>) connection -> connection.incr(keySerial(key)));
+    }
+
+    /**
+     * 指定键值累减操作
+     * @param key 键值
+     * @return 累减结果
+     */
+    public static Long decr(final String key) {
+        return (Long) getTemplate().execute((RedisCallback<Long>) connection -> connection.decr(keySerial(key)));
+    }
+
+    /**
+     * 按键值获取存储数据
+     * @param key 键值
+     * @param <T> 存储值泛型
+     * @return 存储值
+     */
+    public static <T> T cmdGet(final String key, Class<T> cl) {
+        return strDeserial(cl, getBytes(keySerial(key)));
+    }
+
+    /**
+     * 按键值获取存储数据
+     * @param key 键值
+     * @param <T> 存储值泛型
+     * @return 存储值
+     */
+    public static <T> T get(String key) {
+        return objDeserial(getBytes(keySerial(key)));
+    }
+
+    /**
+     * 按键值获取存储数据
+     * @param key 键值
+     * @return 存储值
+     */
+    public static byte[] getBytes(final byte[] key) {
+        return (byte[]) getTemplate().execute((RedisCallback<byte[]>) connection -> connection.get(key));
+    }
+
+    /**
+     * 按键值存储新的值并获取旧存储数据
+     * @param key   键值
+     * @param value 新存储值
+     * @param <T>   存储值泛型
+     * @return 旧存储值
+     */
+    public static <T> T cmdGetAndSet(String key, T value) {
+        Class<T> cl = (Class<T>) value.getClass();
+        return strDeserial(cl, getAndSetBytes(keySerial(key), strSerial(value)));
+    }
+
+    /**
+     * 按键值存储新的值并获取旧存储数据
+     * @param key   键值
+     * @param value 新存储值
+     * @param <T>   存储值泛型
+     * @return 旧存储值
+     */
+    public static <T> T getAndSet(String key, T value) {
+        return objDeserial(getAndSetBytes(keySerial(key), objSerial(value)));
+    }
+
+    /**
+     * 按键值存储新的值并获取旧存储数据
+     * @param key   键值
+     * @param value 新存储值
+     * @return 旧存储值
+     */
+    public static byte[] getAndSetBytes(final byte[] key, final byte[] value) {
+        return (byte[]) getTemplate().execute((RedisCallback<byte[]>) connection -> connection.getSet(key, value));
+    }
+
+    /**
+     * 按键值存储数据
+     * @param key   键值
+     * @param value 存储值
+     * @param <T>   值泛型
+     */
+    public static <T extends Serializable> void cmdSet(String key, T value) {
+        setBytes(keySerial(key), strSerial(value));
+    }
+
+    /**
+     * 按键值存储数据
+     * @param key   键值
+     * @param value 存储值
+     * @param <T>   值泛型
+     */
+    public static <T extends Serializable> void set(String key, T value) {
+        setBytes(keySerial(key), objSerial(value));
+    }
+
+    public static <T extends Serializable> Boolean setNx(String key, T value) {
+        return setNxBytes(keySerial(key), objSerial(value));
+    }
+
+    public static Boolean setNxBytes(final byte[] key, final byte[] value) {
+        return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> connection.setNX(key, value));
+    }
+
+    public static <T extends Serializable> Boolean setNxEx(String key, T value, final int timeout) {
+        return setExNxBytes(keySerial(key), objSerial(value), timeout);
+    }
+
+    public static Boolean setExNxBytes(final byte[] key, final byte[] value, final int timeout) {
+        return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> {
+            Boolean success = connection.setNX(key, value);
+            if (success != null && success) {
+                connection.expire(key, timeout);
+            }
+            return success;
+        });
+    }
+
+    /**
+     * 按键值存储数据
+     * @param key   键值
+     * @param value 存储值
+     */
+    public static void setBytes(final byte[] key, final byte[] value) {
+        getTemplate().execute((RedisCallback<Void>) connection -> {
+            connection.set(key, value);
+            return null;
+        });
+    }
+
+    /**
+     * 存储一定时间内过期的值
+     * @param key     键值
+     * @param value   存储值
+     * @param timeout 超时时长
+     * @param <T>     存储值泛型
+     */
+    public static <T extends Serializable> void cmdSetEx(final String key, final T value, final int timeout) {
+        setExBytes(keySerial(key), strSerial(value), timeout);
+    }
+
+    /**
+     * 存储一定时间内过期的值
+     * @param key     键值
+     * @param value   存储值
+     * @param timeout 超时时长
+     * @param <T>     存储值泛型
+     */
+    public static <T extends Serializable> void setEx(final String key, final T value, final int timeout) {
+        setExBytes(keySerial(key), objSerial(value), timeout);
+    }
+
+    /**
+     * 存储一定时间内过期的值
+     * @param key     键值
+     * @param value   存储值
+     * @param timeout 超时时长
+     */
+    public static void setExBytes(final byte[] key, final byte[] value, final int timeout) {
+        getTemplate().execute((RedisCallback<Void>) connection -> {
+            connection.setEx(key, timeout, value);
+            return null;
+        });
+    }
+
+    /**
+     * 为指定键值增加过期时长
+     * @param key     键值
+     * @param timeout 超时时长
+     */
+    public static boolean expire(final String key, final int timeout) {
+        return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> connection.expire(keySerial(key), timeout));
+    }
+
+    /**
+     * 为指定键值增加有效期(日期时间)
+     * @param key  键值
+     * @param date 有效期
+     */
+    public static boolean expireAt(final String key, final long date) {
+        return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> connection.expireAt(keySerial(key), date));
+    }
+
+    /**
+     * 判断键值还有多少秒超时
+     * @param key 键值
+     */
+    public static long ttl(final String key) {
+        return (Long) getTemplate().execute((RedisCallback<Long>) connection -> connection.ttl(keySerial(key)));
+    }
+
+    /**
+     * 判断指定键值是否存在
+     * @param key 键值
+     * @return 布尔值
+     */
+    public static boolean exists(final String key) {
+        return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> connection.exists(keySerial(key)));
+    }
+
+    /**
+     * 删除指定键值数据
+     * @param keys 键值数组
+     * @return 删除的个数
+     */
+    public static long del(final String... keys) {
+        if (keys == null || keys.length == 0) {
+            return 0;
+        }
+        return (Long) getTemplate().execute((RedisCallback<Long>) connection -> connection.del(fields(keys)));
+    }
+
+    /**
+     * 获取指定的hash存储的值
+     * @param key   键值
+     * @param field hash键值
+     * @param <T>   存储值泛型
+     * @return 存储值
+     */
+    public static <T> T cmdHGet(String key, Class<T> cl, String field) {
+        return strDeserial(cl, hGetBytes(keySerial(key), keySerial(field)));
+    }
+
+    /**
+     * 获取指定的hash存储的值
+     * @param key   键值
+     * @param field hash键值
+     * @param <T>   存储值泛型
+     * @return 存储值
+     */
+    public static <T> T hGet(String key, String field) {
+        return objDeserial(hGetBytes(keySerial(key), keySerial(field)));
+    }
+
+    /**
+     * 获取指定的hash存储的值
+     * @param key   键值
+     * @param field hash键值
+     * @return 存储值
+     */
+    public static byte[] hGetBytes(final byte[] key, final byte[] field) {
+        return (byte[]) getTemplate().execute((RedisCallback<byte[]>) connection -> connection.hGet(key, field));
+    }
+
+
+    /**
+     * 获取多个hash存储的值,由于值类型不同,不做反序列化
+     * @param key    键值
+     * @param fields 多个hash键值
+     * @return 存储值
+     */
+    public static List<byte[]> hMGet(final String key, final String... fields) {
+        return (List<byte[]>) getTemplate().execute((RedisCallback<List<byte[]>>) connection -> connection.hMGet(keySerial(key), fields(fields)));
+    }
+
+    /**
+     * 获取多个hash存储的值,值类型相同时使用
+     * @param key    键值
+     * @param fields 多个hash键值
+     * @param <T>    存储值泛型
+     * @return 存储值
+     */
+    public static <T> List<T> hMGetSameType(Class<T> cl, String key, String... fields) {
+        return strListDeserial(cl, hMGet(key, fields));
+    }
+
+    /**
+     * 获取多个hash存储的值,值类型相同时使用
+     * @param key    键值
+     * @param fields 多个hash键值
+     * @param <T>    存储值泛型
+     * @return 存储值
+     */
+    public static <T> List<T> hMGetSameType(String key, String... fields) {
+        return objListDeserial(hMGet(key, fields));
+    }
+
+    /**
+     * 判断指定的hash存储是否存在
+     * @param key   键值
+     * @param field hash键值
+     * @return 布尔值
+     */
+    public static Boolean hExists(final String key, final String field) {
+        return (Boolean) getTemplate().execute((RedisCallback<Boolean>) connection -> connection.hExists(keySerial(key), keySerial(field)));
+    }
+
+    /**
+     * 获取整个hash存储,由于值类型不同,不做反序列化
+     * @param key 键值
+     * @return 存储的数据映射
+     */
+    public static List<String> hKeys(final String key) {
+        return (List<String>) getTemplate().execute((RedisCallback<List<String>>) connection -> strListDeserial(String.class, connection.hKeys(keySerial(key))));
+    }
+
+    /**
+     * 获取整个hash存储,由于值类型不同,不做反序列化
+     * @param key 键值
+     * @return 存储的数据映射
+     */
+    public static Map<byte[], byte[]> hGetAllBytes(final String key) {
+        return (Map<byte[], byte[]>) getTemplate().execute((RedisCallback<Map<byte[], byte[]>>) connection -> connection.hGetAll(keySerial(key)));
+    }
+
+    /**
+     * 获取整个hash存储,由于值类型不同,不做反序列化
+     * @param key 键值
+     * @return 存储的数据映射
+     */
+    public static Map<String, byte[]> hGetAll(String key) {
+        return mapKeyDeserial(hGetAllBytes(key));
+    }
+
+    /**
+     * 获取整个hash存储,值类型相同时使用
+     *
+     * @param key 键值
+     * @param <T> hash存储值泛型
+     * @return 存储的数据映射
+     */
+    public static <T> Map<String, T> hGetAllSameType(String key, Class<T> cl) {
+        return strMapDeserial(cl, hGetAllBytes(key));
+    }
+
+    /**
+     * 获取整个hash存储,值类型相同时使用
+     * @param key 键值
+     * @param <T> hash存储值泛型
+     * @return 存储的数据映射
+     */
+    public static <T> Map<String, T> hGetAllSameType(final String key) {
+        return objMapDeserial(hGetAllBytes(key));
+    }
+
+    /**
+     * 存储单个hash值
+     * @param key   键值
+     * @param field hash存储
+     * @param value 存储值
+     * @param <T>   存储值泛型
+     */
+    public static <T extends Serializable> void cmdHSet(final String key, final String field, final T value) {
+        hSetBytes(key, field, strSerial(value));
+    }
+
+    /**
+     * 存储单个hash值
+     * @param key   键值
+     * @param field hash存储
+     * @param value 存储值
+     * @param <T>   存储值泛型
+     */
+    public static <T extends Serializable> void hSet(final String key, final String field, final T value) {
+        hSetBytes(key, field, objSerial(value));
+    }
+
+    /**
+     * 存储单个hash值
+     * @param key   键值
+     * @param field hash存储
+     * @param value 存储值
+     */
+    public static void hSetBytes(final String key, final String field, final byte[] value) {
+        getTemplate().execute((RedisCallback<Boolean>) connection -> connection.hSet(keySerial(key), keySerial(field), value));
+    }
+
+    /**
+     * hash存储整个map
+     * @param key 键值
+     * @param map 存储map
+     * @param <T> 存储值泛型
+     */
+    public static <T extends Serializable> void cmdHMSet(final String key, final Map<String, T> map) {
+        hMSetBytes(key, strMapSerial(map));
+    }
+
+    /**
+     * hash存储整个map
+     * @param key 键值
+     * @param map 存储map
+     * @param <T> 存储值泛型
+     */
+    public static <T extends Serializable> void hMSet(final String key, final Map<String, T> map) {
+        hMSetBytes(key, objMapSerial(map));
+    }
+
+    /**
+     * hash存储整个map
+     * @param key 键值
+     * @param map 存储map
+     */
+    public static void hMSetBytes(final String key, final Map<byte[], byte[]> map) {
+        getTemplate().execute((RedisCallback<Object>) connection -> {
+            connection.hMSet(keySerial(key), map);
+            return null;
+        });
+    }
+
+    /**
+     * 删除指定hash数据
+     * @param key    键值
+     * @param fields hash键值
+     * @return 删除的数量
+     */
+    public static long hDel(final String key, final String... fields) {
+        if (StringUtils.isEmpty(key) || fields == null || fields.length == 0) {
+            return 0;
+        }
+        return (Long) getTemplate().execute((RedisCallback<Long>) connection -> connection.hDel(keySerial(key), fields(fields)));
+    }
+
+    /**
+     * set获取所有成员
+     * @param key 键值
+     * @param <T> 值泛型
+     * @return 值列表
+     */
+    public static <T> List<T> sMemebers(String key, Class<T> cl) {
+        return strListDeserial(cl, sMemebersBytesList(key));
+    }
+
+    /**
+     * set获取所有成员
+     * @param key 键值
+     * @param <T> 值泛型
+     * @return 值列表
+     */
+    public static <T> List<T> sMemebers(String key) {
+        return objListDeserial(sMemebersBytesList(key));
+    }
+
+    /**
+     * set获取所有成员
+     * @param key 键值
+     * @return 值列表
+     */
+    public static Set<byte[]> sMemebersBytesList(final String key) {
+        return (Set<byte[]>) getTemplate().execute((RedisCallback<Set<byte[]>>) connection -> connection.sMembers(keySerial(key)));
+    }
+
+    /**
+     * 判断是否为set成员
+     * @param key 键值
+     * @param <T> 值泛型
+     * @return 值列表
+     */
+    public static <T> boolean cmdSIsMemeber(String key, T value) {
+        return sIsMemeber(keySerial(key), strSerial(value));
+    }
+
+    /**
+     * 判断是否为set成员
+     * @param key 键值
+     * @param <T> 值泛型
+     * @return 值列表
+     */
+    public static <T> boolean sIsMemeber(String key, T value) {
+        return sIsMemeber(keySerial(key), objSerial(value));
+    }
+
+    /**
+     * 判断是否为set成员
+     *
+     * @param key 键值
+     * @return 值列表
+     */
+    public static boolean sIsMemeber(final byte[] key, final byte[] value) {
+        return (Boolean) getTemplate().execute(new RedisCallback<Boolean>() {
+            @Override
+            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
+                return connection.sIsMember(key, value);
+            }
+        });
+    }
+
+    /**
+     * 判断是否为set成员
+     *
+     * @param key 键值
+     * @param <T> 值泛型
+     * @return 值列表
+     */
+    public static <T> boolean cmdSContainMemeber(final String key, final T... values) {
+        if (values == null || values.length == 0) {
+            return false;
+        }
+        return sContainMemeber(keySerial(key), strArraySerial(values));
+    }
+
+    /**
+     * 判断是否为set成员
+     *
+     * @param key 键值
+     * @param <T> 值泛型
+     * @return 值列表
+     */
+    public static <T> boolean sContainMemeber(final String key, final T... values) {
+        if (values == null || values.length == 0) {
+            return false;
+        }
+        return sContainMemeber(keySerial(key), objArraySerial(values));
+    }
+
+    /**
+     * 判断是否为set成员
+     *
+     * @param key 键值
+     * @return 值列表
+     */
+    public static boolean sContainMemeber(byte[] key, byte[][] values) {
+        String sContainMemeberScript = "local vs = ARGV[1] for i=1,#vs,1 do if (redis.call('SISMEMBER',KEY[1], vs[i]) == 1) then return true end end return false";
+        ArrayList<byte[]> keys = new ArrayList<>();
+        keys.add(key);
+        Object[] objValues = values;
+        return (Boolean) getTemplate().execute(new DefaultRedisScript<>(sContainMemeberScript, Boolean.class), keys, objValues);
+    }
+
+    /**
+     * set添加
+     *
+     * @param key    键值
+     * @param values 值数组
+     * @param <T>    值泛型
+     */
+    public static <T> long cmdSAdd(final String key, final T... values) {
+        return sAdd(keySerial(key), strArraySerial(values));
+    }
+
+    /**
+     * set添加
+     *
+     * @param key    键值
+     * @param values 值数组
+     * @param <T>    值泛型
+     */
+    public static <T> long sAdd(final String key, final T... values) {
+        return sAdd(keySerial(key), objArraySerial(values));
+    }
+
+    /**
+     * set添加
+     *
+     * @param key    键值
+     * @param values 值数组
+     */
+    public static long sAdd(final byte[] key, final byte[][] values) {
+        return (long) getTemplate().execute(new RedisCallback<Long>() {
+
+            @Override
+            public Long doInRedis(RedisConnection connection) throws DataAccessException {
+                return connection.sAdd(key, values);
+            }
+        });
+    }
+
+    /**
+     * set添加所有的
+     *
+     * @param keys   键值组
+     * @param values 值数组
+     * @param <T>    值泛型
+     */
+    public static <T> long cmdSAddAll(final List<String> keys, final List<T[]> values) {
+        return (long) getTemplate().execute(new RedisCallback<Long>() {
+
+            @Override
+            public Long doInRedis(RedisConnection connection) throws DataAccessException {
+                long count = 0;
+                for (int i = 0; i < keys.size(); i++) {
+                    connection.sAdd(keySerial(keys.get(i)), strArraySerial(values.get(i)));
+                }
+                return count;
+            }
+        });
+    }
+
+    /**
+     * set添加所有的
+     *
+     * @param keys
+     * @param values 值数组
+     * @param <T>    值泛型
+     */
+    public static <T> long sAddAll(final List<String> keys, final List<T[]> values) {
+        return (long) getTemplate().execute(new RedisCallback<Long>() {
+            @Override
+            public Long doInRedis(RedisConnection connection) throws DataAccessException {
+                long count = 0;
+                for (int i = 0; i < keys.size(); i++) {
+                    connection.sAdd(keySerial(keys.get(i)), objArraySerial(values.get(i)));
+                }
+                return count;
+            }
+        });
+    }
+
+    /**
+     * set删除
+     *
+     * @param key    键值
+     * @param values 值数组
+     * @param <T>    值泛型
+     */
+    public static <T> long cmdSRem(final String key, final T... values) {
+        return sRem(keySerial(key), strArraySerial(values));
+    }
+
+    /**
+     * set删除
+     *
+     * @param key    键值
+     * @param values 值数组
+     * @param <T>    值泛型
+     */
+    public static <T> long sRem(final String key, final T... values) {
+        return sRem(keySerial(key), objArraySerial(values));
+    }
+
+    /**
+     * set删除
+     *
+     * @param key    键值
+     * @param values 值数组
+     */
+    public static long sRem(final byte[] key, final byte[]... values) {
+        return (Long) getTemplate().execute(new RedisCallback<Long>() {
+            @Override
+            public Long doInRedis(RedisConnection connection) throws DataAccessException {
+                return connection.sRem(key, values);
+            }
+        });
+    }
+
+    /**
+     * 事务模式执行redis操作
+     *
+     * @param callback 回调方法
+     * @return 返回结果
+     */
+    public static <T> T execute(SessionCallback<T> callback) {
+        return (T) getTemplate().execute(callback);
+    }
+
+
+    /**
+     * 键值序列化
+     *
+     * @param key 键值
+     * @return 结果字节数组
+     */
+    public static byte[] keySerial(String key) {
+        return getKeySerializer().serialize(key);
+    }
+
+    /**
+     * 键值反序列化
+     *
+     * @param key 键值字节数组
+     * @return 键值
+     */
+    public static String keyDeserial(byte[] key) {
+        return (String) getKeySerializer().deserialize(key);
+    }
+
+    /**
+     * 多个键值序列化
+     *
+     * @param fields 键值数据
+     * @return 多个键值的字节矩阵
+     */
+    public static byte[][] fields(String... fields) {
+        RedisSerializer<String> s = getKeySerializer();
+        int len = fields.length;
+        byte[][] fs = new byte[len][];
+        for (int i = 0; i < fields.length; i++) {
+            fs[i] = s.serialize(fields[i]);
+        }
+        return fs;
+    }
+
+    /**
+     * 取得值redis序列化对象
+     *
+     * @return redis序列化对象
+     */
+    public static <T> RedisSerializer<T> getValueSerializer(Class<T> type) {
+        return new GenericToStringSerializer<>(type);
+    }
+
+    /**
+     * 取得值redis序列化对象
+     *
+     * @return redis序列化对象
+     */
+    public static <T> RedisSerializer<T> getValueSerializer() {
+        return getTemplate().getValueSerializer();
+    }
+
+    /**
+     * 取得键redis序列化对象
+     *
+     * @return redis序列化对象
+     */
+    public static <T> RedisSerializer<T> getKeySerializer() {
+        return getTemplate().getStringSerializer();
+    }
+
+    /**
+     * 存储值的序列化
+     *
+     * @param v   值对象
+     * @param <T> 值泛型
+     * @return 结果字节数组
+     */
+    public static <T> byte[] strSerial(T v) {
+        Class<T> c = (Class<T>) v.getClass();
+        return getValueSerializer(c).serialize(v);
+    }
+
+    /**
+     * 存储值的序列化
+     *
+     * @param v   值对象
+     * @param <T> 值泛型
+     * @return 结果字节数组
+     */
+    public static <T> byte[] objSerial(T v) {
+        return getValueSerializer().serialize(v);
+    }
+
+    /**
+     * 存储值的反序列化
+     *
+     * @param cl  结果对象类型
+     * @param v   值字节数组
+     * @param <T> 值泛型
+     * @return 值对象
+     */
+    public static <T> T strDeserial(Class<T> cl, byte[] v) {
+        return getValueSerializer(cl).deserialize(v);
+    }
+
+    /**
+     * 存储值的反序列化
+     *
+     * @param v   值字节数组
+     * @param <T> 值泛型
+     * @return 值对象
+     */
+    public static <T> T objDeserial(byte[] v) {
+        return (T) getValueSerializer().deserialize(v);
+    }
+
+    /**
+     * 反序列化值列表
+     *
+     * @param bs  存储值的字节数组列表
+     * @param <T> 值泛型
+     * @return 值对象列表
+     */
+    public static <T> List<T> strListDeserial(Class<T> cl, Collection<byte[]> bs) {
+        List<T> l = new ArrayList<>();
+        RedisSerializer<T> ser = getValueSerializer(cl);
+        for (byte[] b : bs) {
+            l.add(ser.deserialize(b));
+        }
+        return l;
+    }
+
+    /**
+     * 反序列化值列表
+     *
+     * @param bs  存储值的字节数组列表
+     * @param <T> 值泛型
+     * @return 值对象列表
+     */
+    public static <T> List<T> objListDeserial(Collection<byte[]> bs) {
+        List<T> l = new ArrayList<>();
+        RedisSerializer<T> ser = getValueSerializer();
+        for (byte[] b : bs) {
+            l.add(ser.deserialize(b));
+        }
+        return l;
+    }
+
+    /**
+     * 序列化map数据
+     *
+     * @param m   map对象
+     * @param <T> map中值的泛型
+     * @return 字节到字节映射
+     */
+    public static <T> Map<byte[], byte[]> strMapSerial(Map<String, T> m) {
+        Map<byte[], byte[]> bs = new HashMap<>(16);
+        RedisSerializer<T> serializer = null;
+        for (Map.Entry<String, T> e : m.entrySet()) {
+            T v = e.getValue();
+            if (v == null) {
+                continue;
+            }
+            if (serializer == null) {
+                serializer = getValueSerializer((Class<T>) v.getClass());
+            }
+            bs.put(keySerial(e.getKey()), serializer.serialize(v));
+        }
+        return bs;
+    }
+
+    /**
+     * 序列化map数据
+     *
+     * @param m   map对象
+     * @param <T> map中值的泛型
+     * @return 字节到字节映射
+     */
+    public static <T> Map<byte[], byte[]> objMapSerial(Map<String, T> m) {
+        Map<byte[], byte[]> bs = new HashMap<>(16);
+        RedisSerializer<T> serializer = getValueSerializer();
+        for (Map.Entry<String, T> e : m.entrySet()) {
+            T v = e.getValue();
+            if (v == null) {
+                continue;
+            }
+            bs.put(keySerial(e.getKey()), serializer.serialize(v));
+        }
+        return bs;
+    }
+
+    /**
+     * 反序列化字节到字节映射的键值
+     *
+     * @param bs 字节到字节映射
+     * @return 字符串到字节数据的映射
+     */
+    public static Map<String, byte[]> mapKeyDeserial(Map<byte[], byte[]> bs) {
+        Map<String, byte[]> m = new HashMap<>(16);
+        for (Map.Entry<byte[], byte[]> e : bs.entrySet()) {
+            m.put(keyDeserial(e.getKey()), e.getValue());
+        }
+        return m;
+    }
+
+    /**
+     * 反序列化字节到字节映射数据
+     *
+     * @param bs  字节到字节映射
+     * @param <T> map值泛型
+     * @return map数据对象
+     */
+    public static <T> Map<String, T> strMapDeserial(Class<T> cl, Map<byte[], byte[]> bs) {
+        Map<String, T> m = new HashMap<>(16);
+        RedisSerializer<T> ser = getValueSerializer(cl);
+        for (Map.Entry<byte[], byte[]> e : bs.entrySet()) {
+            m.put(keyDeserial(e.getKey()), ser.deserialize(e.getValue()));
+        }
+        return m;
+    }
+
+    /**
+     * 反序列化字节到字节映射数据
+     *
+     * @param bs  字节到字节映射
+     * @param <T> map值泛型
+     * @return map数据对象
+     */
+    public static <T> Map<String, T> objMapDeserial(Map<byte[], byte[]> bs) {
+        Map<String, T> m = new HashMap<>(16);
+        RedisSerializer<T> ser = getValueSerializer();
+        for (Map.Entry<byte[], byte[]> e : bs.entrySet()) {
+            m.put(keyDeserial(e.getKey()), ser.deserialize(e.getValue()));
+        }
+        return m;
+    }
+
+    /**
+     * 序列化数组
+     *
+     * @param vs  值数组
+     * @param <T> 值泛型
+     * @return 字节二维数组
+     */
+    public static <T> byte[][] strArraySerial(T... vs) {
+        byte[][] bss = new byte[vs.length][];
+        RedisSerializer<T> ser = null;
+        for (int i = 0; i < vs.length; i++) {
+            T v = vs[i];
+            if (v == null) {
+                continue;
+            }
+            if (ser == null) {
+                ser = getValueSerializer((Class<T>) v.getClass());
+            }
+            bss[i] = ser.serialize(v);
+        }
+        return bss;
+    }
+
+    /**
+     * 序列化数组
+     *
+     * @param vs  值数组
+     * @param <T> 值泛型
+     * @return 字节二维数组
+     */
+    public static <T> byte[][] objArraySerial(T... vs) {
+        byte[][] bss = new byte[vs.length][];
+        RedisSerializer<T> ser = getValueSerializer();
+        for (int i = 0; i < vs.length; i++) {
+            T v = vs[i];
+            if (v == null) {
+                continue;
+            }
+            bss[i] = ser.serialize(v);
+        }
+        return bss;
+    }
+
+}

+ 228 - 0
fm-common/src/main/java/com/persagy/fm/common/model/data/DataType.java

@@ -0,0 +1,228 @@
+package com.persagy.fm.common.model.data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 数据类型常量
+ *
+ * @author Sun
+ * @version 1.0 2014年9月24日
+ * @since 1.6
+ */
+public enum DataType {
+
+    /** 未知数据类型 */
+    UNKNOWN("未知类型"),
+
+    /** 字符串数据类型 */
+    STRING("字符串"),
+
+    /** 字节数据类型 */
+    BYTE("字节"),
+
+    /** 短整数类型 */
+    SHORT("短整数"),
+
+    /** 整数类型 */
+    INT("整数"),
+
+    /** 长整数类型 */
+    LONG("长整数"),
+
+    /** 浮点类型 */
+    FLOAT("浮点"),
+
+    /** 双精度浮点类型 */
+    DOUBLE("双精度浮点"),
+
+    /** 大数字类型 */
+    BIGDECIMAL("大数字"),
+
+    /** 布尔类型 */
+    BOOLEAN("布尔"),
+
+    /** 日期类型 */
+    DATE("日期"),
+
+    /** 时间类型 */
+    TIME("时间"),
+
+    /** 日期时间类型 */
+    DATETIME("日期时间"),
+
+    /** 二进制类型类型 */
+    BINARY("二进制类型");
+
+    /** 类型名称 */
+    private String name;
+
+    /**
+     * 构造方法
+     * @param name 名称
+     */
+    private DataType(String name) {
+        this.name = name;
+    }
+
+    /**
+     * 取得类型名称
+     * @return 类型名称
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * 判断是否为数字类型
+     * @param dataType 数据类型
+     * @return 布尔值
+     */
+    public static boolean isNumberType(DataType dataType) {
+        return dataType == BYTE || dataType == SHORT || dataType == INT || dataType == LONG || dataType == FLOAT || dataType == DOUBLE || dataType == BIGDECIMAL;
+    }
+
+    /**
+     * 判断是否是时间类型
+     * @param dataType 数据类型
+     * @return 布尔值
+     */
+    public static boolean isDateType(DataType dataType) {
+        return dataType == DATE || dataType == DATETIME || dataType == TIME;
+    }
+
+    /**
+     * 判断类是否为值类型
+     * @param clazz 类对象
+     * @return 布尔值
+     */
+    public static boolean isBaseDataType(Class<?> clazz) {
+        return clazz.isPrimitive() || clazz.equals(String.class) || (Number.class).isAssignableFrom(clazz) || (Boolean.class).isAssignableFrom(clazz) || (Date.class).isAssignableFrom(clazz);
+    }
+
+    /**
+     * 根据类对象取得对应的整型数据类型定义
+     * @param clazz 类对象
+     * @return 对应的数据类型
+     */
+    public static DataType parse(Class<?> clazz) {
+        if (clazz == null) {
+            return UNKNOWN;
+        }
+        if (clazz.equals(String.class)) {
+            return STRING;
+        }
+        if (clazz.equals(Integer.TYPE)) {
+            return INT;
+        }
+        if (clazz.equals(Integer.class)) {
+            return INT;
+        }
+        if (clazz.equals(Long.TYPE)) {
+            return LONG;
+        }
+        if (clazz.equals(Long.class)) {
+            return LONG;
+        }
+        if (clazz.equals(BigDecimal.class)) {
+            return BIGDECIMAL;
+        }
+        if (clazz.equals(Float.TYPE)) {
+            return FLOAT;
+        }
+        if (clazz.equals(Float.class)) {
+            return FLOAT;
+        }
+        if (clazz.equals(Double.TYPE)) {
+            return DOUBLE;
+        }
+        if (clazz.equals(Double.class)) {
+            return DOUBLE;
+        }
+        if (clazz.equals(Boolean.TYPE)) {
+            return BOOLEAN;
+        }
+        if (clazz.equals(Boolean.class)) {
+            return BOOLEAN;
+        }
+        if (clazz.equals(Date.class)) {
+            return DATE;
+        }
+        if (clazz.equals(java.sql.Date.class)) {
+            return DATE;
+        }
+        if (clazz.equals(java.sql.Time.class)) {
+            return TIME;
+        }
+        if (clazz.equals(java.sql.Timestamp.class)) {
+            return DATETIME;
+        }
+        if (clazz.equals(Byte.TYPE)) {
+            return BYTE;
+        }
+        if (clazz.equals(Byte.class)) {
+            return BYTE;
+        }
+        if (clazz.equals(Short.TYPE)) {
+            return SHORT;
+        }
+        if (clazz.equals(Short.class)) {
+            return SHORT;
+        }
+        return UNKNOWN;
+    }
+
+    /**
+     * 数据类型名称转换成数据类型整数
+     * @param type 数据类型名称
+     * @return 数据类型整数
+     */
+    public static DataType parse(String type) {
+        if (type == null) {
+            return UNKNOWN;
+        }
+        try {
+            type = type.toUpperCase();
+            if ("INTEGER".equals(type)) {
+                return INT;
+            }
+            return DataType.valueOf(type.toUpperCase());
+        } catch (IllegalArgumentException e) {
+            return UNKNOWN;
+        }
+    }
+
+    /**
+     * 数据类型转换成对应的类对象
+     * @param type 数据类型整数
+     * @return 类对象
+     */
+    public static Class<?> typeToClass(DataType type) {
+        switch (type) {
+            case STRING:
+                return String.class;
+            case INT:
+                return Integer.class;
+            case LONG:
+                return Long.class;
+            case BIGDECIMAL:
+                return BigDecimal.class;
+            case BOOLEAN:
+                return Boolean.class;
+            case FLOAT:
+                return Float.class;
+            case DOUBLE:
+                return Double.class;
+            case DATE:
+            case TIME:
+            case DATETIME:
+                return Date.class;
+            case BYTE:
+                return Byte.class;
+            case SHORT:
+                return Short.class;
+            default:
+                return null;
+        }
+    }
+}

+ 0 - 2
fm-common/src/main/java/com/persagy/fm/common/model/entity/AuditableEntity.java

@@ -40,7 +40,6 @@ public abstract class AuditableEntity<T> extends BaseEntity<T> implements IAudit
     /**
      * 创建人
      */
-    @JsonIgnore
     protected String creator;
 
     /**
@@ -53,7 +52,6 @@ public abstract class AuditableEntity<T> extends BaseEntity<T> implements IAudit
     /**
      * 最后一次修改人
      */
-    @JsonIgnore
     protected String modifier;
 
 }

+ 106 - 0
fm-common/src/main/java/com/persagy/fm/common/model/query/ChainLogicExpression.java

@@ -0,0 +1,106 @@
+package com.persagy.fm.common.model.query;
+
+import org.apache.commons.collections4.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 查询条件逻辑表达式表达式
+ * @author Sun
+ * @version 1.0 2014年9月24日
+ * @since 1.6
+ */
+public class ChainLogicExpression implements IExpression {
+
+	/**
+	 * 序列id
+	 */
+	private static final long serialVersionUID = 5682864471973760964L;
+
+	/**
+	 * 表达式列表
+	 */
+	private List<IExpression> expressions = null;
+	
+	/**
+	 * 操作符
+	 */
+	private boolean isOr = false;
+	
+	/**
+	 * 构造方法
+	 */
+	public ChainLogicExpression(){
+		
+	}
+	
+	/**
+	 * 表达式构造方法
+	 * @param isOr       是否为逻辑或相连
+	 * @param expressions 表达式数组
+	 */
+	public ChainLogicExpression(boolean isOr, IExpression...expressions){
+		addExpression(expressions);
+		setOr(isOr);
+	}
+
+	/**
+	 * 取得链式逻辑表达式中包含的表达式列表
+	 * @return 表达式列表
+	 */
+	public List<IExpression> getExpressions() {
+		return expressions;
+	}
+
+	/**
+	 * 设置链式逻辑表达式中包含的表达式列表
+	 * @param expressions 表达式列表
+	 */
+	public void setExpressions(List<IExpression> expressions) {
+		this.expressions = expressions;
+	}
+	
+	/**
+	 * 添加表达式到链式表达式中
+	 * @param expressions 表达式数组
+	 */
+	public void addExpression(IExpression... expressions){
+		if(this.expressions == null) {
+            this.expressions = new ArrayList<>();
+        }
+		CollectionUtils.addAll(this.expressions, expressions);
+	}
+	
+	/**
+	 * 判断是否为‘或’逻辑操作
+	 * @return 布尔值
+	 */
+	public boolean isOr() {
+		return isOr;
+	}
+
+	/**
+	 * 设置是否为‘或’逻辑操作
+	 * @param isOr 布尔值
+	 */
+	public void setOr(boolean isOr) {
+		this.isOr = isOr;
+	}
+
+	@Override
+	public ExpressionType getType() {
+		return ExpressionType.CHAIN;
+	}
+
+	/**
+	 * 取得表达式大小
+	 * @return 表达式大小
+	 */
+	public int size(){
+		if(expressions == null) {
+			return 0;
+		}
+		return expressions.size();
+	}
+}

+ 238 - 0
fm-common/src/main/java/com/persagy/fm/common/model/query/Condition.java

@@ -0,0 +1,238 @@
+package com.persagy.fm.common.model.query;
+
+import com.persagy.fm.common.model.data.DataType;
+import lombok.Data;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 查询类,封装查询条件
+ * @author Sun
+ * @version 1.0 2014年9月24日
+ * @since 1.6
+ */
+@Data
+public class Condition implements Serializable {
+    /**
+     * 序列id
+     */
+    private static final long serialVersionUID = 6256786995910707674L;
+    /**
+     * 条件列表
+     */
+    private List<IExpression> expressions = null;
+    /**
+     * 自由条件列表
+     */
+    private List<String> freeConditions = null;
+    /**
+     * 自由搜索值
+     */
+    private String freeValue = null;
+
+    /**
+     * 前置逻辑操作符,为空时不增加逻辑操作符
+     */
+    private String preLogic = null;
+
+    /**
+     * 用于属性别名转换的别名定义,字符串形式如:User as u,
+     * 该别名应用场景,如果定义的条件名为User.name 实际使用的表别名为u,需要在查询执行时进行替换
+     * User.name将被替换成u.name
+     */
+    private String[] fromAlias = null;
+
+    /**
+     * 无条件时是否忽略条件,还是变成不成立条件
+     */
+    private boolean ignore = true;
+
+    /**
+     * 添加查询条件
+     *
+     * @param condition 条件名称
+     */
+    public void addExpressions(IExpression... condition) {
+        if (this.expressions == null) {
+            this.expressions = new ArrayList<IExpression>();
+        }
+        if (this.expressions.size() == 0) {
+            CollectionUtils.addAll(this.expressions, condition);
+        } else {
+            for (IExpression con : condition) {
+                if (this.expressions.indexOf(con) < 0) {
+                    this.expressions.add(con);
+                }
+            }
+        }
+    }
+
+    /**
+     * 添加查询条件
+     *
+     * @param condition 条件名称
+     * @param value     条件值
+     * @param dataType  数据类型
+     * @param operator  操作符
+     */
+    public void addExpression(String condition, Object value, String dataType, String operator) {
+        ValueExpression exp = new ValueExpression(condition, value, dataType, operator);
+        this.addExpressions(exp);
+    }
+
+    /**
+     * 添加查询条件
+     *
+     * @param condition 条件名称
+     * @param value     条件值
+     * @param operator  操作符
+     */
+    public void addExpression(String condition, Object value, String operator) {
+        ValueExpression exp = new ValueExpression(condition, value, operator);
+        this.addExpressions(exp);
+    }
+
+    /**
+     * 添加查询条件
+     *
+     * @param condition 条件名称
+     * @param value     条件值
+     * @param dataType  数据类型
+     * @param operator  操作符
+     */
+    public void addExpression(String condition, Object value, DataType dataType, Operator operator) {
+        ValueExpression exp = new ValueExpression(condition, value, dataType, operator);
+        this.addExpressions(exp);
+    }
+
+    /**
+     * 添加查询条件
+     *
+     * @param condition 条件名称
+     * @param value     条件值
+     * @param operator  操作符
+     */
+    public void addExpression(String condition, Object value, Operator operator) {
+        ValueExpression exp = new ValueExpression(condition, value, operator);
+        this.addExpressions(exp);
+    }
+
+    /**
+     * 添加查询条件
+     *
+     * @param condition 条件名称
+     * @param value     条件值
+     */
+    public void addExpression(String condition, Object value) {
+        ValueExpression exp = new ValueExpression(condition, value);
+        this.addExpressions(exp);
+    }
+
+    /**
+     * 设置自由条件属性列表
+     *
+     * @param freeConditions 自由条件属性列表
+     */
+    public void setFreeConditions(String... freeConditions) {
+        List<String> freeCons = new ArrayList<String>();
+        CollectionUtils.addAll(freeCons, freeConditions);
+        this.freeConditions = freeCons;
+    }
+
+    /**
+     * 添加自由条件属性
+     *
+     * @param freeCondition 自由条件属性
+     */
+    public void setFreeExpressions(String freeValue, String... freeCondition) {
+        if (freeCondition == null || freeCondition.length == 0 || StringUtils.isEmpty(freeValue)) {
+            return;
+        }
+        setFreeConditions(freeCondition);
+        setFreeValue(freeValue);
+    }
+
+    /**
+     * 添加自由条件属性
+     *
+     * @param freeCondition 自由条件属性
+     */
+    public void addFreeCondition(String... freeCondition) {
+        if (this.freeConditions == null) {
+            this.freeConditions = new ArrayList<String>();
+        }
+        if (this.freeConditions.size() == 0) {
+            CollectionUtils.addAll(this.freeConditions, freeCondition);
+        } else {
+            for (String con : freeCondition) {
+                if (this.freeConditions.indexOf(con) < 0) {
+                    this.freeConditions.add(con);
+                }
+            }
+        }
+    }
+
+    /**
+     * 转换成表达式列表
+     *
+     * @return 表达式列表
+     */
+    public List<IExpression> toExpressions() {
+        List<IExpression> cons = getExpressions();
+        if (StringUtils.isEmpty(getFreeValue())) {
+            return cons;
+        }
+        ChainLogicExpression expression = new ChainLogicExpression();
+        if (cons != null && cons.size() > 0) {
+            for (IExpression con : cons) {
+                expression.addExpression(con);
+            }
+        }
+        List<String> freeCons = getFreeConditions();
+        String freeValue = getFreeValue();
+        if (freeCons != null && freeCons.size() > 0 && StringUtils.isNotEmpty(freeValue)) {
+            ChainLogicExpression freeExp = new ChainLogicExpression(true);
+            for (String freeCon : freeCons) {
+                IExpression exp = new ValueExpression(freeCon, freeValue, Operator.LIKEANY);
+                freeExp.addExpression(exp);
+            }
+            expression.addExpression(freeExp);
+        }
+        List<IExpression> expressions = new ArrayList<>();
+        expressions.add(expression);
+        return expressions;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+
+        List<IExpression> es = toExpressions();
+        if (es != null && es.size() > 0) {
+            if (preLogic != null) {
+                builder.append(preLogic).append(StringUtils.SPACE);
+            }
+            builder.append(StringUtils.join(es, " and "));
+            return builder.toString();
+        } else {
+            return StringUtils.EMPTY;
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !(obj instanceof Condition)) {
+            return false;
+        }
+        return obj.toString().equals(toString());
+    }
+
+    @Override
+    public int hashCode() {
+        return toString().hashCode();
+    }
+}

+ 29 - 0
fm-common/src/main/java/com/persagy/fm/common/model/query/ExpressionType.java

@@ -0,0 +1,29 @@
+package com.persagy.fm.common.model.query;
+
+/**
+ * 表达式类型
+ * @author Sun
+ * @version 1.0 2017-9-2
+ * @since 1.7
+ */
+public enum ExpressionType {
+
+    /**
+     * 简单值表达式
+     */
+    VALUE,
+    /**
+     * 逻辑表达式“与或”
+     */
+    LOGIC,
+
+    /**
+     * 链式逻辑表达式“与或”,表达式列表中的表达式并列与或者或
+     */
+    CHAIN,
+
+    /**
+     * sql表达式,只能在后端使用
+     */
+    SQL
+}

+ 19 - 0
fm-common/src/main/java/com/persagy/fm/common/model/query/IExpression.java

@@ -0,0 +1,19 @@
+package com.persagy.fm.common.model.query;
+
+import java.io.Serializable;
+
+
+/**
+ * 表达式接口
+ * @author Sun
+ * @version 1.0 2014年5月9日
+ * @since 1.7
+ */
+public interface IExpression extends Serializable {
+
+    /**
+     * 取得表达式类型
+     * @return 表达式类型
+     */
+    ExpressionType getType();
+}

+ 109 - 0
fm-common/src/main/java/com/persagy/fm/common/model/query/LogicExpression.java

@@ -0,0 +1,109 @@
+package com.persagy.fm.common.model.query;
+
+/**
+ * 查询条件逻辑表达式表达式
+ * @author Sun
+ * @version 1.0 2014年9月24日
+ * @since 1.6
+ */
+public class LogicExpression implements IExpression {
+
+    /**
+     * 序列id
+     */
+    private static final long serialVersionUID = -1756448873214671761L;
+
+    /**
+     * 左侧表达式
+     */
+    private IExpression left = null;
+
+    /**
+     * 右侧表达式
+     */
+    private IExpression right = null;
+
+    /**
+     * 操作符
+     */
+    private boolean isOr = false;
+
+    /**
+     * 构造方法
+     */
+    public LogicExpression() {
+
+    }
+
+    /**
+     * 表达式构造方法
+     *
+     * @param left 左侧表达式
+     * @param right        右侧表达式
+     * @param isOr     是否为或逻辑
+     */
+    public LogicExpression(IExpression left, IExpression right, boolean isOr) {
+        setLeft(left);
+        setRight(right);
+        setOr(isOr);
+    }
+
+    /**
+     * 取得左侧表达式
+     *
+     * @return 左侧表达式
+     */
+    public IExpression getLeft() {
+        return left;
+    }
+
+    /**
+     * 设置左侧表达式
+     *
+     * @param left 左侧表达式
+     */
+    public void setLeft(IExpression left) {
+        this.left = left;
+    }
+
+    /**
+     * 取得右侧表达式
+     *
+     * @return 右侧表达式
+     */
+    public IExpression getRight() {
+        return right;
+    }
+
+    /**
+     * 设置右侧表达式
+     *
+     * @param right 右侧表达式
+     */
+    public void setRight(IExpression right) {
+        this.right = right;
+    }
+
+    /**
+     * 判断是否为‘或’逻辑操作
+     *
+     * @return 布尔值
+     */
+    public boolean isOr() {
+        return isOr;
+    }
+
+    /**
+     * 设置是否为‘或’逻辑操作
+     *
+     * @param isOr 布尔值
+     */
+    public void setOr(boolean isOr) {
+        this.isOr = isOr;
+    }
+
+    @Override
+    public ExpressionType getType() {
+        return ExpressionType.LOGIC;
+    }
+}

+ 161 - 0
fm-common/src/main/java/com/persagy/fm/common/model/query/Operator.java

@@ -0,0 +1,161 @@
+/*
+ * Copyright(c) FastSpace Software 2014. All Rights Reserved.
+ */
+package com.persagy.fm.common.model.query;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * 操作符枚举
+ *
+ * @author Sun
+ * @version 1.0 2014年9月24日
+ * @since 1.6
+ */
+public enum Operator {
+    /*操作符常量*/
+    /**
+     * =操作符
+     */
+    EQUAL("=", "等于"),
+
+    /**
+     * <>操作符
+     */
+    NOTEQUAL("<>", "不等于"),
+
+    /**
+     * >操作符
+     */
+    GREATER(">", "大于"),
+
+    /**
+     * <操作符
+     */
+    LESS("<", "小于"),
+
+    /**
+     * >=操作符
+     */
+    GREATEREQUAL(">=", "大于等于"),
+
+    /**
+     * <=操作符
+     */
+    LESSEQAUL("<=", "小于等于"),
+
+
+    /**
+     * like操作符,全模糊匹配
+     */
+    LIKEACC("likea", "精确包含"),//前后没有%
+    NOTLIKEACC("not likea", "不精确包含"),//前后没有%
+    LIKEANY("like", "包含"),
+    NOTLIKEANY("not like", "不包含"),
+
+    /**
+     * like操作符,开始匹配,结尾模糊
+     */
+    LIKESTART("likes", "左包含"),
+    NOTLIKESTART("not likes", "不左包含"),
+
+    /**
+     * like操作符,结尾匹配,开始模糊
+     */
+    LIKEEND("likee", "右包含"),
+    NOTLIKEEND("not likee", "不右包含"),
+
+    /**
+     * is null操作符
+     */
+    ISNULL("is null", "为空值"),
+
+    /**
+     * is not null操作符
+     */
+    ISNOTNULL("is not null", "不为空值"),
+
+    /**
+     * in操作符号
+     */
+    IN("in", "列举"),
+
+    /**
+     * not in操作符号
+     */
+    NOTIN("not in", "反列举");
+
+    /**
+     * 操作符编码
+     */
+    private String code;
+
+    /**
+     * 操作符名称
+     */
+    private String name;
+
+    /**
+     * 构造方法
+     *
+     * @param code 编码
+     * @param name 名称
+     */
+    private Operator(String code, String name) {
+        this.code = code;
+        this.name = name;
+    }
+
+    /**
+     * 取得类型编码
+     *
+     * @return 类型编码
+     */
+    public String getCode() {
+        return code;
+    }
+
+    /**
+     * 取得类型名称
+     *
+     * @return 类型名称
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * (non-Javadoc)
+     *
+     * @see Enum#toString()
+     */
+    @Override
+    public String toString() {
+        return getCode();
+    }
+
+    /**
+     * 根据编码获取操作符
+     *
+     * @param code 操作符编码
+     * @return 操作符
+     */
+    public static Operator parse(String code) {
+        code = code.toLowerCase();
+        if (!STR_VAL_MAP.containsKey(code)) {
+            throw new IllegalArgumentException("Unknown String Value: " + code);
+        }
+        return STR_VAL_MAP.get(code);
+    }
+
+    private static final Map<String, Operator> STR_VAL_MAP;
+
+    static {
+        STR_VAL_MAP = new HashMap<String, Operator>();
+        for (final Operator en : Operator.values()) {
+            STR_VAL_MAP.put(en.getCode(), en);
+        }
+    }
+}

+ 64 - 0
fm-common/src/main/java/com/persagy/fm/common/model/query/SqlExpression.java

@@ -0,0 +1,64 @@
+package com.persagy.fm.common.model.query;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+
+/**
+ * 基于sql表达式,只能后台添加
+ * @author Sun
+ * @version 1.0 2014年5月9日
+ * @since 1.7
+ */
+@Data
+public class SqlExpression implements IExpression,Serializable{
+
+
+	/**
+	 * 序列id
+	 */
+	private static final long serialVersionUID = 7826974743759458453L;
+
+	/**
+	 * sql语句
+	 */
+	private String sql = null;
+
+
+	/**
+	 * 构造方法
+	 */
+	public SqlExpression(){
+	}
+
+	/**
+	 * 表达式构造方法,操作符为等于
+	 * @param sql sql语句
+	 */
+	public SqlExpression(String sql){
+		this.sql = sql;
+	}
+
+	@Override
+	public int hashCode() {
+		return toString().hashCode();
+	}
+
+
+	@Override
+	public boolean equals(Object obj) {
+		return obj instanceof SqlExpression && this.toString().equals(obj.toString());
+	}
+
+	@Override
+	public String toString() {
+		return getSql();
+	}
+
+
+	@Override
+	public ExpressionType getType() {
+		return ExpressionType.SQL;
+	}
+}

+ 255 - 0
fm-common/src/main/java/com/persagy/fm/common/model/query/ValueExpression.java

@@ -0,0 +1,255 @@
+package com.persagy.fm.common.model.query;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.persagy.fm.common.model.data.DataType;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.regex.Pattern;
+
+
+/**
+ * 简单值查询表达式
+ * @author Sun
+ * @version 1.0 2014年5月9日
+ * @since 1.7
+ */
+public class ValueExpression implements IExpression{
+
+	/**
+	 * 序列id
+	 */
+	private static final long serialVersionUID = -1127113431925423258L;
+
+	/**
+	 * 查询的实体属性名
+	 */
+	private String propertyName = null;
+	
+	/**
+	 * 属性名测试正则表达式
+	 */
+	private static Pattern propertyParttern = Pattern.compile("^\\w+[\\w+\\.]*\\w*$");
+	
+	/**
+	 * 数据类型
+	 */
+	private DataType dataType = DataType.STRING;
+	
+	/**
+	 * 表达式值
+	 */
+	private Object value = null;
+	
+	/**
+	 * 操作符
+	 */
+	private Operator operator = Operator.EQUAL;
+	
+	/**
+	 * 构造方法
+	 */
+	public ValueExpression(){
+	}
+	
+	/**
+	 * 表达式构造方法,操作符为等于
+	 * @param propertyName 属性名
+	 * @param value 属性值
+	 */
+	public ValueExpression(String propertyName, Object value){
+		this(propertyName,value,Operator.EQUAL);
+	}
+	/**
+	 * 表达式构造方法
+	 * @param propertyName 属性名
+	 * @param value 属性值
+	 * @param operator 操作符
+	 */
+	public ValueExpression(String propertyName, Object value, Operator operator){
+		setPropertyName(propertyName);
+		setOperator(operator);
+		setValue(value);
+	}
+
+	/**
+	 * 表达式构造方法
+	 * @param propertyName 属性名
+	 * @param value 属性值
+	 * @param operator 操作符
+	 */
+	public ValueExpression(String propertyName, Object value, String operator){
+		setPropertyName(propertyName);
+		setOperator(Operator.parse(operator));
+		setValue(value);
+	}
+	/**
+	 * 表达式构造方法
+	 * @param propertyName 属性名
+	 * @param value 属性值
+	 * @param operator 操作符
+	 */
+	public ValueExpression(String propertyName, Object value, DataType dataType,Operator operator){
+		setPropertyName(propertyName);
+		if(dataType != DataType.UNKNOWN) {
+            setDataType(dataType);
+        }
+		setOperator(operator);
+		setValue(value);
+	}
+
+	/**
+	 * 表达式构造方法
+	 * @param propertyName 属性名
+	 * @param value 属性值
+	 * @param operator 操作符
+	 */
+	public ValueExpression(String propertyName, Object value, String dataType, String operator){
+		setPropertyName(propertyName);
+		if(dataType != null){
+			 DataType dt =  DataType.parse(dataType);
+			 if(dt != DataType.UNKNOWN) {
+                 setDataType(dt);
+             }
+		}
+		setOperator(Operator.parse(operator));
+		setValue(value);
+	}
+	/**
+	 * 取得表达式属性名
+	 * @return 属性名
+	 */
+	public String getPropertyName() {
+		return propertyName;
+	}
+
+	/**
+	 * 设置表达式属性名
+	 * @param propertyName 属性名
+	 */
+	public void setPropertyName(String propertyName) {
+		if(propertyName.contains(" ")){
+			String[] temps = propertyName.split(" ");
+			propertyName = temps[0];
+			setDataType(DataType.parse(temps[1]));
+			if(temps.length > 2){
+				setOperator(Operator.parse(temps[2]));
+			}
+		}
+		if(!propertyParttern.matcher(propertyName).matches()) {
+            throw new RuntimeException("非法查询条件");
+        }
+		this.propertyName = propertyName;
+	}
+
+	/**
+	 * 取得表达式条件值
+	 * @return 条件值
+	 */
+	public Object getValue() {
+		return value;
+	}
+
+	/**
+	 * 设置表达式条件值
+	 * @param value 条件值
+	 */
+	public void setValue(Object value) {
+		if(this.operator != Operator.ISNULL || this.operator == Operator.ISNOTNULL) {
+            this.value = value;
+        }
+	}
+
+	/**
+	 * 取得表达式操作符
+	 * @return 表达式操作符
+	 */
+	public Operator getOperator() {
+		return operator;
+	}
+
+	/**
+	 * 设置表达式操作符
+	 * @param operator 表达式操作符
+	 */
+	public void setOperator(Operator operator) {
+		if(this.operator == Operator.ISNULL || this.operator == Operator.ISNOTNULL) {
+            this.value = null;
+        }
+		this.operator = operator;
+	}
+
+	
+	/**
+	 * 取得字段数据类型
+	 * @return 字段数据类型
+	 */
+	public DataType getDataType() {
+		return dataType;
+	}
+
+	/**
+	 * 设置字段数据类型
+	 * @param dataType 字段数据类型
+	 */
+	public void setDataType(DataType dataType) {
+		if(dataType==DataType.UNKNOWN) {
+            return;
+        }
+		this.dataType = dataType;
+	}
+
+	@Override
+	public ExpressionType getType() {
+		return ExpressionType.VALUE;
+	}
+
+	/**
+	 * (non-Javadoc)
+	 * @see Object#hashCode()
+	 */
+	@Override
+	public int hashCode() {
+		return toString().hashCode();
+	}
+
+	/**
+	 * (non-Javadoc)
+	 * @see Object#equals(Object)
+	 */
+	@Override
+	public boolean equals(Object obj) {
+		if(!(obj instanceof ValueExpression)) {
+            return false;
+        }
+		return this.toString().equals(obj.toString());
+	}
+
+	/**
+	 * (non-Javadoc)
+	 * @see Object#toString()
+	 */
+	@Override
+	public String toString() {
+		if(getValue() == null) {
+            return getPropertyName()+" "+getDataType()+ " " + getOperator();
+        } else {
+            return getPropertyName()+" "+getDataType()+ " " + getOperator()+getValue();
+        }
+	}
+	
+	/**
+	 * 判断当前表达式是否合法
+	 * @return 布尔值
+	 */
+	@JsonIgnore
+	public boolean isValidate(){
+		if(StringUtils.isBlank(propertyName)) {
+            return false;
+        }
+		if(getOperator() == Operator.ISNULL || getOperator() == Operator.ISNOTNULL) {
+            return true;
+        }
+		return value != null;
+	}
+	
+}

+ 43 - 35
fm-common/src/main/java/com/persagy/fm/common/utils/TypeConvertUtil.java

@@ -1,5 +1,6 @@
 package com.persagy.fm.common.utils;
 
+import com.persagy.fm.common.model.data.DataType;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 
@@ -23,7 +24,6 @@ public class TypeConvertUtil {
 
     }
 
-
     /**
      * 将Object对象解析成字符串类型
      * @param obj 需要转换的对象
@@ -273,42 +273,50 @@ public class TypeConvertUtil {
      * @return 目标类型对象
      */
     public static final Object translate(Class clazz, Object obj) {
-        if (clazz == null) {
-            return null;
-        }
-        if (clazz.equals(String.class)) {
-            return parseString(obj);
-        }
-        if (clazz.equals(Integer.TYPE) ||
-                clazz.equals(Integer.class)) {
-            return parseInt(obj);
-        }
-        if (clazz.equals(Long.TYPE) || clazz.equals(Long.class)) {
-            return parseLong(obj);
-        }
-        if (clazz.equals(BigDecimal.class)) {
-            return parseBigDecimal(obj);
-        }
-        if (clazz.equals(Float.TYPE) || clazz.equals(Float.class)) {
-            return parseFloat(obj);
-        }
-        if (clazz.equals(Double.TYPE) || clazz.equals(Double.class)) {
-            return parseDouble(obj);
-        }
-        if (clazz.equals(Boolean.TYPE) || clazz.equals(Boolean.class)) {
-            return parseBoolean(obj);
-        }
-        if (clazz.equals(Date.class) || clazz.equals(java.sql.Date.class) ||
-                clazz.equals(java.sql.Time.class) || clazz.equals(java.sql.Timestamp.class)) {
-            return parseDate(obj);
-        }
-        if (clazz.equals(Byte.TYPE) || clazz.equals(Byte.class)) {
-            return parseByte(obj);
+        return translate(DataType.parse(clazz), obj);
+    }
+
+    /**
+     * 将未知类型的对象转换成指定类型的对象
+     * @param dataType 数据类型
+     * @param obj      未知类型对象
+     * @return 目标类型对象
+     */
+    public static final Object translate(DataType dataType, Object obj) {
+        boolean isBlank = obj == null || (obj instanceof String) && ((String) obj).length() == 0;
+        if (isBlank) {
+            if (dataType == DataType.STRING) {
+                return obj;
+            } else {
+                return null;
+            }
         }
-        if (clazz.equals(Short.TYPE) || clazz.equals(Short.class)) {
-            return parseShort(obj);
+        switch (dataType) {
+            case STRING:
+                return parseString(obj);
+            case INT:
+                return parseInt(obj);
+            case LONG:
+                return parseLong(obj);
+            case BIGDECIMAL:
+                return parseBigDecimal(obj);
+            case BOOLEAN:
+                return parseBoolean(obj);
+            case FLOAT:
+                return parseFloat(obj);
+            case DOUBLE:
+                return parseDouble(obj);
+            case DATE:
+            case TIME:
+            case DATETIME:
+                return parseDate(obj);
+            case BYTE:
+                return parseByte(obj);
+            case SHORT:
+                return parseShort(obj);
+            default:
+                return obj;
         }
-        return obj;
     }
 
     /**

+ 160 - 125
fm-parent/pom.xml

@@ -20,10 +20,22 @@
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
         <version>2.1.14.RELEASE</version>
+        <relativePath/>
     </parent>
+    <!-- 版本管理 -->
     <properties>
         <fm.version>3.0.0</fm.version>
         <platform.version>1.0.0</platform.version>
+        <mybatis.version>3.4.6</mybatis.version>
+        <mybatis-spring.version>1.3.2</mybatis-spring.version>
+        <mybatis.springboot.version>1.3.2</mybatis.springboot.version>
+        <tomcat-jdbc.version>9.0.31</tomcat-jdbc.version>
+        <HikariCP.version>3.2.0</HikariCP.version>
+        <mysql.version>8.0.15</mysql.version>
+        <guava.version>27.0.1-jre</guava.version>
+        <spring.version>5.1.15.RELEASE</spring.version>
+        <spring-data.version>2.1.17.RELEASE</spring-data.version>
+        <spring-data-jpa.version>2.0.11.RELEASE</spring-data-jpa.version>
     </properties>
 
     <dependencyManagement>
@@ -35,6 +47,11 @@
             </dependency>
             <dependency>
                 <groupId>com.persagy</groupId>
+                <artifactId>fm-common</artifactId>
+                <version>${fm.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.persagy</groupId>
                 <artifactId>integrated-common-core</artifactId>
                 <version>${platform.version}</version>
             </dependency>
@@ -43,6 +60,147 @@
                 <artifactId>integrated-log-spring-boot-starter</artifactId>
                 <version>${platform.version}</version>
             </dependency>
+            <dependency>
+                <groupId>com.persagy</groupId>
+                <artifactId>integrated-redis-spring-boot-starter</artifactId>
+                <version>${platform.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.persagy</groupId>
+                <artifactId>integrated-config-client</artifactId>
+                <version>${platform.version}</version>
+            </dependency>
+            <!-- 以下是mybatis版本管理 -->
+            <dependency>
+                <groupId>org.mybatis</groupId>
+                <artifactId>mybatis</artifactId>
+                <version>${mybatis.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.mybatis</groupId>
+                <artifactId>mybatis-spring</artifactId>
+                <version>${mybatis-spring.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.mybatis.spring.boot</groupId>
+                <artifactId>mybatis-spring-boot-starter</artifactId>
+                <version>${mybatis.springboot.version}</version>
+            </dependency>
+            <!-- 以下是数据库连接池版本管理,建议使用provided方式引入-->
+            <dependency>
+                <groupId>org.apache.tomcat</groupId>
+                <artifactId>tomcat-jdbc</artifactId>
+                <version>${tomcat-jdbc.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.zaxxer</groupId>
+                <artifactId>HikariCP</artifactId>
+                <version>${HikariCP.version}</version>
+            </dependency>
+            <!-- 以下是数据库driver版本管理,建议使用provided方式引入 -->
+            <dependency>
+                <groupId>mysql</groupId>
+                <artifactId>mysql-connector-java</artifactId>
+                <version>${mysql.version}</version>
+            </dependency>
+            <!-- 以下是commons工具版本管理 -->
+            <dependency>
+                <groupId>com.google.guava</groupId>
+                <artifactId>guava</artifactId>
+                <version>${guava.version}</version>
+            </dependency>
+            <!-- 以下是spring版本管理 -->
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-context</artifactId>
+                <version>${spring.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-web</artifactId>
+                <version>${spring.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-tx</artifactId>
+                <version>${spring.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-aop</artifactId>
+                <version>${spring.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-test</artifactId>
+                <version>${spring.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-beans</artifactId>
+                <version>${spring.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-webmvc</artifactId>
+                <version>${spring.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-context-support</artifactId>
+                <version>${spring.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-core</artifactId>
+                <version>${spring.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-expression</artifactId>
+                <version>${spring.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-jcl</artifactId>
+                <version>${spring.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-jdbc</artifactId>
+                <version>${spring.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-messaging</artifactId>
+                <version>${spring.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-oxm</artifactId>
+                <version>${spring.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-webflux</artifactId>
+                <version>${spring.version}</version>
+            </dependency>
+            <!-- 以下是spring data版本管理 -->
+            <dependency>
+                <groupId>org.springframework.data</groupId>
+                <artifactId>spring-data-commons</artifactId>
+                <version>${spring-data.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework.data</groupId>
+                <artifactId>spring-data-redis</artifactId>
+                <version>${spring-data.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework.data</groupId>
+                <artifactId>spring-data-jpa</artifactId>
+                <version>${spring-data-jpa.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 
@@ -61,27 +219,6 @@
                         <showWarnings>true</showWarnings>
                     </configuration>
                 </plugin>
-                <plugin>
-                    <groupId>org.scala-tools</groupId>
-                    <artifactId>maven-scala-plugin</artifactId>
-                    <version>2.15.2</version>
-                    <executions>
-                        <execution>
-                            <id>scala-compile-first</id>
-                            <phase>process-resources</phase>
-                            <goals>
-                                <goal>compile</goal>
-                            </goals>
-                        </execution>
-                        <execution>
-                            <id>scala-test-compile</id>
-                            <phase>process-test-resources</phase>
-                            <goals>
-                                <goal>testCompile</goal>
-                            </goals>
-                        </execution>
-                    </executions>
-                </plugin>
                 <!-- resource插件 -->
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
@@ -110,27 +247,13 @@
                     </dependencies>
                 </plugin>
 
-                <!-- 增加更多的Source和Test Source目录插件 -->
-                <plugin>
-                    <groupId>org.codehaus.mojo</groupId>
-                    <artifactId>build-helper-maven-plugin</artifactId>
-                    <version>3.0.0</version>
-                </plugin>
-
-                <!-- cobertura 测试覆盖率统计插件 -->
-                <plugin>
-                    <groupId>org.codehaus.mojo</groupId>
-                    <artifactId>cobertura-maven-plugin</artifactId>
-                    <version>2.7</version>
-                </plugin>
-
                 <!-- war打包插件, 设定war包名称不带版本号 -->
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-war-plugin</artifactId>
-                    <version>3.2.0</version>
+                    <version>3.2.3</version>
                     <configuration>
-                        <warName>${project.artifactId}${chassis.version}</warName>
+                        <warName>${project.artifactId}${fm.version}</warName>
                         <failOnMissingWebXml>false</failOnMissingWebXml>
                     </configuration>
                 </plugin>
@@ -185,94 +308,6 @@
                     <artifactId>maven-install-plugin</artifactId>
                     <version>2.5.2</version>
                 </plugin>
-
-                <!-- enforcer插件, 避免被依赖的依赖引入过期的jar包 -->
-                <plugin>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-enforcer-plugin</artifactId>
-                    <version>1.4.1</version>
-                    <executions>
-                        <execution>
-                            <id>enforce-banned-dependencies</id>
-                            <goals>
-                                <goal>enforce</goal>
-                            </goals>
-                            <configuration>
-                                <rules>
-                                    <requireMavenVersion>
-                                        <version>3.0.3</version>
-                                    </requireMavenVersion>
-                                    <requireJavaVersion>
-                                        <version>1.7</version>
-                                    </requireJavaVersion>
-                                    <bannedDependencies>
-                                        <searchTransitive>true</searchTransitive>
-                                        <excludes>
-                                            <exclude>commons-logging</exclude>
-                                            <exclude>aspectj:aspectj*</exclude>
-                                            <exclude>org.springframework</exclude>
-                                        </excludes>
-                                        <includes>
-                                            <include>org.springframework:*:4.3.*</include>
-                                        </includes>
-                                    </bannedDependencies>
-                                </rules>
-                                <fail>true</fail>
-                            </configuration>
-                        </execution>
-                    </executions>
-                </plugin>
-                <!-- dependency相关插件 -->
-                <plugin>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-dependency-plugin</artifactId>
-                    <version>2.10</version>
-                </plugin>
-                <!-- ant插件 -->
-                <plugin>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-antrun-plugin</artifactId>
-                    <version>1.8</version>
-                </plugin>
-
-                <!-- exec java 插件 -->
-                <plugin>
-                    <groupId>org.codehaus.mojo</groupId>
-                    <artifactId>exec-maven-plugin</artifactId>
-                    <version>1.6.0</version>
-                </plugin>
-                <plugin>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-release-plugin</artifactId>
-                    <version>2.5.3</version>
-                    <configuration>
-                        <!--<tagBase>https://192.168.1.100:8443/svn/myapp/tags/</tagBase>-->
-                    </configuration>
-                </plugin>
-                <plugin>
-                    <groupId>org.eclipse.m2e</groupId>
-                    <artifactId>lifecycle-mapping</artifactId>
-                    <version>1.0.0</version>
-                    <configuration>
-                        <lifecycleMappingMetadata>
-                            <pluginExecutions>
-                                <pluginExecution>
-                                    <pluginExecutionFilter>
-                                        <groupId>org.apache.maven.plugins</groupId>
-                                        <artifactId>maven-dependency-plugin</artifactId>
-                                        <versionRange>[2.0,)</versionRange>
-                                        <goals>
-                                            <goal>copy-dependencies</goal>
-                                        </goals>
-                                    </pluginExecutionFilter>
-                                    <action>
-                                        <ignore/>
-                                    </action>
-                                </pluginExecution>
-                            </pluginExecutions>
-                        </lifecycleMappingMetadata>
-                    </configuration>
-                </plugin>
             </plugins>
         </pluginManagement>
         <plugins>

+ 1 - 3
fm-server/src/main/java/com/persagy/fm/server/ServerApplication.java

@@ -6,11 +6,9 @@ import org.springframework.boot.Banner;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration;
-import org.springframework.boot.context.event.ApplicationStartedEvent;
 import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
 import org.springframework.cloud.openfeign.EnableFeignClients;
-import org.springframework.context.ApplicationListener;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.scheduling.annotation.EnableScheduling;
@@ -36,7 +34,7 @@ public class ServerApplication {
         SpringApplication application = new SpringApplication(ServerApplication.class);
         application.setBannerMode(Banner.Mode.OFF);
 //        application.addListeners((ApplicationListener<ApplicationStartedEvent>) event ->
-//                System.out.println("\033[1;93;44m>>>>>> " + event.getApplicationContext().getApplicationName().substring(1) + " has started!!! \033[m"));
+//                System.out.println("\033[1;93;44m>>>>>> " + event.getApplicationContext().getEnvironment().getProperty("spring.application.name") + " has started!!! \033[m"));
         application.run(args);
     }
 }

+ 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("translate.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>