Explorar el Código

增加对应工具类

lws hace 3 años
padre
commit
78ccf54cc7
Se han modificado 85 ficheros con 7086 adiciones y 628 borrados
  1. 11 10
      pom.xml
  2. 168 0
      src/main/java/com/metro/annotation/Excel.java
  3. 18 0
      src/main/java/com/metro/annotation/Excels.java
  4. 196 0
      src/main/java/com/metro/baseRe/AjaxResult.java
  5. 1 1
      src/main/java/com/metro/baseRe/BaseEntity.java
  6. 119 0
      src/main/java/com/metro/config/Config.java
  7. 114 0
      src/main/java/com/metro/constant/Constants.java
  8. 74 22
      src/main/java/com/metro/controller/AccountController.java
  9. 8 1
      src/main/java/com/metro/controller/MetroController.java
  10. 22 4
      src/main/java/com/metro/controller/PassreCordsController.java
  11. 75 0
      src/main/java/com/metro/controller/PolicController.java
  12. 0 58
      src/main/java/com/metro/controller/QuestionController.java
  13. 0 66
      src/main/java/com/metro/controller/Time.java
  14. 40 0
      src/main/java/com/metro/entity/model/UserOperateModel.java
  15. 7 32
      src/main/java/com/metro/entity/po/Metro.java
  16. 22 9
      src/main/java/com/metro/entity/po/PassreCords.java
  17. 58 56
      src/main/java/com/metro/entity/po/Police.java
  18. 4 4
      src/main/java/com/metro/entity/ro/Police/DeletePoliceReq.java
  19. 2 2
      src/main/java/com/metro/entity/ro/Police/DeletePoliceResp.java
  20. 23 0
      src/main/java/com/metro/entity/ro/Police/DetailPoliceReq.java
  21. 78 0
      src/main/java/com/metro/entity/ro/Police/DetailPoliceResp.java
  22. 40 0
      src/main/java/com/metro/entity/ro/Police/InsertPoliceReq.java
  23. 7 4
      src/main/java/com/metro/entity/ro/Police/InsertPoliceResp.java
  24. 8 5
      src/main/java/com/metro/entity/ro/Police/SelectPolicePageReq.java
  25. 44 0
      src/main/java/com/metro/entity/ro/Police/SelectPolicePageResp.java
  26. 0 56
      src/main/java/com/metro/entity/ro/Question/InsertQuestionReq.java
  27. 0 50
      src/main/java/com/metro/entity/ro/Question/SelectQuestionPageResp.java
  28. 1 1
      src/main/java/com/metro/entity/ro/account/DetailAccountResp.java
  29. 2 2
      src/main/java/com/metro/entity/ro/account/InsertAccountReq.java
  30. 3 0
      src/main/java/com/metro/entity/ro/account/LoginAccountResp.java
  31. 6 0
      src/main/java/com/metro/entity/ro/account/SelectAccountPageResp.java
  32. 3 0
      src/main/java/com/metro/entity/ro/account/UpdateAccountReq.java
  33. 40 0
      src/main/java/com/metro/entity/ro/metro/SelectMetroParentResp.java
  34. 2 5
      src/main/java/com/metro/entity/ro/metro/SelectMetroReq.java
  35. 3 17
      src/main/java/com/metro/entity/ro/metro/SelectMetroResp.java
  36. 23 0
      src/main/java/com/metro/entity/ro/passrecords/DeletePassreCordsReq.java
  37. 23 0
      src/main/java/com/metro/entity/ro/passrecords/DeletePassreCordsResq.java
  38. 23 0
      src/main/java/com/metro/entity/ro/passrecords/DetailPassreCordsReq.java
  39. 23 0
      src/main/java/com/metro/entity/ro/passrecords/DetialPassreCordsResq.java
  40. 7 5
      src/main/java/com/metro/entity/ro/passrecords/SelectPassreCordsPageListReq.java
  41. 28 19
      src/main/java/com/metro/entity/ro/passrecords/SelectPassreCordsPageListResp.java
  42. 18 0
      src/main/java/com/metro/exception/FileException.java
  43. 16 0
      src/main/java/com/metro/exception/FileNameLengthLimitExceededException.java
  44. 14 0
      src/main/java/com/metro/exception/FileSizeLimitExceededException.java
  45. 69 0
      src/main/java/com/metro/exception/InvalidExtensionException.java
  46. 22 0
      src/main/java/com/metro/exception/UtilException.java
  47. 84 0
      src/main/java/com/metro/exception/base/BaseException.java
  48. 7 3
      src/main/java/com/metro/job/HealthCodeTask.java
  49. 2 3
      src/main/java/com/metro/mapper/PassreCordsMapper.java
  50. 4 5
      src/main/java/com/metro/mapper/PoliceMapper.java
  51. 0 12
      src/main/java/com/metro/mapper/UserMapper.java
  52. 7 5
      src/main/java/com/metro/service/AccountService.java
  53. 6 1
      src/main/java/com/metro/service/MetroService.java
  54. 13 3
      src/main/java/com/metro/service/PassreCordsService.java
  55. 36 0
      src/main/java/com/metro/service/PoliceService.java
  56. 0 36
      src/main/java/com/metro/service/QuestionService.java
  57. 86 17
      src/main/java/com/metro/service/impl/AccountServiceImpl.java
  58. 42 22
      src/main/java/com/metro/service/impl/MetroServiceImpl.java
  59. 38 14
      src/main/java/com/metro/service/impl/PassreCordsServiceImpl.java
  60. 109 0
      src/main/java/com/metro/service/impl/PoliceServiceImpl.java
  61. 0 76
      src/main/java/com/metro/service/impl/QuestionServiceImpl.java
  62. 189 0
      src/main/java/com/metro/utils/CacheUtils.java
  63. 160 0
      src/main/java/com/metro/utils/DateUtils.java
  64. 80 0
      src/main/java/com/metro/utils/DictUtils.java
  65. 29 1
      src/main/java/com/metro/utils/HttpClientUtil.java
  66. 24 0
      src/main/java/com/metro/utils/MessageUtils.java
  67. 323 0
      src/main/java/com/metro/utils/ReflectUtils.java
  68. 516 0
      src/main/java/com/metro/utils/StringUtils.java
  69. 110 0
      src/main/java/com/metro/utils/bean/BeanUtils.java
  70. 21 0
      src/main/java/com/metro/utils/bean/BeanValidators.java
  71. 77 0
      src/main/java/com/metro/utils/file/FileTypeUtils.java
  72. 197 0
      src/main/java/com/metro/utils/file/FileUploadUtils.java
  73. 229 0
      src/main/java/com/metro/utils/file/FileUtils.java
  74. 99 0
      src/main/java/com/metro/utils/file/ImageUtils.java
  75. 59 0
      src/main/java/com/metro/utils/file/MimeTypeUtils.java
  76. 19 0
      src/main/java/com/metro/utils/poi/ExcelHandlerAdapter.java
  77. 1162 0
      src/main/java/com/metro/utils/poi/ExcelUtil.java
  78. 131 0
      src/main/java/com/metro/utils/spring/SpringUtils.java
  79. 88 0
      src/main/java/com/metro/utils/text/CharsetKit.java
  80. 1006 0
      src/main/java/com/metro/utils/text/Convert.java
  81. 77 0
      src/main/java/com/metro/utils/text/StrFormatter.java
  82. 49 0
      src/main/java/com/metro/utils/uuid/IdUtils.java
  83. 81 0
      src/main/java/com/metro/utils/uuid/Seq.java
  84. 442 0
      src/main/java/com/metro/utils/uuid/UUID.java
  85. 19 1
      src/main/resources/application.yml

+ 11 - 10
pom.xml

@@ -80,23 +80,24 @@
         </dependency>
 
         <dependency>
-            <groupId>org.apache.httpcomponents</groupId>
-            <artifactId>httpclient</artifactId>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.11.0</version>
         </dependency>
 
-
-        <!-- xls格式excel依赖包 -->
+        <!-- excel工具 -->
         <dependency>
             <groupId>org.apache.poi</groupId>
-            <artifactId>poi</artifactId>
-            <version>3.17</version>
+            <artifactId>poi-ooxml</artifactId>
+            <version>4.1.2</version>
         </dependency>
-        <!--xlsx格式excel依赖包-->
+
         <dependency>
-            <groupId>org.apache.poi</groupId>
-            <artifactId>poi-ooxml</artifactId>
-            <version>3.17</version>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
         </dependency>
+
+
         <!-- easyexcel依赖包 -->
         <dependency>
         <groupId>com.alibaba</groupId>

+ 168 - 0
src/main/java/com/metro/annotation/Excel.java

@@ -0,0 +1,168 @@
+package com.metro.annotation;
+
+
+import com.metro.utils.poi.ExcelHandlerAdapter;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.math.BigDecimal;
+
+/**
+ * 自定义导出Excel数据注解
+ *
+ * @author ruoyi
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Excel {
+    /**
+     * 导出时在excel中排序
+     */
+    public int sort() default Integer.MAX_VALUE;
+
+    /**
+     * 导出到Excel中的名字.
+     */
+    public String name() default "";
+
+    /**
+     * 日期格式, 如: yyyy-MM-dd
+     */
+    public String dateFormat() default "";
+
+    /**
+     * 如果是字典类型,请设置字典的type值 (如: sys_user_sex)
+     */
+    public String dictType() default "";
+
+    /**
+     * 读取内容转表达式 (如: 0=男,1=女,2=未知)
+     */
+    public String readConverterExp() default "";
+
+    /**
+     * 分隔符,读取字符串组内容
+     */
+    public String separator() default ",";
+
+    /**
+     * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
+     */
+    public int scale() default -1;
+
+    /**
+     * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
+     */
+    public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
+
+    /**
+     * 导出类型(0数字 1字符串)
+     */
+    public ColumnType cellType() default ColumnType.STRING;
+
+    /**
+     * 导出时在excel中每个列的高度 单位为字符
+     */
+    public double height() default 14;
+
+    /**
+     * 导出时在excel中每个列的宽 单位为字符
+     */
+    public double width() default 16;
+
+    /**
+     * 文字后缀,如% 90 变成90%
+     */
+    public String suffix() default "";
+
+    /**
+     * 当值为空时,字段的默认值
+     */
+    public String defaultValue() default "";
+
+    /**
+     * 提示信息
+     */
+    public String prompt() default "";
+
+    /**
+     * 设置只能选择不能输入的列内容.
+     */
+    public String[] combo() default {};
+
+    /**
+     * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
+     */
+    public boolean isExport() default true;
+
+    /**
+     * 另一个类中的属性名称,支持多级获取,以小数点隔开
+     */
+    public String targetAttr() default "";
+
+    /**
+     * 是否自动统计数据,在最后追加一行统计数据总和
+     */
+    public boolean isStatistics() default false;
+
+    /**
+     * 导出字段对齐方式(0:默认;1:靠左;2:居中;3:靠右)
+     */
+    public Align align() default Align.AUTO;
+
+    /**
+     * 自定义数据处理器
+     */
+    public Class<?> handler() default ExcelHandlerAdapter.class;
+
+    /**
+     * 自定义数据处理器参数
+     */
+    public String[] args() default {};
+
+    public enum Align {
+        AUTO(0), LEFT(1), CENTER(2), RIGHT(3);
+        private final int value;
+
+        Align(int value) {
+            this.value = value;
+        }
+
+        public int value() {
+            return this.value;
+        }
+    }
+
+    /**
+     * 字段类型(0:导出导入;1:仅导出;2:仅导入)
+     */
+    Type type() default Type.ALL;
+
+    public enum Type {
+        ALL(0), EXPORT(1), IMPORT(2);
+        private final int value;
+
+        Type(int value) {
+            this.value = value;
+        }
+
+        public int value() {
+            return this.value;
+        }
+    }
+
+    public enum ColumnType {
+        NUMERIC(0), STRING(1), IMAGE(2);
+        private final int value;
+
+        ColumnType(int value) {
+            this.value = value;
+        }
+
+        public int value() {
+            return this.value;
+        }
+    }
+}

+ 18 - 0
src/main/java/com/metro/annotation/Excels.java

@@ -0,0 +1,18 @@
+package com.metro.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Excel注解集
+ * 
+ * @author ruoyi
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Excels
+{
+    Excel[] value();
+}

+ 196 - 0
src/main/java/com/metro/baseRe/AjaxResult.java

@@ -0,0 +1,196 @@
+package com.metro.baseRe;
+
+import com.metro.utils.StringUtils;
+
+import java.util.HashMap;
+
+/**
+ * 操作消息提醒
+ *
+ * @author ruoyi
+ */
+public class AjaxResult extends HashMap<String, Object>
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 状态码 */
+    public static final String CODE_TAG = "code";
+
+    /** 返回内容 */
+    public static final String MSG_TAG = "msg";
+
+    /** 数据对象 */
+    public static final String DATA_TAG = "data";
+
+    /**
+     * 状态类型
+     */
+    public enum Type
+    {
+        /** 成功 */
+        SUCCESS(0),
+        /** 警告 */
+        WARN(301),
+        /** 错误 */
+        ERROR(500);
+        private final int value;
+
+        Type(int value)
+        {
+            this.value = value;
+        }
+
+        public int value()
+        {
+            return this.value;
+        }
+    }
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
+     */
+    public AjaxResult()
+    {
+    }
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象
+     *
+     * @param type 状态类型
+     * @param msg 返回内容
+     */
+    public AjaxResult(Type type, String msg)
+    {
+        super.put(CODE_TAG, type.value);
+        super.put(MSG_TAG, msg);
+    }
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象
+     *
+     * @param type 状态类型
+     * @param msg 返回内容
+     * @param data 数据对象
+     */
+    public AjaxResult(Type type, String msg, Object data)
+    {
+        super.put(CODE_TAG, type.value);
+        super.put(MSG_TAG, msg);
+        if (StringUtils.isNotNull(data)) {
+            super.put(DATA_TAG, data);
+        }
+    }
+
+    /**
+     * 方便链式调用
+     *
+     * @param key 键
+     * @param value 值
+     * @return 数据对象
+     */
+    @Override
+    public AjaxResult put(String key, Object value)
+    {
+        super.put(key, value);
+        return this;
+    }
+
+    /**
+     * 返回成功消息
+     *
+     * @return 成功消息
+     */
+    public static AjaxResult success()
+    {
+        return AjaxResult.success("操作成功");
+    }
+
+    /**
+     * 返回成功数据
+     *
+     * @return 成功消息
+     */
+    public static AjaxResult success(Object data)
+    {
+        return AjaxResult.success("操作成功", data);
+    }
+
+    /**
+     * 返回成功消息
+     *
+     * @param msg 返回内容
+     * @return 成功消息
+     */
+    public static AjaxResult success(String msg)
+    {
+        return AjaxResult.success(msg, null);
+    }
+
+    /**
+     * 返回成功消息
+     *
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 成功消息
+     */
+    public static AjaxResult success(String msg, Object data)
+    {
+        return new AjaxResult(Type.SUCCESS, msg, data);
+    }
+
+    /**
+     * 返回警告消息
+     *
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static AjaxResult warn(String msg)
+    {
+        return AjaxResult.warn(msg, null);
+    }
+
+    /**
+     * 返回警告消息
+     *
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 警告消息
+     */
+    public static AjaxResult warn(String msg, Object data)
+    {
+        return new AjaxResult(Type.WARN, msg, data);
+    }
+
+    /**
+     * 返回错误消息
+     *
+     * @return
+     */
+    public static AjaxResult error()
+    {
+        return AjaxResult.error("操作失败");
+    }
+
+    /**
+     * 返回错误消息
+     *
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static AjaxResult error(String msg)
+    {
+        return AjaxResult.error(msg, null);
+    }
+
+    /**
+     * 返回错误消息
+     *
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 警告消息
+     */
+    public static AjaxResult error(String msg, Object data)
+    {
+        return new AjaxResult(Type.ERROR, msg, data);
+    }
+}

+ 1 - 1
src/main/java/com/metro/baseRe/BaseEntity.java

@@ -15,7 +15,7 @@ import lombok.Data;
 public class BaseEntity {
 
     @ApiModelProperty("当前页数")
-    private Integer pageNo = 1;
+    private Integer pageNum = 1;
 
     @ApiModelProperty("每页条数")
     private Integer pageSize = 10;

+ 119 - 0
src/main/java/com/metro/config/Config.java

@@ -0,0 +1,119 @@
+package com.metro.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 全局配置类
+ *
+ * @author lws
+ */
+@Component
+@ConfigurationProperties(prefix = "metro")
+public class Config {
+    /**
+     * 项目名称
+     */
+    private static String name;
+
+    /**
+     * 版本
+     */
+    private static String version;
+
+    /**
+     * 版权年份
+     */
+    private static String copyrightYear;
+
+    /**
+     * 实例演示开关
+     */
+    private static boolean demoEnabled;
+
+    /**
+     * 上传路径
+     */
+    private static String profile;
+
+    /**
+     * 获取地址开关
+     */
+    private static boolean addressEnabled;
+
+    public static String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        Config.name = name;
+    }
+
+    public static String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        Config.version = version;
+    }
+
+    public static String getCopyrightYear() {
+        return copyrightYear;
+    }
+
+    public void setCopyrightYear(String copyrightYear) {
+        Config.copyrightYear = copyrightYear;
+    }
+
+    public static boolean isDemoEnabled() {
+        return demoEnabled;
+    }
+
+    public void setDemoEnabled(boolean demoEnabled) {
+        Config.demoEnabled = demoEnabled;
+    }
+
+    public static String getProfile() {
+        return profile;
+    }
+
+    public void setProfile(String profile) {
+        Config.profile = profile;
+    }
+
+    public static boolean isAddressEnabled() {
+        return addressEnabled;
+    }
+
+    public void setAddressEnabled(boolean addressEnabled) {
+        Config.addressEnabled = addressEnabled;
+    }
+
+    /**
+     * 获取导入上传路径
+     */
+    public static String getImportPath() {
+        return getProfile() + "/import";
+    }
+
+    /**
+     * 获取头像上传路径
+     */
+    public static String getAvatarPath() {
+        return getProfile() + "/avatar";
+    }
+
+    /**
+     * 获取下载路径
+     */
+    public static String getDownloadPath() {
+        return getProfile() + "/download/";
+    }
+
+    /**
+     * 获取上传路径
+     */
+    public static String getUploadPath() {
+        return getProfile() + "/upload";
+    }
+}

+ 114 - 0
src/main/java/com/metro/constant/Constants.java

@@ -0,0 +1,114 @@
+package com.metro.constant;
+
+/**
+ * 通用常量信息
+ * 
+ * @author lws
+ */
+public class Constants {
+    /**
+     * UTF-8 字符集
+     */
+    public static final String UTF8 = "UTF-8";
+
+    /**
+     * GBK 字符集
+     */
+    public static final String GBK = "GBK";
+
+    /**
+     * http请求
+     */
+    public static final String HTTP = "http://";
+
+    /**
+     * https请求
+     */
+    public static final String HTTPS = "https://";
+
+    /**
+     * 通用成功标识
+     */
+    public static final String SUCCESS = "0";
+
+    /**
+     * 通用失败标识
+     */
+    public static final String FAIL = "1";
+
+    /**
+     * 登录成功
+     */
+    public static final String LOGIN_SUCCESS = "Success";
+
+    /**
+     * 注销
+     */
+    public static final String LOGOUT = "Logout";
+
+    /**
+     * 注册
+     */
+    public static final String REGISTER = "Register";
+
+    /**
+     * 登录失败
+     */
+    public static final String LOGIN_FAIL = "Error";
+
+    /**
+     * 系统用户授权缓存
+     */
+    public static final String SYS_AUTH_CACHE = "sys-authCache";
+
+    /**
+     * 参数管理 cache name
+     */
+    public static final String SYS_CONFIG_CACHE = "sys-config";
+
+    /**
+     * 参数管理 cache key
+     */
+    public static final String SYS_CONFIG_KEY = "sys_config:";
+
+    /**
+     * 字典管理 cache name
+     */
+    public static final String SYS_DICT_CACHE = "sys-dict";
+
+    /**
+     * 字典管理 cache key
+     */
+    public static final String SYS_DICT_KEY = "sys_dict:";
+
+    /**
+     * 资源映射路径 前缀
+     */
+    public static final String RESOURCE_PREFIX = "/profile";
+
+    /**
+     * RMI 远程方法调用
+     */
+    public static final String LOOKUP_RMI = "rmi:";
+
+    /**
+     * LDAP 远程方法调用
+     */
+    public static final String LOOKUP_LDAP = "ldap:";
+
+    /**
+     * LDAPS 远程方法调用
+     */
+    public static final String LOOKUP_LDAPS = "ldaps:";
+
+    /**
+     * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
+     */
+    public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" };
+
+    /**
+     * 定时任务违规的字符
+     */
+    public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
+            "org.springframework", "org.apache", "com.ruoyi.common.utils.file" };
+}

+ 74 - 22
src/main/java/com/metro/controller/AccountController.java

@@ -1,24 +1,35 @@
 package com.metro.controller;
 
+import com.auth0.jwt.JWT;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.metro.baseRe.AjaxResult;
 import com.metro.baseRe.BaseResponse;
-import com.metro.entity.po.Excel;
+import com.metro.config.Config;
+import com.metro.entity.model.UserOperateModel;
+import com.metro.entity.po.Account;
 import com.metro.entity.ro.account.*;
 import com.metro.restful.RestResponse;
 import com.metro.restful.RestResult;
 import com.metro.security.Authority;
+import com.metro.security.JWTUtils;
 import com.metro.service.AccountService;
-import com.metro.utils.FileUtils;
+import com.metro.utils.StringUtils;
+import com.metro.utils.file.FileUtils;
+import com.metro.utils.poi.ExcelUtil;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
-import lombok.SneakyThrows;
+import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
+import sun.plugin.javascript.navig4.Window;
+
+import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
-import java.io.*;
+import java.awt.*;
 import java.util.List;
 
 /**
@@ -32,6 +43,7 @@ import java.util.List;
 @RestController
 @RequestMapping("/account")
 @Api(tags = "账号管理")
+@Log4j2
 public class AccountController extends BaseController {
 
     @Autowired
@@ -39,6 +51,7 @@ public class AccountController extends BaseController {
 
     /**
      * 登录
+     *
      * @param req 账号密码
      * @return 是否成功
      */
@@ -48,9 +61,17 @@ public class AccountController extends BaseController {
     public BaseResponse<LoginAccountResp> doLogin(@Valid @RequestBody LoginAccountReq req) {
         return BaseResponse.ok(service.login(req));
     }
+    @ApiOperation("后台管理退出登录")
+    @Authority(required = false)
+    @GetMapping("/doLogout")
+    public RestResult<Boolean> doLogout() {
+        return RestResponse.ok(service.logout());
+    }
+
 
     /**
      * 获取用户信息
+     *
      * @return 是否成功
      */
     @ApiOperation("获取用户信息")
@@ -59,6 +80,10 @@ public class AccountController extends BaseController {
         return RestResponse.ok(service.getUserInfo(getUserId()));
     }
 
+
+
+
+
     /**
      * 账号列表查询
      *
@@ -120,6 +145,7 @@ public class AccountController extends BaseController {
     @Authority
     @ApiOperation("帐号详情")
     public BaseResponse<DetailAccountResp> detail(@Valid @RequestBody DetailAccountReq req) {
+
         return BaseResponse.ok(service.detail(req));
     }
 
@@ -150,42 +176,68 @@ public class AccountController extends BaseController {
     }
 
     /**
-     *  预览照片
+     * 预览照片
+     *
      * @param response response
-     * @param fileUrl fileUrl
+     * @param fileUrl  fileUrl
      */
     @ApiOperation("预览照片")
     @Authority
     @GetMapping("/showPhoto")
     public BaseResponse<Integer> showPhoto(HttpServletResponse response, @RequestParam("fileUrl") @ApiParam("文件路径") String fileUrl) {
         service.showPhoto(response, fileUrl);
-       return BaseResponse.ok();
+        return BaseResponse.ok();
     }
 
     /**
-     * 下载文件:将输入流中的数据循环写入到响应输出流中
+     * 通用下载请求
      *
-     * @param path     指想要下载的文件的路径
-     * @param response response
+     * @param fileName 文件名称
+     * @param delete   是否删除
      */
-    @ApiOperation("下载文件")
     @GetMapping("/download")
-    public BaseResponse<Integer> downloadLocal(String path, HttpServletResponse response) throws IOException {
-        FileUtils.downLoadPhoto(path, response);
-        return BaseResponse.ok();
+    @ApiOperation("下载文件")
+    public void fileDownload(String fileName, Boolean delete, HttpServletResponse response) {
+        try {
+            if (!FileUtils.checkAllowDownload(fileName)) {
+                throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
+            }
+            String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
+            String filePath = Config.getDownloadPath() + fileName;
+
+            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
+            FileUtils.setAttachmentResponseHeader(response, realFileName);
+            FileUtils.writeBytes(filePath, response.getOutputStream());
+            if (delete) {
+                FileUtils.deleteFile(filePath);
+            }
+        } catch (Exception e) {
+            log.error("下载文件失败", e);
+        }
     }
 
+
     /**
-     * 导入excel 用户数据
-     * @param file file
+     * 下载模板
      */
-    @ApiOperation("导入")
-    @GetMapping("/importSheet")
-    public BaseResponse<Integer> importSheet(MultipartFile file) {
-        String filePath = "C:\\Users\\任瑞鑫\\Desktop\\新建 XLSX 工作表.xlsx";
-        service.importSheet(filePath);
-        return BaseResponse.ok();
+    @GetMapping("/importTemplate")
+    @ApiOperation("获取下载文件名")
+    public AjaxResult importTemplate()
+    {
+        ExcelUtil<UserOperateModel> util = new ExcelUtil<UserOperateModel>(UserOperateModel.class);
+        return util.importTemplateExcel("用户数据");
+    }
+    /*
+    * 导入
+    * */
+    @PostMapping("/importData")
+    public AjaxResult importData(MultipartFile file,String type) throws Exception {
+        ExcelUtil<UserOperateModel> util = new ExcelUtil<>(UserOperateModel.class);
+        List<UserOperateModel> userList = util.importExcel(file.getInputStream());
+        return AjaxResult.success(service.importUser(userList,type));
     }
+
+
 }
 
 

+ 8 - 1
src/main/java/com/metro/controller/MetroController.java

@@ -1,6 +1,8 @@
 package com.metro.controller;
 
 import com.metro.baseRe.BaseResponse;
+import com.metro.entity.po.Metro;
+import com.metro.entity.ro.metro.SelectMetroParentResp;
 import com.metro.entity.ro.metro.SelectMetroReq;
 import com.metro.entity.ro.metro.SelectMetroResp;
 import com.metro.service.MetroService;
@@ -10,7 +12,10 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
+import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * @ClassName: 地铁
@@ -42,7 +47,9 @@ public class MetroController {
 
     @PostMapping("/selectAll")
     @ApiOperation("地铁列表")
-    public BaseResponse<List<SelectMetroResp>> selectMetroAll() {
+    public BaseResponse<List<SelectMetroParentResp>> selectMetroAll() {
         return BaseResponse.ok(service.selectMetroAll());
     }
+
+
 }

+ 22 - 4
src/main/java/com/metro/controller/PassreCordsController.java

@@ -2,9 +2,14 @@ package com.metro.controller;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.metro.baseRe.BaseResponse;
-import com.metro.entity.ro.passrecords.SelectPassreCordsPageListReq;
-import com.metro.entity.ro.passrecords.SelectPassreCordsPageListResp;
+import com.metro.entity.ro.Police.InsertPoliceReq;
+import com.metro.entity.ro.Police.InsertPoliceResp;
+import com.metro.entity.ro.account.InsertAccountReq;
+import com.metro.entity.ro.account.InsertAccountResp;
+import com.metro.entity.ro.passrecords.*;
+import com.metro.security.Authority;
 import com.metro.service.PassreCordsService;
+import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
@@ -20,7 +25,8 @@ import javax.validation.Valid;
  * ...
  */
 @RestController
-@RequestMapping("/passrecords")
+@RequestMapping("/passRecords")
+@Api(tags = "盘查")
 public class PassreCordsController {
 
     @Autowired
@@ -32,9 +38,21 @@ public class PassreCordsController {
      * @return page列表
      */
     @PostMapping("/page")
-    @ApiOperation("查询公司信息")
+    @ApiOperation("盘查记录")
     public BaseResponse<IPage<SelectPassreCordsPageListResp>> page (@Valid @RequestBody SelectPassreCordsPageListReq req) {
         return BaseResponse.ok(passreCordsservice.page(req));
     }
+    @PostMapping("/delete")
+    @ApiOperation("删除盘查记录")
+    public BaseResponse<DeletePassreCordsResq> delete(@Valid @RequestBody DeletePassreCordsReq req) {
+        return BaseResponse.ok(passreCordsservice.delete(req));
+    }
+
+    @PostMapping("/selectById")
+    @ApiOperation("查看详情")
+    public BaseResponse<DetialPassreCordsResq> selectById(@Valid @RequestBody DetailPassreCordsReq req) {
+        return BaseResponse.ok(passreCordsservice.selectById(req));
+    }
+
 
 }

+ 75 - 0
src/main/java/com/metro/controller/PolicController.java

@@ -0,0 +1,75 @@
+package com.metro.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.metro.baseRe.BaseResponse;
+import com.metro.entity.ro.Police.*;
+
+import com.metro.entity.ro.account.*;
+import com.metro.security.Authority;
+import com.metro.service.PoliceService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+
+/**
+ * @ClassName: PolicController
+ * @Description: TODO
+ * @Author: ZS
+ * @CreateName: lws
+ * @Date 2022/3/18 0:53
+ * ...
+ */
+@RestController
+@RequestMapping("/police")
+@Api(tags = "派出所管理")
+public class PolicController {
+    @Autowired
+    private PoliceService service;
+
+
+    @PostMapping("/page")
+    @Authority
+    @ApiOperation("账号列表查询")
+    public BaseResponse<IPage<SelectPolicePageResp>> page(@Valid @RequestBody(required = false) SelectPolicePageReq req) {
+        return BaseResponse.ok(service.page(req));
+    }
+    @PostMapping("/add")
+    @Authority
+    @ApiOperation("添加账号")
+    public BaseResponse<InsertPoliceResp> add(@Valid @RequestBody InsertPoliceReq req) {
+        return BaseResponse.ok(service.add(req));
+    }
+    /**
+     * 账号详情
+     *
+     * @param req 主键
+     * @return 账号详情
+     */
+    @PostMapping("/detail")
+    @Authority
+    @ApiOperation("帐号详情")
+    public BaseResponse<DetailPoliceResp> detail(@Valid @RequestBody DetailPoliceReq req) {
+        return BaseResponse.ok(service.detail(req));
+    }
+
+    @PostMapping("/update")
+    @Authority
+    @ApiOperation("修改账号")
+    public BaseResponse<UpdateAccountResp> update(@Valid @RequestBody UpdateAccountReq req) {
+        return BaseResponse.ok(service.update(req));
+    }
+    @PostMapping("/delete")
+    @Authority
+    @ApiOperation("删除账号")
+    public BaseResponse<DeleteAccountResp> delete(@Valid @RequestBody DeleteAccountReq req) {
+        return BaseResponse.ok(service.delete(req));
+    }
+
+
+}

+ 0 - 58
src/main/java/com/metro/controller/QuestionController.java

@@ -1,58 +0,0 @@
-package com.metro.controller;
-
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.metro.baseRe.BaseResponse;
-import com.metro.entity.ro.Question.*;
-import com.metro.service.QuestionService;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import javax.validation.Valid;
-
-/**
- * @ClassName: PoliceController
- * @Description: TODO
- * @Author: ZS
- * @CreateName: lws
- * @Date 2022/3/9 11:06
- * ...
- */
-@RestController
-@RequestMapping("/question")
-@Api(tags = "盘查记录")
-public class QuestionController {
-
-    @Autowired
-    private QuestionService service;
-
-    /**
-     * 派出所管理列表查询
-     *
-     * @param req 查询条件
-     * @return page列表*/
-    @PostMapping("/page")
-    @ApiOperation("盘查记录列表")
-    public BaseResponse<IPage<SelectQuestionPageResp>> page(@Valid @RequestBody SelectQuestionPageReq req) {
-        return BaseResponse.ok(service.page(req));
-    }
-
-    @PostMapping("/delete")
-    @ApiOperation("删除盘查记录")
-    public BaseResponse<DeleteQuestionResq> delete(@Valid @RequestBody DeleteQuestionReq req) {
-        return BaseResponse.ok(service.delete(req));
-    }
-
-    @PostMapping("/selectById")
-    @ApiOperation("查看详情")
-    public BaseResponse<SelectQuestionPageResp> selectById(@Valid @RequestBody SelectQuestionPageReq req) {
-        return BaseResponse.ok(service.selectById(req));
-    }
-
-
-
-}

+ 0 - 66
src/main/java/com/metro/controller/Time.java

@@ -1,66 +0,0 @@
-package com.metro.controller;
-
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.utils.URIBuilder;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.util.EntityUtils;
-
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.springframework.scheduling.annotation.Scheduled;
-import springfox.documentation.spring.web.json.Json;
-
-/**
- * @ClassName: Time
- * @Description: TODO
- * @Author: ZS
- * @CreateName: lws
- * @Date 2022/3/16 11:33
- * ...
- */
-
-public class Time {
-   /* @Scheduled(cron = "0 0 5 * * ?")
-    public void  test() {
-
-
-    }
-
-    public static String doPostData() throws Exception {
-        String url1 = "http:// 115.236.38.205:8903/id/hvn";
-
-
-
-        HttpPost post = new HttpPost(url1);
-        String result = "";
-        HttpResponse res = null;
-        try {
-            StringEntity s = new StringEntity(json.toString(), "UTF-8");
-            s.setContentType("application/json");
-            post.setHeader("Accept", "application/json");
-            post.setHeader("Content-type", "application/json; charset=utf-8");
-            post.setEntity(s);
-            res = client.execute(post);
-            if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
-                result = EntityUtils.toString(res.getEntity());
-                return HttpStatus.SC_OK + "";
-            }
-        } catch (Exception e) {
-            if(res == null) {
-                return "HttpResponse 为 null!";
-            }
-            throw new RuntimeException(e);
-        }
-        if(res == null || res.getStatusLine() == null) {
-            return "无响应";
-        }
-        return res.getStatusLine().getStatusCode() + "";
-    }
-*/
-}

+ 40 - 0
src/main/java/com/metro/entity/model/UserOperateModel.java

@@ -0,0 +1,40 @@
+package com.metro.entity.model;
+
+
+import com.metro.annotation.Excel;
+import com.metro.annotation.Excel.Type;
+import lombok.Data;
+
+
+import java.util.Date;
+
+@Data
+public class UserOperateModel {
+    private static final long serialVersionUID = 1L;
+
+    @Excel(name = "账号")
+    private String account;
+
+    @Excel(name = "用户姓名")
+    private String userName;
+
+    @Excel(name = "用户手机")
+    private String phone;
+
+    @Excel(name = "用户密码")
+    private String password;
+
+    @Excel(name = "警号")
+    private String policeId;
+
+    @Excel(name = "详细地址")
+    private String address;
+
+    @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT)
+    private Date createTime;
+
+    public UserOperateModel() {
+
+    }
+
+}

+ 7 - 32
src/main/java/com/metro/entity/po/Metro.java

@@ -1,5 +1,6 @@
 package com.metro.entity.po;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
@@ -20,37 +21,11 @@ import java.util.List;
 @TableName("j_metro")
 public class Metro implements Serializable {
 
-    @TableId(value = "id")
-    private Integer id;
-
-    /**
-     * 线路Id
-     */
-    private String lineId;
-
-    /**
-     * 地铁线路名
-     */
-    private String lineName;
-
-    /**
-     * 地铁站Id
-     */
-    private String metroId;
-
-    /**
-     * 地铁站名
-     */
-    private String metroName;
-
-    /**
-     * 盘查口Id
-     */
-    private String questionId;
-
-    /**
-     * 盘查口名
-     */
-    private String questionName;
+    @TableId
+    private String id;
+    private String name;
+    private String pid;
+    private Integer level;
+    private Integer sort;
 
 }

+ 22 - 9
src/main/java/com/metro/entity/po/Question.java → src/main/java/com/metro/entity/po/PassreCords.java

@@ -20,7 +20,7 @@ import java.util.Date;
 @TableName("j_passrecords")
 @Data
 @Accessors(chain = true)
-public class Question {
+public class PassreCords {
     /**
      * id
      */
@@ -91,16 +91,16 @@ public class Question {
      */
     private String warningMsg;
 
- /*   *//**
+    /**
      * 身份证照片
-     *//*
-    private int idPhoto;
-*/
- /*   *//**
+     **/
+    private String idPhoto;
+
+    /**
      * 现场照片
-     *//*
-    private int scenePhoto;
-*/
+     **/
+
+    private String scenePhoto;
     /**
      * 身份证地区
      */
@@ -172,8 +172,21 @@ public class Question {
      * */
     private Boolean isDelete;
 
+
+    /*
+    * 创建时间
+    * */
+    private LocalDateTime createTime;
+
     /*
     * 修改时间
     * */
     private LocalDateTime updateTimestamp;
+
+    /*
+    * 警示记录
+    * */
+    private String warning;
+
+
 }

+ 58 - 56
src/main/java/com/metro/entity/po/Police.java

@@ -1,8 +1,6 @@
 package com.metro.entity.po;
 
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.*;
 import io.swagger.models.auth.In;
 import lombok.Data;
 import lombok.experimental.Accessors;
@@ -18,86 +16,90 @@ import java.time.LocalDateTime;
  * ...
  */
 @Data
-@TableName("j_persons")
+@TableName("j_account")
 @Accessors(chain = true)
 public class Police {
-    /**
-     * id
-     */
     @TableId(value = "id",type = IdType.AUTO)
     private Integer id;
-    /*
-    * 公安id
-    * */
-    private String companyId;
 
-    /*
-     * 派出所id
-     * */
-    private String branchId;
+    /**
+     * 账号名称
+     */
+    private String account;
 
-    /*
-     * 身份证
-     * */
-    private String cardId;
+    /**
+     * 管理员姓名
+     */
+    private String userName;
 
-    /*
+    /**
+     * 账号密码
+     */
+    private String password;
+
+    /**
+     * 管理员手机号
+     */
+    private String phone;
+
+    /**
+     * 用户类型 1:管理员 2:派出所 3
+     */
+    private Integer type;
+
+    /**
      * 地区id
-     * */
+     */
     private Integer areaId;
 
-    /*
-     * 姓名
-     * */
-    private String xm;
-
-    /*
-     * 性别
-     * */
-    private String xb;
+    /**
+     * 详细地址
+     */
+    private String address;
 
-    /*
-     * 照片
-     * */
-    private String photo;
+    /**
+     * 警号Id
+     */
+    private String policeId;
 
-    /*
-     * 手机号
-     * */
-    private String phone;
-    /*
-     * 部门id?
-     * */
-    private Integer isAdmin;
+    /**
+     * 部门Id
+     */
+    private String companyId;
 
+    /**
+     * 创建时间
+     */
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+    public void setCreateTime(LocalDateTime createTime) {
+        this.createTime = LocalDateTime.now();
+    }
     /*
-     * 备注
+     * 创建人
      * */
-    private String bz;
 
-    /*
-     * 注册时间
-     * */
-    private LocalDateTime regTime;
-    public void regTime(LocalDateTime regTime) {
-        this.regTime = LocalDateTime.now();
-    }
+    private Integer createId;
 
     /*
      * 修改时间
      * */
-    private LocalDateTime updateTimestamp;
-    private Integer flag;
+    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
 
     /*
-     * 密码
+     * 修改人
      * */
-    private String passwd;
-
+    private Integer updateId;
 
     /*
      * 是否删除 true 删除    false 不删除
      * */
     private Boolean isDelete;
 
+    /*
+     * 状态  true 正常    false 停用
+     * */
+    private Boolean status;
+
 }

+ 4 - 4
src/main/java/com/metro/entity/ro/Question/DeleteQuestionReq.java → src/main/java/com/metro/entity/ro/Police/DeletePoliceReq.java

@@ -1,9 +1,9 @@
-package com.metro.entity.ro.Question;
+package com.metro.entity.ro.Police;
 
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
-import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
 import java.io.Serializable;
 
 /**
@@ -15,9 +15,9 @@ import java.io.Serializable;
  * ...
  */
 @Data
-public class DeleteQuestionReq implements Serializable {
+public class DeletePoliceReq implements Serializable {
 
     @ApiModelProperty("主键")
-    @NotEmpty(message = "主键为必填项!")
+    @NotNull(message = "主键为必填项!")
     private Integer id;
 }

+ 2 - 2
src/main/java/com/metro/entity/ro/Question/DeleteQuestionResq.java → src/main/java/com/metro/entity/ro/Police/DeletePoliceResp.java

@@ -1,4 +1,4 @@
-package com.metro.entity.ro.Question;
+package com.metro.entity.ro.Police;
 
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -16,7 +16,7 @@ import java.io.Serializable;
  */
 @Data
 @Accessors(chain = true)
-public class DeleteQuestionResq implements Serializable {
+public class DeletePoliceResp implements Serializable {
 
     @ApiModelProperty("影响行数")
     private Integer count;

+ 23 - 0
src/main/java/com/metro/entity/ro/Police/DetailPoliceReq.java

@@ -0,0 +1,23 @@
+package com.metro.entity.ro.Police;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @ClassName: DetailQuestionReq
+ * @Description: TODO
+ * @Author: ZS
+ * @CreateName: lws
+ * @Date 2022/3/16 14:56
+ * ...
+ */
+@Data
+@ApiModel
+public class DetailPoliceReq  implements Serializable {
+    @ApiModelProperty("id")
+    private Integer id;
+
+}

+ 78 - 0
src/main/java/com/metro/entity/ro/Police/DetailPoliceResp.java

@@ -0,0 +1,78 @@
+package com.metro.entity.ro.Police;
+
+import com.metro.enums.UserEnums;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * @ClassName: DetailPoliceResp
+ * @Description: TODO
+ * @Author: ZS
+ * @CreateName: lws
+ * @Date 2022/3/18 9:46
+ * ...
+ */
+@Data
+@ApiModel
+public class DetailPoliceResp {
+    @ApiModelProperty(value = "id")
+    private Integer id;
+
+    @ApiModelProperty(value = "账号名称")
+    private String account;
+
+    @ApiModelProperty(value = "管理员姓名")
+    private String userName;
+
+    @ApiModelProperty(value = "管理员手机号")
+    private String phone;
+
+    @ApiModelProperty(value = "地区Id")
+    private String areaId;
+
+    @ApiModelProperty(value = "地区中文")
+    private String areaDesc;
+
+    @ApiModelProperty(value = "省区id")
+    private String provinceId;
+
+    @ApiModelProperty(value = "市区id,")
+    private String cityId;
+
+    @ApiModelProperty(value = "用户类行 1admin 2分局管理员 3,")
+    private Integer type;
+
+    @ApiModelProperty(value = "详细地址")
+    private String address;
+
+    @ApiModelProperty(value = "创建时间")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty(value = "创建人")
+    private LocalDateTime createId;
+
+    @ApiModelProperty(value = "修改时间")
+    private LocalDateTime updateTime;
+
+    @ApiModelProperty(value = "修改人")
+    private LocalDateTime updateId;
+
+    @ApiModelProperty(value = "是否删除")
+    private boolean isDelete;
+    public String getIsDeleteName(){
+        return UserEnums.deleteEnums.getStatusName(this.isDelete);
+    }
+
+    @ApiModelProperty(value = "账号状态码")
+    private boolean status;
+
+    @ApiModelProperty(value = "账号状态")
+    private String statusNme;
+    public String getStatusName(){
+        return UserEnums.statusEnums.getStatusName(this.status);
+    }
+
+}

+ 40 - 0
src/main/java/com/metro/entity/ro/Police/InsertPoliceReq.java

@@ -0,0 +1,40 @@
+package com.metro.entity.ro.Police;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+
+/**
+ * @ClassName: InsertPoliceReq
+ * @Description: TODO
+ * @Author: ZS
+ * @CreateName: lws
+ * @Date 2022/3/18 9:33
+ * ...
+ */
+@Data
+public class InsertPoliceReq implements Serializable {
+    @ApiModelProperty(value = "账号名称")
+    private String account;
+
+    @ApiModelProperty(value = "管理员姓名")
+    private String userName;
+
+    @ApiModelProperty(value = "管理员手机号")
+    private String phone;
+
+    @ApiModelProperty(value = "密码")
+    private String password;
+
+    @ApiModelProperty(value = "地区Id")
+    private Integer areaId;
+
+    @ApiModelProperty(value = "详细地址")
+    private String address;
+
+    @ApiModelProperty("警号Id")
+    private String policeId;
+
+}

+ 7 - 4
src/main/java/com/metro/entity/ro/Question/InsertQuestionResq.java → src/main/java/com/metro/entity/ro/Police/InsertPoliceResp.java

@@ -1,20 +1,23 @@
-package com.metro.entity.ro.Question;
+package com.metro.entity.ro.Police;
 
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.experimental.Accessors;
 
 /**
- * @ClassName: InsertPoliceResq
+ * @ClassName: InsertPoliceResp
  * @Description: TODO
  * @Author: ZS
  * @CreateName: lws
- * @Date 2022/3/14 15:25
+ * @Date 2022/3/18 9:35
  * ...
  */
+
 @Data
 @Accessors(chain = true)
-public class InsertQuestionResq {
+public class InsertPoliceResp {
+
     @ApiModelProperty("影响行数")
     private Integer count;
+
 }

+ 8 - 5
src/main/java/com/metro/entity/ro/Question/SelectQuestionPageReq.java → src/main/java/com/metro/entity/ro/Police/SelectPolicePageReq.java

@@ -1,4 +1,4 @@
-package com.metro.entity.ro.Question;
+package com.metro.entity.ro.Police;
 
 import com.metro.baseRe.BaseEntity;
 import io.swagger.annotations.ApiModel;
@@ -19,15 +19,18 @@ import java.io.Serializable;
 @EqualsAndHashCode(callSuper = true)
 @Data
 @ApiModel
-public class SelectQuestionPageReq extends BaseEntity implements Serializable {
+public class SelectPolicePageReq extends BaseEntity implements Serializable {
 
-    @ApiModelProperty("id")
-    private Integer id;
+    @ApiModelProperty("派出所名称")
+    private String companyId;
 
     @ApiModelProperty("姓名")
-    private String xm;
+    private String userName;
 
     @ApiModelProperty("手机号")
     private String phone;
 
+    @ApiModelProperty("派出所地址")
+    private String address;
+
 }

+ 44 - 0
src/main/java/com/metro/entity/ro/Police/SelectPolicePageResp.java

@@ -0,0 +1,44 @@
+package com.metro.entity.ro.Police;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.util.Date;
+
+/**
+ * @ClassName: SelectPassreCordsPageListResp
+ * @Description: TODO
+ * @Author: ZS
+ * @CreateName: lws
+ * @Date 2022/3/13 14:48
+ * ...
+ */
+@Data
+@ApiModel
+@Accessors(chain = true)
+public class SelectPolicePageResp implements Serializable {
+
+    @ApiModelProperty("ID")
+    private Integer id;
+
+    @ApiModelProperty(value = "派出所名称")
+    private String companyName;
+
+    @ApiModelProperty(value = "管理员姓名")
+    private String userName;
+
+    @ApiModelProperty(value = "管理员手机号")
+    private String phone;
+
+    @ApiModelProperty(value = "创建时间")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty(value = "派出所地址")
+    private String address;
+
+
+}

+ 0 - 56
src/main/java/com/metro/entity/ro/Question/InsertQuestionReq.java

@@ -1,56 +0,0 @@
-package com.metro.entity.ro.Question;
-
-import com.metro.enums.vaildated.IsPhone;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import javax.validation.constraints.NotEmpty;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Size;
-import java.io.Serializable;
-
-/**
- * @ClassName: InsertPoliceReq
- * @Description: TODO
- * @Author: ZS
- * @CreateName: lws
- * @Date 2022/3/14 15:24
- * ...
- */
-@Data
-public class InsertQuestionReq implements Serializable {
-
-    @ApiModelProperty(value = "账号名称")
-    @NotEmpty(message = "账号名称不能为空")
-    @Size(max = 12,min = 6,message = "账号长度不能小于6、大于12")
-    private String account;
-
-    @ApiModelProperty(value = "管理员姓名")
-    @NotEmpty(message = "管理员姓名不能为空")
-    @Size(min = 2,max = 12,message = "管理员姓名不能大于12,小于2")
-    private String userName;
-
-    @ApiModelProperty(value = "管理员手机号")
-    @NotEmpty(message = "管理员手机号不能为空")
-    @IsPhone(message = "手机号格式不正确!")
-    @Size(min = 11,max = 11,message = "手机号格式不正确!")
-    private String phone;
-
-    @ApiModelProperty(value = "密码")
-    @NotEmpty(message = "密码不能为空")
-    @Size(max = 12,min = 6,message = "密码长度不能小于6、大于12")
-    private String password;
-
-    @ApiModelProperty(value = "地区Id")
-    @NotNull(message = "请选择地址!")
-    private Integer areaId;
-
-    @ApiModelProperty(value = "详细地址")
-    @NotEmpty(message = "详细地址不能为空!")
-    @Size(max = 1000 ,min = 1, message = "详细地址不能大于1000个字符,小于1")
-    private String address;
-
-    @ApiModelProperty("警号Id")
-    @NotEmpty(message = "警号Id必填")
-    private String policeId;
-}

+ 0 - 50
src/main/java/com/metro/entity/ro/Question/SelectQuestionPageResp.java

@@ -1,50 +0,0 @@
-package com.metro.entity.ro.Question;
-
-import com.metro.baseRe.BaseEntity;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-
-/**
- * @ClassName: SelectPassreCordsPageListResp
- * @Description: TODO
- * @Author: ZS
- * @CreateName: lws
- * @Date 2022/3/13 14:48
- * ...
- */
-@Data
-@ApiModel
-@Accessors(chain = true)
-public class SelectQuestionPageResp implements Serializable {
-
-    @ApiModelProperty("id")
-    private Integer id;
-
-    @ApiModelProperty("派出所名称")
-    private String brachName;
-
-    @ApiModelProperty("姓名")
-    private String xm;
-
-    @ApiModelProperty("照片")
-    private String photo;
-
-    @ApiModelProperty(value ="手机号")
-    private String phone;
-
-    @ApiModelProperty(value = "住址")
-    private Integer address;
-
-    @ApiModelProperty(value = "验证方式")
-    private String areaDesc;
-
-    @ApiModelProperty(value = "是否通行")
-    private String displayMsg;
-
-
-
-}

+ 1 - 1
src/main/java/com/metro/entity/ro/account/DetailAccountResp.java

@@ -39,7 +39,7 @@ public class DetailAccountResp implements Serializable {
     private String areaDesc;
 
     @ApiModelProperty(value = "省区id")
-    private String proviceId;
+    private String provinceId;
 
     @ApiModelProperty(value = "市区id,")
     private String cityId;

+ 2 - 2
src/main/java/com/metro/entity/ro/account/InsertAccountReq.java

@@ -39,8 +39,8 @@ public class InsertAccountReq implements Serializable {
     @ApiModelProperty(value = "详细地址")
     private String address;
 
-    @ApiModelProperty("文件路径")
-    private String fileUrl;
+    @ApiModelProperty("用户类型")
+    private Integer type;
 
     @ApiModelProperty("警号Id")
     private String policeId;

+ 3 - 0
src/main/java/com/metro/entity/ro/account/LoginAccountResp.java

@@ -30,6 +30,9 @@ public class LoginAccountResp {
     @ApiModelProperty(value = "密码",hidden = true)
     private String passWord;
 
+    @ApiModelProperty("用户类型")
+    private Integer type;
+
     @ApiModelProperty(value = "token")
     private String token;
 

+ 6 - 0
src/main/java/com/metro/entity/ro/account/SelectAccountPageResp.java

@@ -70,6 +70,12 @@ public class SelectAccountPageResp implements Serializable {
     @ApiModelProperty(value = "账号状态码")
     private boolean status;
 
+    @ApiModelProperty(value = "警号id")
+    private String policeId;
+
+    @ApiModelProperty(value = "照片")
+    private String fileName;
+
     @ApiModelProperty(value = "账号状态")
     private String statusNme;
     public String getStatusName(){

+ 3 - 0
src/main/java/com/metro/entity/ro/account/UpdateAccountReq.java

@@ -41,6 +41,9 @@ public class UpdateAccountReq implements Serializable {
     @ApiModelProperty(value = "详细地址")
     private String address;
 
+    @ApiModelProperty(value = "人员类型")
+    private Integer type;
+
     @ApiModelProperty("文件路径")
     private String fileUrl;
 

+ 40 - 0
src/main/java/com/metro/entity/ro/metro/SelectMetroParentResp.java

@@ -0,0 +1,40 @@
+package com.metro.entity.ro.metro;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @ClassName: Metro
+ * @Description: TODO
+ * @Author: ZS
+ * @CreateName: lws
+ * @Date 2022/3/9 1:24
+ * ...
+ */
+@Data
+@Accessors(chain = true)
+public class SelectMetroParentResp implements Serializable {
+
+    @ApiModelProperty("")
+    private String id;
+
+    @ApiModelProperty("")
+    private String name;
+
+    @ApiModelProperty("")
+    private String pid;
+
+    @ApiModelProperty("")
+    private Integer level;
+
+    @ApiModelProperty("")
+    private Integer sort;
+
+    @ApiModelProperty("子集")
+    private List<SelectMetroParentResp> children; //子集
+
+}

+ 2 - 5
src/main/java/com/metro/entity/ro/metro/SelectMetroReq.java

@@ -22,10 +22,7 @@ import java.util.Set;
 @Data
 public class SelectMetroReq implements Serializable {
 
-    @ApiModelProperty("Id")
-    private String id;
+    @ApiModelProperty("pid")
+    private Integer pid;
 
-    @ApiModelProperty("等级 1查询地铁线 2查询地铁站 3查询地铁盘口")
-    @NotNull(message = "等级不能为空")
-    private Integer level;
 }

+ 3 - 17
src/main/java/com/metro/entity/ro/metro/SelectMetroResp.java

@@ -20,22 +20,8 @@ import java.util.List;
 @Data
 public class SelectMetroResp implements Serializable {
 
-    @ApiModelProperty("线路Id")
-    private String lineId;
-
-    @ApiModelProperty("线路名称")
-    private String lineName;
-
-    @ApiModelProperty("地铁ID")
-    private String metroId;
-
-    @ApiModelProperty("地铁名称")
-    private String metroName;
-
-    @ApiModelProperty("盘查口ID")
-    private String questionId;
-
-    @ApiModelProperty("盘查口名称")
-    private String questionName;
+    private String id;
+    private String name;
+    private String pid;
 
 }

+ 23 - 0
src/main/java/com/metro/entity/ro/passrecords/DeletePassreCordsReq.java

@@ -0,0 +1,23 @@
+package com.metro.entity.ro.passrecords;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * @ClassName: DeletePoliceReq
+ * @Description: TODO
+ * @Author: ZS
+ * @CreateName: lws
+ * @Date 2022/3/14 16:17
+ * ...
+ */
+@Data
+public class DeletePassreCordsReq implements Serializable {
+
+    @ApiModelProperty("主键")
+    @NotNull(message = "主键为必填项!")
+    private Integer id;
+}

+ 23 - 0
src/main/java/com/metro/entity/ro/passrecords/DeletePassreCordsResq.java

@@ -0,0 +1,23 @@
+package com.metro.entity.ro.passrecords;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * @ClassName: DeletePoliceResq
+ * @Description: TODO
+ * @Author: ZS
+ * @CreateName: lws
+ * @Date 2022/3/14 16:17
+ * ...
+ */
+@Data
+@Accessors(chain = true)
+public class DeletePassreCordsResq implements Serializable {
+
+    @ApiModelProperty("影响行数")
+    private Integer count;
+}

+ 23 - 0
src/main/java/com/metro/entity/ro/passrecords/DetailPassreCordsReq.java

@@ -0,0 +1,23 @@
+package com.metro.entity.ro.passrecords;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @ClassName: DetailQuestionReq
+ * @Description: TODO
+ * @Author: ZS
+ * @CreateName: lws
+ * @Date 2022/3/16 14:56
+ * ...
+ */
+@Data
+@ApiModel
+public class DetailPassreCordsReq implements Serializable {
+    @ApiModelProperty("id")
+    private Integer id;
+
+}

+ 23 - 0
src/main/java/com/metro/entity/ro/passrecords/DetialPassreCordsResq.java

@@ -0,0 +1,23 @@
+package com.metro.entity.ro.passrecords;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+
+/**
+ * @ClassName: DetialQuestionResq
+ * @Description: TODO
+ * @Author: ZS
+ * @CreateName: lws
+ * @Date 2022/3/18 0:58
+ * ...
+ */
+@Data
+@ApiModel
+public class DetialPassreCordsResq {
+    @ApiModelProperty("警示记录")
+    private String warning;
+}

+ 7 - 5
src/main/java/com/metro/entity/ro/passrecords/SelectPassreCordsPageListReq.java

@@ -19,10 +19,12 @@ import java.io.Serializable;
 @Data
 public class SelectPassreCordsPageListReq extends BaseEntity implements Serializable {
 
-    @ApiModelProperty("")
-    private String branchName;
+    @ApiModelProperty("姓名")
+    private String xm;
 
-    @ApiModelProperty("")
-    private String staffName;
+    @ApiModelProperty("手机号")
+    private String phone;
 
-}
+    @ApiModelProperty("身份证号")
+    private String cardId;
+}

+ 28 - 19
src/main/java/com/metro/entity/ro/passrecords/SelectPassreCordsPageListResp.java

@@ -1,9 +1,12 @@
 package com.metro.entity.ro.passrecords;
 
+import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
+import lombok.experimental.Accessors;
 
 import java.io.Serializable;
+import java.time.LocalDateTime;
 import java.util.Date;
 
 /**
@@ -15,35 +18,41 @@ import java.util.Date;
  * ...
  */
 @Data
+@ApiModel
+@Accessors(chain = true)
 public class SelectPassreCordsPageListResp implements Serializable {
 
-    @ApiModelProperty("主键")
-    private Integer id;
+    private int id;
+    @ApiModelProperty(value = "姓名")
+    private String xm;
+
+    @ApiModelProperty(value = "身份证号")
+    private String cardId;
 
-    @ApiModelProperty("")
+    @ApiModelProperty(value = "现场照片")
     private String scenePhoto;
 
-    @ApiModelProperty("")
-    private String carId;
+    @ApiModelProperty(value = "手机号")
+    private String phone;
 
-    @ApiModelProperty("")
-    private String carType;
+    @ApiModelProperty(value = "住址")
+    private String cardAddr;
 
-    @ApiModelProperty("")
-    private String xm;
+    @ApiModelProperty(value = "验证方式")
+    private String cardType;
 
-    @ApiModelProperty("")
-    private String phone;
+    @ApiModelProperty(value = "是否通行0 = 阻止通行 1 = 可通行,警示信息 2 = 正常通行")
+    private String pass;
+
+    @ApiModelProperty(value = "所属派出所")
+    private String companyId;
+
+    @ApiModelProperty(value = "所属派分局")
+    private String branchId;
 
-    @ApiModelProperty("")
-    private String address;
+    @ApiModelProperty(value = "创建时间")
+    private String createTime;
 
-    @ApiModelProperty("")
-    private String passAddr;
 
-    @ApiModelProperty("")
-    private String displayMsg;
 
-    @ApiModelProperty("")
-    private String kind;
 }

+ 18 - 0
src/main/java/com/metro/exception/FileException.java

@@ -0,0 +1,18 @@
+package com.metro.exception;
+
+
+import com.metro.exception.base.BaseException;
+
+/**
+ * 文件信息异常类
+ *
+ * @author ruoyi
+ */
+public class FileException extends BaseException {
+    private static final long serialVersionUID = 1L;
+
+    public FileException(String code, Object[] args) {
+        super("file", code, args, null);
+    }
+
+}

+ 16 - 0
src/main/java/com/metro/exception/FileNameLengthLimitExceededException.java

@@ -0,0 +1,16 @@
+package com.metro.exception;
+
+/**
+ * 文件名称超长限制异常类
+ * 
+ * @author ruoyi
+ */
+public class FileNameLengthLimitExceededException extends FileException
+{
+    private static final long serialVersionUID = 1L;
+
+    public FileNameLengthLimitExceededException(int defaultFileNameLength)
+    {
+        super("upload.filename.exceed.length", new Object[] { defaultFileNameLength });
+    }
+}

+ 14 - 0
src/main/java/com/metro/exception/FileSizeLimitExceededException.java

@@ -0,0 +1,14 @@
+package com.metro.exception;
+
+/**
+ * 文件名大小限制异常类
+ *
+ * @author ruoyi
+ */
+public class FileSizeLimitExceededException extends FileException {
+    private static final long serialVersionUID = 1L;
+
+    public FileSizeLimitExceededException(long defaultMaxSize) {
+        super("upload.exceed.maxSize", new Object[]{defaultMaxSize});
+    }
+}

+ 69 - 0
src/main/java/com/metro/exception/InvalidExtensionException.java

@@ -0,0 +1,69 @@
+package com.metro.exception;
+
+import org.apache.tomcat.util.http.fileupload.FileUploadException;
+
+import java.util.Arrays;
+
+/**
+ * 文件上传 误异常类
+ *
+ * @author ruoyi
+ */
+public class InvalidExtensionException extends FileUploadException {
+    private static final long serialVersionUID = 1L;
+
+    private String[] allowedExtension;
+    private String extension;
+    private String filename;
+
+    public InvalidExtensionException(String[] allowedExtension, String extension, String filename) {
+        super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]");
+        this.allowedExtension = allowedExtension;
+        this.extension = extension;
+        this.filename = filename;
+    }
+
+    public String[] getAllowedExtension() {
+        return allowedExtension;
+    }
+
+    public String getExtension() {
+        return extension;
+    }
+
+    public String getFilename() {
+        return filename;
+    }
+
+    public static class InvalidImageExtensionException extends InvalidExtensionException {
+        private static final long serialVersionUID = 1L;
+
+        public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) {
+            super(allowedExtension, extension, filename);
+        }
+    }
+
+    public static class InvalidFlashExtensionException extends InvalidExtensionException {
+        private static final long serialVersionUID = 1L;
+
+        public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) {
+            super(allowedExtension, extension, filename);
+        }
+    }
+
+    public static class InvalidMediaExtensionException extends InvalidExtensionException {
+        private static final long serialVersionUID = 1L;
+
+        public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) {
+            super(allowedExtension, extension, filename);
+        }
+    }
+
+    public static class InvalidVideoExtensionException extends InvalidExtensionException {
+        private static final long serialVersionUID = 1L;
+
+        public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) {
+            super(allowedExtension, extension, filename);
+        }
+    }
+}

+ 22 - 0
src/main/java/com/metro/exception/UtilException.java

@@ -0,0 +1,22 @@
+package com.metro.exception;
+
+/**
+ * 工具类异常
+ *
+ * @author ruoyi
+ */
+public class UtilException extends RuntimeException {
+    private static final long serialVersionUID = 8247610319171014183L;
+
+    public UtilException(Throwable e) {
+        super(e.getMessage(), e);
+    }
+
+    public UtilException(String message) {
+        super(message);
+    }
+
+    public UtilException(String message, Throwable throwable) {
+        super(message, throwable);
+    }
+}

+ 84 - 0
src/main/java/com/metro/exception/base/BaseException.java

@@ -0,0 +1,84 @@
+package com.metro.exception.base;
+
+import com.metro.utils.MessageUtils;
+import com.metro.utils.StringUtils;
+
+/**
+ * 基础异常
+ *
+ * @author ruoyi
+ */
+public class BaseException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 所属模块
+     */
+    private String module;
+
+    /**
+     * 错误码
+     */
+    private String code;
+
+    /**
+     * 错误码对应的参数
+     */
+    private Object[] args;
+
+    /**
+     * 错误消息
+     */
+    private String defaultMessage;
+
+    public BaseException(String module, String code, Object[] args, String defaultMessage) {
+        this.module = module;
+        this.code = code;
+        this.args = args;
+        this.defaultMessage = defaultMessage;
+    }
+
+    public BaseException(String module, String code, Object[] args) {
+        this(module, code, args, null);
+    }
+
+    public BaseException(String module, String defaultMessage) {
+        this(module, null, null, defaultMessage);
+    }
+
+    public BaseException(String code, Object[] args) {
+        this(null, code, args, null);
+    }
+
+    public BaseException(String defaultMessage) {
+        this(null, null, null, defaultMessage);
+    }
+
+    @Override
+    public String getMessage() {
+        String message = null;
+        if (!StringUtils.isEmpty(code)) {
+            message = MessageUtils.message(code, args);
+        }
+        if (message == null) {
+            message = defaultMessage;
+        }
+        return message;
+    }
+
+    public String getModule() {
+        return module;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public Object[] getArgs() {
+        return args;
+    }
+
+    public String getDefaultMessage() {
+        return defaultMessage;
+    }
+}

+ 7 - 3
src/main/java/com/metro/job/HealthCodeTask.java

@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.metro.utils.HttpClientUtil;
 import lombok.extern.log4j.Log4j2;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.annotation.Scheduled;
@@ -20,17 +21,19 @@ import java.time.LocalDateTime;
 @Component
 @EnableScheduling
 @Log4j2
+@Slf4j
 public class HealthCodeTask {
 
     /**
      * 健康码-新冠疫苗接种-核酸检测3合1接口
      * 健康码检测定时任务
      */
-    @Scheduled(cron = "0/5 * * * * ? ") // 间隔5秒执行
+    @Scheduled(cron = "0/5 * * * * ? ") // 间隔5秒执行*/
     public void healthCode() {
+
         long begin = System.currentTimeMillis();
         String params = "{\"sfzh\": \"410322199907309877\",\"xm\": \"\"}";
-        String result = HttpClientUtil.httpPost("http://115.236.38.205:8903/id/hvn", params);
+        String result = HttpClientUtil.httpPostSetConfig("http://115.236.38.205:8903/id/hvn", params);
         if (StringUtils.isEmpty(result)) {
             long timeValue = System.currentTimeMillis() - begin;
             log.error("调用健康码-新冠疫苗接种-核酸检测3合1接口调用失败,总耗时: {} 毫秒",timeValue);
@@ -39,13 +42,14 @@ public class HealthCodeTask {
         JSONObject jsonObject = JSON.parseObject(result);
         if (jsonObject.get("error_code").equals("00")) {
             long timeValue = System.currentTimeMillis() - begin;
-            log.info("调用健康码-新冠疫苗接种-核酸检测3合1接口成功,总耗时:{} 毫秒",timeValue);
         } else {
             long timeValue = System.currentTimeMillis() - begin;
             log.error("调用健康码-新冠疫苗接种-核酸检测3合1接口失败!:返回结果为:{},总耗时:{} 毫秒", result,timeValue);
             // TODO 如果需要记录到数库中需要建日志表,表字段参考返回值,将错误信息插入
         }
 
+
+
     }
 
 }

+ 2 - 3
src/main/java/com/metro/mapper/PassreCordsMapper.java

@@ -1,8 +1,7 @@
 package com.metro.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.metro.entity.po.Metro;
-import com.metro.entity.po.Question;
+import com.metro.entity.po.PassreCords;
 
 /**
  * @ClassName: PassreCordsMapper
@@ -12,5 +11,5 @@ import com.metro.entity.po.Question;
  * @Date 2022/3/15 16:41
  * ...
  */
-public interface PassreCordsMapper  extends BaseMapper<Question> {
+public interface PassreCordsMapper  extends BaseMapper<PassreCords> {
 }

+ 4 - 5
src/main/java/com/metro/mapper/QuestionMapper.java → src/main/java/com/metro/mapper/PoliceMapper.java

@@ -1,16 +1,15 @@
 package com.metro.mapper;
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.metro.entity.po.Question;
 
-import java.util.Arrays;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.metro.entity.po.Police;
 
 /**
  * @ClassName: PoliceMapper
  * @Description: TODO
  * @Author: ZS
  * @CreateName: lws
- * @Date 2022/3/14 11:18
+ * @Date 2022/3/18 1:28
  * ...
  */
-public interface QuestionMapper extends BaseMapper<Question> {
+public interface PoliceMapper extends BaseMapper<Police> {
 }

+ 0 - 12
src/main/java/com/metro/mapper/UserMapper.java

@@ -1,12 +0,0 @@
-package com.metro.mapper;
-
-/**
- * @ClassName: UserMapper
- * @Description: TODO
- * @Author: ZS
- * @CreateName: lws
- * @Date 2022/3/16 11:11
- * ...
- */
-public interface UserMapper {
-}

+ 7 - 5
src/main/java/com/metro/service/AccountService.java

@@ -2,10 +2,13 @@ package com.metro.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.metro.baseRe.BaseResponse;
+import com.metro.entity.model.UserOperateModel;
+import com.metro.entity.po.Account;
 import com.metro.entity.ro.account.*;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletResponse;
+import java.util.List;
 
 /**
  * @ClassName: AccountService
@@ -95,11 +98,7 @@ public interface AccountService {
      */
     LoginAccountResp login(LoginAccountReq req);
 
-    /**
-     * 退出登录
-     * @return 是否成功
-     */
-    Boolean exit(Long userId);
+
 
     /**
      * 获取用户信息
@@ -107,4 +106,7 @@ public interface AccountService {
      */
     LoginAccountResp getUserInfo(Long userId);
 
+    String importUser(List<UserOperateModel> userList,String type);
+
+    Boolean logout();
 }

+ 6 - 1
src/main/java/com/metro/service/MetroService.java

@@ -1,5 +1,6 @@
 package com.metro.service;
 
+import com.metro.entity.ro.metro.SelectMetroParentResp;
 import com.metro.entity.ro.metro.SelectMetroReq;
 import com.metro.entity.ro.metro.SelectMetroResp;
 
@@ -23,6 +24,10 @@ public interface MetroService {
      */
     //一二级
     List<SelectMetroResp> list(SelectMetroReq req);
+    /**
+     * 地铁查询
+     * @return 返回结果
+     */
+    List<SelectMetroParentResp> selectMetroAll();
 
-    List selectMetroAll();
 }

+ 13 - 3
src/main/java/com/metro/service/PassreCordsService.java

@@ -1,8 +1,9 @@
 package com.metro.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.metro.entity.ro.passrecords.SelectPassreCordsPageListResp;
-import com.metro.entity.ro.passrecords.SelectPassreCordsPageListReq;
+import com.metro.entity.ro.Police.InsertPoliceReq;
+import com.metro.entity.ro.Police.InsertPoliceResp;
+import com.metro.entity.ro.passrecords.*;
 
 /**
  * @ClassName: PassreCordsService
@@ -15,10 +16,19 @@ import com.metro.entity.ro.passrecords.SelectPassreCordsPageListReq;
 public interface PassreCordsService {
 
     /**
-     * 查询公司信息
+     * 查询派出所
      * @param req 查询条件
      * @return page列表
      */
     IPage<SelectPassreCordsPageListResp> page(SelectPassreCordsPageListReq req);
+    /*
+    * 删除
+    * */
+    DeletePassreCordsResq delete(DeletePassreCordsReq req);
+
+    /*
+    * 详情
+    * */
+    DetialPassreCordsResq selectById(DetailPassreCordsReq req);
 
 }

+ 36 - 0
src/main/java/com/metro/service/PoliceService.java

@@ -0,0 +1,36 @@
+package com.metro.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.metro.entity.ro.Police.*;
+import com.metro.entity.ro.account.*;
+
+/**
+ * @ClassName: PoliceService
+ * @Description: TODO
+ * @Author: ZS
+ * @CreateName: lws
+ * @Date 2022/3/18 1:25
+ * ...
+ */
+public interface PoliceService {
+
+    IPage<SelectPolicePageResp> page(SelectPolicePageReq req);
+    /**
+     * 添加
+     *
+     * @param req 账号信息
+     * @return 影响行数
+     */
+    InsertPoliceResp add(InsertPoliceReq req);
+
+
+    /*
+     *
+     * 修改回显
+     * */
+    DetailPoliceResp detail(DetailPoliceReq req);
+
+    UpdateAccountResp update(UpdateAccountReq req);
+
+    DeleteAccountResp delete(DeleteAccountReq req);
+}

+ 0 - 36
src/main/java/com/metro/service/QuestionService.java

@@ -1,36 +0,0 @@
-package com.metro.service;
-
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.metro.entity.ro.Question.*;
-
-/**
- * @ClassName: PoliceService
- * @Description: TODO
- * @Author: ZS
- * @CreateName: lws
- * @Date 2022/3/9 11:25
- * ...
- */
-public interface QuestionService {
-    /**
-     * 警示记录查询
-     *
-     * @param req 查询条件
-     * @return page列表
-     */
-    IPage page(SelectQuestionPageReq req);
-
-
-    /**
-     * 删除记录
-     *
-     * @param req 主键
-     * @return 影响行数
-     */
-    DeleteQuestionResq delete(DeleteQuestionReq req);
-
-    /*
-    * 查看详情
-    * */
-    SelectQuestionPageResp selectById(SelectQuestionPageReq req);
-}

+ 86 - 17
src/main/java/com/metro/service/impl/AccountServiceImpl.java

@@ -7,25 +7,33 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.metro.baseRe.BaseResponse;
 import com.metro.constant.UserConstant;
+import com.metro.entity.model.UserOperateModel;
 import com.metro.entity.po.Account;
 import com.metro.entity.po.Area;
 import com.metro.entity.ro.account.*;
 import com.metro.exception.BusinessException;
 import com.metro.mapper.AccountsMapper;
 import com.metro.mapper.AreaMapper;
+import com.metro.restful.ServiceException;
 import com.metro.security.JWTUtils;
 import com.metro.service.AccountService;
 import com.metro.utils.*;
+import com.metro.utils.bean.BeanValidators;
+import com.sun.java.swing.plaf.windows.resources.windows;
 import io.github.classgraph.utils.Join;
+import jdk.nashorn.internal.parser.Token;
+import lombok.extern.log4j.Log4j2;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.time.DateUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.DigestUtils;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletResponse;
+import javax.validation.Validator;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -43,12 +51,15 @@ import java.util.stream.Collectors;
  * ...
  */
 @Service
+@Log4j2
 public class AccountServiceImpl implements AccountService {
 
     @Autowired
     private AccountsMapper accountsMapper;
     @Autowired
     private AreaMapper areaMapper;
+    @Autowired
+    protected Validator validator;
 
     /**
      * 账号查询
@@ -58,8 +69,8 @@ public class AccountServiceImpl implements AccountService {
      */
     @Override
     public IPage<SelectAccountPageResp> page(SelectAccountPageReq req) {
-        IPage<SelectAccountPageResp> respPage = new Page<>(req.getPageNo(), req.getPageSize());
-        IPage<Account> selectPage = accountsMapper.selectPage(new Page<>(req.getPageNo(), req.getPageSize()), pageWrapper(req));
+        IPage<SelectAccountPageResp> respPage = new Page<>(req.getPageNum(), req.getPageSize());
+        IPage<Account> selectPage = accountsMapper.selectPage(new Page<>(req.getPageNum(), req.getPageSize()), pageWrapper(req));
         if (CollectionUtils.isEmpty(selectPage.getRecords())) return respPage;
         Set<Integer> areaIdSet = selectPage.getRecords().stream().map(Account::getAreaId).collect(Collectors.toSet());
         Map<Integer, String> areaMap = areaMapper.selectList(new LambdaQueryWrapper<Area>().in(Area::getId, areaIdSet)).stream().collect(Collectors.toMap(Area::getId, p ->
@@ -75,7 +86,7 @@ public class AccountServiceImpl implements AccountService {
         wrapper.like(StringUtils.isNotEmpty(req.getUserName()), Account::getUserName, req.getUserName());
         wrapper.eq(StringUtils.isNotEmpty(req.getPhone()), Account::getPhone, req.getPhone());
         wrapper.eq(Account::getIsDelete, UserConstant.NOT_DELETE);
-        wrapper.ge(Account::getType, req.getType());
+        /*wrapper.ge(Account::getType, req.getType());*/
         return wrapper;
     }
 
@@ -91,9 +102,11 @@ public class AccountServiceImpl implements AccountService {
         if (!StringUtil.isChinese(req.getUserName())) throw new BusinessException(UserConstant.NOT_CHINESE_USERNAME);
         Account account = accountsMapper.selectOne(new LambdaQueryWrapper<Account>().eq(Account::getAccount, req.getAccount()).eq(Account::getIsDelete, UserConstant.NOT_DELETE));
         if (ObjectUtil.isNotEmpty(account)) throw new BusinessException(account.getAccount() + "已经存在!不能注册");
-        return new InsertAccountResp().setCount(accountsMapper.insert(MapperManager.mapper(req, Account.class, p -> p.setPassword(MD5UtilNew.string2MD5(req.getPassword())).setType(1))));
+        return new InsertAccountResp().setCount(accountsMapper.insert(MapperManager.mapper(req, Account.class, p -> p.setPassword(MD5UtilNew.string2MD5(req.getPassword())).setType(3))));
     }
 
+
+
     /**
      * 校验账号是否存在
      *
@@ -131,12 +144,12 @@ public class AccountServiceImpl implements AccountService {
         String[] split = area.getDistrictSqe().trim().split(",");
         String areaId = null;
         String cityId = null;
-        String proviceId = null;
+        String provinceId = null;
         List<String> strings = Arrays.asList(split);
         // .11.150.1275.
         for (int i = 0; i < strings.size(); i++) {
             if (i == 0) {
-                proviceId = strings.get(i);
+                provinceId = strings.get(i);
             } else if (i == 1) {
                 cityId = strings.get(i);
             } else if (i == 2) {
@@ -145,11 +158,11 @@ public class AccountServiceImpl implements AccountService {
         }
         String finalAreaId = areaId;
         String finalCityId = cityId;
-        String finalProviceId = proviceId;
+        String finalprovinceId = provinceId;
         return MapperManager.mapper(account, DetailAccountResp.class, p -> {
             p.setAreaId(finalAreaId);
             p.setCityId(finalCityId);
-            p.setProviceId(finalProviceId);
+            p.setProvinceId(finalprovinceId);
         });
     }
 
@@ -161,12 +174,16 @@ public class AccountServiceImpl implements AccountService {
      */
     @Override
     public UpdateAccountResp update(UpdateAccountReq req) {
+        return getUpdateAccountResp(req, accountsMapper);
+    }
+
+    static UpdateAccountResp getUpdateAccountResp(UpdateAccountReq req, AccountsMapper accountsMapper) {
         if (StringUtil.isChinese(req.getAccount())) throw new BusinessException(UserConstant.NOT_CHINESE_ACCOUNT);
         if (!StringUtil.isChinese(req.getUserName())) throw new BusinessException(UserConstant.NOT_CHINESE_USERNAME);
         Account account = accountsMapper.selectOne(new LambdaQueryWrapper<Account>().eq(Account::getAccount, req.getAccount()).eq(Account::getIsDelete, UserConstant.NOT_DELETE));
         if (ObjectUtil.isEmpty(account)) throw new BusinessException(account.getAccount() + "不存在!不能修改");
         return new UpdateAccountResp().setCount(accountsMapper.updateById(MapperManager.mapper(req, Account.class, p -> p.setPassword(MD5UtilNew.string2MD5(req.getPassword()))
-                .setUpdateTime(LocalDateTime.now()).setType(1).setUpdateId(req.getUpdateId()))));
+                .setUpdateTime(LocalDateTime.now()).setType(req.getType()).setUpdateId(req.getUpdateId()))));
     }
 
     /**
@@ -244,28 +261,80 @@ public class AccountServiceImpl implements AccountService {
         return MapperManager.mapper(account, LoginAccountResp.class, p -> p.setToken(JWTUtils.sign(account.getId().toString(), account.getPassword(), DateUtils.addDays(new Date(), 1))));
     }
 
+
+
     /**
-     * 退出登录
+     * 获取用户信息
      *
      * @return 是否成功
      */
     @Override
-    public Boolean exit(Long userId) {
-        return null;
+    public LoginAccountResp getUserInfo(Long userId) {
+        Account account = accountsMapper.selectOne(new LambdaQueryWrapper<Account>().eq(Account::getId, userId).eq(Account::getIsDelete, UserConstant.NOT_DELETE));
+        if (ObjectUtil.isEmpty(account)) throw new BusinessException("获取用户信息失败");
+        return MapperManager.mapper(account, LoginAccountResp.class);
     }
 
     /**
-     * 获取用户信息
+     * 导入用户数据
+     *
+     * @param userList  用户数据列表
+     * @return 结果
+     */
+    @Override
+    @Transactional
+    public String importUser(List<UserOperateModel> userList,String type) {
+        if (CollectionUtils.isEmpty(userList)) {
+            throw new ServiceException("导入用户数据不能为空!");
+        }
+        int successNum = 0;
+        int failureNum = 0;
+        StringBuilder successMsg = new StringBuilder();
+        StringBuilder failureMsg = new StringBuilder();
+        for (UserOperateModel user : userList) {
+            Account account = accountsMapper.selectOne(new LambdaQueryWrapper<Account>().eq(Account::getAccount, user.getAccount()).eq(Account::getIsDelete, UserConstant.NOT_DELETE));
+            try {
+                if (ObjectUtil.isNotEmpty(account)) { // 存在账号
+                    failureNum++;
+                    failureMsg.append("<br/>" + failureNum + "、账号 " + user.getAccount() + " 已存在");
+                } else { // 账号不存在新增数据
+                    BeanValidators.validateWithException(validator, user);
+                    successNum++;
+                    Account mapper = MapperManager.mapper(user, Account.class, p -> {
+                        p.setPassword(MD5UtilNew.string2MD5(user.getPassword())).setAreaId(3230).setType(Integer.valueOf(type)).setCreateTime(LocalDateTime.now());
+                        p.setType(1);
+                    });
+                    accountsMapper.insert(mapper);
+                    successMsg.append("<br/>" + successNum + "、账号 " + user.getAccount() + " 导入成功");
+                }
+
+            } catch (Exception e) {
+                failureNum++;
+                String msg = "<br/>" + failureNum + "、账号 " + user.getAccount() + " 导入失败:";
+                failureMsg.append(msg + e.getMessage());
+                log.error(msg, e);
+            }
+        }
+        if (failureNum > 0) {
+            failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
+            throw new BusinessException(failureMsg.toString());
+        } else {
+            successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
+        }
+        return successMsg.toString();
+    }
+
+    /**
+     * 退出登录
      *
      * @return 是否成功
      */
     @Override
-    public LoginAccountResp getUserInfo(Long userId) {
-        Account account = accountsMapper.selectOne(new LambdaQueryWrapper<Account>().eq(Account::getId, userId).eq(Account::getIsDelete, UserConstant.NOT_DELETE));
-        if (ObjectUtil.isEmpty(account)) throw new BusinessException("获取用户信息失败");
-        return MapperManager.mapper(account, LoginAccountResp.class);
+    public Boolean logout() {
+        return null;
     }
 
+    public static ThreadLocal<Long> loginUserThreadLocal = new ThreadLocal<>();
 
 }
 

+ 42 - 22
src/main/java/com/metro/service/impl/MetroServiceImpl.java

@@ -1,16 +1,20 @@
 package com.metro.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.metro.entity.po.Area;
 import com.metro.entity.po.Metro;
+import com.metro.entity.ro.metro.SelectMetroParentResp;
 import com.metro.entity.ro.metro.SelectMetroReq;
 import com.metro.entity.ro.metro.SelectMetroResp;
 import com.metro.mapper.MetroMapper;
 import com.metro.service.MetroService;
+import com.metro.utils.MapperManager;
+import org.assertj.core.util.Lists;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
 
 import java.math.BigDecimal;
 import java.util.*;
@@ -38,33 +42,49 @@ public class MetroServiceImpl implements MetroService {
      */
     @Override
     public List<SelectMetroResp> list(SelectMetroReq req) {
-        List<Metro> metros = metroMapper.selectList(selectListWrapper(req));
-        List<SelectMetroResp> respList = metros.stream().map(p -> BeanUtil.toBean(p, SelectMetroResp.class)).collect(Collectors.toList());
-        if (req.getLevel() == BigDecimal.ONE.intValue()) {
-            return respList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() ->
-                    new TreeSet<>(Comparator.comparing(o -> o.getLineId()))), ArrayList::new));
-        } else if (req.getLevel() == BigDecimal.ROUND_FLOOR) {
-            return respList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() ->
-                    new TreeSet<>(Comparator.comparing(o -> o.getQuestionId()))), ArrayList::new));
-        }
-        return respList;
-    }
-    /*
-    * 地铁查询
-    * */
-    @Override
-    public List selectMetroAll() {
-        LambdaQueryWrapper<Metro> wrapper = new LambdaQueryWrapper<>();
-        List<Metro> areaList = metroMapper.selectList(wrapper);
-        return areaList;
+        return  metroMapper.selectList(selectListWrapper(req)).stream().map(p -> MapperManager.mapper(p,SelectMetroResp.class)).collect(Collectors.toList());
     }
 
     // 地铁查询wrapper
     private Wrapper<Metro> selectListWrapper(SelectMetroReq req) {
         LambdaQueryWrapper<Metro> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(req.getLevel() == BigDecimal.ROUND_CEILING, Metro::getLineId, req.getId());
-        wrapper.eq(req.getLevel() == BigDecimal.ROUND_FLOOR, Metro::getMetroId, req.getId());
+        wrapper.eq(ObjectUtil.isNull(req.getPid()),Metro::getPid,0);
+        wrapper.eq(ObjectUtil.isNotNull(req.getPid()),Metro::getPid,req.getPid());
         return wrapper;
     }
 
+    @Override
+    public List<SelectMetroParentResp> selectMetroAll() {
+        //1、查询数据库钟,所有的级联信息( 我这里是使用上面的默认数据在测试)
+        List<Metro> list = metroMapper.selectList(null);
+        if(CollectionUtils.isEmpty(list)) return Lists.newArrayList();
+        List<SelectMetroParentResp> parentResps = list.stream().map(p -> MapperManager.mapper(p, SelectMetroParentResp.class)).collect(Collectors.toList());
+        //2、先找到一级父类,再通过级联去查询子类菜单
+        return  parentResps.stream().filter(linkageEntities ->
+                linkageEntities.getPid().equals("0")     //查询父类
+        ).map((menu) -> {
+            menu.setChildren(getChildrens(menu, parentResps)); //查询子类菜单
+            return menu;
+        }).sorted((menu1, menu2) -> {
+            //排序
+            return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
+        }).collect(Collectors.toList());
+    }
+
+
+
+    //递归查找所有菜单的子类菜单
+    private static List<SelectMetroParentResp> getChildrens(SelectMetroParentResp root, List<SelectMetroParentResp> all) {
+        return all.stream().filter(linkageEntities -> {
+            return linkageEntities.getPid().equals(root.getId());
+        }).map(linkageEntities -> {
+            //1、递归找到子菜单
+            linkageEntities.setChildren(getChildrens(linkageEntities, all));
+            return linkageEntities;
+        }).sorted((menu1, menu2) -> {
+            //2、菜单的排序
+            return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
+        }).collect(Collectors.toList());
+    }
+
 }

+ 38 - 14
src/main/java/com/metro/service/impl/PassreCordsServiceImpl.java

@@ -1,19 +1,21 @@
 package com.metro.service.impl;
-
 import cn.hutool.core.bean.BeanUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.metro.entity.po.Question;
-import com.metro.entity.ro.passrecords.SelectPassreCordsPageListReq;
-import com.metro.entity.ro.passrecords.SelectPassreCordsPageListResp;
+import com.metro.constant.UserConstant;
+import com.metro.entity.po.PassreCords;
+import com.metro.entity.ro.passrecords.*;
 import com.metro.mapper.PassreCordsMapper;
 import com.metro.service.PassreCordsService;
-import org.apache.commons.lang3.StringUtils;
+import com.metro.utils.MapperManager;
+import com.metro.utils.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.util.CollectionUtils;
 
+import java.time.LocalDateTime;
+
 /**
  * @ClassName: PassreCordsServiceImpl
  * @Description: TODO
@@ -29,23 +31,45 @@ public class PassreCordsServiceImpl implements PassreCordsService {
     private PassreCordsMapper passreCordsMapper;
 
     /**
-     * 查询公司信息
+     * 警示记录
+     *
      * @param req 查询条件
      * @return page列表
      */
     @Override
     public IPage<SelectPassreCordsPageListResp> page(SelectPassreCordsPageListReq req) {
-        IPage<SelectPassreCordsPageListResp> respPage = new Page<>(req.getPageNo(), req.getPageSize());
-        Page<Question> selectPage = passreCordsMapper.selectPage(new Page<>(req.getPageNo(), req.getPageSize()), pageWrapper(req));
-        if(CollectionUtils.isEmpty(selectPage.getRecords())) return respPage;
-        return selectPage.convert(p -> BeanUtil.toBean(p,SelectPassreCordsPageListResp.class));
+        IPage<SelectPassreCordsPageListResp> respPage = new Page<>(req.getPageNum(), req.getPageSize());
+        Page<PassreCords> selectPage = passreCordsMapper.selectPage(new Page<>(req.getPageNum(), req.getPageSize()), pageWrapper(req));
+        if (CollectionUtils.isEmpty(selectPage.getRecords())) return respPage;
+        return selectPage.convert(p -> BeanUtil.toBean(p, SelectPassreCordsPageListResp.class));
+    }
+    /*
+    * 删除
+    * */
+    @Override
+    public DeletePassreCordsResq delete(DeletePassreCordsReq req) {
+            return new DeletePassreCordsResq().setCount(passreCordsMapper.updateById(new PassreCords().setId(req.getId())
+                    .setIsDelete(UserConstant.DELETE).setUpdateTimestamp(LocalDateTime.now())));
+
     }
+    /*
+    * 详情
+    * */
+    @Override
+    public DetialPassreCordsResq selectById(DetailPassreCordsReq req) {
+        PassreCords passreCords = passreCordsMapper.selectById(req.getId());
+        return MapperManager.mapper(passreCords, DetialPassreCordsResq.class);
+
+    }
+
 
     // page Wrapper
-    private LambdaQueryWrapper<Question> pageWrapper(SelectPassreCordsPageListReq req) {
-        LambdaQueryWrapper<Question> wrapper = new LambdaQueryWrapper<>();
-        wrapper.like(StringUtils.isNotEmpty(req.getBranchName()),Question::getBranchName,req.getBranchName());
-        wrapper.like(StringUtils.isNotEmpty(req.getStaffName()),Question::getStaffName,req.getStaffName());
+    private LambdaQueryWrapper<PassreCords> pageWrapper(SelectPassreCordsPageListReq req) {
+        LambdaQueryWrapper<PassreCords> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(PassreCords::getIsDelete, UserConstant.NOT_DELETE);
+        wrapper.eq(StringUtils.isNotEmpty(req.getXm()), PassreCords::getXm, req.getXm());
+        wrapper.eq(StringUtils.isNotEmpty(req.getCardId()),PassreCords::getCardId,req.getCardId());
+        wrapper.eq(StringUtils.isNotEmpty(req.getPhone()),PassreCords::getPhone,req.getPhone());
         return wrapper;
     }
 

+ 109 - 0
src/main/java/com/metro/service/impl/PoliceServiceImpl.java

@@ -0,0 +1,109 @@
+package com.metro.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.metro.constant.UserConstant;
+import com.metro.entity.po.Account;
+import com.metro.entity.po.Area;
+import com.metro.entity.po.Company;
+import com.metro.entity.po.Police;
+import com.metro.entity.ro.Police.*;
+import com.metro.entity.ro.account.*;
+import com.metro.exception.BusinessException;
+import com.metro.mapper.AccountsMapper;
+import com.metro.mapper.CompanysMapper;
+import com.metro.mapper.PoliceMapper;
+import com.metro.service.PoliceService;
+import com.metro.utils.MD5UtilNew;
+import com.metro.utils.MapperManager;
+import com.metro.utils.StringUtil;
+import io.github.classgraph.utils.Join;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.metro.service.impl.AccountServiceImpl.getUpdateAccountResp;
+
+
+/**
+ * @ClassName: PoliceServiceImpl
+ * @Description: TODO
+ * @Author: ZS
+ * @CreateName: lws
+ * @Date 2022/3/18 1:25
+ * ...
+ */
+@Service
+public class PoliceServiceImpl implements PoliceService{
+    @Autowired
+    private PoliceMapper mapper;
+    @Autowired
+    private CompanysMapper companysMapper;
+    @Autowired
+    private AccountsMapper accountsMapper;
+    @Override
+    public IPage<SelectPolicePageResp> page(SelectPolicePageReq req) {
+        IPage<SelectPolicePageResp> respPage = new Page<>(req.getPageNum(), req.getPageSize());
+        IPage<Police> selectPage = mapper.selectPage(new Page<>(req.getPageNum(), req.getPageSize()), pageWrapper(req));
+        if (org.springframework.util.CollectionUtils.isEmpty(selectPage.getRecords())) return respPage;
+        Set<String> deptSet = selectPage.getRecords().stream().map(Police::getCompanyId).collect(Collectors.toSet());
+        Map<String, String> companysMap = companysMapper.selectList(new LambdaQueryWrapper<Company>().in(Company::getCompanyId,deptSet)).stream().collect(Collectors.toMap(Company::getCompanyId, Company::getCompanyName, (t1, t2) -> t1));
+        return selectPage.convert(p -> MapperManager.mapper(p, SelectPolicePageResp.class, t -> t.setCompanyName(companysMap.get(p.getCompanyId()))));
+    }
+
+    /*
+    * 新增
+    * */
+    @Override
+    public InsertPoliceResp add(InsertPoliceReq req) {
+        Police account = mapper.selectOne(new LambdaQueryWrapper<Police>().eq(Police::getAccount, req.getAccount()).eq(Police::getIsDelete, UserConstant.NOT_DELETE));
+        if (ObjectUtil.isNotEmpty(account)) throw new BusinessException(account.getAccount() + "已经存在!不能注册");
+        return new InsertPoliceResp().setCount(mapper.insert(MapperManager.mapper(req, Police.class, p -> p.setPassword(MD5UtilNew.string2MD5(req.getPassword())).setType(4))));
+    }
+    /*
+    * 查看详情
+    * */
+    @Override
+    public DetailPoliceResp detail(DetailPoliceReq req) {
+        Police Police = mapper.selectById(req.getId());
+        return MapperManager.mapper(Police,DetailPoliceResp.class);
+    }
+
+    /*
+    * 修改账号
+    * */
+    @Override
+    public UpdateAccountResp update(UpdateAccountReq req) {
+        return getUpdateAccountResp(req, accountsMapper);    }
+    /*
+    * 删除账号
+    * */
+    @Override
+    public DeleteAccountResp delete(DeleteAccountReq req) {
+        return new DeleteAccountResp().setCount(accountsMapper.updateById(new Account().setId(req.getId())
+                .setIsDelete(UserConstant.DELETE).setUpdateTime(LocalDateTime.now())));
+    }
+
+    /*
+    * 查询条件
+    * */
+    private LambdaQueryWrapper<Police> pageWrapper(SelectPolicePageReq req) {
+            LambdaQueryWrapper<Police> wrapper = new LambdaQueryWrapper<>();
+            wrapper.like(StringUtils.isNotEmpty(req.getUserName()), Police::getUserName, req.getUserName());
+            wrapper.eq(StringUtils.isNotEmpty(req.getPhone()), Police::getPhone, req.getPhone());
+            wrapper.eq(Police::getIsDelete, UserConstant.NOT_DELETE);
+            wrapper.like(StringUtils.isNotEmpty(req.getAddress()), Police::getAddress,req.getAddress());
+            wrapper.eq(StringUtils.isNotEmpty(req.getCompanyId()),Police::getCompanyId,req.getCompanyId());
+         return wrapper;
+    }
+}

+ 0 - 76
src/main/java/com/metro/service/impl/QuestionServiceImpl.java

@@ -1,76 +0,0 @@
-package com.metro.service.impl;
-import cn.hutool.core.bean.BeanUtil;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.metro.constant.UserConstant;
-import com.metro.entity.po.Account;
-import com.metro.entity.po.Area;
-import com.metro.entity.po.Question;
-import com.metro.entity.ro.Question.DeleteQuestionReq;
-import com.metro.entity.ro.Question.DeleteQuestionResq;
-import com.metro.entity.ro.Question.SelectQuestionPageResp;
-import com.metro.entity.ro.Question.SelectQuestionPageReq;
-import com.metro.entity.ro.account.DetailAccountResp;
-import com.metro.mapper.QuestionMapper;
-import com.metro.service.QuestionService;
-import com.metro.utils.MapperManager;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.util.CollectionUtils;
-
-import java.time.LocalDateTime;
-
-/**
- * @ClassName: PoliceServiceImpl
- * @Description: TODO
- * @Author: ZS
- * @CreateName: lws
- * @Date 2022/3/9 11:26
- * ...
- */
-@Service
-public class QuestionServiceImpl implements QuestionService {
-    @Autowired
-    private QuestionMapper mapper;
-    @Override
-
-    /**
-     * 警示记录
-     * @param req 查询条件
-     * @return page 列表
-     */
-    public IPage<SelectQuestionPageResp> page(SelectQuestionPageReq req) {
-        IPage<SelectQuestionPageResp> respPage = new Page<>(req.getPageNo(), req.getPageSize());
-        Page<Question> selectPage = mapper.selectPage(new Page<>(req.getPageNo(), req.getPageSize()), pageWrapper(req));
-        if(CollectionUtils.isEmpty(selectPage.getRecords())) return respPage;
-        return selectPage.convert(p -> BeanUtil.toBean(p,SelectQuestionPageResp.class));
-    }
-
-
-    // page wrapper 查询条件
-    private LambdaQueryWrapper<Question> pageWrapper(SelectQuestionPageReq req) {
-        LambdaQueryWrapper<Question> wrapper = new LambdaQueryWrapper<>();
-        wrapper.like(StringUtils.isNotEmpty(req.getXm()), Question::getXm, req.getXm());
-        wrapper.eq(StringUtils.isNotEmpty(req.getPhone()), Question::getPhone, req.getPhone());
-        return wrapper;
-    }
-
-    /*
-    * 删除记录
-    * */
-    @Override
-    public DeleteQuestionResq delete(DeleteQuestionReq req) {
-        return new DeleteQuestionResq().setCount(mapper.updateById(new Question().setId(req.getId())
-                .setIsDelete(UserConstant.DELETE).setUpdateTimestamp(LocalDateTime.now())));
-    }
-    /*
-    * 查看详情
-    * */
-    @Override
-    public SelectQuestionPageResp selectById(SelectQuestionPageReq req) {
-        Question question = mapper.selectById(req.getId());
-        return MapperManager.mapper(question, SelectQuestionPageResp.class);
-    }
-}

+ 189 - 0
src/main/java/com/metro/utils/CacheUtils.java

@@ -0,0 +1,189 @@
+package com.metro.utils;
+
+import com.metro.utils.spring.SpringUtils;
+import org.apache.shiro.cache.Cache;
+import org.apache.shiro.cache.CacheManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Cache工具类
+ * 
+ * @author ruoyi
+ */
+public class CacheUtils
+{
+    private static Logger logger = LoggerFactory.getLogger(CacheUtils.class);
+
+    private static CacheManager cacheManager = SpringUtils.getBean(CacheManager.class);
+
+    private static final String SYS_CACHE = "sys-cache";
+
+    /**
+     * 获取SYS_CACHE缓存
+     * 
+     * @param key
+     * @return
+     */
+    public static Object get(String key)
+    {
+        return get(SYS_CACHE, key);
+    }
+
+    /**
+     * 获取SYS_CACHE缓存
+     * 
+     * @param key
+     * @param defaultValue
+     * @return
+     */
+    public static Object get(String key, Object defaultValue)
+    {
+        Object value = get(key);
+        return value != null ? value : defaultValue;
+    }
+
+    /**
+     * 写入SYS_CACHE缓存
+     * 
+     * @param key
+     * @return
+     */
+    public static void put(String key, Object value)
+    {
+        put(SYS_CACHE, key, value);
+    }
+
+    /**
+     * 从SYS_CACHE缓存中移除
+     * 
+     * @param key
+     * @return
+     */
+    public static void remove(String key)
+    {
+        remove(SYS_CACHE, key);
+    }
+
+    /**
+     * 获取缓存
+     * 
+     * @param cacheName
+     * @param key
+     * @return
+     */
+    public static Object get(String cacheName, String key)
+    {
+        return getCache(cacheName).get(getKey(key));
+    }
+
+    /**
+     * 获取缓存
+     * 
+     * @param cacheName
+     * @param key
+     * @param defaultValue
+     * @return
+     */
+    public static Object get(String cacheName, String key, Object defaultValue)
+    {
+        Object value = get(cacheName, getKey(key));
+        return value != null ? value : defaultValue;
+    }
+
+    /**
+     * 写入缓存
+     * 
+     * @param cacheName
+     * @param key
+     * @param value
+     */
+    public static void put(String cacheName, String key, Object value)
+    {
+        getCache(cacheName).put(getKey(key), value);
+    }
+
+    /**
+     * 从缓存中移除
+     * 
+     * @param cacheName
+     * @param key
+     */
+    public static void remove(String cacheName, String key)
+    {
+        getCache(cacheName).remove(getKey(key));
+    }
+
+    /**
+     * 从缓存中移除所有
+     * 
+     * @param cacheName
+     */
+    public static void removeAll(String cacheName)
+    {
+        Cache<String, Object> cache = getCache(cacheName);
+        Set<String> keys = cache.keys();
+        for (Iterator<String> it = keys.iterator(); it.hasNext();)
+        {
+            cache.remove(it.next());
+        }
+        logger.info("清理缓存: {} => {}", cacheName, keys);
+    }
+
+    /**
+     * 从缓存中移除指定key
+     * 
+     * @param keys
+     */
+    public static void removeByKeys(Set<String> keys)
+    {
+        removeByKeys(SYS_CACHE, keys);
+    }
+
+    /**
+     * 从缓存中移除指定key
+     * 
+     * @param cacheName
+     * @param keys
+     */
+    public static void removeByKeys(String cacheName, Set<String> keys)
+    {
+        for (Iterator<String> it = keys.iterator(); it.hasNext();)
+        {
+            remove(it.next());
+        }
+        logger.info("清理缓存: {} => {}", cacheName, keys);
+    }
+
+    /**
+     * 获取缓存键名
+     * 
+     * @param key
+     * @return
+     */
+    private static String getKey(String key)
+    {
+        return key;
+    }
+
+    /**
+     * 获得一个Cache,没有则显示日志。
+     * 
+     * @param cacheName
+     * @return
+     */
+    public static Cache<String, Object> getCache(String cacheName)
+    {
+        Cache<String, Object> cache = cacheManager.getCache(cacheName);
+        if (cache == null)
+        {
+            throw new RuntimeException("当前系统中没有定义“" + cacheName + "”这个缓存。");
+        }
+        return cache;
+    }
+
+
+}

+ 160 - 0
src/main/java/com/metro/utils/DateUtils.java

@@ -0,0 +1,160 @@
+package com.metro.utils;
+
+import org.apache.commons.lang3.time.DateFormatUtils;
+
+import java.lang.management.ManagementFactory;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.*;
+import java.util.Date;
+
+/**
+ * 时间工具类
+ *
+ * @author ruoyi
+ */
+public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
+    public static String YYYY = "yyyy";
+
+    public static String YYYY_MM = "yyyy-MM";
+
+    public static String YYYY_MM_DD = "yyyy-MM-dd";
+
+    public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
+
+    public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
+
+    private static String[] parsePatterns = {
+            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
+            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
+            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
+
+    /**
+     * 获取当前Date型日期
+     *
+     * @return Date() 当前日期
+     */
+    public static Date getNowDate() {
+        return new Date();
+    }
+
+    /**
+     * 获取当前日期, 默认格式为yyyy-MM-dd
+     *
+     * @return String
+     */
+    public static String getDate() {
+        return dateTimeNow(YYYY_MM_DD);
+    }
+
+    public static final String getTime() {
+        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
+    }
+
+    public static final String dateTimeNow() {
+        return dateTimeNow(YYYYMMDDHHMMSS);
+    }
+
+    public static final String dateTimeNow(final String format) {
+        return parseDateToStr(format, new Date());
+    }
+
+    public static final String dateTime(final Date date) {
+        return parseDateToStr(YYYY_MM_DD, date);
+    }
+
+    public static final String parseDateToStr(final String format, final Date date) {
+        return new SimpleDateFormat(format).format(date);
+    }
+
+    public static final Date dateTime(final String format, final String ts) {
+        try {
+            return new SimpleDateFormat(format).parse(ts);
+        } catch (ParseException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 日期路径 即年/月/日 如2018/08/08
+     */
+    public static final String datePath() {
+        Date now = new Date();
+        return DateFormatUtils.format(now, "yyyy/MM/dd");
+    }
+
+    /**
+     * 日期路径 即年/月/日 如20180808
+     */
+    public static final String dateTime() {
+        Date now = new Date();
+        return DateFormatUtils.format(now, "yyyyMMdd");
+    }
+
+    /**
+     * 日期型字符串转化为日期 格式
+     */
+    public static Date parseDate(Object str) {
+        if (str == null) {
+            return null;
+        }
+        try {
+            return parseDate(str.toString(), parsePatterns);
+        } catch (ParseException e) {
+            return null;
+        }
+    }
+
+    /**
+     * 获取服务器启动时间
+     */
+    public static Date getServerStartDate() {
+        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
+        return new Date(time);
+    }
+
+    /**
+     * 计算相差天数
+     */
+    public static int differentDaysByMillisecond(Date date1, Date date2) {
+        return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
+    }
+
+    /**
+     * 计算两个时间差
+     */
+    public static String getDatePoor(Date endDate, Date nowDate) {
+        long nd = 1000 * 24 * 60 * 60;
+        long nh = 1000 * 60 * 60;
+        long nm = 1000 * 60;
+        // long ns = 1000;
+        // 获得两个时间的毫秒时间差异
+        long diff = endDate.getTime() - nowDate.getTime();
+        // 计算差多少天
+        long day = diff / nd;
+        // 计算差多少小时
+        long hour = diff % nd / nh;
+        // 计算差多少分钟
+        long min = diff % nd % nh / nm;
+        // 计算差多少秒//输出结果
+        // long sec = diff % nd % nh % nm / ns;
+        return day + "天" + hour + "小时" + min + "分钟";
+    }
+
+    /**
+     * 增加 LocalDateTime ==> Date
+     */
+    public static Date toDate(LocalDateTime temporalAccessor) {
+        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
+        return Date.from(zdt.toInstant());
+    }
+
+    /**
+     * 增加 LocalDate ==> Date
+     */
+    public static Date toDate(LocalDate temporalAccessor) {
+        LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
+        ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
+        return Date.from(zdt.toInstant());
+    }
+}

+ 80 - 0
src/main/java/com/metro/utils/DictUtils.java

@@ -0,0 +1,80 @@
+package com.metro.utils;
+
+import com.metro.constant.Constants;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 字典工具类
+ *
+ * @author ruoyi
+ */
+@Component
+public class DictUtils {
+    /**
+     * 分隔符
+     */
+    public static final String SEPARATOR = ",";
+
+
+
+    /**
+     * 根据字典类型和字典值获取字典标签
+     *
+     * @param dictType  字典类型
+     * @param dictValue 字典值
+     * @return 字典标签
+     */
+    public static String getDictLabel(String dictType, String dictValue,String a) {
+        return getDictLabel(dictType, dictValue,SEPARATOR);
+    }
+
+    /**
+     * 根据字典类型和字典标签获取字典值
+     *
+     * @param dictType  字典类型
+     * @param dictLabel 字典标签
+     * @return 字典值
+     */
+    public static String getDictValue(String dictType, String dictLabel,String a) {
+        return getDictValue(dictType, dictLabel,SEPARATOR);
+    }
+
+
+
+    /**
+     * 删除指定字典缓存
+     *
+     * @param key 字典键
+     */
+    public static void removeDictCache(String key) {
+        CacheUtils.remove(getCacheName(), getCacheKey(key));
+    }
+
+    /**
+     * 清空字典缓存
+     */
+    public static void clearDictCache() {
+        CacheUtils.removeAll(getCacheName());
+    }
+
+    /**
+     * 获取cache name
+     *
+     * @return 缓存名
+     */
+    public static String getCacheName() {
+        return Constants.SYS_DICT_CACHE;
+    }
+
+    /**
+     * 设置cache key
+     *
+     * @param configKey 参数键
+     * @return 缓存键key
+     */
+    public static String getCacheKey(String configKey) {
+        return Constants.SYS_DICT_KEY + configKey;
+    }
+}

+ 29 - 1
src/main/java/com/metro/utils/HttpClientUtil.java

@@ -25,7 +25,8 @@ import java.net.URLEncoder;
 import java.util.Iterator;
 import java.util.Map;
 
-public class HttpClientUtil {
+public class
+HttpClientUtil {
 
 
     public static String post(String url, String data) throws ClientProtocolException, IOException {
@@ -204,5 +205,32 @@ public class HttpClientUtil {
         }
         return returnValue;
     }
+    public static String httpPostSetConfig(String url, String json) {
+        String returnValue = null;
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        ResponseHandler<String> responseHandler = new BasicResponseHandler();
+        try {
+            httpClient = HttpClients.createDefault();
+            HttpPost httpPost = new HttpPost(url);
+            StringEntity requestEntity = new StringEntity(json, "utf-8");
+            requestEntity.setContentEncoding("UTF-8");
+            httpPost.setHeader("Content-type", "application/json");
+            httpPost.setEntity(requestEntity);
+            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(20000).setConnectTimeout(20000).build();
+            httpPost.setConfig(requestConfig);
+            returnValue = httpClient.execute(httpPost, responseHandler);
+
 
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                httpClient.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return returnValue;
+    }
 }

+ 24 - 0
src/main/java/com/metro/utils/MessageUtils.java

@@ -0,0 +1,24 @@
+package com.metro.utils;
+
+import com.metro.utils.spring.SpringUtils;
+import org.springframework.context.MessageSource;
+import org.springframework.context.i18n.LocaleContextHolder;
+
+/**
+ * 获取i18n资源文件
+ *
+ * @author ruoyi
+ */
+public class MessageUtils {
+    /**
+     * 根据消息键和参数 获取消息 委托给spring messageSource
+     *
+     * @param code 消息键
+     * @param args 参数
+     * @return 获取国际化翻译值
+     */
+    public static String message(String code, Object... args) {
+        MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
+        return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
+    }
+}

+ 323 - 0
src/main/java/com/metro/utils/ReflectUtils.java

@@ -0,0 +1,323 @@
+package com.metro.utils;
+
+import com.metro.utils.text.Convert;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+import org.apache.poi.ss.usermodel.DateUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.*;
+import java.util.Date;
+
+/**
+ * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
+ *
+ * @author ruoyi
+ */
+@SuppressWarnings("rawtypes")
+public class ReflectUtils {
+    private static final String SETTER_PREFIX = "set";
+
+    private static final String GETTER_PREFIX = "get";
+
+    private static final String CGLIB_CLASS_SEPARATOR = "$$";
+
+    private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);
+
+    /**
+     * 调用Getter方法.
+     * 支持多级,如:对象名.对象名.方法
+     */
+    @SuppressWarnings("unchecked")
+    public static <E> E invokeGetter(Object obj, String propertyName) {
+        Object object = obj;
+        for (String name : StringUtils.split(propertyName, ".")) {
+            String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
+            object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{});
+        }
+        return (E) object;
+    }
+
+    /**
+     * 调用Setter方法, 仅匹配方法名。
+     * 支持多级,如:对象名.对象名.方法
+     */
+    public static <E> void invokeSetter(Object obj, String propertyName, E value) {
+        Object object = obj;
+        String[] names = StringUtils.split(propertyName, ".");
+        for (int i = 0; i < names.length; i++) {
+            if (i < names.length - 1) {
+                String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
+                object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{});
+            } else {
+                String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
+                invokeMethodByName(object, setterMethodName, new Object[]{value});
+            }
+        }
+    }
+
+    /**
+     * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
+     */
+    @SuppressWarnings("unchecked")
+    public static <E> E getFieldValue(final Object obj, final String fieldName) {
+        Field field = getAccessibleField(obj, fieldName);
+        if (field == null) {
+            logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
+            return null;
+        }
+        E result = null;
+        try {
+            result = (E) field.get(obj);
+        } catch (IllegalAccessException e) {
+            logger.error("不可能抛出的异常{}", e.getMessage());
+        }
+        return result;
+    }
+
+    /**
+     * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
+     */
+    public static <E> void setFieldValue(final Object obj, final String fieldName, final E value) {
+        Field field = getAccessibleField(obj, fieldName);
+        if (field == null) {
+            // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
+            logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
+            return;
+        }
+        try {
+            field.set(obj, value);
+        } catch (IllegalAccessException e) {
+            logger.error("不可能抛出的异常: {}", e.getMessage());
+        }
+    }
+
+    /**
+     * 直接调用对象方法, 无视private/protected修饰符.
+     * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
+     * 同时匹配方法名+参数类型,
+     */
+    @SuppressWarnings("unchecked")
+    public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
+                                     final Object[] args) {
+        if (obj == null || methodName == null) {
+            return null;
+        }
+        Method method = getAccessibleMethod(obj, methodName, parameterTypes);
+        if (method == null) {
+            logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
+            return null;
+        }
+        try {
+            return (E) method.invoke(obj, args);
+        } catch (Exception e) {
+            String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
+            throw convertReflectionExceptionToUnchecked(msg, e);
+        }
+    }
+
+    /**
+     * 直接调用对象方法, 无视private/protected修饰符,
+     * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
+     * 只匹配函数名,如果有多个同名函数调用第一个。
+     */
+    @SuppressWarnings("unchecked")
+    public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args) {
+        Method method = getAccessibleMethodByName(obj, methodName, args.length);
+        if (method == null) {
+            // 如果为空不报错,直接返回空。
+            logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
+            return null;
+        }
+        try {
+            // 类型转换(将参数数据类型转换为目标方法参数类型)
+            Class<?>[] cs = method.getParameterTypes();
+            for (int i = 0; i < cs.length; i++) {
+                if (args[i] != null && !args[i].getClass().equals(cs[i])) {
+                    if (cs[i] == String.class) {
+                        args[i] = Convert.toStr(args[i]);
+                        if (StringUtils.endsWith((String) args[i], ".0")) {
+                            args[i] = StringUtils.substringBefore((String) args[i], ".0");
+                        }
+                    } else if (cs[i] == Integer.class) {
+                        args[i] = Convert.toInt(args[i]);
+                    } else if (cs[i] == Long.class) {
+                        args[i] = Convert.toLong(args[i]);
+                    } else if (cs[i] == Double.class) {
+                        args[i] = Convert.toDouble(args[i]);
+                    } else if (cs[i] == Float.class) {
+                        args[i] = Convert.toFloat(args[i]);
+                    } else if (cs[i] == Date.class) {
+                        if (args[i] instanceof String) {
+                            args[i] = DateUtils.parseDate(args[i]);
+                        } else {
+                            args[i] = DateUtil.getJavaDate((Double) args[i]);
+                        }
+                    } else if (cs[i] == boolean.class || cs[i] == Boolean.class) {
+                        args[i] = Convert.toBool(args[i]);
+                    }
+                }
+            }
+            return (E) method.invoke(obj, args);
+        } catch (Exception e) {
+            String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
+            throw convertReflectionExceptionToUnchecked(msg, e);
+        }
+    }
+
+    /**
+     * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
+     * 如向上转型到Object仍无法找到, 返回null.
+     */
+    public static Field getAccessibleField(final Object obj, final String fieldName) {
+        // 为空不报错。直接返回 null
+        if (obj == null) {
+            return null;
+        }
+        Validate.notBlank(fieldName, "fieldName can't be blank");
+        for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
+            try {
+                Field field = superClass.getDeclaredField(fieldName);
+                makeAccessible(field);
+                return field;
+            } catch (NoSuchFieldException e) {
+                continue;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
+     * 如向上转型到Object仍无法找到, 返回null.
+     * 匹配函数名+参数类型。
+     * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
+     */
+    public static Method getAccessibleMethod(final Object obj, final String methodName,
+                                             final Class<?>... parameterTypes) {
+        // 为空不报错。直接返回 null
+        if (obj == null) {
+            return null;
+        }
+        Validate.notBlank(methodName, "methodName can't be blank");
+        for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
+            try {
+                Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
+                makeAccessible(method);
+                return method;
+            } catch (NoSuchMethodException e) {
+                continue;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
+     * 如向上转型到Object仍无法找到, 返回null.
+     * 只匹配函数名。
+     * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
+     */
+    public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) {
+        // 为空不报错。直接返回 null
+        if (obj == null) {
+            return null;
+        }
+        Validate.notBlank(methodName, "methodName can't be blank");
+        for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
+            Method[] methods = searchType.getDeclaredMethods();
+            for (Method method : methods) {
+                if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) {
+                    makeAccessible(method);
+                    return method;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
+     */
+    public static void makeAccessible(Method method) {
+        if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
+                && !method.isAccessible()) {
+            method.setAccessible(true);
+        }
+    }
+
+    /**
+     * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
+     */
+    public static void makeAccessible(Field field) {
+        if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
+                || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {
+            field.setAccessible(true);
+        }
+    }
+
+    /**
+     * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
+     * 如无法找到, 返回Object.class.
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> Class<T> getClassGenricType(final Class clazz) {
+        return getClassGenricType(clazz, 0);
+    }
+
+    /**
+     * 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
+     * 如无法找到, 返回Object.class.
+     */
+    public static Class getClassGenricType(final Class clazz, final int index) {
+        Type genType = clazz.getGenericSuperclass();
+
+        if (!(genType instanceof ParameterizedType)) {
+            logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType");
+            return Object.class;
+        }
+
+        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
+
+        if (index >= params.length || index < 0) {
+            logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
+                    + params.length);
+            return Object.class;
+        }
+        if (!(params[index] instanceof Class)) {
+            logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
+            return Object.class;
+        }
+
+        return (Class) params[index];
+    }
+
+    public static Class<?> getUserClass(Object instance) {
+        if (instance == null) {
+            throw new RuntimeException("Instance must not be null");
+        }
+        Class clazz = instance.getClass();
+        if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
+            Class<?> superClass = clazz.getSuperclass();
+            if (superClass != null && !Object.class.equals(superClass)) {
+                return superClass;
+            }
+        }
+        return clazz;
+
+    }
+
+    /**
+     * 将反射时的checked exception转换为unchecked exception.
+     */
+    public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) {
+        if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
+                || e instanceof NoSuchMethodException) {
+            return new IllegalArgumentException(msg, e);
+        } else if (e instanceof InvocationTargetException) {
+            return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException());
+        }
+        return new RuntimeException(msg, e);
+    }
+}

+ 516 - 0
src/main/java/com/metro/utils/StringUtils.java

@@ -0,0 +1,516 @@
+package com.metro.utils;
+
+import com.metro.constant.Constants;
+import com.metro.utils.text.StrFormatter;
+import org.springframework.util.AntPathMatcher;
+
+import java.util.*;
+
+/**
+ * 字符串工具类
+ *
+ * @author ruoyi
+ */
+public class StringUtils extends org.apache.commons.lang3.StringUtils {
+    /**
+     * 空字符串
+     */
+    private static final String NULLSTR = "";
+
+    /**
+     * 下划线
+     */
+    private static final char SEPARATOR = '_';
+
+    /**
+     * 获取参数不为空值
+     *
+     * @param value defaultValue 要判断的value
+     * @return value 返回值
+     */
+    public static <T> T nvl(T value, T defaultValue) {
+        return value != null ? value : defaultValue;
+    }
+
+    /**
+     * * 判断一个Collection是否为空, 包含List,Set,Queue
+     *
+     * @param coll 要判断的Collection
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Collection<?> coll) {
+        return isNull(coll) || coll.isEmpty();
+    }
+
+    /**
+     * * 判断一个Collection是否非空,包含List,Set,Queue
+     *
+     * @param coll 要判断的Collection
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Collection<?> coll) {
+        return !isEmpty(coll);
+    }
+
+    /**
+     * * 判断一个对象数组是否为空
+     *
+     * @param objects 要判断的对象数组
+     *                * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Object[] objects) {
+        return isNull(objects) || (objects.length == 0);
+    }
+
+    /**
+     * * 判断一个对象数组是否非空
+     *
+     * @param objects 要判断的对象数组
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Object[] objects) {
+        return !isEmpty(objects);
+    }
+
+    /**
+     * * 判断一个Map是否为空
+     *
+     * @param map 要判断的Map
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Map<?, ?> map) {
+        return isNull(map) || map.isEmpty();
+    }
+
+    /**
+     * * 判断一个Map是否为空
+     *
+     * @param map 要判断的Map
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Map<?, ?> map) {
+        return !isEmpty(map);
+    }
+
+    /**
+     * * 判断一个字符串是否为空串
+     *
+     * @param str String
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(String str) {
+        return isNull(str) || NULLSTR.equals(str.trim());
+    }
+
+    /**
+     * * 判断一个字符串是否为非空串
+     *
+     * @param str String
+     * @return true:非空串 false:空串
+     */
+    public static boolean isNotEmpty(String str) {
+        return !isEmpty(str);
+    }
+
+    /**
+     * * 判断一个对象是否为空
+     *
+     * @param object Object
+     * @return true:为空 false:非空
+     */
+    public static boolean isNull(Object object) {
+        return object == null;
+    }
+
+    /**
+     * * 判断一个对象是否非空
+     *
+     * @param object Object
+     * @return true:非空 false:空
+     */
+    public static boolean isNotNull(Object object) {
+        return !isNull(object);
+    }
+
+    /**
+     * * 判断一个对象是否是数组类型(Java基本型别的数组)
+     *
+     * @param object 对象
+     * @return true:是数组 false:不是数组
+     */
+    public static boolean isArray(Object object) {
+        return isNotNull(object) && object.getClass().isArray();
+    }
+
+    /**
+     * 去空格
+     */
+    public static String trim(String str) {
+        return (str == null ? "" : str.trim());
+    }
+
+    /**
+     * 截取字符串
+     *
+     * @param str   字符串
+     * @param start 开始
+     * @return 结果
+     */
+    public static String substring(final String str, int start) {
+        if (str == null) {
+            return NULLSTR;
+        }
+
+        if (start < 0) {
+            start = str.length() + start;
+        }
+
+        if (start < 0) {
+            start = 0;
+        }
+        if (start > str.length()) {
+            return NULLSTR;
+        }
+
+        return str.substring(start);
+    }
+
+    /**
+     * 截取字符串
+     *
+     * @param str   字符串
+     * @param start 开始
+     * @param end   结束
+     * @return 结果
+     */
+    public static String substring(final String str, int start, int end) {
+        if (str == null) {
+            return NULLSTR;
+        }
+
+        if (end < 0) {
+            end = str.length() + end;
+        }
+        if (start < 0) {
+            start = str.length() + start;
+        }
+
+        if (end > str.length()) {
+            end = str.length();
+        }
+
+        if (start > end) {
+            return NULLSTR;
+        }
+
+        if (start < 0) {
+            start = 0;
+        }
+        if (end < 0) {
+            end = 0;
+        }
+
+        return str.substring(start, end);
+    }
+
+    /**
+     * 格式化文本, {} 表示占位符<br>
+     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
+     * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
+     * 例:<br>
+     * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
+     * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
+     * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
+     *
+     * @param template 文本模板,被替换的部分用 {} 表示
+     * @param params   参数值
+     * @return 格式化后的文本
+     */
+    public static String format(String template, Object... params) {
+        if (isEmpty(params) || isEmpty(template)) {
+            return template;
+        }
+        return StrFormatter.format(template, params);
+    }
+
+    /**
+     * 是否为http(s)://开头
+     *
+     * @param link 链接
+     * @return 结果
+     */
+    public static boolean ishttp(String link) {
+        return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
+    }
+
+    /**
+     * 字符串转set
+     *
+     * @param str 字符串
+     * @param sep 分隔符
+     * @return set集合
+     */
+    public static final Set<String> str2Set(String str, String sep) {
+        return new HashSet<String>(str2List(str, sep, true, false));
+    }
+
+    /**
+     * 字符串转list
+     *
+     * @param str         字符串
+     * @param sep         分隔符
+     * @param filterBlank 过滤纯空白
+     * @param trim        去掉首尾空白
+     * @return list集合
+     */
+    public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) {
+        List<String> list = new ArrayList<String>();
+        if (StringUtils.isEmpty(str)) {
+            return list;
+        }
+
+        // 过滤空白字符串
+        if (filterBlank && StringUtils.isBlank(str)) {
+            return list;
+        }
+        String[] split = str.split(sep);
+        for (String string : split) {
+            if (filterBlank && StringUtils.isBlank(string)) {
+                continue;
+            }
+            if (trim) {
+                string = string.trim();
+            }
+            list.add(string);
+        }
+
+        return list;
+    }
+
+    /**
+     * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
+     *
+     * @param cs                  指定字符串
+     * @param searchCharSequences 需要检查的字符串数组
+     * @return 是否包含任意一个字符串
+     */
+    public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) {
+        if (isEmpty(cs) || isEmpty(searchCharSequences)) {
+            return false;
+        }
+        for (CharSequence testStr : searchCharSequences) {
+            if (containsIgnoreCase(cs, testStr)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 驼峰转下划线命名
+     */
+    public static String toUnderScoreCase(String str) {
+        if (str == null) {
+            return null;
+        }
+        StringBuilder sb = new StringBuilder();
+        // 前置字符是否大写
+        boolean preCharIsUpperCase = true;
+        // 当前字符是否大写
+        boolean curreCharIsUpperCase = true;
+        // 下一字符是否大写
+        boolean nexteCharIsUpperCase = true;
+        for (int i = 0; i < str.length(); i++) {
+            char c = str.charAt(i);
+            if (i > 0) {
+                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
+            } else {
+                preCharIsUpperCase = false;
+            }
+
+            curreCharIsUpperCase = Character.isUpperCase(c);
+
+            if (i < (str.length() - 1)) {
+                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
+            }
+
+            if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) {
+                sb.append(SEPARATOR);
+            } else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) {
+                sb.append(SEPARATOR);
+            }
+            sb.append(Character.toLowerCase(c));
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * 是否包含字符串
+     *
+     * @param str  验证字符串
+     * @param strs 字符串组
+     * @return 包含返回true
+     */
+    public static boolean inStringIgnoreCase(String str, String... strs) {
+        if (str != null && strs != null) {
+            for (String s : strs) {
+                if (str.equalsIgnoreCase(trim(s))) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 删除最后一个字符串
+     *
+     * @param str  输入字符串
+     * @param spit 以什么类型结尾的
+     * @return 截取后的字符串
+     */
+    public static String lastStringDel(String str, String spit) {
+        if (!StringUtils.isEmpty(str) && str.endsWith(spit)) {
+            return str.subSequence(0, str.length() - 1).toString();
+        }
+        return str;
+    }
+
+    /**
+     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
+     *
+     * @param name 转换前的下划线大写方式命名的字符串
+     * @return 转换后的驼峰式命名的字符串
+     */
+    public static String convertToCamelCase(String name) {
+        StringBuilder result = new StringBuilder();
+        // 快速检查
+        if (name == null || name.isEmpty()) {
+            // 没必要转换
+            return "";
+        } else if (!name.contains("_")) {
+            // 不含下划线,仅将首字母大写
+            return name.substring(0, 1).toUpperCase() + name.substring(1);
+        }
+        // 用下划线将原始字符串分割
+        String[] camels = name.split("_");
+        for (String camel : camels) {
+            // 跳过原始字符串中开头、结尾的下换线或双重下划线
+            if (camel.isEmpty()) {
+                continue;
+            }
+            // 首字母大写
+            result.append(camel.substring(0, 1).toUpperCase());
+            result.append(camel.substring(1).toLowerCase());
+        }
+        return result.toString();
+    }
+
+    /**
+     * 驼峰式命名法
+     * 例如:user_name->userName
+     */
+    public static String toCamelCase(String s) {
+        if (s == null) {
+            return null;
+        }
+        if (s.indexOf(SEPARATOR) == -1) {
+            return s;
+        }
+        s = s.toLowerCase();
+        StringBuilder sb = new StringBuilder(s.length());
+        boolean upperCase = false;
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+
+            if (c == SEPARATOR) {
+                upperCase = true;
+            } else if (upperCase) {
+                sb.append(Character.toUpperCase(c));
+                upperCase = false;
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
+     *
+     * @param str  指定字符串
+     * @param strs 需要检查的字符串数组
+     * @return 是否匹配
+     */
+    public static boolean matches(String str, List<String> strs) {
+        if (isEmpty(str) || isEmpty(strs)) {
+            return false;
+        }
+        for (String pattern : strs) {
+            if (isMatch(pattern, str)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 判断url是否与规则配置:
+     * ? 表示单个字符;
+     * * 表示一层路径内的任意字符串,不可跨层级;
+     * ** 表示任意层路径;
+     *
+     * @param pattern 匹配规则
+     * @param url     需要匹配的url
+     * @return
+     */
+    public static boolean isMatch(String pattern, String url) {
+        AntPathMatcher matcher = new AntPathMatcher();
+        return matcher.match(pattern, url);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> T cast(Object obj) {
+        return (T) obj;
+    }
+
+    /**
+     * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
+     *
+     * @param num  数字对象
+     * @param size 字符串指定长度
+     * @return 返回数字的字符串格式,该字符串为指定长度。
+     */
+    public static final String padl(final Number num, final int size) {
+        return padl(num.toString(), size, '0');
+    }
+
+    /**
+     * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
+     *
+     * @param s    原始字符串
+     * @param size 字符串指定长度
+     * @param c    用于补齐的字符
+     * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
+     */
+    public static final String padl(final String s, final int size, final char c) {
+        final StringBuilder sb = new StringBuilder(size);
+        if (s != null) {
+            final int len = s.length();
+            if (s.length() <= size) {
+                for (int i = size - len; i > 0; i--) {
+                    sb.append(c);
+                }
+                sb.append(s);
+            } else {
+                return s.substring(len - size, len);
+            }
+        } else {
+            for (int i = size; i > 0; i--) {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+}

+ 110 - 0
src/main/java/com/metro/utils/bean/BeanUtils.java

@@ -0,0 +1,110 @@
+package com.metro.utils.bean;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Bean 工具类
+ * 
+ * @author ruoyi
+ */
+public class BeanUtils extends org.springframework.beans.BeanUtils
+{
+    /** Bean方法名中属性名开始的下标 */
+    private static final int BEAN_METHOD_PROP_INDEX = 3;
+
+    /** * 匹配getter方法的正则表达式 */
+    private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)");
+
+    /** * 匹配setter方法的正则表达式 */
+    private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)");
+
+    /**
+     * Bean属性复制工具方法。
+     * 
+     * @param dest 目标对象
+     * @param src 源对象
+     */
+    public static void copyBeanProp(Object dest, Object src)
+    {
+        try
+        {
+            copyProperties(src, dest);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 获取对象的setter方法。
+     * 
+     * @param obj 对象
+     * @return 对象的setter方法列表
+     */
+    public static List<Method> getSetterMethods(Object obj)
+    {
+        // setter方法列表
+        List<Method> setterMethods = new ArrayList<Method>();
+
+        // 获取所有方法
+        Method[] methods = obj.getClass().getMethods();
+
+        // 查找setter方法
+
+        for (Method method : methods)
+        {
+            Matcher m = SET_PATTERN.matcher(method.getName());
+            if (m.matches() && (method.getParameterTypes().length == 1))
+            {
+                setterMethods.add(method);
+            }
+        }
+        // 返回setter方法列表
+        return setterMethods;
+    }
+
+    /**
+     * 获取对象的getter方法。
+     * 
+     * @param obj 对象
+     * @return 对象的getter方法列表
+     */
+
+    public static List<Method> getGetterMethods(Object obj)
+    {
+        // getter方法列表
+        List<Method> getterMethods = new ArrayList<Method>();
+        // 获取所有方法
+        Method[] methods = obj.getClass().getMethods();
+        // 查找getter方法
+        for (Method method : methods)
+        {
+            Matcher m = GET_PATTERN.matcher(method.getName());
+            if (m.matches() && (method.getParameterTypes().length == 0))
+            {
+                getterMethods.add(method);
+            }
+        }
+        // 返回getter方法列表
+        return getterMethods;
+    }
+
+    /**
+     * 检查Bean方法名中的属性名是否相等。<br>
+     * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。
+     * 
+     * @param m1 方法名1
+     * @param m2 方法名2
+     * @return 属性名一样返回true,否则返回false
+     */
+
+    public static boolean isMethodPropEquals(String m1, String m2)
+    {
+        return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX));
+    }
+}

+ 21 - 0
src/main/java/com/metro/utils/bean/BeanValidators.java

@@ -0,0 +1,21 @@
+package com.metro.utils.bean;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validator;
+import java.util.Set;
+
+/**
+ * bean对象属性验证
+ *
+ * @author ruoyi
+ */
+public class BeanValidators {
+    public static void validateWithException(Validator validator, Object object, Class<?>... groups)
+            throws ConstraintViolationException {
+        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
+        if (!constraintViolations.isEmpty()) {
+            throw new ConstraintViolationException(constraintViolations);
+        }
+    }
+}

+ 77 - 0
src/main/java/com/metro/utils/file/FileTypeUtils.java

@@ -0,0 +1,77 @@
+package com.metro.utils.file;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.File;
+
+/**
+ * 文件类型工具类
+ *
+ * @author ruoyi
+ */
+public class FileTypeUtils
+{
+    /**
+     * 获取文件类型
+     * <p>
+     * 例如: ruoyi.txt, 返回: txt
+     * 
+     * @param file 文件名
+     * @return 后缀(不含".")
+     */
+    public static String getFileType(File file)
+    {
+        if (null == file)
+        {
+            return StringUtils.EMPTY;
+        }
+        return getFileType(file.getName());
+    }
+
+    /**
+     * 获取文件类型
+     * <p>
+     * 例如: ruoyi.txt, 返回: txt
+     *
+     * @param fileName 文件名
+     * @return 后缀(不含".")
+     */
+    public static String getFileType(String fileName)
+    {
+        int separatorIndex = fileName.lastIndexOf(".");
+        if (separatorIndex < 0)
+        {
+            return "";
+        }
+        return fileName.substring(separatorIndex + 1).toLowerCase();
+    }
+
+    /**
+     * 获取文件类型
+     * 
+     * @param photoByte 文件字节码
+     * @return 后缀(不含".")
+     */
+    public static String getFileExtendName(byte[] photoByte)
+    {
+        String strFileExtendName = "JPG";
+        if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
+                && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
+        {
+            strFileExtendName = "GIF";
+        }
+        else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
+        {
+            strFileExtendName = "JPG";
+        }
+        else if ((photoByte[0] == 66) && (photoByte[1] == 77))
+        {
+            strFileExtendName = "BMP";
+        }
+        else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
+        {
+            strFileExtendName = "PNG";
+        }
+        return strFileExtendName;
+    }
+}

+ 197 - 0
src/main/java/com/metro/utils/file/FileUploadUtils.java

@@ -0,0 +1,197 @@
+package com.metro.utils.file;
+
+import com.metro.config.Config;
+import com.metro.constant.Constants;
+import com.metro.exception.FileNameLengthLimitExceededException;
+import com.metro.exception.FileSizeLimitExceededException;
+import com.metro.exception.InvalidExtensionException;
+import com.metro.utils.DateUtils;
+import com.metro.utils.StringUtils;
+import com.metro.utils.uuid.Seq;
+import org.apache.commons.io.FilenameUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * 文件上传工具类
+ *
+ * @author ruoyi
+ */
+public class FileUploadUtils {
+    /**
+     * 默认大小 50M
+     */
+    public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
+
+    /**
+     * 默认的文件名最大长度 100
+     */
+    public static final int DEFAULT_FILE_NAME_LENGTH = 100;
+
+    /**
+     * 默认上传的地址
+     */
+    private static String defaultBaseDir = Config.getProfile();
+
+    public static void setDefaultBaseDir(String defaultBaseDir) {
+        FileUploadUtils.defaultBaseDir = defaultBaseDir;
+    }
+
+    public static String getDefaultBaseDir() {
+        return defaultBaseDir;
+    }
+
+    /**
+     * 以默认配置进行文件上传
+     *
+     * @param file 上传的文件
+     * @return 文件名称
+     * @throws Exception
+     */
+    public static final String upload(MultipartFile file) throws IOException {
+        try {
+            return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
+        } catch (Exception e) {
+            throw new IOException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 根据文件路径上传
+     *
+     * @param baseDir 相对应用的基目录
+     * @param file    上传的文件
+     * @return 文件名称
+     * @throws IOException
+     */
+    public static final String upload(String baseDir, MultipartFile file) throws IOException {
+        try {
+            return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
+        } catch (Exception e) {
+            throw new IOException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 文件上传
+     *
+     * @param baseDir          相对应用的基目录
+     * @param file             上传的文件
+     * @param allowedExtension 上传文件类型
+     * @return 返回上传成功的文件名
+     * @throws FileSizeLimitExceededException       如果超出最大大小
+     * @throws FileNameLengthLimitExceededException 文件名太长
+     * @throws IOException                          比如读写文件出错时
+     * @throws InvalidExtensionException            文件校验异常
+     */
+    public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
+            throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
+            InvalidExtensionException {
+        int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
+        if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
+            throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
+        }
+
+        assertAllowed(file, allowedExtension);
+
+        String fileName = extractFilename(file);
+
+        File desc = getAbsoluteFile(baseDir, fileName);
+        file.transferTo(desc);
+        return getPathFileName(baseDir, fileName);
+    }
+
+    /**
+     * 编码文件名
+     */
+    public static final String extractFilename(MultipartFile file) {
+        return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
+                FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
+    }
+
+    public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
+        File desc = new File(uploadDir + File.separator + fileName);
+
+        if (!desc.exists()) {
+            if (!desc.getParentFile().exists()) {
+                desc.getParentFile().mkdirs();
+            }
+        }
+        return desc;
+    }
+
+    public static final String getPathFileName(String uploadDir, String fileName) throws IOException {
+        int dirLastIndex = Config.getProfile().length() + 1;
+        String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
+        return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
+    }
+
+    /**
+     * 文件大小校验
+     *
+     * @param file 上传的文件
+     * @return
+     * @throws FileSizeLimitExceededException 如果超出最大大小
+     * @throws InvalidExtensionException
+     */
+    public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
+            throws FileSizeLimitExceededException, InvalidExtensionException {
+        long size = file.getSize();
+        if (size > DEFAULT_MAX_SIZE) {
+            throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
+        }
+
+        String fileName = file.getOriginalFilename();
+        String extension = getExtension(file);
+        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
+            if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) {
+                throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
+                        fileName);
+            } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) {
+                throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
+                        fileName);
+            } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) {
+                throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
+                        fileName);
+            } else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) {
+                throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
+                        fileName);
+            } else {
+                throw new InvalidExtensionException(allowedExtension, extension, fileName);
+            }
+        }
+    }
+
+    /**
+     * 判断MIME类型是否是允许的MIME类型
+     *
+     * @param extension
+     * @param allowedExtension
+     * @return
+     */
+    public static final boolean isAllowedExtension(String extension, String[] allowedExtension) {
+        for (String str : allowedExtension) {
+            if (str.equalsIgnoreCase(extension)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 获取文件名的后缀
+     *
+     * @param file 表单文件
+     * @return 后缀名
+     */
+    public static final String getExtension(MultipartFile file) {
+        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
+        if (StringUtils.isEmpty(extension)) {
+            extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
+        }
+        return extension;
+    }
+}

+ 229 - 0
src/main/java/com/metro/utils/file/FileUtils.java

@@ -0,0 +1,229 @@
+package com.metro.utils.file;
+
+import com.metro.config.Config;
+import com.metro.utils.DateUtils;
+import com.metro.utils.StringUtils;
+import com.metro.utils.uuid.IdUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.ArrayUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 文件处理工具类
+ *
+ * @author ruoyi
+ */
+public class FileUtils {
+    public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
+
+    /**
+     * 输出指定文件的byte数组
+     *
+     * @param filePath 文件路径
+     * @param os       输出流
+     * @return
+     */
+    public static void writeBytes(String filePath, OutputStream os) throws IOException {
+        FileInputStream fis = null;
+        try {
+            File file = new File(filePath);
+            if (!file.exists()) {
+                throw new FileNotFoundException(filePath);
+            }
+            fis = new FileInputStream(file);
+            byte[] b = new byte[1024];
+            int length;
+            while ((length = fis.read(b)) > 0) {
+                os.write(b, 0, length);
+            }
+        } catch (IOException e) {
+            throw e;
+        } finally {
+            IOUtils.close(os);
+            IOUtils.close(fis);
+        }
+    }
+
+    /**
+     * 写数据到文件中
+     *
+     * @param data 数据
+     * @return 目标文件
+     * @throws IOException IO异常
+     */
+    public static String writeImportBytes(byte[] data) throws IOException {
+        return writeBytes(data, Config.getImportPath());
+    }
+
+    /**
+     * 写数据到文件中
+     *
+     * @param data      数据
+     * @param uploadDir 目标文件
+     * @return 目标文件
+     * @throws IOException IO异常
+     */
+    public static String writeBytes(byte[] data, String uploadDir) throws IOException {
+        FileOutputStream fos = null;
+        String pathName = "";
+        try {
+            String extension = getFileExtendName(data);
+            pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
+            File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName);
+            fos = new FileOutputStream(file);
+            fos.write(data);
+        } finally {
+            IOUtils.close(fos);
+        }
+        return FileUploadUtils.getPathFileName(uploadDir, pathName);
+    }
+
+    /**
+     * 删除文件
+     *
+     * @param filePath 文件
+     * @return
+     */
+    public static boolean deleteFile(String filePath) {
+        boolean flag = false;
+        File file = new File(filePath);
+        // 路径为文件且不为空则进行删除
+        if (file.isFile() && file.exists()) {
+            file.delete();
+            flag = true;
+        }
+        return flag;
+    }
+
+    /**
+     * 文件名称验证
+     *
+     * @param filename 文件名称
+     * @return true 正常 false 非法
+     */
+    public static boolean isValidFilename(String filename) {
+        return filename.matches(FILENAME_PATTERN);
+    }
+
+    /**
+     * 检查文件是否可下载
+     *
+     * @param resource 需要下载的文件
+     * @return true 正常 false 非法
+     */
+    public static boolean checkAllowDownload(String resource) {
+        // 禁止目录上跳级别
+        if (StringUtils.contains(resource, "..")) {
+            return false;
+        }
+
+        // 检查允许下载的文件规则
+        if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) {
+            return true;
+        }
+
+        // 不在允许下载的文件规则
+        return false;
+    }
+
+    /**
+     * 下载文件名重新编码
+     *
+     * @param request  请求对象
+     * @param fileName 文件名
+     * @return 编码后的文件名
+     */
+    public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException {
+        final String agent = request.getHeader("USER-AGENT");
+        String filename = fileName;
+        if (agent.contains("MSIE")) {
+            // IE浏览器
+            filename = URLEncoder.encode(filename, "utf-8");
+            filename = filename.replace("+", " ");
+        } else if (agent.contains("Firefox")) {
+            // 火狐浏览器
+            filename = new String(fileName.getBytes(), "ISO8859-1");
+        } else if (agent.contains("Chrome")) {
+            // google浏览器
+            filename = URLEncoder.encode(filename, "utf-8");
+        } else {
+            // 其它浏览器
+            filename = URLEncoder.encode(filename, "utf-8");
+        }
+        return filename;
+    }
+
+    /**
+     * 下载文件名重新编码
+     *
+     * @param response     响应对象
+     * @param realFileName 真实文件名
+     * @return
+     */
+    public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {
+        String percentEncodedFileName = percentEncode(realFileName);
+
+        StringBuilder contentDispositionValue = new StringBuilder();
+        contentDispositionValue.append("attachment; filename=")
+                .append(percentEncodedFileName)
+                .append(";")
+                .append("filename*=")
+                .append("utf-8''")
+                .append(percentEncodedFileName);
+
+        response.setHeader("Content-disposition", contentDispositionValue.toString());
+    }
+
+    /**
+     * 百分号编码工具方法
+     *
+     * @param s 需要百分号编码的字符串
+     * @return 百分号编码后的字符串
+     */
+    public static String percentEncode(String s) throws UnsupportedEncodingException {
+        String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
+        return encode.replaceAll("\\+", "%20");
+    }
+
+    /**
+     * 获取图像后缀
+     *
+     * @param photoByte 图像数据
+     * @return 后缀名
+     */
+    public static String getFileExtendName(byte[] photoByte) {
+        String strFileExtendName = "jpg";
+        if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
+                && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) {
+            strFileExtendName = "gif";
+        } else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) {
+            strFileExtendName = "jpg";
+        } else if ((photoByte[0] == 66) && (photoByte[1] == 77)) {
+            strFileExtendName = "bmp";
+        } else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) {
+            strFileExtendName = "png";
+        }
+        return strFileExtendName;
+    }
+
+    /**
+     * 获取名称
+     *
+     * @param fileName 路径名称
+     * @return 没有文件路径的名称
+     */
+    public static String getName(String fileName) {
+        if (fileName == null) {
+            return null;
+        }
+        int lastUnixPos = fileName.lastIndexOf('/');
+        int lastWindowsPos = fileName.lastIndexOf('\\');
+        int index = Math.max(lastUnixPos, lastWindowsPos);
+        return fileName.substring(index + 1);
+    }
+}

+ 99 - 0
src/main/java/com/metro/utils/file/ImageUtils.java

@@ -0,0 +1,99 @@
+package com.metro.utils.file;
+
+import com.metro.config.Config;
+import com.metro.constant.Constants;
+import com.metro.utils.StringUtils;
+import org.apache.poi.util.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Arrays;
+
+/**
+ * 图片处理工具类
+ *
+ * @author ruoyi
+ */
+public class ImageUtils
+{
+    private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);
+
+    public static byte[] getImage(String imagePath)
+    {
+        InputStream is = getFile(imagePath);
+        try
+        {
+            return IOUtils.toByteArray(is);
+        }
+        catch (Exception e)
+        {
+            log.error("图片加载异常 {}", e);
+            return null;
+        }
+        finally
+        {
+            IOUtils.closeQuietly(is);
+        }
+    }
+
+    public static InputStream getFile(String imagePath)
+    {
+        try
+        {
+            byte[] result = readFile(imagePath);
+            result = Arrays.copyOf(result, result.length);
+            return new ByteArrayInputStream(result);
+        }
+        catch (Exception e)
+        {
+            log.error("获取图片异常 {}", e);
+        }
+        return null;
+    }
+
+    /**
+     * 读取文件为字节数据
+     * 
+     * @param url 地址
+     * @return 字节数据
+     */
+    public static byte[] readFile(String url)
+    {
+        InputStream in = null;
+        try
+        {
+            if (url.startsWith("http"))
+            {
+                // 网络地址
+                URL urlObj = new URL(url);
+                URLConnection urlConnection = urlObj.openConnection();
+                urlConnection.setConnectTimeout(30 * 1000);
+                urlConnection.setReadTimeout(60 * 1000);
+                urlConnection.setDoInput(true);
+                in = urlConnection.getInputStream();
+            }
+            else
+            {
+                // 本机地址
+                String localPath = Config.getProfile();
+                String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX);
+                in = new FileInputStream(downloadPath);
+            }
+            return IOUtils.toByteArray(in);
+        }
+        catch (Exception e)
+        {
+            log.error("获取文件路径异常 {}", e);
+            return null;
+        }
+        finally
+        {
+            IOUtils.closeQuietly(in);
+        }
+    }
+}

+ 59 - 0
src/main/java/com/metro/utils/file/MimeTypeUtils.java

@@ -0,0 +1,59 @@
+package com.metro.utils.file;
+
+/**
+ * 媒体类型工具类
+ * 
+ * @author ruoyi
+ */
+public class MimeTypeUtils
+{
+    public static final String IMAGE_PNG = "image/png";
+
+    public static final String IMAGE_JPG = "image/jpg";
+
+    public static final String IMAGE_JPEG = "image/jpeg";
+
+    public static final String IMAGE_BMP = "image/bmp";
+
+    public static final String IMAGE_GIF = "image/gif";
+
+    public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" };
+
+    public static final String[] FLASH_EXTENSION = { "swf", "flv" };
+
+    public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
+            "asf", "rm", "rmvb" };
+
+    public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" };
+
+    public static final String[] DEFAULT_ALLOWED_EXTENSION = {
+            // 图片
+            "bmp", "gif", "jpg", "jpeg", "png",
+            // word excel powerpoint
+            "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
+            // 压缩文件
+            "rar", "zip", "gz", "bz2",
+            // 视频格式
+            "mp4", "avi", "rmvb",
+            // pdf
+            "pdf" };
+
+    public static String getExtension(String prefix)
+    {
+        switch (prefix)
+        {
+            case IMAGE_PNG:
+                return "png";
+            case IMAGE_JPG:
+                return "jpg";
+            case IMAGE_JPEG:
+                return "jpeg";
+            case IMAGE_BMP:
+                return "bmp";
+            case IMAGE_GIF:
+                return "gif";
+            default:
+                return "";
+        }
+    }
+}

+ 19 - 0
src/main/java/com/metro/utils/poi/ExcelHandlerAdapter.java

@@ -0,0 +1,19 @@
+package com.metro.utils.poi;
+
+/**
+ * Excel数据格式处理适配器
+ * 
+ * @author ruoyi
+ */
+public interface ExcelHandlerAdapter
+{
+    /**
+     * 格式化
+     * 
+     * @param value 单元格数据值
+     * @param args excel注解args参数组
+     *
+     * @return 处理后的值
+     */
+    Object format(Object value, String[] args);
+}

+ 1162 - 0
src/main/java/com/metro/utils/poi/ExcelUtil.java

@@ -0,0 +1,1162 @@
+package com.metro.utils.poi;
+
+import com.metro.annotation.Excel;
+import com.metro.annotation.Excel.ColumnType;
+import com.metro.annotation.Excel.Type;
+import com.metro.annotation.Excels;
+import com.metro.baseRe.AjaxResult;
+import com.metro.config.Config;
+import com.metro.exception.UtilException;
+import com.metro.utils.DateUtils;
+import com.metro.utils.DictUtils;
+import com.metro.utils.ReflectUtils;
+import com.metro.utils.StringUtils;
+import com.metro.utils.file.FileTypeUtils;
+import com.metro.utils.file.FileUtils;
+import com.metro.utils.file.ImageUtils;
+import com.metro.utils.text.Convert;
+import org.apache.poi.hssf.usermodel.*;
+import org.apache.poi.ooxml.POIXMLDocumentPart;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellRangeAddressList;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xssf.usermodel.*;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Excel相关处理
+ *
+ * @author ruoyi
+ */
+public class ExcelUtil<T> {
+    private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);
+
+    public static final String[] FORMULA_STR = {"=", "-", "+", "@"};
+
+    /**
+     * Excel sheet最大行数,默认65536
+     */
+    public static final int sheetSize = 65536;
+
+    /**
+     * 工作表名称
+     */
+    private String sheetName;
+
+    /**
+     * 导出类型(EXPORT:导出数据;IMPORT:导入模板)
+     */
+    private Excel.Type type;
+
+    /**
+     * 工作薄对象
+     */
+    private Workbook wb;
+
+    /**
+     * 工作表对象
+     */
+    private Sheet sheet;
+
+    /**
+     * 样式列表
+     */
+    private Map<String, CellStyle> styles;
+
+    /**
+     * 导入导出数据列表
+     */
+    private List<T> list;
+
+    /**
+     * 注解列表
+     */
+    private List<Object[]> fields;
+
+    /**
+     * 当前行号
+     */
+    private int rownum;
+
+    /**
+     * 标题
+     */
+    private String title;
+
+    /**
+     * 最大高度
+     */
+    private short maxHeight;
+
+    /**
+     * 统计列表
+     */
+    private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
+
+    /**
+     * 数字格式
+     */
+    private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
+
+    /**
+     * 实体对象
+     */
+    public Class<T> clazz;
+
+    public ExcelUtil(Class<T> clazz) {
+        this.clazz = clazz;
+    }
+
+    public void init(List<T> list, String sheetName, String title, Type type) {
+        if (list == null) {
+            list = new ArrayList<T>();
+        }
+        this.list = list;
+        this.sheetName = sheetName;
+        this.type = type;
+        this.title = title;
+        createExcelField();
+        createWorkbook();
+        createTitle();
+    }
+
+    /**
+     * 创建excel第一行标题
+     */
+    public void createTitle() {
+        if (StringUtils.isNotEmpty(title)) {
+            Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);
+            titleRow.setHeightInPoints(30);
+            Cell titleCell = titleRow.createCell(0);
+            titleCell.setCellStyle(styles.get("title"));
+            titleCell.setCellValue(title);
+            sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(),
+                    this.fields.size() - 1));
+        }
+    }
+
+    /**
+     * 对excel表单默认第一个索引名转换成list
+     *
+     * @param is 输入流
+     * @return 转换后集合
+     */
+    public List<T> importExcel(InputStream is) throws Exception {
+        return importExcel(is, 0);
+    }
+
+    /**
+     * 对excel表单默认第一个索引名转换成list
+     *
+     * @param is       输入流
+     * @param titleNum 标题占用行数
+     * @return 转换后集合
+     */
+    public List<T> importExcel(InputStream is, int titleNum) throws Exception {
+        return importExcel(StringUtils.EMPTY, is, titleNum);
+    }
+
+    /**
+     * 对excel表单指定表格索引名转换成list
+     *
+     * @param sheetName 表格索引名
+     * @param titleNum  标题占用行数
+     * @param is        输入流
+     * @return 转换后集合
+     */
+    public List<T> importExcel(String sheetName, InputStream is, int titleNum) throws Exception {
+        this.type = Type.IMPORT;
+        this.wb = WorkbookFactory.create(is);
+        List<T> list = new ArrayList<T>();
+        // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet
+        Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);
+        if (sheet == null) {
+            throw new IOException("文件sheet不存在");
+        }
+        boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook);
+        Map<String, PictureData> pictures;
+        if (isXSSFWorkbook) {
+            pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb);
+        } else {
+            pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb);
+        }
+        // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1
+        int rows = sheet.getLastRowNum();
+
+        if (rows > 0) {
+            // 定义一个map用于存放excel列的序号和field.
+            Map<String, Integer> cellMap = new HashMap<String, Integer>();
+            // 获取表头
+            Row heard = sheet.getRow(titleNum);
+            for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) {
+                Cell cell = heard.getCell(i);
+                if (StringUtils.isNotNull(cell)) {
+                    String value = this.getCellValue(heard, i).toString();
+                    cellMap.put(value, i);
+                } else {
+                    cellMap.put(null, i);
+                }
+            }
+            // 有数据时才处理 得到类的所有field.
+            List<Object[]> fields = this.getFields();
+            Map<Integer, Object[]> fieldsMap = new HashMap<Integer, Object[]>();
+            for (Object[] objects : fields) {
+                Excel attr = (Excel) objects[1];
+                Integer column = cellMap.get(attr.name());
+                if (column != null) {
+                    fieldsMap.put(column, objects);
+                }
+            }
+            for (int i = titleNum + 1; i <= rows; i++) {
+                // 从第2行开始取数据,默认第一行是表头.
+                Row row = sheet.getRow(i);
+                // 判断当前行是否是空行
+                if (isRowEmpty(row)) {
+                    continue;
+                }
+                T entity = null;
+                for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet()) {
+                    Object val = this.getCellValue(row, entry.getKey());
+
+                    // 如果不存在实例则新建.
+                    entity = (entity == null ? clazz.newInstance() : entity);
+                    // 从map中得到对应列的field.
+                    Field field = (Field) entry.getValue()[0];
+                    Excel attr = (Excel) entry.getValue()[1];
+                    // 取得类型,并根据对象类型设置值.
+                    Class<?> fieldType = field.getType();
+                    if (String.class == fieldType) {
+                        String s = Convert.toStr(val);
+                        if (StringUtils.endsWith(s, ".0")) {
+                            val = StringUtils.substringBefore(s, ".0");
+                        } else {
+                            String dateFormat = field.getAnnotation(Excel.class).dateFormat();
+                            if (StringUtils.isNotEmpty(dateFormat)) {
+                                val = parseDateToStr(dateFormat, (Date) val);
+                            } else {
+                                val = Convert.toStr(val);
+                            }
+                        }
+                    } else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) {
+                        val = Convert.toInt(val);
+                    } else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) {
+                        val = Convert.toLong(val);
+                    } else if (Double.TYPE == fieldType || Double.class == fieldType) {
+                        val = Convert.toDouble(val);
+                    } else if (Float.TYPE == fieldType || Float.class == fieldType) {
+                        val = Convert.toFloat(val);
+                    } else if (BigDecimal.class == fieldType) {
+                        val = Convert.toBigDecimal(val);
+                    } else if (Date.class == fieldType) {
+                        if (val instanceof String) {
+                            val = DateUtils.parseDate(val);
+                        } else if (val instanceof Double) {
+                            val = DateUtil.getJavaDate((Double) val);
+                        }
+                    } else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) {
+                        val = Convert.toBool(val, false);
+                    }
+                    if (StringUtils.isNotNull(fieldType)) {
+                        String propertyName = field.getName();
+                        if (StringUtils.isNotEmpty(attr.targetAttr())) {
+                            propertyName = field.getName() + "." + attr.targetAttr();
+                        } else if (StringUtils.isNotEmpty(attr.readConverterExp())) {
+                            val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
+                        } else if (StringUtils.isNotEmpty(attr.dictType())) {
+                            val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
+                        } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) {
+                            val = dataFormatHandlerAdapter(val, attr);
+                        } else if (Excel.ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures)) {
+                            PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey());
+                            if (image == null) {
+                                val = "";
+                            } else {
+                                byte[] data = image.getData();
+                                val = FileUtils.writeImportBytes(data);
+                            }
+                        }
+                        ReflectUtils.invokeSetter(entity, propertyName, val);
+                    }
+                }
+                list.add(entity);
+            }
+        }
+        return list;
+    }
+
+    /**
+     * 对list数据源将其里面的数据导入到excel表单
+     *
+     * @param list      导出数据集合
+     * @param sheetName 工作表的名称
+     * @return 结果
+     */
+    public AjaxResult exportExcel(List<T> list, String sheetName) {
+        return exportExcel(list, sheetName, StringUtils.EMPTY);
+    }
+
+    /**
+     * 对list数据源将其里面的数据导入到excel表单
+     *
+     * @param list      导出数据集合
+     * @param sheetName 工作表的名称
+     * @param title     标题
+     * @return 结果
+     */
+    public AjaxResult exportExcel(List<T> list, String sheetName, String title) {
+        this.init(list, sheetName, title, Excel.Type.EXPORT);
+        return exportExcel();
+    }
+
+    /**
+     * 对list数据源将其里面的数据导入到excel表单
+     *
+     * @param response  返回数据
+     * @param list      导出数据集合
+     * @param sheetName 工作表的名称
+     * @return 结果
+     */
+    public void exportExcel(HttpServletResponse response, List<T> list, String sheetName) {
+        exportExcel(response, list, sheetName, StringUtils.EMPTY);
+    }
+
+    /**
+     * 对list数据源将其里面的数据导入到excel表单
+     *
+     * @param response  返回数据
+     * @param list      导出数据集合
+     * @param sheetName 工作表的名称
+     * @param title     标题
+     * @return 结果
+     */
+    public void exportExcel(HttpServletResponse response, List<T> list, String sheetName, String title) {
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setCharacterEncoding("utf-8");
+        this.init(list, sheetName, title, Type.EXPORT);
+        exportExcel(response);
+    }
+
+    /**
+     * 对list数据源将其里面的数据导入到excel表单
+     *
+     * @param sheetName 工作表的名称
+     * @return 结果
+     */
+    public AjaxResult importTemplateExcel(String sheetName) {
+        return importTemplateExcel(sheetName, StringUtils.EMPTY);
+    }
+
+    /**
+     * 对list数据源将其里面的数据导入到excel表单
+     *
+     * @param sheetName 工作表的名称
+     * @param title     标题
+     * @return 结果
+     */
+    public AjaxResult importTemplateExcel(String sheetName, String title) {
+        this.init(null, sheetName, title, Type.IMPORT);
+        return exportExcel();
+    }
+
+    /**
+     * 对list数据源将其里面的数据导入到excel表单
+     *
+     * @param sheetName 工作表的名称
+     * @return 结果
+     */
+    public void importTemplateExcel(HttpServletResponse response, String sheetName) {
+        importTemplateExcel(response, sheetName, StringUtils.EMPTY);
+    }
+
+    /**
+     * 对list数据源将其里面的数据导入到excel表单
+     *
+     * @param sheetName 工作表的名称
+     * @param title     标题
+     * @return 结果
+     */
+    public void importTemplateExcel(HttpServletResponse response, String sheetName, String title) {
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setCharacterEncoding("utf-8");
+        this.init(null, sheetName, title, Type.IMPORT);
+        exportExcel(response);
+    }
+
+    /**
+     * 对list数据源将其里面的数据导入到excel表单
+     *
+     * @return 结果
+     */
+    public void exportExcel(HttpServletResponse response) {
+        try {
+            writeSheet();
+            wb.write(response.getOutputStream());
+        } catch (Exception e) {
+            log.error("导出Excel异常{}", e.getMessage());
+        } finally {
+            IOUtils.closeQuietly(wb);
+        }
+    }
+
+    /**
+     * 对list数据源将其里面的数据导入到excel表单
+     *
+     * @return 结果
+     */
+    public AjaxResult exportExcel() {
+        OutputStream out = null;
+        try {
+            writeSheet();
+            String filename = encodingFilename(sheetName);
+            out = new FileOutputStream(getAbsoluteFile(filename));
+            wb.write(out);
+            return AjaxResult.success(filename);
+        } catch (Exception e) {
+            log.error("导出Excel异常{}", e.getMessage());
+            throw new UtilException("导出Excel失败,请联系网站管理员!");
+        } finally {
+            IOUtils.closeQuietly(wb);
+            IOUtils.closeQuietly(out);
+        }
+    }
+
+    /**
+     * 创建写入数据到Sheet
+     */
+    public void writeSheet() {
+        // 取出一共有多少个sheet.
+        int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize));
+        for (int index = 0; index < sheetNo; index++) {
+            createSheet(sheetNo, index);
+
+            // 产生一行
+            Row row = sheet.createRow(rownum);
+            int column = 0;
+            // 写入各个字段的列头名称
+            for (Object[] os : fields) {
+                Excel excel = (Excel) os[1];
+                this.createCell(excel, row, column++);
+            }
+            if (Type.EXPORT.equals(type)) {
+                fillExcelData(index, row);
+                addStatisticsRow();
+            }
+        }
+    }
+
+    /**
+     * 填充excel数据
+     *
+     * @param index 序号
+     * @param row   单元格行
+     */
+    public void fillExcelData(int index, Row row) {
+        int startNo = index * sheetSize;
+        int endNo = Math.min(startNo + sheetSize, list.size());
+        for (int i = startNo; i < endNo; i++) {
+            row = sheet.createRow(i + 1 + rownum - startNo);
+            // 得到导出对象.
+            T vo = (T) list.get(i);
+            int column = 0;
+            for (Object[] os : fields) {
+                Field field = (Field) os[0];
+                Excel excel = (Excel) os[1];
+                this.addCell(excel, row, vo, field, column++);
+            }
+        }
+    }
+
+    /**
+     * 创建表格样式
+     *
+     * @param wb 工作薄对象
+     * @return 样式列表
+     */
+    private Map<String, CellStyle> createStyles(Workbook wb) {
+        // 写入各条记录,每条记录对应excel表中的一行
+        Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
+        CellStyle style = wb.createCellStyle();
+        style.setAlignment(HorizontalAlignment.CENTER);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+        Font titleFont = wb.createFont();
+        titleFont.setFontName("Arial");
+        titleFont.setFontHeightInPoints((short) 16);
+        titleFont.setBold(true);
+        style.setFont(titleFont);
+        styles.put("title", style);
+
+        style = wb.createCellStyle();
+        style.setAlignment(HorizontalAlignment.CENTER);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+        style.setBorderRight(BorderStyle.THIN);
+        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+        style.setBorderTop(BorderStyle.THIN);
+        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+        Font dataFont = wb.createFont();
+        dataFont.setFontName("Arial");
+        dataFont.setFontHeightInPoints((short) 10);
+        style.setFont(dataFont);
+        styles.put("data", style);
+
+        style = wb.createCellStyle();
+        style.cloneStyleFrom(styles.get("data"));
+        style.setAlignment(HorizontalAlignment.CENTER);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+        style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
+        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+        Font headerFont = wb.createFont();
+        headerFont.setFontName("Arial");
+        headerFont.setFontHeightInPoints((short) 10);
+        headerFont.setBold(true);
+        headerFont.setColor(IndexedColors.WHITE.getIndex());
+        style.setFont(headerFont);
+        styles.put("header", style);
+
+        style = wb.createCellStyle();
+        style.setAlignment(HorizontalAlignment.CENTER);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+        Font totalFont = wb.createFont();
+        totalFont.setFontName("Arial");
+        totalFont.setFontHeightInPoints((short) 10);
+        style.setFont(totalFont);
+        styles.put("total", style);
+
+        style = wb.createCellStyle();
+        style.cloneStyleFrom(styles.get("data"));
+        style.setAlignment(HorizontalAlignment.LEFT);
+        styles.put("data1", style);
+
+        style = wb.createCellStyle();
+        style.cloneStyleFrom(styles.get("data"));
+        style.setAlignment(HorizontalAlignment.CENTER);
+        styles.put("data2", style);
+
+        style = wb.createCellStyle();
+        style.cloneStyleFrom(styles.get("data"));
+        style.setAlignment(HorizontalAlignment.RIGHT);
+        styles.put("data3", style);
+
+        return styles;
+    }
+
+    /**
+     * 创建单元格
+     */
+    public Cell createCell(Excel attr, Row row, int column) {
+        // 创建列
+        Cell cell = row.createCell(column);
+        // 写入列信息
+        cell.setCellValue(attr.name());
+        setDataValidation(attr, row, column);
+        cell.setCellStyle(styles.get("header"));
+        return cell;
+    }
+
+    /**
+     * 设置单元格信息
+     *
+     * @param value 单元格值
+     * @param attr  注解相关
+     * @param cell  单元格信息
+     */
+    public void setCellVo(Object value, Excel attr, Cell cell) {
+        if (ColumnType.STRING == attr.cellType()) {
+            String cellValue = Convert.toStr(value);
+            // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。
+            if (StringUtils.containsAny(cellValue, FORMULA_STR)) {
+                cellValue = StringUtils.replaceEach(cellValue, FORMULA_STR, new String[]{"\t=", "\t-", "\t+", "\t@"});
+            }
+            cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix());
+        } else if (ColumnType.NUMERIC == attr.cellType()) {
+            if (StringUtils.isNotNull(value)) {
+                cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value));
+            }
+        } else if (ColumnType.IMAGE == attr.cellType()) {
+            ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1);
+            String imagePath = Convert.toStr(value);
+            if (StringUtils.isNotEmpty(imagePath)) {
+                byte[] data = ImageUtils.getImage(imagePath);
+                getDrawingPatriarch(cell.getSheet()).createPicture(anchor,
+                        cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
+            }
+        }
+    }
+
+    /**
+     * 获取画布
+     */
+    public static Drawing<?> getDrawingPatriarch(Sheet sheet) {
+        if (sheet.getDrawingPatriarch() == null) {
+            sheet.createDrawingPatriarch();
+        }
+        return sheet.getDrawingPatriarch();
+    }
+
+    /**
+     * 获取图片类型,设置图片插入类型
+     */
+    public int getImageType(byte[] value) {
+        String type = FileTypeUtils.getFileExtendName(value);
+        if ("JPG".equalsIgnoreCase(type)) {
+            return Workbook.PICTURE_TYPE_JPEG;
+        } else if ("PNG".equalsIgnoreCase(type)) {
+            return Workbook.PICTURE_TYPE_PNG;
+        }
+        return Workbook.PICTURE_TYPE_JPEG;
+    }
+
+    /**
+     * 创建表格样式
+     */
+    public void setDataValidation(Excel attr, Row row, int column) {
+        if (attr.name().indexOf("注:") >= 0) {
+            sheet.setColumnWidth(column, 6000);
+        } else {
+            // 设置列宽
+            sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
+        }
+        // 如果设置了提示信息则鼠标放上去提示.
+        if (StringUtils.isNotEmpty(attr.prompt())) {
+            // 这里默认设了2-101列提示.
+            setXSSFPrompt(sheet, "", attr.prompt(), 1, 100, column, column);
+        }
+        // 如果设置了combo属性则本列只能选择不能输入
+        if (attr.combo().length > 0) {
+            // 这里默认设了2-101列只能选择不能输入.
+            setXSSFValidation(sheet, attr.combo(), 1, 100, column, column);
+        }
+    }
+
+    /**
+     * 添加单元格
+     */
+    public Cell addCell(Excel attr, Row row, T vo, Field field, int column) {
+        Cell cell = null;
+        try {
+            // 设置行高
+            row.setHeight(maxHeight);
+            // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
+            if (attr.isExport()) {
+                // 创建cell
+                cell = row.createCell(column);
+                int align = attr.align().value();
+                cell.setCellStyle(styles.get("data" + (align >= 1 && align <= 3 ? align : "")));
+
+                // 用于读取对象中的属性
+                Object value = getTargetValue(vo, field, attr);
+                String dateFormat = attr.dateFormat();
+                String readConverterExp = attr.readConverterExp();
+                String separator = attr.separator();
+                String dictType = attr.dictType();
+                if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) {
+                    cell.setCellValue(parseDateToStr(dateFormat, (Date) value));
+                } else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) {
+                    cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator));
+                } else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value)) {
+                    cell.setCellValue(convertDictByExp(Convert.toStr(value), dictType, separator));
+                } else if (value instanceof BigDecimal && -1 != attr.scale()) {
+                    cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).toString());
+                } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) {
+                    cell.setCellValue(dataFormatHandlerAdapter(value, attr));
+                } else {
+                    // 设置列类型
+                    setCellVo(value, attr, cell);
+                }
+                addStatisticsData(column, Convert.toStr(value), attr);
+            }
+        } catch (Exception e) {
+            log.error("导出Excel失败{}", e);
+        }
+        return cell;
+    }
+
+    /**
+     * 设置 POI XSSFSheet 单元格提示
+     *
+     * @param sheet         表单
+     * @param promptTitle   提示标题
+     * @param promptContent 提示内容
+     * @param firstRow      开始行
+     * @param endRow        结束行
+     * @param firstCol      开始列
+     * @param endCol        结束列
+     */
+    public void setXSSFPrompt(Sheet sheet, String promptTitle, String promptContent, int firstRow, int endRow,
+                              int firstCol, int endCol) {
+        DataValidationHelper helper = sheet.getDataValidationHelper();
+        DataValidationConstraint constraint = helper.createCustomConstraint("DD1");
+        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
+        DataValidation dataValidation = helper.createValidation(constraint, regions);
+        dataValidation.createPromptBox(promptTitle, promptContent);
+        dataValidation.setShowPromptBox(true);
+        sheet.addValidationData(dataValidation);
+    }
+
+    /**
+     * 设置某些列的值只能输入预制的数据,显示下拉框.
+     *
+     * @param sheet    要设置的sheet.
+     * @param textlist 下拉框显示的内容
+     * @param firstRow 开始行
+     * @param endRow   结束行
+     * @param firstCol 开始列
+     * @param endCol   结束列
+     * @return 设置好的sheet.
+     */
+    public void setXSSFValidation(Sheet sheet, String[] textlist, int firstRow, int endRow, int firstCol, int endCol) {
+        DataValidationHelper helper = sheet.getDataValidationHelper();
+        // 加载下拉列表内容
+        DataValidationConstraint constraint = helper.createExplicitListConstraint(textlist);
+        // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
+        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
+        // 数据有效性对象
+        DataValidation dataValidation = helper.createValidation(constraint, regions);
+        // 处理Excel兼容性问题
+        if (dataValidation instanceof XSSFDataValidation) {
+            dataValidation.setSuppressDropDownArrow(true);
+            dataValidation.setShowErrorBox(true);
+        } else {
+            dataValidation.setSuppressDropDownArrow(false);
+        }
+
+        sheet.addValidationData(dataValidation);
+    }
+
+    /**
+     * 解析导出值 0=男,1=女,2=未知
+     *
+     * @param propertyValue 参数值
+     * @param converterExp  翻译注解
+     * @param separator     分隔符
+     * @return 解析后值
+     */
+    public static String convertByExp(String propertyValue, String converterExp, String separator) {
+        StringBuilder propertyString = new StringBuilder();
+        String[] convertSource = converterExp.split(",");
+        for (String item : convertSource) {
+            String[] itemArray = item.split("=");
+            if (StringUtils.containsAny(separator, propertyValue)) {
+                for (String value : propertyValue.split(separator)) {
+                    if (itemArray[0].equals(value)) {
+                        propertyString.append(itemArray[1] + separator);
+                        break;
+                    }
+                }
+            } else {
+                if (itemArray[0].equals(propertyValue)) {
+                    return itemArray[1];
+                }
+            }
+        }
+        return StringUtils.stripEnd(propertyString.toString(), separator);
+    }
+
+    /**
+     * 反向解析值 男=0,女=1,未知=2
+     *
+     * @param propertyValue 参数值
+     * @param converterExp  翻译注解
+     * @param separator     分隔符
+     * @return 解析后值
+     */
+    public static String reverseByExp(String propertyValue, String converterExp, String separator) {
+        StringBuilder propertyString = new StringBuilder();
+        String[] convertSource = converterExp.split(",");
+        for (String item : convertSource) {
+            String[] itemArray = item.split("=");
+            if (StringUtils.containsAny(separator, propertyValue)) {
+                for (String value : propertyValue.split(separator)) {
+                    if (itemArray[1].equals(value)) {
+                        propertyString.append(itemArray[0] + separator);
+                        break;
+                    }
+                }
+            } else {
+                if (itemArray[1].equals(propertyValue)) {
+                    return itemArray[0];
+                }
+            }
+        }
+        return StringUtils.stripEnd(propertyString.toString(), separator);
+    }
+
+    /**
+     * 解析字典值
+     *
+     * @param dictValue 字典值
+     * @param dictType  字典类型
+     * @param separator 分隔符
+     * @return 字典标签
+     */
+    public static String convertDictByExp(String dictValue, String dictType, String separator) {
+        return DictUtils.getDictLabel(dictType, dictValue, separator);
+    }
+
+    /**
+     * 反向解析值字典值
+     *
+     * @param dictLabel 字典标签
+     * @param dictType  字典类型
+     * @param separator 分隔符
+     * @return 字典值
+     */
+    public static String reverseDictByExp(String dictLabel, String dictType, String separator) {
+        return DictUtils.getDictValue(dictType, dictLabel, separator);
+    }
+
+    /**
+     * 数据处理器
+     *
+     * @param value 数据值
+     * @param excel 数据注解
+     * @return
+     */
+    public String dataFormatHandlerAdapter(Object value, Excel excel) {
+        try {
+            Object instance = excel.handler().newInstance();
+            Method formatMethod = excel.handler().getMethod("format", new Class[]{Object.class, String[].class});
+            value = formatMethod.invoke(instance, value, excel.args());
+        } catch (Exception e) {
+            log.error("不能格式化数据 " + excel.handler(), e.getMessage());
+        }
+        return Convert.toStr(value);
+    }
+
+    /**
+     * 合计统计信息
+     */
+    private void addStatisticsData(Integer index, String text, Excel entity) {
+        if (entity != null && entity.isStatistics()) {
+            Double temp = 0D;
+            if (!statistics.containsKey(index)) {
+                statistics.put(index, temp);
+            }
+            try {
+                temp = Double.valueOf(text);
+            } catch (NumberFormatException e) {
+            }
+            statistics.put(index, statistics.get(index) + temp);
+        }
+    }
+
+    /**
+     * 创建统计行
+     */
+    public void addStatisticsRow() {
+        if (statistics.size() > 0) {
+            Row row = sheet.createRow(sheet.getLastRowNum() + 1);
+            Set<Integer> keys = statistics.keySet();
+            Cell cell = row.createCell(0);
+            cell.setCellStyle(styles.get("total"));
+            cell.setCellValue("合计");
+
+            for (Integer key : keys) {
+                cell = row.createCell(key);
+                cell.setCellStyle(styles.get("total"));
+                cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key)));
+            }
+            statistics.clear();
+        }
+    }
+
+    /**
+     * 编码文件名
+     */
+    public String encodingFilename(String filename) {
+        filename = UUID.randomUUID().toString() + "_" + filename + ".xlsx";
+        return filename;
+    }
+
+    /**
+     * 获取下载路径
+     *
+     * @param filename 文件名称
+     */
+    public String getAbsoluteFile(String filename) {
+        String downloadPath = Config.getDownloadPath() + filename;
+        File desc = new File(downloadPath);
+        if (!desc.getParentFile().exists()) {
+            desc.getParentFile().mkdirs();
+        }
+        return downloadPath;
+    }
+
+    /**
+     * 获取bean中的属性值
+     *
+     * @param vo    实体对象
+     * @param field 字段
+     * @param excel 注解
+     * @return 最终的属性值
+     * @throws Exception
+     */
+    private Object getTargetValue(T vo, Field field, Excel excel) throws Exception {
+        Object o = field.get(vo);
+        if (StringUtils.isNotEmpty(excel.targetAttr())) {
+            String target = excel.targetAttr();
+            if (target.contains(".")) {
+                String[] targets = target.split("[.]");
+                for (String name : targets) {
+                    o = getValue(o, name);
+                }
+            } else {
+                o = getValue(o, target);
+            }
+        }
+        return o;
+    }
+
+    /**
+     * 以类的属性的get方法方法形式获取值
+     *
+     * @param o
+     * @param name
+     * @return value
+     * @throws Exception
+     */
+    private Object getValue(Object o, String name) throws Exception {
+        if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) {
+            Class<?> clazz = o.getClass();
+            Field field = clazz.getDeclaredField(name);
+            field.setAccessible(true);
+            o = field.get(o);
+        }
+        return o;
+    }
+
+    /**
+     * 得到所有定义字段
+     */
+    private void createExcelField() {
+        this.fields = getFields();
+        this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList());
+        this.maxHeight = getRowHeight();
+    }
+
+    /**
+     * 获取字段注解信息
+     */
+    public List<Object[]> getFields() {
+        List<Object[]> fields = new ArrayList<Object[]>();
+        List<Field> tempFields = new ArrayList<>();
+        tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
+        tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
+        for (Field field : tempFields) {
+            // 单注解
+            if (field.isAnnotationPresent(Excel.class)) {
+                Excel attr = field.getAnnotation(Excel.class);
+                if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) {
+                    field.setAccessible(true);
+                    fields.add(new Object[]{field, attr});
+                }
+            }
+
+            // 多注解
+            if (field.isAnnotationPresent(Excels.class)) {
+                Excels attrs = field.getAnnotation(Excels.class);
+                Excel[] excels = attrs.value();
+                for (Excel attr : excels) {
+                    if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) {
+                        field.setAccessible(true);
+                        fields.add(new Object[]{field, attr});
+                    }
+                }
+            }
+        }
+        return fields;
+    }
+
+    /**
+     * 根据注解获取最大行高
+     */
+    public short getRowHeight() {
+        double maxHeight = 0;
+        for (Object[] os : this.fields) {
+            Excel excel = (Excel) os[1];
+            maxHeight = Math.max(maxHeight, excel.height());
+        }
+        return (short) (maxHeight * 20);
+    }
+
+    /**
+     * 创建一个工作簿
+     */
+    public void createWorkbook() {
+        this.wb = new SXSSFWorkbook(500);
+        this.sheet = wb.createSheet();
+        wb.setSheetName(0, sheetName);
+        this.styles = createStyles(wb);
+    }
+
+    /**
+     * 创建工作表
+     *
+     * @param sheetNo sheet数量
+     * @param index   序号
+     */
+    public void createSheet(int sheetNo, int index) {
+        // 设置工作表的名称.
+        if (sheetNo > 1 && index > 0) {
+            this.sheet = wb.createSheet();
+            this.createTitle();
+            wb.setSheetName(index, sheetName + index);
+        }
+    }
+
+    /**
+     * 获取单元格值
+     *
+     * @param row    获取的行
+     * @param column 获取单元格列号
+     * @return 单元格值
+     */
+    public Object getCellValue(Row row, int column) {
+        if (row == null) {
+            return row;
+        }
+        Object val = "";
+        try {
+            Cell cell = row.getCell(column);
+            if (StringUtils.isNotNull(cell)) {
+                if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) {
+                    val = cell.getNumericCellValue();
+                    if (DateUtil.isCellDateFormatted(cell)) {
+                        val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换
+                    } else {
+                        if ((Double) val % 1 != 0) {
+                            val = new BigDecimal(val.toString());
+                        } else {
+                            val = new DecimalFormat("0").format(val);
+                        }
+                    }
+                } else if (cell.getCellType() == CellType.STRING) {
+                    val = cell.getStringCellValue();
+                } else if (cell.getCellType() == CellType.BOOLEAN) {
+                    val = cell.getBooleanCellValue();
+                } else if (cell.getCellType() == CellType.ERROR) {
+                    val = cell.getErrorCellValue();
+                }
+
+            }
+        } catch (Exception e) {
+            return val;
+        }
+        return val;
+    }
+
+    /**
+     * 判断是否是空行
+     *
+     * @param row 判断的行
+     * @return
+     */
+    private boolean isRowEmpty(Row row) {
+        if (row == null) {
+            return true;
+        }
+        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
+            Cell cell = row.getCell(i);
+            if (cell != null && !CellType.BLANK.equals(cell.getCellType())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 获取Excel2003图片
+     *
+     * @param sheet    当前sheet对象
+     * @param workbook 工作簿对象
+     * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData
+     */
+    public static Map<String, PictureData> getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook) {
+        Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>();
+        List<HSSFPictureData> pictures = workbook.getAllPictures();
+        if (!pictures.isEmpty()) {
+            for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()) {
+                HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor();
+                if (shape instanceof HSSFPicture) {
+                    HSSFPicture pic = (HSSFPicture) shape;
+                    int pictureIndex = pic.getPictureIndex() - 1;
+                    HSSFPictureData picData = pictures.get(pictureIndex);
+                    String picIndex = String.valueOf(anchor.getRow1()) + "_" + String.valueOf(anchor.getCol1());
+                    sheetIndexPicMap.put(picIndex, picData);
+                }
+            }
+            return sheetIndexPicMap;
+        } else {
+            return sheetIndexPicMap;
+        }
+    }
+
+    /**
+     * 获取Excel2007图片
+     *
+     * @param sheet    当前sheet对象
+     * @param workbook 工作簿对象
+     * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData
+     */
+    public static Map<String, PictureData> getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook) {
+        Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>();
+        for (POIXMLDocumentPart dr : sheet.getRelations()) {
+            if (dr instanceof XSSFDrawing) {
+                XSSFDrawing drawing = (XSSFDrawing) dr;
+                List<XSSFShape> shapes = drawing.getShapes();
+                for (XSSFShape shape : shapes) {
+                    if (shape instanceof XSSFPicture) {
+                        XSSFPicture pic = (XSSFPicture) shape;
+                        XSSFClientAnchor anchor = pic.getPreferredSize();
+                        CTMarker ctMarker = anchor.getFrom();
+                        String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol();
+                        sheetIndexPicMap.put(picIndex, pic.getPictureData());
+                    }
+                }
+            }
+        }
+        return sheetIndexPicMap;
+    }
+
+    /**
+     * 格式化不同类型的日期对象
+     *
+     * @param dateFormat 日期格式
+     * @param val        被格式化的日期对象
+     * @return 格式化后的日期字符
+     */
+    public String parseDateToStr(String dateFormat, Object val) {
+        if (val == null) {
+            return "";
+        }
+        String str;
+        if (val instanceof Date) {
+            str = DateUtils.parseDateToStr(dateFormat, (Date) val);
+        } else if (val instanceof LocalDateTime) {
+            str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val));
+        } else if (val instanceof LocalDate) {
+            str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val));
+        } else {
+            str = val.toString();
+        }
+        return str;
+    }
+}

+ 131 - 0
src/main/java/com/metro/utils/spring/SpringUtils.java

@@ -0,0 +1,131 @@
+package com.metro.utils.spring;
+
+import com.metro.utils.StringUtils;
+import org.springframework.aop.framework.AopContext;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * spring工具类 方便在非spring管理环境中获取bean
+ *
+ * @author ruoyi
+ */
+@Component
+public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware {
+    /**
+     * Spring应用上下文环境
+     */
+    private static ConfigurableListableBeanFactory beanFactory;
+
+    private static ApplicationContext applicationContext;
+
+    @Override
+    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+        SpringUtils.beanFactory = beanFactory;
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        SpringUtils.applicationContext = applicationContext;
+    }
+
+    /**
+     * 获取对象
+     *
+     * @param name
+     * @return Object 一个以所给名字注册的bean的实例
+     * @throws org.springframework.beans.BeansException
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getBean(String name) throws BeansException {
+        return (T) beanFactory.getBean(name);
+    }
+
+    /**
+     * 获取类型为requiredType的对象
+     *
+     * @param clz
+     * @return
+     * @throws org.springframework.beans.BeansException
+     */
+    public static <T> T getBean(Class<T> clz) throws BeansException {
+        T result = (T) beanFactory.getBean(clz);
+        return result;
+    }
+
+    /**
+     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
+     *
+     * @param name
+     * @return boolean
+     */
+    public static boolean containsBean(String name) {
+        return beanFactory.containsBean(name);
+    }
+
+    /**
+     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
+     *
+     * @param name
+     * @return boolean
+     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+     */
+    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.isSingleton(name);
+    }
+
+    /**
+     * @param name
+     * @return Class 注册对象的类型
+     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+     */
+    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.getType(name);
+    }
+
+    /**
+     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
+     *
+     * @param name
+     * @return
+     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+     */
+    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.getAliases(name);
+    }
+
+    /**
+     * 获取aop代理对象
+     *
+     * @param invoker
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getAopProxy(T invoker) {
+        return (T) AopContext.currentProxy();
+    }
+
+    /**
+     * 获取当前的环境配置,无配置返回null
+     *
+     * @return 当前的环境配置
+     */
+    public static String[] getActiveProfiles() {
+        return applicationContext.getEnvironment().getActiveProfiles();
+    }
+
+    /**
+     * 获取当前的环境配置,当有多个环境配置时,只获取第一个
+     *
+     * @return 当前的环境配置
+     */
+    public static String getActiveProfile() {
+        final String[] activeProfiles = getActiveProfiles();
+        return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
+    }
+}

+ 88 - 0
src/main/java/com/metro/utils/text/CharsetKit.java

@@ -0,0 +1,88 @@
+package com.metro.utils.text;
+
+
+import com.metro.utils.StringUtils;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 字符集工具类
+ * 
+ * @author ruoyi
+ */
+public class CharsetKit
+{
+    /** ISO-8859-1 */
+    public static final String ISO_8859_1 = "ISO-8859-1";
+    /** UTF-8 */
+    public static final String UTF_8 = "UTF-8";
+    /** GBK */
+    public static final String GBK = "GBK";
+
+    /** ISO-8859-1 */
+    public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);
+    /** UTF-8 */
+    public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
+    /** GBK */
+    public static final Charset CHARSET_GBK = Charset.forName(GBK);
+
+    /**
+     * 转换为Charset对象
+     * 
+     * @param charset 字符集,为空则返回默认字符集
+     * @return Charset
+     */
+    public static Charset charset(String charset)
+    {
+        return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset);
+    }
+
+    /**
+     * 转换字符串的字符集编码
+     * 
+     * @param source 字符串
+     * @param srcCharset 源字符集,默认ISO-8859-1
+     * @param destCharset 目标字符集,默认UTF-8
+     * @return 转换后的字符集
+     */
+    public static String convert(String source, String srcCharset, String destCharset)
+    {
+        return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset));
+    }
+
+    /**
+     * 转换字符串的字符集编码
+     * 
+     * @param source 字符串
+     * @param srcCharset 源字符集,默认ISO-8859-1
+     * @param destCharset 目标字符集,默认UTF-8
+     * @return 转换后的字符集
+     */
+    public static String convert(String source, Charset srcCharset, Charset destCharset)
+    {
+        if (null == srcCharset)
+        {
+            srcCharset = StandardCharsets.ISO_8859_1;
+        }
+
+        if (null == destCharset)
+        {
+            destCharset = StandardCharsets.UTF_8;
+        }
+
+        if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset))
+        {
+            return source;
+        }
+        return new String(source.getBytes(srcCharset), destCharset);
+    }
+
+    /**
+     * @return 系统字符集编码
+     */
+    public static String systemCharset()
+    {
+        return Charset.defaultCharset().name();
+    }
+}

+ 1006 - 0
src/main/java/com/metro/utils/text/Convert.java

@@ -0,0 +1,1006 @@
+package com.metro.utils.text;
+
+import com.metro.utils.StringUtils;
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.text.NumberFormat;
+import java.util.Set;
+
+/**
+ * 类型转换器
+ * 
+ * @author ruoyi
+ */
+public class Convert
+{
+    /**
+     * 转换为字符串<br>
+     * 如果给定的值为null,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static String toStr(Object value, String defaultValue)
+    {
+        if (null == value)
+        {
+            return defaultValue;
+        }
+        if (value instanceof String)
+        {
+            return (String) value;
+        }
+        return value.toString();
+    }
+
+    /**
+     * 转换为字符串<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static String toStr(Object value)
+    {
+        return toStr(value, null);
+    }
+
+    /**
+     * 转换为字符<br>
+     * 如果给定的值为null,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Character toChar(Object value, Character defaultValue)
+    {
+        if (null == value)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Character)
+        {
+            return (Character) value;
+        }
+
+        final String valueStr = toStr(value, null);
+        return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0);
+    }
+
+    /**
+     * 转换为字符<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Character toChar(Object value)
+    {
+        return toChar(value, null);
+    }
+
+    /**
+     * 转换为byte<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Byte toByte(Object value, Byte defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Byte)
+        {
+            return (Byte) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).byteValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Byte.parseByte(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为byte<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Byte toByte(Object value)
+    {
+        return toByte(value, null);
+    }
+
+    /**
+     * 转换为Short<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Short toShort(Object value, Short defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Short)
+        {
+            return (Short) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).shortValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Short.parseShort(valueStr.trim());
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Short<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Short toShort(Object value)
+    {
+        return toShort(value, null);
+    }
+
+    /**
+     * 转换为Number<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Number toNumber(Object value, Number defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Number)
+        {
+            return (Number) value;
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return NumberFormat.getInstance().parse(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Number<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Number toNumber(Object value)
+    {
+        return toNumber(value, null);
+    }
+
+    /**
+     * 转换为int<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Integer toInt(Object value, Integer defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Integer)
+        {
+            return (Integer) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).intValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Integer.parseInt(valueStr.trim());
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为int<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Integer toInt(Object value)
+    {
+        return toInt(value, null);
+    }
+
+    /**
+     * 转换为Integer数组<br>
+     * 
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static Integer[] toIntArray(String str)
+    {
+        return toIntArray(",", str);
+    }
+
+    /**
+     * 转换为Long数组<br>
+     * 
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static Long[] toLongArray(String str)
+    {
+        return toLongArray(",", str);
+    }
+
+    /**
+     * 转换为Integer数组<br>
+     * 
+     * @param split 分隔符
+     * @param split 被转换的值
+     * @return 结果
+     */
+    public static Integer[] toIntArray(String split, String str)
+    {
+        if (StringUtils.isEmpty(str))
+        {
+            return new Integer[] {};
+        }
+        String[] arr = str.split(split);
+        final Integer[] ints = new Integer[arr.length];
+        for (int i = 0; i < arr.length; i++)
+        {
+            final Integer v = toInt(arr[i], 0);
+            ints[i] = v;
+        }
+        return ints;
+    }
+
+    /**
+     * 转换为Long数组<br>
+     * 
+     * @param split 分隔符
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static Long[] toLongArray(String split, String str)
+    {
+        if (StringUtils.isEmpty(str))
+        {
+            return new Long[] {};
+        }
+        String[] arr = str.split(split);
+        final Long[] longs = new Long[arr.length];
+        for (int i = 0; i < arr.length; i++)
+        {
+            final Long v = toLong(arr[i], null);
+            longs[i] = v;
+        }
+        return longs;
+    }
+
+    /**
+     * 转换为String数组<br>
+     * 
+     * @param str 被转换的值
+     * @return 结果
+     */
+    public static String[] toStrArray(String str)
+    {
+        return toStrArray(",", str);
+    }
+
+    /**
+     * 转换为String数组<br>
+     * 
+     * @param split 分隔符
+     * @param split 被转换的值
+     * @return 结果
+     */
+    public static String[] toStrArray(String split, String str)
+    {
+        return str.split(split);
+    }
+
+    /**
+     * 转换为long<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Long toLong(Object value, Long defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Long)
+        {
+            return (Long) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).longValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            // 支持科学计数法
+            return new BigDecimal(valueStr.trim()).longValue();
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为long<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Long toLong(Object value)
+    {
+        return toLong(value, null);
+    }
+
+    /**
+     * 转换为double<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Double toDouble(Object value, Double defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Double)
+        {
+            return (Double) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).doubleValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            // 支持科学计数法
+            return new BigDecimal(valueStr.trim()).doubleValue();
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为double<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Double toDouble(Object value)
+    {
+        return toDouble(value, null);
+    }
+
+    /**
+     * 转换为Float<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Float toFloat(Object value, Float defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Float)
+        {
+            return (Float) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).floatValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Float.parseFloat(valueStr.trim());
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Float<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Float toFloat(Object value)
+    {
+        return toFloat(value, null);
+    }
+
+    /**
+     * 转换为boolean<br>
+     * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Boolean toBool(Object value, Boolean defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Boolean)
+        {
+            return (Boolean) value;
+        }
+        String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        valueStr = valueStr.trim().toLowerCase();
+        switch (valueStr)
+        {
+            case "true":
+                return true;
+            case "false":
+                return false;
+            case "yes":
+                return true;
+            case "ok":
+                return true;
+            case "no":
+                return false;
+            case "1":
+                return true;
+            case "0":
+                return false;
+            default:
+                return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为boolean<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Boolean toBool(Object value)
+    {
+        return toBool(value, null);
+    }
+
+    /**
+     * 转换为Enum对象<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 
+     * @param clazz Enum的Class
+     * @param value 值
+     * @param defaultValue 默认值
+     * @return Enum
+     */
+    public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (clazz.isAssignableFrom(value.getClass()))
+        {
+            @SuppressWarnings("unchecked")
+            E myE = (E) value;
+            return myE;
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Enum.valueOf(clazz, valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Enum对象<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 
+     * @param clazz Enum的Class
+     * @param value 值
+     * @return Enum
+     */
+    public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value)
+    {
+        return toEnum(clazz, value, null);
+    }
+
+    /**
+     * 转换为BigInteger<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static BigInteger toBigInteger(Object value, BigInteger defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof BigInteger)
+        {
+            return (BigInteger) value;
+        }
+        if (value instanceof Long)
+        {
+            return BigInteger.valueOf((Long) value);
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return new BigInteger(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为BigInteger<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static BigInteger toBigInteger(Object value)
+    {
+        return toBigInteger(value, null);
+    }
+
+    /**
+     * 转换为BigDecimal<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof BigDecimal)
+        {
+            return (BigDecimal) value;
+        }
+        if (value instanceof Long)
+        {
+            return new BigDecimal((Long) value);
+        }
+        if (value instanceof Double)
+        {
+            return new BigDecimal((Double) value);
+        }
+        if (value instanceof Integer)
+        {
+            return new BigDecimal((Integer) value);
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return new BigDecimal(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为BigDecimal<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static BigDecimal toBigDecimal(Object value)
+    {
+        return toBigDecimal(value, null);
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     * 
+     * @param obj 对象
+     * @return 字符串
+     */
+    public static String utf8Str(Object obj)
+    {
+        return str(obj, CharsetKit.CHARSET_UTF_8);
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     * 
+     * @param obj 对象
+     * @param charsetName 字符集
+     * @return 字符串
+     */
+    public static String str(Object obj, String charsetName)
+    {
+        return str(obj, Charset.forName(charsetName));
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     * 
+     * @param obj 对象
+     * @param charset 字符集
+     * @return 字符串
+     */
+    public static String str(Object obj, Charset charset)
+    {
+        if (null == obj)
+        {
+            return null;
+        }
+
+        if (obj instanceof String)
+        {
+            return (String) obj;
+        }
+        else if (obj instanceof byte[])
+        {
+            return str((byte[]) obj, charset);
+        }
+        else if (obj instanceof Byte[])
+        {
+            byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj);
+            return str(bytes, charset);
+        }
+        else if (obj instanceof ByteBuffer)
+        {
+            return str((ByteBuffer) obj, charset);
+        }
+        return obj.toString();
+    }
+
+    /**
+     * 将byte数组转为字符串
+     * 
+     * @param bytes byte数组
+     * @param charset 字符集
+     * @return 字符串
+     */
+    public static String str(byte[] bytes, String charset)
+    {
+        return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset));
+    }
+
+    /**
+     * 解码字节码
+     * 
+     * @param data 字符串
+     * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
+     * @return 解码后的字符串
+     */
+    public static String str(byte[] data, Charset charset)
+    {
+        if (data == null)
+        {
+            return null;
+        }
+
+        if (null == charset)
+        {
+            return new String(data);
+        }
+        return new String(data, charset);
+    }
+
+    /**
+     * 将编码的byteBuffer数据转换为字符串
+     * 
+     * @param data 数据
+     * @param charset 字符集,如果为空使用当前系统字符集
+     * @return 字符串
+     */
+    public static String str(ByteBuffer data, String charset)
+    {
+        if (data == null)
+        {
+            return null;
+        }
+
+        return str(data, Charset.forName(charset));
+    }
+
+    /**
+     * 将编码的byteBuffer数据转换为字符串
+     * 
+     * @param data 数据
+     * @param charset 字符集,如果为空使用当前系统字符集
+     * @return 字符串
+     */
+    public static String str(ByteBuffer data, Charset charset)
+    {
+        if (null == charset)
+        {
+            charset = Charset.defaultCharset();
+        }
+        return charset.decode(data).toString();
+    }
+
+    // ----------------------------------------------------------------------- 全角半角转换
+    /**
+     * 半角转全角
+     * 
+     * @param input String.
+     * @return 全角字符串.
+     */
+    public static String toSBC(String input)
+    {
+        return toSBC(input, null);
+    }
+
+    /**
+     * 半角转全角
+     * 
+     * @param input String
+     * @param notConvertSet 不替换的字符集合
+     * @return 全角字符串.
+     */
+    public static String toSBC(String input, Set<Character> notConvertSet)
+    {
+        char c[] = input.toCharArray();
+        for (int i = 0; i < c.length; i++)
+        {
+            if (null != notConvertSet && notConvertSet.contains(c[i]))
+            {
+                // 跳过不替换的字符
+                continue;
+            }
+
+            if (c[i] == ' ')
+            {
+                c[i] = '\u3000';
+            }
+            else if (c[i] < '\177')
+            {
+                c[i] = (char) (c[i] + 65248);
+
+            }
+        }
+        return new String(c);
+    }
+
+    /**
+     * 全角转半角
+     * 
+     * @param input String.
+     * @return 半角字符串
+     */
+    public static String toDBC(String input)
+    {
+        return toDBC(input, null);
+    }
+
+    /**
+     * 替换全角为半角
+     * 
+     * @param text 文本
+     * @param notConvertSet 不替换的字符集合
+     * @return 替换后的字符
+     */
+    public static String toDBC(String text, Set<Character> notConvertSet)
+    {
+        char c[] = text.toCharArray();
+        for (int i = 0; i < c.length; i++)
+        {
+            if (null != notConvertSet && notConvertSet.contains(c[i]))
+            {
+                // 跳过不替换的字符
+                continue;
+            }
+
+            if (c[i] == '\u3000')
+            {
+                c[i] = ' ';
+            }
+            else if (c[i] > '\uFF00' && c[i] < '\uFF5F')
+            {
+                c[i] = (char) (c[i] - 65248);
+            }
+        }
+        String returnString = new String(c);
+
+        return returnString;
+    }
+
+    /**
+     * 数字金额大写转换 先写个完整的然后将如零拾替换成零
+     * 
+     * @param n 数字
+     * @return 中文大写数字
+     */
+    public static String digitUppercase(double n)
+    {
+        String[] fraction = { "角", "分" };
+        String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };
+        String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } };
+
+        String head = n < 0 ? "负" : "";
+        n = Math.abs(n);
+
+        String s = "";
+        for (int i = 0; i < fraction.length; i++)
+        {
+            s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
+        }
+        if (s.length() < 1)
+        {
+            s = "整";
+        }
+        int integerPart = (int) Math.floor(n);
+
+        for (int i = 0; i < unit[0].length && integerPart > 0; i++)
+        {
+            String p = "";
+            for (int j = 0; j < unit[1].length && n > 0; j++)
+            {
+                p = digit[integerPart % 10] + unit[1][j] + p;
+                integerPart = integerPart / 10;
+            }
+            s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s;
+        }
+        return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整");
+    }
+}

+ 77 - 0
src/main/java/com/metro/utils/text/StrFormatter.java

@@ -0,0 +1,77 @@
+package com.metro.utils.text;
+
+
+import com.metro.utils.StringUtils;
+
+/**
+ * 字符串格式化
+ *
+ * @author ruoyi
+ */
+public class StrFormatter {
+    public static final String EMPTY_JSON = "{}";
+    public static final char C_BACKSLASH = '\\';
+    public static final char C_DELIM_START = '{';
+    public static final char C_DELIM_END = '}';
+
+    /**
+     * 格式化字符串<br>
+     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
+     * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
+     * 例:<br>
+     * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
+     * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
+     * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
+     *
+     * @param strPattern 字符串模板
+     * @param argArray   参数列表
+     * @return 结果
+     */
+    public static String format(final String strPattern, final Object... argArray) {
+        if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) {
+            return strPattern;
+        }
+        final int strPatternLength = strPattern.length();
+
+        // 初始化定义好的长度以获得更好的性能
+        StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
+
+        int handledPosition = 0;
+        int delimIndex;// 占位符所在位置
+        for (int argIndex = 0; argIndex < argArray.length; argIndex++) {
+            delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition);
+            if (delimIndex == -1) {
+                if (handledPosition == 0) {
+                    return strPattern;
+                } else { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果
+                    sbuf.append(strPattern, handledPosition, strPatternLength);
+                    return sbuf.toString();
+                }
+            } else {
+                if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) {
+                    if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) {
+                        // 转义符之前还有一个转义符,占位符依旧有效
+                        sbuf.append(strPattern, handledPosition, delimIndex - 1);
+                        sbuf.append(Convert.utf8Str(argArray[argIndex]));
+                        handledPosition = delimIndex + 2;
+                    } else {
+                        // 占位符被转义
+                        argIndex--;
+                        sbuf.append(strPattern, handledPosition, delimIndex - 1);
+                        sbuf.append(C_DELIM_START);
+                        handledPosition = delimIndex + 1;
+                    }
+                } else {
+                    // 正常占位符
+                    sbuf.append(strPattern, handledPosition, delimIndex);
+                    sbuf.append(Convert.utf8Str(argArray[argIndex]));
+                    handledPosition = delimIndex + 2;
+                }
+            }
+        }
+        // 加入最后一个占位符后所有的字符
+        sbuf.append(strPattern, handledPosition, strPattern.length());
+
+        return sbuf.toString();
+    }
+}

+ 49 - 0
src/main/java/com/metro/utils/uuid/IdUtils.java

@@ -0,0 +1,49 @@
+package com.metro.utils.uuid;
+
+/**
+ * ID生成器工具类
+ * 
+ * @author ruoyi
+ */
+public class IdUtils
+{
+    /**
+     * 获取随机UUID
+     * 
+     * @return 随机UUID
+     */
+    public static String randomUUID()
+    {
+        return UUID.randomUUID().toString();
+    }
+
+    /**
+     * 简化的UUID,去掉了横线
+     * 
+     * @return 简化的UUID,去掉了横线
+     */
+    public static String simpleUUID()
+    {
+        return UUID.randomUUID().toString(true);
+    }
+
+    /**
+     * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID
+     * 
+     * @return 随机UUID
+     */
+    public static String fastUUID()
+    {
+        return UUID.fastUUID().toString();
+    }
+
+    /**
+     * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID
+     * 
+     * @return 简化的UUID,去掉了横线
+     */
+    public static String fastSimpleUUID()
+    {
+        return UUID.fastUUID().toString(true);
+    }
+}

+ 81 - 0
src/main/java/com/metro/utils/uuid/Seq.java

@@ -0,0 +1,81 @@
+package com.metro.utils.uuid;
+
+
+import com.metro.utils.DateUtils;
+import com.metro.utils.StringUtils;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author ruoyi 序列生成类
+ */
+public class Seq {
+    // 通用序列类型
+    public static final String commSeqType = "COMMON";
+
+    // 上传序列类型
+    public static final String uploadSeqType = "UPLOAD";
+
+    // 通用接口序列数
+    private static AtomicInteger commSeq = new AtomicInteger(1);
+
+    // 上传接口序列数
+    private static AtomicInteger uploadSeq = new AtomicInteger(1);
+
+    // 机器标识
+    private static String machineCode = "A";
+
+    /**
+     * 获取通用序列号
+     *
+     * @return 序列值
+     */
+    public static String getId() {
+        return getId(commSeqType);
+    }
+
+    /**
+     * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串
+     *
+     * @return 序列值
+     */
+    public static String getId(String type) {
+        AtomicInteger atomicInt = commSeq;
+        if (uploadSeqType.equals(type)) {
+            atomicInt = uploadSeq;
+        }
+        return getId(atomicInt, 3);
+    }
+
+    /**
+     * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串
+     *
+     * @param atomicInt 序列数
+     * @param length    数值长度
+     * @return 序列值
+     */
+    public static String getId(AtomicInteger atomicInt, int length) {
+        String result = DateUtils.dateTimeNow();
+        result += machineCode;
+        result += getSeq(atomicInt, length);
+        return result;
+    }
+
+    /**
+     * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数
+     *
+     * @return 序列值
+     */
+    private synchronized static String getSeq(AtomicInteger atomicInt, int length) {
+        // 先取值再+1
+        int value = atomicInt.getAndIncrement();
+
+        // 如果更新后值>=10 的 (length)幂次方则重置为1
+        int maxSeq = (int) Math.pow(10, length);
+        if (atomicInt.get() >= maxSeq) {
+            atomicInt.set(1);
+        }
+        // 转字符串,用0左补齐
+        return StringUtils.padl(value, length);
+    }
+}

+ 442 - 0
src/main/java/com/metro/utils/uuid/UUID.java

@@ -0,0 +1,442 @@
+package com.metro.utils.uuid;
+
+
+import com.metro.exception.UtilException;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * 提供通用唯一识别码(universally unique identifier)(UUID)实现
+ *
+ * @author ruoyi
+ */
+public final class UUID implements java.io.Serializable, Comparable<UUID> {
+    private static final long serialVersionUID = -1185015143654744140L;
+
+    /**
+     * SecureRandom 的单例
+     */
+    private static class Holder {
+        static final SecureRandom numberGenerator = getSecureRandom();
+    }
+
+    /**
+     * 此UUID的最高64有效位
+     */
+    private final long mostSigBits;
+
+    /**
+     * 此UUID的最低64有效位
+     */
+    private final long leastSigBits;
+
+    /**
+     * 私有构造
+     *
+     * @param data 数据
+     */
+    private UUID(byte[] data) {
+        long msb = 0;
+        long lsb = 0;
+        assert data.length == 16 : "data must be 16 bytes in length";
+        for (int i = 0; i < 8; i++) {
+            msb = (msb << 8) | (data[i] & 0xff);
+        }
+        for (int i = 8; i < 16; i++) {
+            lsb = (lsb << 8) | (data[i] & 0xff);
+        }
+        this.mostSigBits = msb;
+        this.leastSigBits = lsb;
+    }
+
+    /**
+     * 使用指定的数据构造新的 UUID。
+     *
+     * @param mostSigBits  用于 {@code UUID} 的最高有效 64 位
+     * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位
+     */
+    public UUID(long mostSigBits, long leastSigBits) {
+        this.mostSigBits = mostSigBits;
+        this.leastSigBits = leastSigBits;
+    }
+
+    /**
+     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。
+     *
+     * @return 随机生成的 {@code UUID}
+     */
+    public static UUID fastUUID() {
+        return randomUUID(false);
+    }
+
+    /**
+     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
+     *
+     * @return 随机生成的 {@code UUID}
+     */
+    public static UUID randomUUID() {
+        return randomUUID(true);
+    }
+
+    /**
+     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
+     *
+     * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能
+     * @return 随机生成的 {@code UUID}
+     */
+    public static UUID randomUUID(boolean isSecure) {
+        final Random ng = isSecure ? Holder.numberGenerator : getRandom();
+
+        byte[] randomBytes = new byte[16];
+        ng.nextBytes(randomBytes);
+        randomBytes[6] &= 0x0f; /* clear version */
+        randomBytes[6] |= 0x40; /* set to version 4 */
+        randomBytes[8] &= 0x3f; /* clear variant */
+        randomBytes[8] |= 0x80; /* set to IETF variant */
+        return new UUID(randomBytes);
+    }
+
+    /**
+     * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。
+     *
+     * @param name 用于构造 UUID 的字节数组。
+     * @return 根据指定数组生成的 {@code UUID}
+     */
+    public static UUID nameUUIDFromBytes(byte[] name) {
+        MessageDigest md;
+        try {
+            md = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException nsae) {
+            throw new InternalError("MD5 not supported");
+        }
+        byte[] md5Bytes = md.digest(name);
+        md5Bytes[6] &= 0x0f; /* clear version */
+        md5Bytes[6] |= 0x30; /* set to version 3 */
+        md5Bytes[8] &= 0x3f; /* clear variant */
+        md5Bytes[8] |= 0x80; /* set to IETF variant */
+        return new UUID(md5Bytes);
+    }
+
+    /**
+     * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。
+     *
+     * @param name 指定 {@code UUID} 字符串
+     * @return 具有指定值的 {@code UUID}
+     * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常
+     */
+    public static UUID fromString(String name) {
+        String[] components = name.split("-");
+        if (components.length != 5) {
+            throw new IllegalArgumentException("Invalid UUID string: " + name);
+        }
+        for (int i = 0; i < 5; i++) {
+            components[i] = "0x" + components[i];
+        }
+
+        long mostSigBits = Long.decode(components[0]).longValue();
+        mostSigBits <<= 16;
+        mostSigBits |= Long.decode(components[1]).longValue();
+        mostSigBits <<= 16;
+        mostSigBits |= Long.decode(components[2]).longValue();
+
+        long leastSigBits = Long.decode(components[3]).longValue();
+        leastSigBits <<= 48;
+        leastSigBits |= Long.decode(components[4]).longValue();
+
+        return new UUID(mostSigBits, leastSigBits);
+    }
+
+    /**
+     * 返回此 UUID 的 128 位值中的最低有效 64 位。
+     *
+     * @return 此 UUID 的 128 位值中的最低有效 64 位。
+     */
+    public long getLeastSignificantBits() {
+        return leastSigBits;
+    }
+
+    /**
+     * 返回此 UUID 的 128 位值中的最高有效 64 位。
+     *
+     * @return 此 UUID 的 128 位值中最高有效 64 位。
+     */
+    public long getMostSignificantBits() {
+        return mostSigBits;
+    }
+
+    /**
+     * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。
+     * <p>
+     * 版本号具有以下含意:
+     * <ul>
+     * <li>1 基于时间的 UUID
+     * <li>2 DCE 安全 UUID
+     * <li>3 基于名称的 UUID
+     * <li>4 随机生成的 UUID
+     * </ul>
+     *
+     * @return 此 {@code UUID} 的版本号
+     */
+    public int version() {
+        // Version is bits masked by 0x000000000000F000 in MS long
+        return (int) ((mostSigBits >> 12) & 0x0f);
+    }
+
+    /**
+     * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。
+     * <p>
+     * 变体号具有以下含意:
+     * <ul>
+     * <li>0 为 NCS 向后兼容保留
+     * <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF&nbsp;RFC&nbsp;4122</a>(Leach-Salz), 用于此类
+     * <li>6 保留,微软向后兼容
+     * <li>7 保留供以后定义使用
+     * </ul>
+     *
+     * @return 此 {@code UUID} 相关联的变体号
+     */
+    public int variant() {
+        // This field is composed of a varying number of bits.
+        // 0 - - Reserved for NCS backward compatibility
+        // 1 0 - The IETF aka Leach-Salz variant (used by this class)
+        // 1 1 0 Reserved, Microsoft backward compatibility
+        // 1 1 1 Reserved for future definition.
+        return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));
+    }
+
+    /**
+     * 与此 UUID 相关联的时间戳值。
+     * <p>
+     * <p>
+     * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br>
+     * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。
+     * <p>
+     * <p>
+     * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
+     * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
+     *
+     * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。
+     */
+    public long timestamp() throws UnsupportedOperationException {
+        checkTimeBase();
+        return (mostSigBits & 0x0FFFL) << 48//
+                | ((mostSigBits >> 16) & 0x0FFFFL) << 32//
+                | mostSigBits >>> 32;
+    }
+
+    /**
+     * 与此 UUID 相关联的时钟序列值。
+     * <p>
+     * <p>
+     * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。
+     * <p>
+     * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出
+     * UnsupportedOperationException。
+     *
+     * @return 此 {@code UUID} 的时钟序列
+     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
+     */
+    public int clockSequence() throws UnsupportedOperationException {
+        checkTimeBase();
+        return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
+    }
+
+    /**
+     * 与此 UUID 相关的节点值。
+     * <p>
+     * <p>
+     * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。
+     * <p>
+     * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
+     * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
+     *
+     * @return 此 {@code UUID} 的节点值
+     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
+     */
+    public long node() throws UnsupportedOperationException {
+        checkTimeBase();
+        return leastSigBits & 0x0000FFFFFFFFFFFFL;
+    }
+
+    /**
+     * 返回此{@code UUID} 的字符串表现形式。
+     * <p>
+     * <p>
+     * UUID 的字符串表示形式由此 BNF 描述:
+     *
+     * <pre>
+     * {@code
+     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
+     * time_low               = 4*<hexOctet>
+     * time_mid               = 2*<hexOctet>
+     * time_high_and_version  = 2*<hexOctet>
+     * variant_and_sequence   = 2*<hexOctet>
+     * node                   = 6*<hexOctet>
+     * hexOctet               = <hexDigit><hexDigit>
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * </pre>
+     *
+     * </blockquote>
+     *
+     * @return 此{@code UUID} 的字符串表现形式
+     * @see #toString(boolean)
+     */
+    @Override
+    public String toString() {
+        return toString(false);
+    }
+
+    /**
+     * 返回此{@code UUID} 的字符串表现形式。
+     * <p>
+     * <p>
+     * UUID 的字符串表示形式由此 BNF 描述:
+     *
+     * <pre>
+     * {@code
+     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
+     * time_low               = 4*<hexOctet>
+     * time_mid               = 2*<hexOctet>
+     * time_high_and_version  = 2*<hexOctet>
+     * variant_and_sequence   = 2*<hexOctet>
+     * node                   = 6*<hexOctet>
+     * hexOctet               = <hexDigit><hexDigit>
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * </pre>
+     *
+     * </blockquote>
+     *
+     * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串
+     * @return 此{@code UUID} 的字符串表现形式
+     */
+    public String toString(boolean isSimple) {
+        final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
+        // time_low
+        builder.append(digits(mostSigBits >> 32, 8));
+        if (false == isSimple) {
+            builder.append('-');
+        }
+        // time_mid
+        builder.append(digits(mostSigBits >> 16, 4));
+        if (false == isSimple) {
+            builder.append('-');
+        }
+        // time_high_and_version
+        builder.append(digits(mostSigBits, 4));
+        if (false == isSimple) {
+            builder.append('-');
+        }
+        // variant_and_sequence
+        builder.append(digits(leastSigBits >> 48, 4));
+        if (false == isSimple) {
+            builder.append('-');
+        }
+        // node
+        builder.append(digits(leastSigBits, 12));
+
+        return builder.toString();
+    }
+
+    /**
+     * 返回此 UUID 的哈希码。
+     *
+     * @return UUID 的哈希码值。
+     */
+    @Override
+    public int hashCode() {
+        long hilo = mostSigBits ^ leastSigBits;
+        return ((int) (hilo >> 32)) ^ (int) hilo;
+    }
+
+    /**
+     * 将此对象与指定对象比较。
+     * <p>
+     * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。
+     *
+     * @param obj 要与之比较的对象
+     * @return 如果对象相同,则返回 {@code true};否则返回 {@code false}
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if ((null == obj) || (obj.getClass() != UUID.class)) {
+            return false;
+        }
+        UUID id = (UUID) obj;
+        return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
+    }
+
+    // Comparison Operations
+
+    /**
+     * 将此 UUID 与指定的 UUID 比较。
+     * <p>
+     * <p>
+     * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。
+     *
+     * @param val 与此 UUID 比较的 UUID
+     * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。
+     */
+    @Override
+    public int compareTo(UUID val) {
+        // The ordering is intentionally set up so that the UUIDs
+        // can simply be numerically compared as two numbers
+        return (this.mostSigBits < val.mostSigBits ? -1 : //
+                (this.mostSigBits > val.mostSigBits ? 1 : //
+                        (this.leastSigBits < val.leastSigBits ? -1 : //
+                                (this.leastSigBits > val.leastSigBits ? 1 : //
+                                        0))));
+    }
+
+    // -------------------------------------------------------------------------------------------------------------------
+    // Private method start
+
+    /**
+     * 返回指定数字对应的hex值
+     *
+     * @param val    值
+     * @param digits 位
+     * @return 值
+     */
+    private static String digits(long val, int digits) {
+        long hi = 1L << (digits * 4);
+        return Long.toHexString(hi | (val & (hi - 1))).substring(1);
+    }
+
+    /**
+     * 检查是否为time-based版本UUID
+     */
+    private void checkTimeBase() {
+        if (version() != 1) {
+            throw new UnsupportedOperationException("Not a time-based UUID");
+        }
+    }
+
+    /**
+     * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG)
+     *
+     * @return {@link SecureRandom}
+     */
+    public static SecureRandom getSecureRandom() {
+        try {
+            return SecureRandom.getInstance("SHA1PRNG");
+        } catch (NoSuchAlgorithmException e) {
+            throw new UtilException(e);
+        }
+    }
+
+    /**
+     * 获取随机数生成器对象<br>
+     * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。
+     *
+     * @return {@link ThreadLocalRandom}
+     */
+    public static ThreadLocalRandom getRandom() {
+        return ThreadLocalRandom.current();
+    }
+}

+ 19 - 1
src/main/resources/application.yml

@@ -1,3 +1,17 @@
+
+# 项目相关配置
+metro:
+  # 名称
+  name: metro
+  # 版本
+  version: 1.0.0
+  # 版权年份
+  copyrightYear: 2022
+  # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
+  profile: D:/uploadPath
+  # 获取ip地址开关
+  addressEnabled: false
+
 server:
   port: 8110
   servlet:
@@ -37,10 +51,14 @@ mybatis-plus:
   configuration:
     map-underscore-to-camel-case: true
     log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
-
 # Swagger配置
 swagger:
   # 是否开启swagger
   enabled: true
   # 请求前缀
   pathMapping:
+  path:
+logging:
+  file:
+    name: ./logs/${spring.application.name}.log
+