Browse Source

开发框架上传

yucheng 3 years ago
parent
commit
34f3a43e3f
100 changed files with 5961 additions and 0 deletions
  1. 9 0
      dc-business/README.md
  2. 9 0
      dc-business/dc-dictionary/README.md
  3. 13 0
      dc-business/dc-dictionary/pom.xml
  4. 9 0
      dc-business/dc-digital/README.md
  5. 13 0
      dc-business/dc-digital/pom.xml
  6. 20 0
      dc-business/pom.xml
  7. 12 0
      dc-cloud/dc-file/README.md
  8. 38 0
      dc-cloud/dc-file/pom.xml
  9. 90 0
      dc-cloud/dc-file/src/main/java/com/persagy/dc/file/controller/FileController.java
  10. 14 0
      dc-cloud/dc-file/src/main/java/com/persagy/dc/file/dao/FileMapper.java
  11. 54 0
      dc-cloud/dc-file/src/main/java/com/persagy/dc/file/service/IFileService.java
  12. 111 0
      dc-cloud/dc-file/src/main/java/com/persagy/dc/file/service/impl/FileServiceImpl.java
  13. 26 0
      dc-cloud/dc-file/src/main/resources/bootstrap.yml
  14. 18 0
      dc-cloud/dc-file/src/main/resources/db/init/table.sql
  15. 22 0
      dc-cloud/dc-file/src/main/resources/mapper/FileMapper.xml
  16. 4 0
      dc-cloud/dc-file/src/main/resources/rebel-remote.xml
  17. 79 0
      dc-cloud/dc-message/pom.xml
  18. 45 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/message/constant/MessageChannelEnum.java
  19. 14 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/message/dao/MessageMapper.java
  20. 14 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/message/dao/MessageTypeMapper.java
  21. 15 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/message/dao/SenderRegisterMapper.java
  22. 22 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/message/model/MessageType.java
  23. 32 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/message/model/MessageVO.java
  24. 27 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/message/model/SenderRegister.java
  25. 38 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/message/service/IMessageService.java
  26. 38 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/message/service/IMessageTypeService.java
  27. 19 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/message/service/ISenderRegisterService.java
  28. 42 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/message/service/impl/MessageServiceImpl.java
  29. 43 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/message/service/impl/MessageTypeServiceImpl.java
  30. 29 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/message/service/impl/SenderRegisterServiceImpl.java
  31. 25 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/sender/service/IMessageSender.java
  32. 86 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/sender/service/MessageSenderFactory.java
  33. 140 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/sender/service/impl/AppMessageSender.java
  34. 37 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/sender/service/impl/MailMessageSender.java
  35. 25 0
      dc-cloud/dc-message/src/main/java/com/persagy/dc/sender/service/impl/SmsMessageSender.java
  36. 3 0
      dc-cloud/dc-message/src/main/resources/bootstrap.yml
  37. 18 0
      dc-cloud/dc-message/src/main/resources/db/init/table.sql
  38. 22 0
      dc-cloud/dc-message/src/main/resources/mapper/FileMapper.xml
  39. 4 0
      dc-cloud/dc-message/src/main/resources/rebel-remote.xml
  40. 19 0
      dc-cloud/pom.xml
  41. 8 0
      dc-common/README.md
  42. 36 0
      dc-common/pom.xml
  43. 12 0
      dc-common/src/main/java/com/persagy/dc/common/config/SpringCommonConfig.java
  44. 108 0
      dc-common/src/main/java/com/persagy/dc/common/constant/ResponseCode.java
  45. 37 0
      dc-common/src/main/java/com/persagy/dc/common/constant/ValidEnum.java
  46. 117 0
      dc-common/src/main/java/com/persagy/dc/common/context/AppContext.java
  47. 41 0
      dc-common/src/main/java/com/persagy/dc/common/context/DefaultAppContext.java
  48. 27 0
      dc-common/src/main/java/com/persagy/dc/common/context/DefaultAppContextFactory.java
  49. 21 0
      dc-common/src/main/java/com/persagy/dc/common/context/IAppContextFactory.java
  50. 109 0
      dc-common/src/main/java/com/persagy/dc/common/exception/BusinessException.java
  51. 153 0
      dc-common/src/main/java/com/persagy/dc/common/helper/SpringHelper.java
  52. 615 0
      dc-common/src/main/java/com/persagy/dc/common/lang/PsDate.java
  53. 326 0
      dc-common/src/main/java/com/persagy/dc/common/lang/PsDateTime.java
  54. 271 0
      dc-common/src/main/java/com/persagy/dc/common/lang/PsTime.java
  55. 228 0
      dc-common/src/main/java/com/persagy/dc/common/model/data/DataType.java
  56. 45 0
      dc-common/src/main/java/com/persagy/dc/common/model/entity/AuditableEntity.java
  57. 91 0
      dc-common/src/main/java/com/persagy/dc/common/model/entity/BaseEntity.java
  58. 59 0
      dc-common/src/main/java/com/persagy/dc/common/model/entity/IAuditableEntity.java
  59. 49 0
      dc-common/src/main/java/com/persagy/dc/common/model/entity/IBaseEntity.java
  60. 36 0
      dc-common/src/main/java/com/persagy/dc/common/model/entity/ITreeEntity.java
  61. 54 0
      dc-common/src/main/java/com/persagy/dc/common/model/response/CommonResult.java
  62. 22 0
      dc-common/src/main/java/com/persagy/dc/common/model/response/PageList.java
  63. 27 0
      dc-common/src/main/java/com/persagy/dc/common/serializer/PsDateDeserializer.java
  64. 22 0
      dc-common/src/main/java/com/persagy/dc/common/serializer/PsDateSerializer.java
  65. 27 0
      dc-common/src/main/java/com/persagy/dc/common/serializer/PsDateTimeDeserializer.java
  66. 22 0
      dc-common/src/main/java/com/persagy/dc/common/serializer/PsDateTimeSerializer.java
  67. 28 0
      dc-common/src/main/java/com/persagy/dc/common/serializer/PsTimeDeserializer.java
  68. 22 0
      dc-common/src/main/java/com/persagy/dc/common/serializer/PsTimeSerializer.java
  69. 127 0
      dc-common/src/main/java/com/persagy/dc/common/utils/ProxyBeanUtil.java
  70. 111 0
      dc-common/src/main/java/com/persagy/dc/common/utils/ResultHelper.java
  71. 338 0
      dc-common/src/main/java/com/persagy/dc/common/utils/TypeConvertUtil.java
  72. 24 0
      dc-comp/dc-amqp-starter/pom.xml
  73. 27 0
      dc-comp/dc-amqp-starter/src/main/java/com/persagy/dc/amqp/config/RabbitBindingConfig.java
  74. 78 0
      dc-comp/dc-amqp-starter/src/main/java/com/persagy/dc/amqp/handler/AbstractRabbitHandlerTemplate.java
  75. 48 0
      dc-comp/dc-amqp-starter/src/main/java/com/persagy/dc/amqp/handler/RabbitMessageSender.java
  76. 48 0
      dc-comp/dc-file-starter/README.md
  77. 38 0
      dc-comp/dc-file-starter/pom.xml
  78. 55 0
      dc-comp/dc-file-starter/src/main/java/com/persagy/dc/file/client/FileClientFacade.java
  79. 49 0
      dc-comp/dc-file-starter/src/main/java/com/persagy/dc/file/client/FileManageClient.java
  80. 31 0
      dc-comp/dc-file-starter/src/main/java/com/persagy/dc/file/model/FileInfo.java
  81. 72 0
      dc-comp/dc-file-starter/src/main/java/com/persagy/dc/file/model/FileInfoCreator.java
  82. 43 0
      dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/config/FileFeignConfig.java
  83. 36 0
      dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/config/MinioConfig.java
  84. 14 0
      dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/constant/FileCommonConst.java
  85. 38 0
      dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/constant/FileStorageEnum.java
  86. 27 0
      dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/service/FileStorageFactory.java
  87. 56 0
      dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/service/IFileStorageService.java
  88. 54 0
      dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/service/impl/MinioExceptionHandler.java
  89. 150 0
      dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/service/impl/MinioStorageServiceImpl.java
  90. 130 0
      dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/utils/FileStorageHelper.java
  91. 55 0
      dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/utils/ImageHelper.java
  92. 176 0
      dc-comp/dc-mybatis/README.md
  93. 29 0
      dc-comp/dc-mybatis/pom.xml
  94. 16 0
      dc-comp/dc-mybatis/src/main/java/com/persagy/dc/mybatis/annotations/FormatMybatisQueryParams.java
  95. 68 0
      dc-comp/dc-mybatis/src/main/java/com/persagy/dc/mybatis/aop/FormatMybatisQueryParamsAspect.java
  96. 29 0
      dc-comp/dc-mybatis/src/main/java/com/persagy/dc/mybatis/config/MyBatisWebConfigurer.java
  97. 36 0
      dc-comp/dc-mybatis/src/main/java/com/persagy/dc/mybatis/config/MybatisPlusConfig.java
  98. 17 0
      dc-comp/dc-mybatis/src/main/java/com/persagy/dc/mybatis/dao/DbDao.java
  99. 30 0
      dc-comp/dc-mybatis/src/main/java/com/persagy/dc/mybatis/dao/SimpleTreeCodeDao.java
  100. 0 0
      dc-comp/dc-mybatis/src/main/java/com/persagy/dc/mybatis/handler/CommonMetaObjectHandler.java

+ 9 - 0
dc-business/README.md

@@ -0,0 +1,9 @@
+数据中心 - 业务服务
+============ 
+
+说明
+---------------
+
+最新变化
+---------------
+

+ 9 - 0
dc-business/dc-dictionary/README.md

@@ -0,0 +1,9 @@
+数据字典服务
+============ 
+
+说明
+---------------
+
+最新变化
+---------------
+

+ 13 - 0
dc-business/dc-dictionary/pom.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>dc-business</artifactId>
+        <groupId>com.persagy</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>dc-dictionary</artifactId>
+    <packaging>jar</packaging>
+</project>

+ 9 - 0
dc-business/dc-digital/README.md

@@ -0,0 +1,9 @@
+数据孪生服务
+============ 
+
+说明
+---------------
+
+最新变化
+---------------
+

+ 13 - 0
dc-business/dc-digital/pom.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>dc-business</artifactId>
+        <groupId>com.persagy</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>dc-digital</artifactId>
+    <packaging>jar</packaging>
+</project>

+ 20 - 0
dc-business/pom.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>dc-package</artifactId>
+        <groupId>com.persagy</groupId>
+        <version>1.0.0</version>
+        <relativePath>../dc-package</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.persagy</groupId>
+    <artifactId>dc-business</artifactId>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>dc-dictionary</module>
+        <module>dc-digital</module>
+    </modules>
+</project>

+ 12 - 0
dc-cloud/dc-file/README.md

@@ -0,0 +1,12 @@
+文件管理服务
+============ 
+文件管理服务
+
+说明
+---------------
+* 文件管理服务只存储文件信息,文件上传由客户端直接上传至文件服务器中,不在文件管理服务做额外中转。
+* 相同路径下出现同名文件,自动更名:原文件名前缀+"-随机数"+后缀。
+
+最新变化
+---------------
+

+ 38 - 0
dc-cloud/dc-file/pom.xml

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>dc-cloud</artifactId>
+        <groupId>com.persagy</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>dc-file</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!-- 配置中心(包含注册中心) -->
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>integrated-config-client</artifactId>
+        </dependency>
+        <!-- 项目启动 -->
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>dc-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>dc-mybatis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>dc-file-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>dc-amqp-starter</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 90 - 0
dc-cloud/dc-file/src/main/java/com/persagy/dc/file/controller/FileController.java

@@ -0,0 +1,90 @@
+package com.persagy.dc.file.controller;
+
+import cn.hutool.core.util.StrUtil;
+import com.persagy.dc.file.model.FileInfo;
+import com.persagy.dc.file.service.IFileService;
+import com.persagy.dc.common.constant.ResponseCode;
+import com.persagy.dc.common.exception.BusinessException;
+import com.persagy.dc.common.model.response.CommonResult;
+import com.persagy.dc.common.model.response.PageList;
+import com.persagy.dc.common.utils.ResultHelper;
+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 java.util.Date;
+import java.util.List;
+
+/**
+ * 文件管理Controller
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+@RestController
+@RequestMapping("/file")
+public class FileController {
+
+    @Autowired
+    IFileService fileService;
+
+    @PostMapping("/insert")
+    public CommonResult<FileInfo> insert(@RequestBody FileInfo fileInfo) {
+        if(StrUtil.isBlank(fileInfo.getFileName()) ||
+                StrUtil.isBlank(fileInfo.getGroupCode()) ||
+                StrUtil.isBlank(fileInfo.getFileBucket())) {
+            throw new BusinessException(ResponseCode.A0400.getCode(), ResponseCode.A0400.getDesc());
+        }
+        fileInfo.setCreationTime(new Date());
+        fileInfo = fileService.insertFile(fileInfo);
+        return ResultHelper.single(fileInfo);
+    }
+
+    @PostMapping("/update")
+    public CommonResult<FileInfo> update(@RequestBody FileInfo fileInfo) {
+        if(StrUtil.isBlank(fileInfo.getId()) ||
+                StrUtil.isBlank(fileInfo.getFileName()) ||
+                StrUtil.isBlank(fileInfo.getGroupCode()) ||
+                StrUtil.isBlank(fileInfo.getFileBucket())) {
+            throw new BusinessException(ResponseCode.A0400.getCode(), ResponseCode.A0400.getDesc());
+        }
+        fileInfo.setModifiedTime(new Date());
+        fileInfo = fileService.updateFile(fileInfo);
+        return ResultHelper.single(fileInfo);
+    }
+
+    @PostMapping("/delete")
+    public CommonResult<String> delete(@RequestBody String id) {
+        if(StrUtil.isBlank(id)) {
+            throw new BusinessException(ResponseCode.A0400.getCode(), ResponseCode.A0400.getDesc());
+        }
+        FileInfo fileInfo = fileService.load(id);
+        if(fileInfo == null) {
+            throw new BusinessException("找不到对应的文件信息");
+        }
+        fileService.deleteFile(fileInfo);
+        return ResultHelper.success();
+    }
+
+    @PostMapping("/load")
+    public CommonResult<FileInfo> load(@RequestBody String id) {
+        if(StrUtil.isBlank(id)) {
+            throw new BusinessException(ResponseCode.A0400.getCode(), ResponseCode.A0400.getDesc());
+        }
+        FileInfo fileInfo = fileService.load(id);
+        if(fileInfo == null) {
+            throw new BusinessException("找不到对应的文件信息");
+        }
+        return ResultHelper.single(fileInfo);
+    }
+
+    @PostMapping("/queryByBusiness")
+    public CommonResult<PageList<FileInfo>> queryByBusiness(@RequestBody String businessId) {
+        if(StrUtil.isBlank(businessId)) {
+            throw new BusinessException(ResponseCode.A0400.getCode(), ResponseCode.A0400.getDesc());
+        }
+        List<FileInfo> voList = fileService.queryByBusinessId(businessId);
+        return ResultHelper.multi(voList);
+    }
+}

+ 14 - 0
dc-cloud/dc-file/src/main/java/com/persagy/dc/file/dao/FileMapper.java

@@ -0,0 +1,14 @@
+package com.persagy.dc.file.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.persagy.dc.file.model.FileInfo;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 文件Dao
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+@Mapper
+public interface FileMapper extends BaseMapper<FileInfo> {
+}

+ 54 - 0
dc-cloud/dc-file/src/main/java/com/persagy/dc/file/service/IFileService.java

@@ -0,0 +1,54 @@
+package com.persagy.dc.file.service;
+
+import com.persagy.dc.file.model.FileInfo;
+
+import java.util.List;
+
+/**
+ * 文件管理接口
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+public interface IFileService {
+
+    /**
+     * 保存
+     * @param vo
+     * @return
+     */
+    FileInfo insertFile(FileInfo vo);
+
+    /**
+     * 保存
+     * @param vo
+     * @return
+     */
+    FileInfo updateFile(FileInfo vo);
+
+    /**
+     * 批量保存
+     * @param voList
+     * @return
+     */
+    List<FileInfo> batchInsert(List<FileInfo> voList);
+
+    /**
+     * 删除
+     * @param vo
+     */
+    void deleteFile(FileInfo vo);
+
+    /**
+     * 通过id查询
+     * @param id
+     * @return
+     */
+    FileInfo load(String id);
+
+    /**
+     * 通过业务ID查询
+     * @param businessId
+     * @return
+     */
+    List<FileInfo> queryByBusinessId(String businessId);
+}

+ 111 - 0
dc-cloud/dc-file/src/main/java/com/persagy/dc/file/service/impl/FileServiceImpl.java

@@ -0,0 +1,111 @@
+package com.persagy.dc.file.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.persagy.dc.file.dao.FileMapper;
+import com.persagy.dc.file.model.FileInfo;
+import com.persagy.dc.file.model.FileInfoCreator;
+import com.persagy.dc.file.service.IFileService;
+import com.persagy.dc.common.constant.ValidEnum;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 文件服务实现类
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+@Service
+@Transactional
+public class FileServiceImpl extends ServiceImpl<FileMapper, FileInfo> implements IFileService {
+
+    @Override
+    public FileInfo insertFile(FileInfo vo) {
+        ensureFilePath(vo);
+        save(vo);
+        return vo;
+    }
+
+    @Override
+    public FileInfo updateFile(FileInfo vo) {
+        FileInfo dbVO = load(vo.getId());
+        vo.setCreationTime(dbVO.getCreationTime());
+        ensureFilePath(vo);
+        updateById(vo);
+        return vo;
+    }
+
+    @Override
+    public List<FileInfo> batchInsert(List<FileInfo> voList) {
+        voList.forEach(vo -> ensureFilePath(vo));
+        saveBatch(voList);
+        return voList;
+    }
+
+    @Override
+    public void deleteFile(FileInfo vo) {
+        vo.setValid(ValidEnum.FALSE.getType());
+        updateFile(vo);
+    }
+
+    @Override
+    public FileInfo load(String id) {
+        return getById(id);
+    }
+
+    @Override
+    public List<FileInfo> queryByBusinessId(String businessId) {
+        if(StrUtil.isBlank(businessId)) {
+            return null;
+        }
+        LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.
+                eq(FileInfo::getBusinessId, businessId).
+                eq(FileInfo::getValid, ValidEnum.TRUE.getType());
+        return list(queryWrapper);
+    }
+
+    /**
+     * 确认文件名称
+     * @param vo 文件管理对象
+     * @return
+     */
+    private void ensureFilePath(FileInfo vo) {
+        // 组装文件路径 - 日期以文件创建日期为准
+        FileInfoCreator.buildFilePath(vo);
+        // 是否存在同名
+        LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.
+                eq(FileInfo::getValid, ValidEnum.TRUE.getType()).
+                eq(FileInfo::getFilePath, vo.getFilePath());
+        if(StrUtil.isNotBlank(vo.getId())) {
+            queryWrapper.ne(FileInfo::getId, vo.getId());
+        }
+        List<FileInfo> dbVOs = list(queryWrapper);
+        if(CollUtil.isEmpty(dbVOs)) {
+            return;
+        }
+        // 存在同名 - 重新命名 +随机数
+        vo.setFileName(resetFileName(vo.getFileName()));
+        ensureFilePath(vo);
+    }
+
+    /**
+     * 重命名
+     * @param oldFileName 原文件名
+     * @return 原文件名前缀+"-随机数"+后缀
+     */
+    private String resetFileName(String oldFileName) {
+        StringBuilder fileName = new StringBuilder();
+        fileName.append(FileUtil.getPrefix(oldFileName)).append("-");
+        fileName.append(RandomUtil.randomInt(100000)).append(".");
+        fileName.append(FileUtil.getSuffix(oldFileName));
+        return fileName.toString();
+    }
+}

+ 26 - 0
dc-cloud/dc-file/src/main/resources/bootstrap.yml

@@ -0,0 +1,26 @@
+spring:
+  application:
+    name: dc-file
+#
+#eureka:
+#  client:
+#    service-url:
+#      defaultZone: http://frame:123456@integrated-eureka:9931/integrated-eureka/eureka/
+#      register-with-eureka: true
+#      fetch-registry: true
+#  instance:
+#    hostname: dc-file
+#    instance-id: ${spring.cloud.client.ip-address}:${server.port}
+#    lease-expiration-duration-in-seconds: 10
+#    lease-renewal-interval-in-seconds: 5
+#    prefer-ip-address: true
+
+persagy:
+  dc:
+    file:
+      url: http://192.168.25.129:9000/
+      accessKey: admin
+      secretKey: 12345678
+  common:
+    token:
+      enabled: false

+ 18 - 0
dc-cloud/dc-file/src/main/resources/db/init/table.sql

@@ -0,0 +1,18 @@
+CREATE TABLE IF NOT EXISTS `file_info` (
+`id` varchar(64) NOT NULL DEFAULT '' COMMENT 'id',
+`group_code` varchar(100) NOT NULL DEFAULT '' COMMENT '集团编码:租户',
+`file_name` varchar(128) NOT NULL DEFAULT '' COMMENT '文件名称:显示用',
+`file_type` varchar(2) NOT NULL DEFAULT '0' COMMENT '文件类型:0-文件,1-目录',
+`file_bucket` varchar(64) NOT NULL DEFAULT '0' COMMENT '文件分组:默认为应用名',
+`file_path` varchar(512) COMMENT '文件路径:含文件名称 - 对应文件存储路径',
+`file_size` int(11) NOT NULL DEFAULT '0' COMMENT '文件大小',
+`business_id` varchar(32) DEFAULT NULL COMMENT '关联业务Id',
+`expire_date` char(8) DEFAULT NULL COMMENT '保留期限',
+`creator` varchar(32) DEFAULT NULL COMMENT '创建人',
+`creation_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
+`modifier` varchar(32) DEFAULT NULL COMMENT '最后修改人',
+`modified_time` timestamp NULL DEFAULT NULL COMMENT '最后修改时间',
+`valid` int(11) DEFAULT NULL COMMENT '合法标识',
+`ts` timestamp default current_timestamp on update current_timestamp NOT NULL COMMENT '乐观锁',
+PRIMARY KEY (`id`)
+) ENGINE = InnoDB CHARSET = utf8mb4 COMMENT '文件信息';

+ 22 - 0
dc-cloud/dc-file/src/main/resources/mapper/FileMapper.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.persagy.dc.file.dao.FileMapper">
+    <resultMap id="FileMap" type="com.persagy.dc.file.model.FileInfo">
+        <result column="id" property="id"/>
+        <result column="group_code" property="groupCode"/>
+        <result column="file_name" property="fileName"/>
+        <result column="file_type" property="fileType"/>
+        <result column="file_bucket" property="fileBucket"/>
+        <result column="file_path" property="filePath"/>
+        <result column="file_size" property="fileSize"/>
+        <result column="business_id" property="businessId"/>
+        <result column="expire_date" property="expireDate"/>
+        <result column="ts" property="ts"/>
+        <result column="creator" property="creator"/>
+        <result column="creation_time" property="creationTime"/>
+        <result column="modifier" property="modifier"/>
+        <result column="modified_time" property="modifiedTime"/>
+        <result column="valid" property="valid"/>
+    </resultMap>
+</mapper>
+

+ 4 - 0
dc-cloud/dc-file/src/main/resources/rebel-remote.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rebel-remote xmlns="http://www.zeroturnaround.com/rebel/remote">
+    <id>com.persagy.dc-file</id>
+</rebel-remote>

+ 79 - 0
dc-cloud/dc-message/pom.xml

@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>dc-cloud</artifactId>
+        <groupId>com.persagy</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>dc-message</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!-- 配置中心(包含注册中心) -->
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>integrated-config-client</artifactId>
+        </dependency>
+        <!-- 项目启动 -->
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>dc-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>dc-mybatis</artifactId>
+        </dependency>
+        <!--
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context-support</artifactId>
+        </dependency>
+        -->
+        <!-- 邮件 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-mail</artifactId>
+        </dependency>
+        <!--//app推送-->
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-push</artifactId>
+            <version>3.10.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-core</artifactId>
+            <version>[4.3.2,5.0.0)</version>
+        </dependency>
+        <!--//app推送-->
+
+        <!--//阿里邮件推送-->
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-dm</artifactId>
+            <version>3.1.0</version>
+        </dependency>
+        <!--//阿里邮件推送-->
+
+        <!--//极光推送-->
+        <dependency>
+            <groupId>cn.jpush.api</groupId>
+            <artifactId>jiguang-common</artifactId>
+            <version>1.1.8</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.bouncycastle</groupId>
+                    <artifactId>bcpkix-jdk15on</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.bouncycastle</groupId>
+                    <artifactId>bcprov-jdk15on</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!--//极光推送-->
+    </dependencies>
+</project>

+ 45 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/message/constant/MessageChannelEnum.java

@@ -0,0 +1,45 @@
+package com.persagy.dc.message.constant;
+
+import lombok.Getter;
+
+/**
+ * 消息发送渠道
+ * @author Charlie Yu
+ * @date 2021-05-24
+ */
+@Getter
+public enum MessageChannelEnum {
+
+    /** 短信 */
+    SMS("SMS", "minioStorageServiceImpl"),
+    /** APP */
+    APP("APP", "minioStorageServiceImpl"),
+    /** 邮箱 */
+    MAIL("MAIL", "minioStorageServiceImpl"),
+    /** 微信 */
+    WECHAT("WECHAT", "minioStorageServiceImpl"),
+    /** 新云 */
+    XY("XY", "minioStorageServiceImpl");
+
+    private String index;
+    private String name;
+
+    MessageChannelEnum(String index, String name) {
+        this.index = index;
+        this.name = name;
+    }
+
+    /**
+     * 获取Enum
+     * @param index
+     * @return
+     */
+    public static MessageChannelEnum load(String index) {
+        for (MessageChannelEnum value: values()) {
+            if (value.getIndex().equals(index)) {
+                return value;
+            }
+        }
+        return null;
+    }
+}

+ 14 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/message/dao/MessageMapper.java

@@ -0,0 +1,14 @@
+package com.persagy.dc.message.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.persagy.dc.message.model.MessageVO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 消息 dao
+ * @author Charlie Yu
+ * @date 2021-05-24
+ */
+@Mapper
+public interface MessageMapper extends BaseMapper<MessageVO> {
+}

+ 14 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/message/dao/MessageTypeMapper.java

@@ -0,0 +1,14 @@
+package com.persagy.dc.message.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.persagy.dc.message.model.MessageType;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 消息类型 dao
+ * @author Charlie Yu
+ * @date 2021-05-24
+ */
+@Mapper
+public interface MessageTypeMapper extends BaseMapper<MessageType> {
+}

+ 15 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/message/dao/SenderRegisterMapper.java

@@ -0,0 +1,15 @@
+package com.persagy.dc.message.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.persagy.dc.message.model.MessageType;
+import com.persagy.dc.message.model.SenderRegister;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 消息渠道注册 dao
+ * @author Charlie Yu
+ * @date 2021-05-24
+ */
+@Mapper
+public interface SenderRegisterMapper extends BaseMapper<SenderRegister> {
+}

+ 22 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/message/model/MessageType.java

@@ -0,0 +1,22 @@
+package com.persagy.dc.message.model;
+
+import com.persagy.dc.common.model.entity.BaseEntity;
+import lombok.Data;
+
+/**
+ * 消息类型
+ * @author Charlie Yu
+ * @date 2021-05-24
+ */
+@Data
+public class MessageType extends BaseEntity {
+
+    /** 消息类型编码 */
+    private String typeCode;
+    /** 消息类型名称 */
+    private String typeName;
+    /** 消息发送渠道 - MessageChannelEnum */
+    private String sendChannel;
+    /** 备注 */
+    private String remark;
+}

+ 32 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/message/model/MessageVO.java

@@ -0,0 +1,32 @@
+package com.persagy.dc.message.model;
+
+import com.persagy.dc.common.model.entity.AuditableEntity;
+import lombok.Data;
+
+/**
+ * 消息
+ * @author Charlie Yu
+ * @date 2021-05-24
+ */
+@Data
+public class MessageVO extends AuditableEntity {
+
+    /** 消息类型 */
+    private String typeId;
+    /** 接收用户 */
+    private String userId;
+    /** 消息标题 */
+    private String title;
+    /** 消息详情 */
+    private String content;
+    /** 关联业务主键 */
+    private String businessId;
+    /** 所属项目 */
+    private String projectId;
+    /** 消息发送渠道 - MessageChannelEnum */
+    private String sendChannel;
+    /** 已读标识 */
+    private Boolean readFlag;
+    /** 置顶标识 */
+    private Boolean topFlag;
+}

+ 27 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/message/model/SenderRegister.java

@@ -0,0 +1,27 @@
+package com.persagy.dc.message.model;
+
+import com.persagy.dc.common.model.entity.BaseEntity;
+import lombok.Data;
+
+/**
+ * 发送渠道注册信息
+ * @author Charlie Yu
+ * @date 2021-05-26
+ */
+@Data
+public class SenderRegister extends BaseEntity {
+
+    /** 发送渠道编码 */
+    private String code;
+    /** 发送渠道名称 */
+    private String name;
+    /** accessKey */
+    private String accessKey;
+    /** accessSecret */
+    private String accessSecret;
+    /** 扩展参数 */
+    private String extra;
+    /** 发送消息实现类 IMessageSender */
+    private String clazz;
+
+}

+ 38 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/message/service/IMessageService.java

@@ -0,0 +1,38 @@
+package com.persagy.dc.message.service;
+
+import com.persagy.dc.message.model.MessageVO;
+
+/**
+ * 消息 服务
+ * @author Charlie Yu
+ * @date 2021-05-24
+ */
+public interface IMessageService {
+
+    /**
+     * 新增
+     * @param vo
+     * @return
+     */
+    MessageVO insertMessage(MessageVO vo);
+
+    /**
+     * 修改
+     * @param vo
+     * @return
+     */
+    MessageVO updateMessage(MessageVO vo);
+
+    /**
+     * 删除
+     * @param id
+     */
+    void deleteMessage(String id);
+
+    /**
+     * 通过主键查询
+     * @param id
+     * @return
+     */
+    MessageVO load(String id);
+}

+ 38 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/message/service/IMessageTypeService.java

@@ -0,0 +1,38 @@
+package com.persagy.dc.message.service;
+
+import com.persagy.dc.message.model.MessageType;
+
+/**
+ * 消息类型 服务
+ * @author Charlie Yu
+ * @date 2021-05-24
+ */
+public interface IMessageTypeService {
+
+    /**
+     * 新增
+     * @param vo
+     * @return
+     */
+    MessageType insertType(MessageType vo);
+
+    /**
+     * 修改
+     * @param vo
+     * @return
+     */
+    MessageType updateType(MessageType vo);
+
+    /**
+     * 删除
+     * @param id
+     */
+    void deleteType(String id);
+
+    /**
+     * 通过主键查询
+     * @param id
+     * @return
+     */
+    MessageType load(String id);
+}

+ 19 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/message/service/ISenderRegisterService.java

@@ -0,0 +1,19 @@
+package com.persagy.dc.message.service;
+
+import com.persagy.dc.message.model.SenderRegister;
+
+import java.util.List;
+
+/**
+ * 消息渠道 接口
+ * @author Charlie Yu
+ * @date 2021-05-26
+ */
+public interface ISenderRegisterService {
+
+    /**
+     * 查询所有消息渠道
+     * @return
+     */
+    List<SenderRegister> queryAll();
+}

+ 42 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/message/service/impl/MessageServiceImpl.java

@@ -0,0 +1,42 @@
+package com.persagy.dc.message.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.persagy.dc.message.dao.MessageMapper;
+import com.persagy.dc.message.model.MessageVO;
+import com.persagy.dc.message.service.IMessageService;
+import com.persagy.dc.common.constant.ValidEnum;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * 消息 服务实现
+ * @author Charlie Yu
+ * @date 2021-05-24
+ */
+@Service
+@Transactional
+public class MessageServiceImpl extends ServiceImpl<MessageMapper, MessageVO> implements IMessageService {
+    @Override
+    public MessageVO insertMessage(MessageVO vo) {
+        save(vo);
+        return vo;
+    }
+
+    @Override
+    public MessageVO updateMessage(MessageVO vo) {
+        updateById(vo);
+        return vo;
+    }
+
+    @Override
+    public void deleteMessage(String id) {
+        MessageVO vo = load(id);
+        vo.setValid(ValidEnum.FALSE.getType());
+        updateMessage(vo);
+    }
+
+    @Override
+    public MessageVO load(String id) {
+        return getById(id);
+    }
+}

+ 43 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/message/service/impl/MessageTypeServiceImpl.java

@@ -0,0 +1,43 @@
+package com.persagy.dc.message.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.persagy.dc.message.dao.MessageTypeMapper;
+import com.persagy.dc.message.model.MessageType;
+import com.persagy.dc.message.service.IMessageTypeService;
+import com.persagy.dc.common.constant.ValidEnum;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * 消息类型 服务实现
+ * @author Charlie Yu
+ * @date 2021-05-24
+ */
+@Service
+@Transactional
+public class MessageTypeServiceImpl extends ServiceImpl<MessageTypeMapper, MessageType> implements IMessageTypeService {
+
+    @Override
+    public MessageType insertType(MessageType vo) {
+        save(vo);
+        return vo;
+    }
+
+    @Override
+    public MessageType updateType(MessageType vo) {
+        updateById(vo);
+        return vo;
+    }
+
+    @Override
+    public void deleteType(String id) {
+        MessageType vo = load(id);
+        vo.setValid(ValidEnum.FALSE.getType());
+        updateType(vo);
+    }
+
+    @Override
+    public MessageType load(String id) {
+        return getById(id);
+    }
+}

+ 29 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/message/service/impl/SenderRegisterServiceImpl.java

@@ -0,0 +1,29 @@
+package com.persagy.dc.message.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.persagy.dc.message.dao.SenderRegisterMapper;
+import com.persagy.dc.message.model.SenderRegister;
+import com.persagy.dc.message.service.ISenderRegisterService;
+import com.persagy.dc.common.constant.ValidEnum;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 消息渠道注册 服务实现
+ * @author Charlie Yu
+ * @date 2021-05-24
+ */
+@Service
+@Transactional
+public class SenderRegisterServiceImpl extends ServiceImpl<SenderRegisterMapper, SenderRegister> implements ISenderRegisterService {
+
+    @Override
+    public List<SenderRegister> queryAll() {
+        LambdaQueryWrapper<SenderRegister> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(SenderRegister::getValid, ValidEnum.TRUE.getType());
+        return list(queryWrapper);
+    }
+}

+ 25 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/sender/service/IMessageSender.java

@@ -0,0 +1,25 @@
+package com.persagy.dc.sender.service;
+
+import com.persagy.dc.message.model.MessageVO;
+import com.persagy.dc.message.model.SenderRegister;
+
+/**
+ * 消息 执行接口
+ * @author Charlie Yu
+ * @date 2021-05-24
+ */
+public interface IMessageSender {
+
+    /**
+     * 初始化消息处理器参数
+     * @param register
+     * @return 是否初始化成功
+     */
+    boolean initParam(SenderRegister register);
+
+    /**
+     * 发送消息
+     * @param vo
+     */
+    void send(MessageVO vo);
+}

+ 86 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/sender/service/MessageSenderFactory.java

@@ -0,0 +1,86 @@
+package com.persagy.dc.sender.service;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.StrUtil;
+import com.persagy.dc.message.model.MessageVO;
+import com.persagy.dc.message.model.SenderRegister;
+import com.persagy.dc.message.service.ISenderRegisterService;
+import com.persagy.dc.common.helper.SpringHelper;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 消息发送 工厂
+ * @author Charlie Yu
+ * @date 2021-05-24
+ */
+@Slf4j
+public class MessageSenderFactory {
+
+    /** 消息处理器 */
+    private static Map<String, IMessageSender> senderMap = new HashMap<>(16);
+
+    /**
+     * 发送消息
+     * @param vo
+     */
+    public void sendMessage(MessageVO vo) {
+        if(vo == null || StrUtil.isBlank(vo.getTypeId())
+                || StrUtil.isBlank(vo.getSendChannel())) {
+            return;
+        }
+        String[] channels = StrUtil.split(vo.getSendChannel(), StrUtil.COMMA);
+        // 发送
+        for(String channel:channels) {
+            IMessageSender sender = senderMap.get(channel);
+            if(sender == null) {
+                log.error("没有找到【{}】对应的消息配置!", channel);
+                continue;
+            }
+            sender.send(vo);
+        }
+    }
+
+    /**
+     * 懒加载
+     * @return
+     */
+    private Map<String, IMessageSender> getSenderList() {
+        if(MapUtil.isEmpty(senderMap)) {
+            ISenderRegisterService senderService = SpringHelper.getBean(ISenderRegisterService.class);
+            List<SenderRegister> registers = senderService.queryAll();
+            initSender(registers);
+        }
+        return senderMap;
+    }
+
+    /**
+     * 重置senderMap
+     * @param registers
+     */
+    private void initSender(List<SenderRegister> registers) {
+        // 先清空Map
+        senderMap.clear();
+        if(CollUtil.isEmpty(registers)) {
+            return;
+        }
+        for(SenderRegister register:registers) {
+            // 配置检查
+            if(StrUtil.isBlank(register.getCode()) || StrUtil.isBlank(register.getClazz())) {
+                continue;
+            }
+            // 得到消息处理器
+            IMessageSender sender = SpringHelper.getBean(register.getClazz(), IMessageSender.class);
+            // 调用消息处理器初始化
+            boolean success = sender.initParam(register);
+            if(!success) {
+                continue;
+            }
+            senderMap.put(register.getCode(), sender);
+        }
+    }
+}

+ 140 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/sender/service/impl/AppMessageSender.java

@@ -0,0 +1,140 @@
+package com.persagy.dc.sender.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.aliyuncs.DefaultAcsClient;
+import com.aliyuncs.IAcsClient;
+import com.aliyuncs.exceptions.ClientException;
+import com.aliyuncs.profile.DefaultProfile;
+import com.aliyuncs.profile.IClientProfile;
+import com.aliyuncs.push.model.v20160801.PushRequest;
+import com.aliyuncs.push.model.v20160801.PushResponse;
+import com.aliyuncs.utils.ParameterHelper;
+import com.persagy.dc.message.model.MessageVO;
+import com.persagy.dc.message.model.SenderRegister;
+import com.persagy.dc.sender.service.IMessageSender;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+/**
+ * App消息
+ * @author Charlie Yu
+ * @date 2021-05-24
+ */
+@Component
+@Slf4j
+public class AppMessageSender implements IMessageSender {
+
+    private IAcsClient client;
+
+    @Override
+    public boolean initParam(SenderRegister register) {
+        // 配置校验
+        if(StrUtil.isBlank(register.getAccessKey()) || StrUtil.isBlank(register.getAccessSecret())) {
+            log.error("【{}】消息参数配置有误!", register.getCode());
+            return false;
+        }
+        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou",
+                register.getAccessKey(), register.getAccessSecret());
+        client = new DefaultAcsClient(profile);
+        return true;
+    }
+
+    @Override
+    public void send(MessageVO vo) {
+        PushRequest request = buildRequest(vo);
+        String messageId = doSend(request);
+    }
+
+    /**
+     * 执行发送
+     * 5次重试
+     * @param request
+     * @return 返回的消息Id,为空表示执行失败
+     */
+    private String doSend(PushRequest request) {
+        int count = 1;
+        // 最多重试5次
+        while (count <= 5) {
+            try {
+                PushResponse response = client.getAcsResponse(request);
+                return response.getMessageId();
+            } catch (ClientException e) {
+                log.error("{}:{},重试第{}次", request.getTargetValue(), e.getMessage(), count);
+                count++;
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException interruptedException) {
+                }
+                continue;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 构建推送参数
+     * @param vo
+     * @return
+     */
+    private PushRequest buildRequest(MessageVO vo) {
+        PushRequest request = new PushRequest();
+        // TODO 增加参数配置,appKey
+        request.setAppKey(12312123123L);
+        //推送目标: DEVICE:推送给设备; ACCOUNT:推送给指定帐号,TAG:推送给自定义标签; ALIAS: 按别名推送; ALL: 全推
+        request.setTarget("ALIAS");
+        //根据Target来设定,如Target=DEVICE, 则对应的值为 设备id1,设备id2. 多个值使用逗号分隔.(帐号与设备有一次最多100个的限制)
+        request.setTargetValue(vo.getUserId());
+        // 设备类型deviceType, iOS设备: "iOS"; Android设备: "ANDROID"; 全部: "ALL", 这是默认值.
+        request.setDeviceType("ALL");
+        request.setBody(vo.getContent());
+        // MESSAGE:表示消息(默认), NOTICE:表示通知
+        request.setPushType("NOTICE");
+        // 消息的标题
+        request.setTitle(vo.getTitle());
+
+        /** iOS参数配置 */
+        // iOS应用图标右上角角标
+        request.setIOSBadge(0);
+        // iOS通知声音
+        request.setIOSMusic("default");
+        // iOS的通知是通过APNs中心来发送的,需要填写对应的环境信息。'DEV': 表示开发环境 'PRODUCT': 表示生产环境
+        request.setIOSApnsEnv("PRODUCT");
+        // 消息推送时设备不在线(既与移动推送的服务端的长连接通道不通),则这条推送会做为通知,通过苹果的APNs通道送达一次。注意:**离线消息转通知仅适用于`生产环境`**
+        request.setIOSRemind(true);
+        // iOS消息转通知时使用的iOS通知内容,仅当iOSApnsEnv=`PRODUCT` && iOSRemind为true时有效
+        request.setIOSRemindBody("PushRequest summary");
+        // 通知的扩展属性(注意 : 该参数要以json map的格式传入,否则会解析出错)
+///        request.setIOSExtParameters("{\"k1\":\"ios\",\"k2\":\"v2\"}");
+
+        /** 安卓参数配置 */
+        request.setAndroidNotifyType("BOTH");
+        request.setAndroidNotificationChannel("1");
+        // 点击通知后动作 "APPLICATION" : 打开应用 "ACTIVITY" : 打开AndroidActivity "URL" : 打开URL "NONE" : 无跳转
+        request.setAndroidOpenType("APPLICATION");
+        // 设定通知打开的activity,仅当AndroidOpenType="Activity"有效
+        request.setAndroidActivity("com.example.alipushdemo");
+        // Android通知音乐
+        request.setAndroidMusic("default");
+        // 设置该参数后启动辅助弹窗功能, 此处指定通知点击后跳转的Activity(辅助弹窗的前提条件:1. 集成第三方辅助通道;2. StoreOffline参数设为true)
+        request.setAndroidPopupActivity("APPLICATION");
+        request.setAndroidPopupTitle(vo.getTitle());
+        request.setAndroidPopupBody(vo.getContent());
+        // 设定通知的扩展属性。(注意 : 该参数要以 json map 的格式传入,否则会解析出错)
+///        request.setAndroidExtParameters("{\"k1\":\"android\",\"k2\":\"v2\"}");
+
+        /** 推送配置 */
+        // 推送时间 - 不设置表示立即推送
+        Date pushDate = new Date(System.currentTimeMillis());
+        String pushTime = ParameterHelper.getISO8601Time(pushDate);
+        request.setPushTime(pushTime);
+        // 失效时间 - 12小时后消息失效, 不会再发送
+        Date expireDate = new Date(System.currentTimeMillis() + 12 * 3600 * 1000);
+        String expireTime = ParameterHelper.getISO8601Time(expireDate);
+        request.setExpireTime(expireTime);
+        // 离线消息是否保存 - 若保存, 在推送时候,用户即使不在线,下一次上线则会收到
+        request.setStoreOffline(true);
+        return request;
+    }
+}

+ 37 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/sender/service/impl/MailMessageSender.java

@@ -0,0 +1,37 @@
+package com.persagy.dc.sender.service.impl;
+
+import com.persagy.dc.message.model.MessageVO;
+import com.persagy.dc.message.model.SenderRegister;
+import com.persagy.dc.sender.service.IMessageSender;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
+import org.springframework.stereotype.Component;
+
+import java.util.Properties;
+
+/**
+ * 邮件消息
+ * @author Charlie Yu
+ * @date 2021-05-24
+ */
+@Component
+public class MailMessageSender implements IMessageSender {
+
+    @Autowired
+    private JavaMailSenderImpl mailSender;
+
+    @Override
+    public boolean initParam(SenderRegister register) {
+        Properties javaMailProperties = new Properties();
+        javaMailProperties.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
+        javaMailProperties.setProperty("mail.smtp.socketFactory.port", "465");
+        javaMailProperties.setProperty("mail.smtp.port", "465");
+        mailSender.setJavaMailProperties(javaMailProperties);
+        return false;
+    }
+
+    @Override
+    public void send(MessageVO vo) {
+
+    }
+}

+ 25 - 0
dc-cloud/dc-message/src/main/java/com/persagy/dc/sender/service/impl/SmsMessageSender.java

@@ -0,0 +1,25 @@
+package com.persagy.dc.sender.service.impl;
+
+import com.persagy.dc.message.model.MessageVO;
+import com.persagy.dc.message.model.SenderRegister;
+import com.persagy.dc.sender.service.IMessageSender;
+import org.springframework.stereotype.Component;
+
+/**
+ * 短信消息
+ * @author Charlie Yu
+ * @date 2021-05-24
+ */
+@Component
+public class SmsMessageSender implements IMessageSender {
+
+    @Override
+    public boolean initParam(SenderRegister register) {
+        return false;
+    }
+
+    @Override
+    public void send(MessageVO vo) {
+
+    }
+}

+ 3 - 0
dc-cloud/dc-message/src/main/resources/bootstrap.yml

@@ -0,0 +1,3 @@
+spring:
+  application:
+    name: dc-message

+ 18 - 0
dc-cloud/dc-message/src/main/resources/db/init/table.sql

@@ -0,0 +1,18 @@
+CREATE TABLE IF NOT EXISTS `file_info` (
+`id` varchar(64) NOT NULL DEFAULT '' COMMENT 'id',
+`group_code` varchar(100) NOT NULL DEFAULT '' COMMENT '集团编码:租户',
+`file_name` varchar(128) NOT NULL DEFAULT '' COMMENT '文件名称:显示用',
+`file_type` varchar(2) NOT NULL DEFAULT '0' COMMENT '文件类型:0-文件,1-目录',
+`file_bucket` varchar(64) NOT NULL DEFAULT '0' COMMENT '文件分组:默认为应用名',
+`file_path` varchar(512) COMMENT '文件路径:含文件名称 - 对应文件存储路径',
+`file_size` int(11) NOT NULL DEFAULT '0' COMMENT '文件大小',
+`business_id` varchar(32) DEFAULT NULL COMMENT '关联业务Id',
+`expire_date` char(8) DEFAULT NULL COMMENT '保留期限',
+`creator` varchar(32) DEFAULT NULL COMMENT '创建人',
+`creation_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
+`modifier` varchar(32) DEFAULT NULL COMMENT '最后修改人',
+`modified_time` timestamp NULL DEFAULT NULL COMMENT '最后修改时间',
+`valid` int(11) DEFAULT NULL COMMENT '合法标识',
+`ts` timestamp default current_timestamp on update current_timestamp NOT NULL COMMENT '乐观锁',
+PRIMARY KEY (`id`)
+) ENGINE = InnoDB CHARSET = utf8mb4 COMMENT '文件信息';

+ 22 - 0
dc-cloud/dc-message/src/main/resources/mapper/FileMapper.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.persagy.dc.file.dao.FileMapper">
+    <resultMap id="FileMap" type="com.persagy.dc.file.model.FileInfo">
+        <result column="id" property="id"/>
+        <result column="group_code" property="groupCode"/>
+        <result column="file_name" property="fileName"/>
+        <result column="file_type" property="fileType"/>
+        <result column="file_bucket" property="fileBucket"/>
+        <result column="file_path" property="filePath"/>
+        <result column="file_size" property="fileSize"/>
+        <result column="business_id" property="businessId"/>
+        <result column="expire_date" property="expireDate"/>
+        <result column="ts" property="ts"/>
+        <result column="creator" property="creator"/>
+        <result column="creation_time" property="creationTime"/>
+        <result column="modifier" property="modifier"/>
+        <result column="modified_time" property="modifiedTime"/>
+        <result column="valid" property="valid"/>
+    </resultMap>
+</mapper>
+

+ 4 - 0
dc-cloud/dc-message/src/main/resources/rebel-remote.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rebel-remote xmlns="http://www.zeroturnaround.com/rebel/remote">
+    <id>com.persagy.dc-file</id>
+</rebel-remote>

+ 19 - 0
dc-cloud/pom.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>dc-package</artifactId>
+        <groupId>com.persagy</groupId>
+        <version>1.0.0</version>
+        <relativePath>../dc-package</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.persagy</groupId>
+    <artifactId>dc-cloud</artifactId>
+
+    <modules>
+        <module>dc-file</module>
+        <module>dc-message</module>
+    </modules>
+</project>

+ 8 - 0
dc-common/README.md

@@ -0,0 +1,8 @@
+通用工具
+============ 
+
+说明
+---------------
+
+最新变化
+---------------

+ 36 - 0
dc-common/pom.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>dc-parent</artifactId>
+        <groupId>com.persagy</groupId>
+        <version>1.0.0</version>
+        <relativePath>../dc-parent</relativePath>
+    </parent>
+    <artifactId>dc-common</artifactId>
+    <version>1.0.0</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <!--
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        -->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+        </dependency>
+        <!-- web -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 12 - 0
dc-common/src/main/java/com/persagy/dc/common/config/SpringCommonConfig.java

@@ -0,0 +1,12 @@
+package com.persagy.dc.common.config;
+
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 通用配置
+ * @author Charlie Yu
+ * @date 2021-06-02
+ */
+@Configuration
+public class SpringCommonConfig {
+}

+ 108 - 0
dc-common/src/main/java/com/persagy/dc/common/constant/ResponseCode.java

@@ -0,0 +1,108 @@
+package com.persagy.dc.common.constant;
+
+/**
+ * @description 
+ * @author zhangqiankun
+ * @since 2020年8月26日:	下午5:17:08
+ */
+public enum ResponseCode {
+
+	A00000("00000", "success"),
+	
+	A10000("10000", "failure"),
+    
+	A0001("A0001", "用户端错误"),
+	
+	A0100("A0100", "用户注册错误"),
+   
+    A0135("A0135", "Method Not Allowed"),
+    
+    A0136("A0136", "Unsupported Media Type"),
+    
+    A0137("A0137", "Not Acceptable"),
+	
+	A0151("A0151", "手机格式校验失败"),
+
+	A0152("A0152", "地址格式校验失败"),
+    
+	A0153("A0153", "邮箱格式校验失败"),
+
+    A0154("A0154", "账号名称重复"),
+
+    A0155("A0155", "员工编号重复"),
+
+    A0156("A0156", "手机号重复"),
+
+    A0157("A0157", "邮箱重复"),
+    
+    A0201("A0201", "用户账户不存在"),
+    
+    A0202("A0202", "用户账户被冻结"),
+    
+    A0203("A0203", "用户账户已作废"),
+    
+    A0210("A0210", "用户名/密码错误"),
+    
+	A0220("A0220", "用户身份校验失败"),
+	
+	A0301("A0301", "访问未授权"),
+	
+	A0302("A0302", "正在授权中"),
+	
+	A0303("A0303", "拒绝授权申请"),
+	
+	A0311("A0311", "授权已过期"),
+	
+	A0324("A0324", "网关访问受限"),
+	
+	A0340("A0340", "签名异常"),
+	
+	A0400("A0400", "用户请求参数错误"),
+	
+	A0402("A0402", "无效的用户输入"),
+	
+	A0422("A0422", "地址不在服务范围"),
+	
+	A0427("A0427", "请求JSON 解析失败"),
+	
+    B0001("B0001", "系统执行出错"),
+    
+    B0300("B0300", "系统资源异常"),
+	
+    C0001("C0001", "调用第三方服务出错"),
+    
+    C0100("C0100", "中间件服务出错"),
+    
+    C0110("C0110", "RPC服务出错"),
+    
+    C0134("C0134", "不支持的数据格式"),
+    
+    C0320("C0320", "数据不存在"),
+    
+    C0341("C0341", "主键冲突"),
+	
+	Z9999("Z9999", "系统繁忙");	// 未知异常
+	
+    private String code;
+    
+    private String desc;
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    private ResponseCode(String code, String desc) {
+        this.desc = desc;
+        this.code = code;
+    }
+
+    @Override
+    public String toString() {
+        return this.code + "_" + this.desc;
+    }
+
+}

+ 37 - 0
dc-common/src/main/java/com/persagy/dc/common/constant/ValidEnum.java

@@ -0,0 +1,37 @@
+package com.persagy.dc.common.constant;
+
+/**
+ * @description: 合法标志
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2021/3/9 2:56 下午
+ * @version: V1.0
+ */
+public enum ValidEnum {
+    FALSE(0, false),
+    TRUE(1, true);
+
+    private Integer type;
+    private Boolean desc;
+
+    ValidEnum(Integer type, Boolean desc) {
+        this.type = type;
+        this.desc = desc;
+    }
+
+    public Integer getType() {
+        return type;
+    }
+
+    public void setType(Integer type) {
+        this.type = type;
+    }
+
+    public Boolean getDesc() {
+        return desc;
+    }
+
+    public void setDesc(Boolean desc) {
+        this.desc = desc;
+    }
+}

+ 117 - 0
dc-common/src/main/java/com/persagy/dc/common/context/AppContext.java

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

+ 41 - 0
dc-common/src/main/java/com/persagy/dc/common/context/DefaultAppContext.java

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

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

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

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

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

+ 109 - 0
dc-common/src/main/java/com/persagy/dc/common/exception/BusinessException.java

@@ -0,0 +1,109 @@
+package com.persagy.dc.common.exception;
+
+
+import com.persagy.dc.common.constant.ResponseCode;
+
+/**
+ * @description 
+ * @author zhangqiankun
+ * @since 2020年8月26日:	下午5:17:08
+ */
+public class BusinessException extends RuntimeException {
+	private static final long serialVersionUID = -3325873096967595156L;
+	
+	private String errorCode;
+    private String errorDesc;		// 给前端看
+    
+	private Object respJson; 		// 响应content中的数据,可加可不加
+
+    public BusinessException() {
+        super();
+    }
+
+    public BusinessException(Throwable e) {
+        super(e);
+    }
+
+    public BusinessException(String message, Throwable e) {
+        super(message, e);
+    }
+
+    /**
+     * @param errorDesc
+     */
+    public BusinessException(String errorDesc) {
+    	super(errorDesc);
+        this.errorCode = ResponseCode.B0001.getCode();
+        this.errorDesc = errorDesc;
+    }
+    
+    /**
+     * 业务异常错误码和错误描述构造器
+     *
+     * @param errorCode
+     * @param errorDesc
+     */
+    public BusinessException(String errorCode, String errorDesc) {
+    	super(errorDesc);
+        this.errorCode = errorCode;
+        this.errorDesc = errorDesc;
+    }
+    
+    public BusinessException(String errorCode, String errorDesc, Object respJson) {
+    	super(errorDesc);
+        this.errorCode = errorCode;
+        this.errorDesc = errorDesc;
+        this.respJson = respJson;
+    }
+
+    /**
+     * 业务异常信息、错误码和错误描述构造器
+     *
+     * @param message 后台日志看
+     * @param errorCode 错误码
+     * @param errorDesc	给前端看
+     */
+    public BusinessException(String message, String errorCode, String errorDesc, Object respJson) {
+        super(message);
+        this.errorCode = errorCode;
+        this.errorDesc = errorDesc;
+        this.respJson = respJson;
+    }
+
+    public BusinessException(Throwable cause, String errorCode, String errorDesc) {
+        super(cause);
+        this.errorCode = errorCode;
+        this.errorDesc = errorDesc;
+    }
+
+    public BusinessException(String message, Throwable cause, String errorCode, String errorDesc) {
+        super(message, cause);
+        this.errorCode = errorCode;
+        this.errorDesc = errorDesc;
+    }
+
+    public String getErrorCode() {
+        return errorCode;
+    }
+
+    public void setErrorCode(String errorCode) {
+        this.errorCode = errorCode;
+    }
+
+    public String getErrorDesc() {
+        return errorDesc;
+    }
+
+    public void setErrorDesc(String errorDesc) {
+        this.errorDesc = errorDesc;
+    }
+
+	public Object getRespJson() {
+		return respJson;
+	}
+
+	public void setRespJson(Object respJson) {
+		this.respJson = respJson;
+	}
+    
+}

+ 153 - 0
dc-common/src/main/java/com/persagy/dc/common/helper/SpringHelper.java

@@ -0,0 +1,153 @@
+package com.persagy.dc.common.helper;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import com.persagy.dc.common.utils.TypeConvertUtil;
+import org.springframework.context.ApplicationContext;
+
+import java.util.Map;
+
+/**
+ * Spring助手
+ * @author Charlie Yu
+ * @version 1.0 2021-03-02
+ */
+public class SpringHelper {
+
+    /**
+     * spring上下文
+     * @return spring上下文
+     */
+    public static ApplicationContext getContext(){
+        return SpringUtil.getApplicationContext();
+    }
+
+    /**
+     * 根据类名搜索相应的bean对象
+     * @param <T> 类模板标识返回值类型
+     * @param clazz 模板类
+     * @return bean对象
+     */
+    public static <T>T getBean(Class<T> clazz){
+        return SpringUtil.getBean(clazz);
+    }
+
+    /**
+     * 根据类型取得所有
+     * @param type 接口类
+     * @return 取得所有的实现bean
+     */
+    public static <T> Map<String, T> getBeansOfType(Class<T> type){
+        return SpringUtil.getBeansOfType(type);
+    }
+
+    /**
+     * 根据bean名称和bean类型搜索相应的bean对象
+     * @param <T> 类模板标识返回值类型
+     * @param beanName bean名称
+     * @param clazz 模板类
+     * @return bean对象
+     */
+    public static <T>T getBean(String beanName,Class<T> clazz){
+        return SpringUtil.getBean(beanName, clazz);
+    }
+
+    /**
+     * 根据bean名称搜索相应的bean对象
+     * @param beanName bean名称
+     * @return bean对象
+     */
+    public static <T>T getBean(String beanName){
+        return SpringUtil.getBean(beanName);
+    }
+
+    /**
+     * 根据配置名称取得字符串配置值(配置名形式为nodeName.nodeName)
+     * @param key 配置名称
+     * @return 指定配置字符串值
+     */
+    public static String getString(String key) {
+        return SpringUtil.getProperty(key);
+    }
+
+    /**
+     * 根据配置名称取得字符串配置值,如果配置值为空使用指定的默认值
+     * @param key         配置名称
+     * @param defaultValue 指定默认值
+     * @return 最终配置值
+     */
+    public static String getString(String key, String defaultValue) {
+        String value = getString(key);
+        return StrUtil.isBlank(value) ? defaultValue : value;
+    }
+
+    /**
+     * 根据配置名称取得整型配置值,如果配置值为空使用指定的默认值
+     * @param key         配置名称
+     * @param defaultValue 默认整型值
+     * @return 最终配置值
+     */
+    public static int getInt(String key, int defaultValue) {
+        String value = getString(key);
+        if (StrUtil.isEmpty(value)) {
+            return defaultValue;
+        }
+        return TypeConvertUtil.parseInt(value);
+    }
+    /**
+     * 根据配置名称取得布尔配置值,如果配置值为空使用指定的默认值
+     * @param key         配置名称
+     * @param defaultValue 默认布尔值
+     * @return 最终配置值
+     */
+    public static boolean getBoolean(String key, boolean defaultValue) {
+        String value = getString(key);
+        if (StrUtil.isEmpty(value)) {
+            return defaultValue;
+        }
+        return TypeConvertUtil.parseBoolean(value);
+    }
+
+    /**
+     * 根据配置名称取得长整型配置值,如果配置值为空使用指定的默认值
+     * @param key         配置名称
+     * @param defaultValue 默认长整型值
+     * @return 最终配置值
+     */
+    public static long getLong(String key, long defaultValue) {
+        String value = getString(key);
+        if (StrUtil.isEmpty(value)) {
+            return defaultValue;
+        }
+        return TypeConvertUtil.parseLong(value);
+    }
+
+    /**
+     * 根据配置名称取得浮点型配置值,如果配置值为空使用指定的默认值
+     * @param key         配置名称
+     * @param defaultValue 默认浮点型值
+     * @return 最终配置值
+     */
+    public static float getFloat(String key, float defaultValue) {
+        String value = getString(key);
+        if (StrUtil.isEmpty(value)) {
+            return defaultValue;
+        }
+        return TypeConvertUtil.parseFloat(value);
+    }
+
+    /**
+     * 根据配置名称取得双浮点型配置值,如果配置值为空使用指定的默认值
+     * @param key         配置名称
+     * @param defaultValue 默认双浮点型值
+     * @return 最终配置值
+     */
+    public static double getDouble(String key, double defaultValue) {
+        String value = getString(key);
+        if (StrUtil.isEmpty(value)) {
+            return defaultValue;
+        }
+        return TypeConvertUtil.parseDouble(value);
+    }
+
+}

+ 615 - 0
dc-common/src/main/java/com/persagy/dc/common/lang/PsDate.java

@@ -0,0 +1,615 @@
+package com.persagy.dc.common.lang;
+
+import cn.hutool.core.util.StrUtil;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.Date;
+
+
+/**
+ * 自定日期类,用于转化表示日期串,日期对象等
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+public class PsDate implements Serializable, Comparable<PsDate>, Cloneable {
+
+    /** 序列id */
+    private static final long serialVersionUID = 5668159214884319312L;
+
+    /** 默认的日期格式化对象 */
+    public static DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd");
+
+    /** 默认时区 */
+    public static ZoneId DEFAULT_ZONE_ID = ZoneId.of(ZoneId.SHORT_IDS.get("CTT"));
+
+    /*日期时间常量*/
+    /** 一天毫秒数 */
+    public static final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
+    /** 一小时毫秒数 */
+    public static final int MILLIS_PER_HOUR = 60 * 60 * 1000;
+    /** 一分钟毫秒数 */
+    public static final int MILLIS_PER_MINUTE = 60 * 1000;
+    /** 一秒毫秒数 */
+    public static final int MILLIS_PER_SECOND = 1000;
+
+    /** 保存时间的日期对象 */
+    protected Calendar calendar = null;
+
+    /**
+     * 自定义日期无参构造方法。
+     */
+    public PsDate() {
+        calendar = Calendar.getInstance();
+        init();
+    }
+
+    /**
+     * 通过毫秒数来构造自定义日期对象
+     * (以从1970年1月1日0时0分0秒到现在的毫秒数来构造日期)
+     * @param millis 毫秒数
+     */
+    public PsDate(long millis) {
+        this();
+        setMillis(millis);
+    }
+
+    /**
+     * 通过日期对象构造自定义日期对象
+     * @param date 日期对象
+     */
+    public PsDate(Date date) {
+        this();
+        setDate(date);
+    }
+
+    /**
+     * 通过默认形式日期串构造自定义日期对象
+     * @param date 默认格式日期串
+     */
+    public PsDate(String date) {
+        this(date, DEFAULT_FORMAT);
+    }
+
+    /**
+     * 通过默认形式日期串构造自定义日期对象
+     * @param date   默认格式日期串
+     * @param format 格式化串
+     */
+    public PsDate(String date, String format) {
+        this();
+        DateTimeFormatter dateFormat = null;
+        if (format != null) {
+            dateFormat = DateTimeFormatter.ofPattern(format);
+        }
+        parse(date, dateFormat);
+    }
+
+    /**
+     * 通过日期串和指定的格式化对象构造自定义日期对象
+     * @param date       符合格式化对象的日期时间对象
+     * @param dateFormat 格式化对象
+     */
+    public PsDate(String date, DateTimeFormatter dateFormat) {
+        this();
+        parse(date, dateFormat);
+    }
+
+    /**
+     * 按指定日期形式解析日期字符串
+     * @param date       日期字符串
+     * @param dateFormat 日期形式
+     */
+    protected void parse(String date, DateTimeFormatter dateFormat) {
+        if (StrUtil.isEmpty(date)) {
+            return;
+        }
+        if (dateFormat == null) {
+            dateFormat = DEFAULT_FORMAT;
+        }
+        LocalDate localDate = LocalDate.parse(date, dateFormat);
+        setDate(Date.from(localDate.atStartOfDay().toInstant(ZoneOffset.UTC)));
+    }
+
+    /**
+     * 设置指定时间属性的值
+     * @param field 时间属性
+     * @param value 时间属性的值
+     */
+    public void set(int field, int value) {
+        calendar.set(field, value);
+    }
+
+    /**
+     * 取得定时间属性的值
+     * @param field 时间属性
+     * @return 定时间属性的值
+     */
+    public int get(int field) {
+        return calendar.get(field);
+    }
+
+    /**
+     * 初始方法
+     */
+    protected void init() {
+        set(Calendar.HOUR_OF_DAY, 0);
+        set(Calendar.MINUTE, 0);
+        set(Calendar.SECOND, 0);
+        set(Calendar.MILLISECOND, 0);
+    }
+
+    /**
+     * 根据对象构建自定义日期对象
+     * @param obj 对象
+     * @return 自定义日期对象
+     */
+    public static PsDate parsePsDate(Object obj) {
+        if (obj == null || "".equals(obj.toString().trim())) {
+            return null;
+        }
+        if (obj instanceof PsDate) {
+            return new PsDate(((PsDate) obj).getMillis());
+        }
+
+        if (obj instanceof Date) {
+            return new PsDate((Date) obj);
+        }
+        if (obj instanceof Integer) {
+            return new PsDate(((Number) obj).longValue() * 1000);
+        }
+        if (obj instanceof Number) {
+            return new PsDate(((Number) obj).longValue());
+        }
+        // 目前存储的格式,都是数字,兼容时间戳会误判,暂不这样处理了
+//        if (NumberUtils.isCreatable(obj.toString())) {
+//            return new PsDate(NumberUtils.toLong(obj.toString()));
+//        }
+        return new PsDate(obj.toString());
+
+    }
+
+    /**
+     * 设置日期对象的时间
+     * @param date 日期对象
+     */
+    public void setDate(Date date) {
+        calendar.setTime(date);
+        init();
+    }
+
+    /**
+     * 取得当前日期所表示的java日期对象
+     * @return java日期对象
+     */
+    public Date getDate() {
+        return calendar.getTime();
+    }
+
+    /**
+     * 设置日期对象的时间
+     * @param millis 日期对象
+     */
+    public void setMillis(long millis) {
+        calendar.setTimeInMillis(millis);
+        init();
+    }
+
+    /**
+     * 取得日期对象的毫秒数
+     * @return 毫秒数
+     */
+    public long getMillis() {
+        return calendar.getTimeInMillis();
+    }
+
+    /**
+     * 比较日期先后,对象日期在参数日期之后为true
+     * @param when 比较的日期的对象
+     * @return 布尔值
+     */
+    public boolean after(PsDate when) {
+        return compareTo(when) > 0;
+    }
+
+    /**
+     * 比较日期先后,对象日期在参数日期之前为true
+     * @param when 比较的日期的对象
+     * @return 布尔值
+     */
+    public boolean before(PsDate when) {
+        return compareTo(when) < 0;
+    }
+
+    /**
+     * 返回指定天数之后的自定义日期对象
+     * @param days 指定的天数
+     * @return 自定义日期对象
+     */
+    public PsDate getDateAfter(int days) {
+        long millis = getMillis() + MILLIS_PER_DAY * days;
+        return new PsDate(millis);
+    }
+
+    /**
+     * 返回指定天数之前的自定义日期对象
+     * @param days 指定的天数
+     * @return 自定义日期对象
+     */
+    public PsDate getDateBefore(int days) {
+        return getDateAfter(-days);
+    }
+
+    /**
+     * 取得当前日期对象在指定的日期对象之后多少天
+     * @param when 要比较的自定义对象
+     * @return 在比较的自定义对象之后多少天
+     */
+    public int getDaysAfter(PsDate when) {
+        return getDaysBetween(when, this);
+    }
+
+    /**
+     * 取得开始日期到结束日期直接相差多少天
+     * @param begin 开始日期
+     * @param end   结束日期
+     * @return 相差天数
+     */
+    public static int getDaysBetween(PsDate begin, PsDate end) {
+        int days = 0;
+        if (begin != null && end != null) {
+            PsDate e = new PsDate(end.getMillis());
+            PsDate b = new PsDate(begin.getMillis());
+            days = (int) (e.getMillis() / MILLIS_PER_DAY - b.getMillis() / MILLIS_PER_DAY);
+        }
+        return days;
+    }
+
+    /**
+     * 格式化时间数字串为两位
+     * @param number 数字
+     * @return 数字串
+     */
+    public String formatNumber(int number) {
+        return number < 10 ? "0" + number : Integer.toString(number);
+    }
+
+    /**
+     * 取得日期对象中的日
+     * @return 天数
+     */
+    public int getDay() {
+        return get(Calendar.DATE);
+    }
+
+    /**
+     * 取得日期对象中的日串
+     * @return 天数串(两位)
+     */
+    public String getDAY() {
+        return formatNumber(getDay());
+    }
+
+    /**
+     * 取得日期对象的月份
+     * @return
+     */
+    public int getMonth() {
+        return get(Calendar.MONTH);
+    }
+
+    /**
+     * 取得日期对象的月份串(两位)
+     * @return 月份串(两位)
+     */
+    public String getMONTH() {
+        return formatNumber(getMonth() + 1);
+    }
+
+    /**
+     * 取得当前日期对象所在月的英文写法
+     * @return 月的英文串
+     */
+    public String getEnMonth() {
+        switch (getMonth()) {
+            case 1:
+                return "Jan";
+            case 2:
+                return "Feb";
+            case 3:
+                return "Mar";
+            case 4:
+                return "Apr";
+            case 5:
+                return "May";
+            case 6:
+                return "Jun";
+            case 7:
+                return "Jul";
+            case 8:
+                return "Aug";
+            case 9:
+                return "Sep";
+            case 10:
+                return "Oct";
+            case 11:
+                return "Nov";
+            case 12:
+                return "Dec";
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * 取得日期对象的年份
+     * @return 年份串
+     */
+    public int getYear() {
+        return get(Calendar.YEAR);
+    }
+
+    /**
+     * 取得年字符串
+     * @return 年字符串
+     */
+    public String getYEAR() {
+        return Integer.toString(getYear());
+    }
+
+
+    /**
+     * 取得当前日期对象的星期表示
+     * @return 星期表示
+     */
+    public int getWeek() {
+        return get(Calendar.DAY_OF_WEEK);
+    }
+
+    /**
+     * 取得当前日期对象的星期中文简单表示
+     * @return 星期中文简单表示
+     */
+    public String getSimpleZhWeek() {
+        switch (getWeek()) {
+            case 1:
+                return "日";
+            case 2:
+                return "一";
+            case 3:
+                return "二";
+            case 4:
+                return "三";
+            case 5:
+                return "四";
+            case 6:
+                return "五";
+            case 7:
+                return "六";
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * 取得当前日期对象的星期英文表示
+     * @return 星期英文表示
+     */
+    public String getZhWeek() {
+        switch (getWeek()) {
+            case 1:
+                return "星期日";
+            case 2:
+                return "星期一";
+            case 3:
+                return "星期二";
+            case 4:
+                return "星期三";
+            case 5:
+                return "星期四";
+            case 6:
+                return "星期五";
+            case 7:
+                return "星期六";
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * 取得当前日期对象的星期英文表示
+     * @return 星期英文表示
+     */
+    public String getEnWeek() {
+        switch (getWeek()) {
+            case 1:
+                return "Sunday";
+            case 2:
+                return "Monday";
+            case 3:
+                return "Tuesday";
+            case 4:
+                return "Wednesday";
+            case 5:
+                return "Thursday";
+            case 6:
+                return "Friday";
+            case 7:
+                return "Saturday";
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * 取得当前日期对象的星期英文简单表示
+     * @return 星期英文简单表示
+     */
+    public String getSimpleEnWeek() {
+        switch (getWeek()) {
+            case 1:
+                return "Sun";
+            case 2:
+                return "Mon";
+            case 3:
+                return "Tue";
+            case 4:
+                return "Wed";
+            case 5:
+                return "Thu";
+            case 6:
+                return "Fri";
+            case 7:
+                return "Sat";
+            default:
+                return null;
+        }
+    }
+
+
+    /**
+     * 判断当前日期所在是否是闰年
+     * @return 布尔值
+     */
+    public boolean isLeapYear() {
+        return isLeapYear(getYear());
+    }
+
+    /**
+     * 判断指定年是否是闰年
+     * @param year  指定的年
+     * @return 布尔值
+     */
+    public static boolean isLeapYear(int year) {
+        return (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0);
+    }
+
+    /**
+     * 取得当前日期对象所在月有多少天
+     * @return 天数
+     */
+    public int getDaysInMonth() {
+        return getDaysMonth(getYear(), getMonth());
+    }
+
+    /**
+     * 取得指定的年月里一个月有多少天
+     * @param year  指定的年
+     * @param month 指定的月
+     * @return 天数
+     */
+    public static int getDaysMonth(int year, int month) {
+        switch (month) {
+            case 1:
+            case 3:
+            case 5:
+            case 7:
+            case 8:
+            case 10:
+            case 12:
+                return 31;
+            case 2:
+                if (isLeapYear(year)) {
+                    return 29;
+                } else {
+                    return 28;
+                }
+            default:
+                return 30;
+        }
+    }
+
+    /**
+     * 取得当前日期所在年包含的周数
+     * @return 年包含的周数
+     */
+    public int getWeekOfYear() {
+        return get(Calendar.WEEK_OF_YEAR);
+    }
+
+    /**
+     * 用默认的格式化串转化日期
+     * @see Object#toString()
+     */
+    @Override
+    public String toString() {
+        return toString(DEFAULT_FORMAT);
+    }
+
+    /**
+     * 用指定的格式化串,格式化日期对象为日期串
+     * @param format 格式化串
+     * @return 格式化后的日期串
+     */
+    public String toString(String format) {
+        if (format == null) {
+            return toString();
+        }
+        DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern(format);
+        return toString(dateFormat);
+    }
+
+    /**
+     * 用指定的格式化对象,格式化日期对象为日期串
+     * @param dateFormat 指定的格式化对象
+     * @return 格式化后的日期串
+     */
+    public String toString(DateTimeFormatter dateFormat) {
+        if (dateFormat == null) {
+            return toString();
+        }
+        return dateFormat.format(getDate().toInstant().atZone(PsDate.DEFAULT_ZONE_ID).toLocalDate());
+    }
+
+    /**
+     * 实现比较接口方法
+     * @see Comparable#compareTo(Object)
+     */
+    @Override
+    public int compareTo(PsDate psDate) {
+        return calendar.compareTo(psDate.calendar);
+    }
+
+    /**
+     * 比较日期是否相等
+     * @param o 比较的对象
+     * @return 布尔值
+     */
+    @Override
+    public boolean equals(Object o) {
+        if ((o != null) && (o instanceof PsDate)) {
+            return this.getMillis() == ((PsDate) o).getMillis();
+        }
+        return false;
+    }
+
+    /**
+     * 克隆日期对象。
+     * @see Object#clone()
+     */
+    @Override
+    public Object clone() {
+        PsDate psDate;
+        try {
+            psDate = (PsDate) super.clone();
+        } catch (CloneNotSupportedException e) {
+            return new PsDate(getMillis());
+        }
+        psDate.calendar = (Calendar) calendar.clone();
+        return psDate;
+    }
+
+    /**
+     * 取得对象的hashCode
+     * @see Date#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return calendar.hashCode();
+    }
+
+}

+ 326 - 0
dc-common/src/main/java/com/persagy/dc/common/lang/PsDateTime.java

@@ -0,0 +1,326 @@
+package com.persagy.dc.common.lang;
+
+import cn.hutool.core.util.StrUtil;
+
+import java.time.LocalDateTime;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.Date;
+
+
+/**
+ * 自定日期时间类,用于转化表示日期时间串等
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+public class PsDateTime extends PsDate implements Comparable<PsDate> {
+    /** 序列化id */
+    private static final long serialVersionUID = 7280791980952901691L;
+
+    /** 日期格式化对象 */
+    public static DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+
+    /** 自定义日期时间无参构造方法。 */
+    public PsDateTime() {
+        super();
+    }
+
+    /**
+     * 通过毫秒数构造自定义日期时间对象
+     * (以从1970年1月1日0时0分0秒到现在的毫秒数来构造日期)
+     * @param millis 毫秒数
+     */
+    public PsDateTime(long millis) {
+        super(millis);
+    }
+
+    /**
+     * 通过日期对象构造自定义日期时间对象
+     * @param date 日期对象
+     */
+    public PsDateTime(Date date) {
+        super(date);
+    }
+
+    /**
+     * 通过自定义日期对象构造自定义日期时间对象
+     * @param date 日期对象
+     */
+    public PsDateTime(PsDate date) {
+        super(date.getMillis());
+    }
+
+    /**
+     * 通过自定义日期对象和时间对象构造自定义日期时间对象
+     * @param date 日期对象
+     */
+    public PsDateTime(PsDate date, PsTime time) {
+        super(date.getMillis() + time.getMillis());
+    }
+
+    /**
+     * 通过默认形式日期串构造自定义日期对象
+     * @param date 默认格式日期串
+     */
+    public PsDateTime(String date) {
+        super(date, DEFAULT_FORMAT);
+    }
+
+    /**
+     * 通过日期串和指定的格式化串构造自定义日期时间对象
+     * @param date   符合格式串的日期时间对象
+     * @param format 日期格式化串
+     */
+    public PsDateTime(String date, String format) {
+        super(date, format);
+    }
+
+    /**
+     * 通过日期串和指定的格式化对象构造自定义日期时间对象
+     * @param date       符合格式化对象的日期时间对象
+     * @param dateFormat 格式化对象
+     */
+    public PsDateTime(String date, DateTimeFormatter dateFormat) {
+        super(date, dateFormat);
+    }
+
+
+    /**
+     * 用日期对象初始化对象
+     */
+    @Override
+    protected void init() {
+        set(Calendar.MILLISECOND, 0);
+    }
+
+    @Override
+    protected void parse(String date, DateTimeFormatter dateFormat) {
+        if (StrUtil.isEmpty(date)) {
+            return;
+        }
+        if (dateFormat == null) {
+            dateFormat = DEFAULT_FORMAT;
+        }
+        LocalDateTime localDate = LocalDateTime.parse(date, dateFormat);
+        ZonedDateTime zdt = localDate.atZone(PsDate.DEFAULT_ZONE_ID);
+        setDate(Date.from(zdt.toInstant()));
+    }
+
+    /**
+     * 根据对象构建自定义日期时间对象
+     * @param obj 对象
+     * @return 自定义日期时间对象
+     */
+    public static PsDateTime parsePsDateTime(Object obj) {
+        if (obj == null || "".equals(obj.toString())) {
+            return null;
+        }
+        if (obj instanceof PsDate) {
+            return new PsDateTime(((PsDate) obj).getMillis());
+        }
+
+        if (obj instanceof Date) {
+            return new PsDateTime((Date) obj);
+        }
+        if (obj instanceof Integer) {
+            return new PsDateTime(((Integer) obj).longValue() * 1000);
+        }
+        if (obj instanceof Number) {
+            return new PsDateTime(((Number) obj).longValue());
+        }
+//        if (NumberUtils.isCreatable(obj.toString())) {
+//            return new PsDateTime(NumberUtils.toLong(obj.toString()));
+//        }
+        return new PsDateTime(obj.toString());
+
+    }
+
+    /**
+     * 取得自定义日期时间对象中的自定义日期对象
+     * @return 自定义日期对象
+     */
+    public PsDate getDDate() {
+        return new PsDate(getMillis());
+    }
+
+    /**
+     * 取得自定义日期时间对象中的自定义时间对象
+     * @return 自定义时间对象
+     */
+    public PsTime getDTime() {
+        return new PsTime(getMillis());
+    }
+
+    /**
+     * 返回指定天数之后的自定义日期时间对象
+     */
+    @Override
+    public PsDateTime getDateAfter(int days) {
+        return new PsDateTime(getMillis() + MILLIS_PER_DAY * days);
+    }
+
+    /**
+     * 返回指定天数之前的自定义日期时间对象
+     */
+    @Override
+    public PsDateTime getDateBefore(int days) {
+        return getDateAfter(-days);
+    }
+
+    /**
+     * 取得两个日期时间对象相差的小时数
+     * @param begin 开始日期时间
+     * @param end   结束日期时间
+     * @return 小时数
+     */
+    public static int getHoursBetween(PsDateTime begin, PsDateTime end) {
+        return (int) (end.getMillis() / PsDate.MILLIS_PER_HOUR - begin.getMillis() / PsDate.MILLIS_PER_HOUR);
+    }
+
+    /**
+     * 取得两个日期时间对象相差的分钟数
+     * @param begin 开始日期时间
+     * @param end   结束日期时间
+     * @return 分钟数
+     */
+    public static int getMinutesBetween(PsDateTime begin, PsDateTime end) {
+        return (int) (end.getMillis() / PsDate.MILLIS_PER_MINUTE - begin.getMillis() / PsDate.MILLIS_PER_MINUTE);
+    }
+
+    /**
+     * 取得两个日期时间对象相差的秒数
+     * @param begin 开始日期时间
+     * @param end   结束日期时间
+     * @return 秒数
+     */
+    public static int getSecondsBetween(PsDateTime begin, PsDateTime end) {
+        return (int) (end.getMillis() / PsDate.MILLIS_PER_SECOND - begin.getMillis() / PsDate.MILLIS_PER_SECOND);
+    }
+
+    /**
+     * 取得两个日期时间对象相差的毫秒数
+     * @param begin 开始日期时间
+     * @param end   结束日期时间
+     * @return 毫秒数
+     */
+    private static long getMillisBetween(PsDateTime begin, PsDateTime end) {
+        if (begin == null || end == null) {
+            return 0;
+        }
+        return end.getMillis() - begin.getMillis();
+    }
+
+    /**
+     * 取得当前日期时间对象在指定的日期时间对象之后多少毫秒
+     * @param dateTime 要比较的自定义日期时间
+     * @return 当前时间在给定日期时间
+     */
+    public long getMillisAfter(PsDateTime dateTime) {
+        return getMillisBetween(dateTime, this);
+    }
+
+    /**
+     * 取得小时数
+     * @return 小时数
+     */
+    public int getHour() {
+        return get(Calendar.HOUR_OF_DAY);
+    }
+
+    /**
+     * 取得小时数字符串
+     * @return 小时数字符串
+     */
+    public String getHOUR() {
+        return formatNumber(getHour());
+    }
+
+    /**
+     * 取得分钟数
+     * @return 分钟数
+     */
+    public int getMinute() {
+        return get(Calendar.MINUTE);
+    }
+
+    /**
+     * 取得分钟数字符串
+     * @return 分钟数字符串
+     */
+    public String getMINUTE() {
+        return formatNumber(getMinute());
+    }
+
+    /**
+     * 取得秒数
+     * @return 秒数
+     */
+    public int getSecond() {
+        return get(Calendar.SECOND);
+    }
+
+    /**
+     * 取得秒数字符串
+     * @return 秒数字符串
+     */
+    public String getSECOND() {
+        return formatNumber(getSecond());
+    }
+
+    /**
+     * 取得毫秒数
+     * @return 毫秒数
+     */
+    public int getMilliSecond() {
+        return get(Calendar.MILLISECOND);
+    }
+
+    /**
+     * 取得毫秒数串
+     * @return 毫秒数串
+     */
+    public String getMILLISECOND() {
+        int ms = getMilliSecond();
+        return Integer.toString(ms + 1000).substring(1);
+    }
+
+    /**
+     * 实现比较接口方法
+     * @see Comparable#compareTo(Object)
+     */
+    @Override
+    public int compareTo(PsDate psDate) {
+        return calendar.compareTo(psDate.calendar);
+    }
+
+    /**
+     * 比较日期是否相等
+     * @param o 比较的对象
+     * @return 布尔值
+     */
+    @Override
+    public boolean equals(Object o) {
+        if ((o != null) && (o instanceof PsDate)) {
+            return this.getMillis() == ((PsDate) o).getMillis();
+        }
+        return false;
+    }
+
+    /**
+     * 用默认的格式化串转化日期时间
+     * @see Object#toString()
+     */
+    @Override
+    public String toString() {
+        return toString(DEFAULT_FORMAT);
+    }
+
+    @Override
+    public String toString(DateTimeFormatter dateFormat) {
+        if (dateFormat == null) {
+            return toString();
+        }
+        return dateFormat.format(getDate().toInstant().atZone(PsDate.DEFAULT_ZONE_ID));
+    }
+}

+ 271 - 0
dc-common/src/main/java/com/persagy/dc/common/lang/PsTime.java

@@ -0,0 +1,271 @@
+package com.persagy.dc.common.lang;
+
+import cn.hutool.core.util.StrUtil;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.Date;
+
+
+/**
+ * 自定时间类,用于转化表示时间串等
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+public class PsTime extends PsDate {
+    /** 序列化id */
+    private static final long serialVersionUID = 733417362967334525L;
+
+    /** 时间格式化对象 */
+    public static DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("HHmmss");
+
+    /**
+     * 自定义时间无参构造方法。
+     */
+    public PsTime() {
+        super();
+    }
+
+    /**
+     * 通过毫秒数构造自定义时间对象
+     * (以从1970年1月1日0时0分0秒到现在的毫秒数来构造日期)
+     * @param millis 毫秒数
+     */
+    public PsTime(long millis) {
+        super(millis);
+    }
+
+    /**
+     * 通过日期对象构造自定义时间对象
+     *
+     * @param date 日期对象
+     */
+    public PsTime(Date date) {
+        super(date);
+    }
+
+    /**
+     * 通过默认形式时间串构造自定义时间对象
+     *
+     * @param time 默认格式日期串
+     */
+    public PsTime(String time) {
+        super(time, DEFAULT_FORMAT);
+    }
+
+    /**
+     * 通过时间串和指定的格式化串构造自定义时间对象
+     *
+     * @param time   符合格式串的时间对象
+     * @param format 时间格式化串
+     */
+    public PsTime(String time, String format) {
+        super(time, format);
+    }
+
+    /**
+     * 通过时间串和指定的格式化对象构造自定义时间对象
+     *
+     * @param time       符合格式化对象的时间对象
+     * @param timeFormat 格式化对象
+     */
+    public PsTime(String time, DateTimeFormatter timeFormat) {
+        super(time, timeFormat);
+    }
+
+    /**
+     * 根据对象构建自定义时间对象
+     * @param obj 对象
+     * @return 自定义时间对象
+     */
+    public static PsTime parsePsTime(Object obj) {
+        if (obj == null || "".equals(obj.toString().trim())) {
+            return null;
+        }
+        if (obj instanceof PsDate) {
+            return new PsTime(((PsDate) obj).getMillis());
+        }
+
+        if (obj instanceof Date) {
+            return new PsTime((Date) obj);
+        }
+        if (obj instanceof Integer) {
+            return new PsTime(((Integer) obj).longValue() * 1000);
+        }
+        if (obj instanceof Number) {
+            return new PsTime(((Number) obj).longValue());
+        }
+//        if (NumberUtils.isCreatable(obj.toString())) {
+//            return new PsTime(NumberUtils.toLong(obj.toString()));
+//        }
+        return new PsTime(obj.toString());
+    }
+
+    /**
+     * 用时间对象初始化对象
+     */
+    @Override
+    protected void init() {
+        set(Calendar.YEAR, 1970);
+        set(Calendar.MONTH, 1);
+        set(Calendar.DATE, 1);
+    }
+
+    @Override
+    protected void parse(String date, DateTimeFormatter dateFormat) {
+        if (StrUtil.isEmpty(date)) {
+            return;
+        }
+        if (dateFormat == null) {
+            dateFormat = DEFAULT_FORMAT;
+        }
+        LocalTime localDate = LocalTime.parse(date, dateFormat);
+        ZonedDateTime zdt = localDate.atDate(LocalDate.now()).atZone(PsDate.DEFAULT_ZONE_ID);
+        setDate(Date.from(zdt.toInstant()));
+    }
+
+    /**
+     * 比较时间先后,对象时间在参数时间之后为true
+     * @param when 比较的时间对象
+     * @return 布尔值
+     */
+    public boolean after(PsTime when) {
+        return compareTo(when) > 0;
+    }
+
+    /**
+     * 比较时间先后,对象时间在参数时间之前为true
+     * @param when 比较的时间对象
+     * @return 布尔值
+     */
+    public boolean before(PsTime when) {
+        return compareTo(when) < 0;
+    }
+
+    /**
+     * 比较日期先后,对象日期在参数日期之后为true
+     * @param when 比较的日期的对象
+     * @return 布尔值
+     * @deprecated
+     */
+    @Override
+    public boolean after(PsDate when) {
+        return getMillis() > 0;
+    }
+
+    /**
+     * 比较日期先后,对象日期在参数日期之前为true
+     * @param when 比较的日期的对象
+     * @return 布尔值
+     * @deprecated
+     */
+    @Override
+    public boolean before(PsDate when) {
+        return getMillis() < 0;
+    }
+
+    /**
+     * 返回指定天数之后的自定义日期对象
+     * @param days 指定的天数
+     * @return 自定义日期对象
+     * @deprecated
+     */
+    @Override
+    public PsDate getDateAfter(int days) {
+        return null;
+    }
+
+    /**
+     * 返回指定天数之前的自定义日期对象
+     * @param days 指定的天数
+     * @return 自定义日期对象
+     * @deprecated
+     */
+    @Override
+    public PsDate getDateBefore(int days) {
+        return getDateAfter(-days);
+    }
+
+    /**
+     * 取得当前日期对象在指定的日期对象之后多少天
+     * @param when 要比较的自定义对象
+     * @return 在比较的自定义对象之后多少天
+     * @deprecated
+     */
+    @Override
+    public int getDaysAfter(PsDate when) {
+        return 0;
+    }
+
+    /**
+     * 取得两个时间对象相差的小时数
+     * @param begin 开始时间
+     * @param end   结束时间
+     * @return 相差的小时整数
+     */
+    public static int getHoursBetween(PsTime begin, PsTime end) {
+        return (int) (getMilisBetween(begin, end) / MILLIS_PER_HOUR);
+    }
+
+    /**
+     * 取得两个时间对象相差的分钟数
+     * @param begin 开始时间
+     * @param end   结束时间
+     * @return 相差的分钟整数
+     */
+    public static int getMinutesBetween(PsTime begin, PsTime end) {
+        return (int) (getMilisBetween(begin, end) / MILLIS_PER_MINUTE);
+    }
+
+    /**
+     * 取得两个时间对象相差的秒数
+     * @param begin 开始时间
+     * @param end   结束时间
+     * @return 相差的秒整数
+     */
+    public static int getSecondsBetween(PsTime begin, PsTime end) {
+        return (int) (getMilisBetween(begin, end) / MILLIS_PER_SECOND);
+    }
+
+    /**
+     * 取得两个时间对象相差的毫秒数
+     * @param begin 开始时间
+     * @param end   结束时间
+     * @return 相差的毫秒整数
+     */
+    public static long getMilisBetween(PsTime begin, PsTime end) {
+        if (begin == null || end == null) {
+            return 0;
+        }
+        return end.getMillis() - begin.getMillis();
+    }
+
+    /**
+     * 取得当前时间对象在指定的时间对象之后多少毫秒
+     * @param time 要比较的自定义时间
+     * @return 当前时间在给定时间
+     */
+    public long getMillisAfter(PsTime time) {
+        return getMilisBetween(time, this);
+    }
+
+    /**
+     * 用默认的格式化串转化日期
+     * @see Object#toString()
+     */
+    @Override
+    public String toString() {
+        return toString(DEFAULT_FORMAT);
+    }
+
+    @Override
+    public String toString(DateTimeFormatter dateFormat) {
+        if (dateFormat == null) {
+            return toString();
+        }
+        return dateFormat.format(getDate().toInstant().atZone(PsDate.DEFAULT_ZONE_ID).toLocalTime());
+    }
+}

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

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

+ 45 - 0
dc-common/src/main/java/com/persagy/dc/common/model/entity/AuditableEntity.java

@@ -0,0 +1,45 @@
+package com.persagy.dc.common.model.entity;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * 审计信息父类
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public abstract class AuditableEntity<T> extends BaseEntity<T> implements IAuditableEntity {
+
+    /** 序列id */
+    private static final long serialVersionUID = -6039076670318997447L;
+
+    /** 创建时间 */
+    public static final String PROP_CREATIONTIME = "creation_time";
+    public static final String DO_PROP_CREATIONTIME = "creationTime";
+
+    /** 创建人 */
+    public static final String PROP_CREATOR = "creator";
+
+    /** 最后一次修改时间 */
+    public static final String PROP_MODIFIEDTIME = "modified_time";
+    public static final String DO_PROP_MODIFIEDTIME = "modifiedTime";
+
+    /** 最后一个修改人 */
+    public static final String PROP_MODIFIER = "modifier";
+
+    @JsonIgnore
+    protected Date creationTime;
+
+    protected String creator;
+
+    @JsonIgnore
+    protected Date modifiedTime;
+
+    protected String modifier;
+
+}

+ 91 - 0
dc-common/src/main/java/com/persagy/dc/common/model/entity/BaseEntity.java

@@ -0,0 +1,91 @@
+package com.persagy.dc.common.model.entity;
+
+import lombok.AccessLevel;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 实体基类
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+@Data
+public abstract class BaseEntity<T> implements IBaseEntity,Serializable,Cloneable{
+
+	/** 序列id */
+	private static final long serialVersionUID = -3231114188611235386L;
+
+	/** 实体属性主键id */
+	public final static String PROP_ID = "id";
+
+	/** 实体属性版本 */
+	public final static String PROP_TS = "ts";
+
+	/** 实体属性实体状态 */
+	public final static String PROP_VALID = "valid";
+
+	/** hash码 */
+	@Getter(AccessLevel.NONE)
+	@Setter(AccessLevel.NONE)
+	private int hashCode = Integer.MIN_VALUE;
+
+	/** 实体类必须有的属性 */
+	protected String id;
+
+	/** 时间戳 */
+	protected Date ts;
+
+	/** 实体状态 - 有效标识 */
+	protected Integer valid;
+
+	@Override
+	public boolean equals (Object obj) {
+		if (null == obj) {
+            return false;
+        }
+		if (!(obj instanceof BaseEntity)) {
+			return false;
+		} else {
+			BaseEntity entity = (BaseEntity) obj;
+			if (null == this.getId() || null == entity.getId()) {
+				return false;
+			} else {
+				return (this.getId().equals(entity.getId()));
+			}
+		}
+	}
+
+	@Override
+	public int hashCode () {
+		if (Integer.MIN_VALUE != this.hashCode) {
+			return this.hashCode;
+		}
+		if (null == this.getId()) {
+			return super.hashCode();
+		}
+		String hashStr = this.getClass().getName() + ":" + this.getId().hashCode();
+		this.hashCode = hashStr.hashCode();
+		return this.hashCode;
+	}
+
+	@Override
+	public int compareTo (IBaseEntity obj) {
+		if (obj.hashCode() > hashCode()) {
+			return 1;
+		} else if (obj.hashCode() < hashCode()) {
+			return -1;
+		} else {
+			return 0;
+		}
+	}
+
+	@Override
+	public Object clone() throws CloneNotSupportedException {
+		return super.clone();
+	}
+
+}

+ 59 - 0
dc-common/src/main/java/com/persagy/dc/common/model/entity/IAuditableEntity.java

@@ -0,0 +1,59 @@
+package com.persagy.dc.common.model.entity;
+
+import java.util.Date;
+
+/**
+ * 审计信息类接口
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+public interface IAuditableEntity extends IBaseEntity {
+
+	/**
+	 * 取得创建时间
+	 * @return 创建时间
+	 */
+	Date getCreationTime();
+
+	/**
+	 * 设置创建时间,记录插入时有效
+	 * @param creationtime 创建时间
+	 */
+	void setCreationTime(Date creationtime);
+
+	/**
+	 * 取得创建实体的操作员的登录名
+	 * @return 操作员标识
+	 */
+	String getCreator() ;
+
+	/**
+	 * 设置创建实体的操作员标识,记录插入时有效
+	 * @param creator 操作员标识
+	 */
+	void setCreator(String creator);
+
+	/**
+	 * 取得最后修改时间.
+	 * @return 最后修改时间
+	 */
+	Date getModifiedTime();
+
+	/**
+	 * 设置最后修改时间.记录更新时有效
+	 * @param modifiedtime 最后修改时间
+	 */
+	void setModifiedTime(Date modifiedtime);
+
+	/**
+	 * 取得最后修改的操作员的登录名.
+	 * @return 操作员的登录名.
+	 */
+	String getModifier();
+
+	/**
+	 * 设置最后修改的操作员的登录名,记录更新时有效
+	 * @param modifier 操作员的登录名.
+	 */
+	void setModifier(String modifier);
+}

+ 49 - 0
dc-common/src/main/java/com/persagy/dc/common/model/entity/IBaseEntity.java

@@ -0,0 +1,49 @@
+package com.persagy.dc.common.model.entity;
+
+
+import java.util.Date;
+
+/**
+ * 基础实体接口
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+public interface IBaseEntity extends Comparable<IBaseEntity>{
+
+	/**
+	 * 取得实体主键
+	 * @return 实体主键
+	 */
+	String getId();
+
+	/**
+	 * 设置实体主键
+	 * @param id 实体主键
+	 */
+	void setId(String id);
+
+	/**
+	 * 取得时间戳
+	 * @return 时间戳
+	 */
+	Date getTs();
+
+	/**
+	 * 设置时间戳
+	 * @param ts 时间戳
+	 */
+	void setTs(Date ts);
+
+	/**
+	 * 取得实体状态
+	 * @return 实体状态
+	 */
+	Integer getValid();
+
+	/**
+	 * 设置实体状态
+	 * @param status 实体状态
+	 */
+	void setValid(Integer status);
+	
+}

+ 36 - 0
dc-common/src/main/java/com/persagy/dc/common/model/entity/ITreeEntity.java

@@ -0,0 +1,36 @@
+package com.persagy.dc.common.model.entity;
+
+/**
+ * 树形接口
+ * @author Charlie Yu
+ * @version 1.0 2021-03-04
+ */
+public interface ITreeEntity extends IBaseEntity {
+
+    /** 实体属性树形编码 */
+    String PROP_INNER_CODE = "innerCode";
+
+    /**
+     * 取得树形编码
+     * @return 树形编码
+     */
+    String getInnerCode();
+
+    /**
+     * 设置树形编码
+     * @param innerCode 设置树形编码
+     */
+    void setInnerCode(String innerCode);
+
+    /**
+     * 取得innercode属性名
+     * @return
+     */
+    String getInnerCodeField();
+
+    /**
+     * 取得当前实体表名
+     * @return 表名
+     */
+    String getTreeTableName();
+}

+ 54 - 0
dc-common/src/main/java/com/persagy/dc/common/model/response/CommonResult.java

@@ -0,0 +1,54 @@
+package com.persagy.dc.common.model.response;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.persagy.dc.common.constant.ResponseCode;
+import lombok.Data;
+
+/**
+ * 服务调用消息结果
+ * @author Charlie Yu
+ * @date 2021-03-25
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class CommonResult<T> {
+
+    /** 不需要提示 */
+    public static final CommonResult SUCCESS = new CommonResult();
+
+    private String respCode;
+    private String respMsg;
+    private T content;
+
+    /**
+     * 构造方法
+     */
+    public CommonResult(){
+        this(ResponseCode.A00000.getCode(), ResponseCode.A00000.getDesc());
+    }
+
+    /**
+     * 构造方法
+     * @param respCode 响应码
+     * @param respMsg 提示信息
+     */
+    public CommonResult(String respCode, String respMsg) {
+        this(respCode, respMsg,null);
+    }
+
+    /**
+     * 构造方法
+     * @param respCode 响应码
+     * @param respMsg 提示信息
+     * @param content 数据
+     */
+    public CommonResult(String respCode, String respMsg, T content){
+        this.respCode = respCode;
+        this.respMsg = respMsg;
+        this.content = content;
+    }
+
+    public static <T> CommonResult<T> success(T t) {
+        return  new CommonResult<>(ResponseCode.A00000.getCode(), ResponseCode.A00000.getDesc(),t);
+    }
+}

+ 22 - 0
dc-common/src/main/java/com/persagy/dc/common/model/response/PageList.java

@@ -0,0 +1,22 @@
+package com.persagy.dc.common.model.response;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 分页列表
+ * @author Charlie Yu
+ * @data 2021-03-25
+ */
+@Data
+public class PageList<T> {
+
+    private List<T> records;
+    private long total;
+
+    public PageList(final List<T> records, final long total) {
+        this.records = records;
+        this.total = total;
+    }
+}

+ 27 - 0
dc-common/src/main/java/com/persagy/dc/common/serializer/PsDateDeserializer.java

@@ -0,0 +1,27 @@
+package com.persagy.dc.common.serializer;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.persagy.dc.common.lang.PsDate;
+import org.springframework.boot.jackson.JsonComponent;
+
+import java.io.IOException;
+
+/**
+ * @Description 日期反序列化
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+@JsonComponent
+public class PsDateDeserializer extends JsonDeserializer<PsDate> {
+    @Override
+    public PsDate deserialize(JsonParser p, DeserializationContext context) throws IOException, JsonProcessingException {
+        Long millis = p.getValueAsLong();
+        if(millis <= 0) {
+            return null;
+        }
+        return PsDate.parsePsDate(millis);
+    }
+}

+ 22 - 0
dc-common/src/main/java/com/persagy/dc/common/serializer/PsDateSerializer.java

@@ -0,0 +1,22 @@
+package com.persagy.dc.common.serializer;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.persagy.dc.common.lang.PsDate;
+import org.springframework.boot.jackson.JsonComponent;
+
+import java.io.IOException;
+
+/**
+ * @Description 日期序列化
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+@JsonComponent
+public class PsDateSerializer extends JsonSerializer<PsDate> {
+    @Override
+    public void serialize(PsDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+        gen.writeNumber(value.getMillis());
+    }
+}

+ 27 - 0
dc-common/src/main/java/com/persagy/dc/common/serializer/PsDateTimeDeserializer.java

@@ -0,0 +1,27 @@
+package com.persagy.dc.common.serializer;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.persagy.dc.common.lang.PsDateTime;
+import org.springframework.boot.jackson.JsonComponent;
+
+import java.io.IOException;
+
+/**
+ * @Description 时间反序列化
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+@JsonComponent
+public class PsDateTimeDeserializer extends JsonDeserializer<PsDateTime> {
+    @Override
+    public PsDateTime deserialize(JsonParser p, DeserializationContext context) throws IOException, JsonProcessingException {
+        Long millis = p.getValueAsLong();
+        if(millis <= 0) {
+            return null;
+        }
+        return PsDateTime.parsePsDateTime(millis);
+    }
+}

+ 22 - 0
dc-common/src/main/java/com/persagy/dc/common/serializer/PsDateTimeSerializer.java

@@ -0,0 +1,22 @@
+package com.persagy.dc.common.serializer;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.persagy.dc.common.lang.PsDateTime;
+import org.springframework.boot.jackson.JsonComponent;
+
+import java.io.IOException;
+
+/**
+ * @Description 时间序列化
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+@JsonComponent
+public class PsDateTimeSerializer extends JsonSerializer<PsDateTime> {
+    @Override
+    public void serialize(PsDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+        gen.writeNumber(value.getMillis());
+    }
+}

+ 28 - 0
dc-common/src/main/java/com/persagy/dc/common/serializer/PsTimeDeserializer.java

@@ -0,0 +1,28 @@
+package com.persagy.dc.common.serializer;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.persagy.dc.common.lang.PsTime;
+import org.springframework.boot.jackson.JsonComponent;
+
+import java.io.IOException;
+
+/**
+ * @Description 时间反序列化
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+@JsonComponent
+public class PsTimeDeserializer extends JsonDeserializer<PsTime> {
+
+    @Override
+    public PsTime deserialize(JsonParser p, DeserializationContext context) throws IOException, JsonProcessingException {
+        Long millis = p.getValueAsLong();
+        if(millis <= 0) {
+            return null;
+        }
+        return PsTime.parsePsTime(millis);
+    }
+}

+ 22 - 0
dc-common/src/main/java/com/persagy/dc/common/serializer/PsTimeSerializer.java

@@ -0,0 +1,22 @@
+package com.persagy.dc.common.serializer;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.persagy.dc.common.lang.PsTime;
+import org.springframework.boot.jackson.JsonComponent;
+
+import java.io.IOException;
+
+/**
+ * @Description 时间序列化
+ * @author Charlie Yu
+ * @version 1.0 2021-04-08
+ */
+@JsonComponent
+public class PsTimeSerializer extends JsonSerializer<PsTime> {
+    @Override
+    public void serialize(PsTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+        gen.writeNumber(value.getMillis());
+    }
+}

+ 127 - 0
dc-common/src/main/java/com/persagy/dc/common/utils/ProxyBeanUtil.java

@@ -0,0 +1,127 @@
+package com.persagy.dc.common.utils;
+
+import org.springframework.cglib.beans.BeanGenerator;
+import org.springframework.cglib.beans.BeanMap;
+
+import java.util.Map;
+
+/**
+ * 动态bean处理工具类
+ * @author Charlie Yu
+ * @version 1.0 2021-03-03
+ */
+public class ProxyBeanUtil {
+
+    /**
+     * 创建Bean生成器
+     * @param source 原始Bean
+     * @param properties 附加属性
+     * @return Bean生成器
+     */
+    public static BeanGenerator createBeanGenerator(Object source, String... properties) {
+        BeanMap sourceMap = BeanMap.create(source);
+        return createBeanGenerator(sourceMap,properties);
+    }
+
+    /**
+     * 创建Bean生成器
+     * @param source 原始Bean
+     * @param propertyMap 附加属性
+     * @return Bean生成器
+     */
+    public static BeanGenerator createBeanGenerator(Object source, Map<String, Class<?>> propertyMap) {
+        BeanMap sourceMap = BeanMap.create(source);
+        return createBeanGenerator(sourceMap,propertyMap);
+    }
+
+    /**
+     * 创建Bean生成器
+     * @param sourceMap 原始Bean对应beanMap
+     * @param properties 附加属性
+     * @return Bean生成器
+     */
+    public static BeanGenerator  createBeanGenerator(BeanMap sourceMap, String... properties) {
+        BeanGenerator generator = new BeanGenerator();
+        generator.setSuperclass(sourceMap.getBean().getClass());
+        int count = 0;
+        for (String prop : properties) {
+            if(sourceMap.containsKey(prop)) {
+                continue;
+            }
+            generator.addProperty(prop,String.class);
+            count++;
+        }
+        if(count  == 0) {
+            return null;
+        }
+        return generator;
+    }
+
+    /**
+     * 创建Bean生成器
+     * @param sourceMap 原始Bean对应beanMap
+     * @param propertyMap 附加属性
+     * @return Bean生成器
+     */
+    public static BeanGenerator  createBeanGenerator(BeanMap sourceMap, Map<String, Class<?>> propertyMap) {
+        BeanGenerator generator = new BeanGenerator();
+        generator.setSuperclass(sourceMap.getBean().getClass());
+        int count = 0;
+        for (Map.Entry<String,Class<?>> entry:propertyMap.entrySet()) {
+            String key = entry.getKey();
+            if (sourceMap.containsKey(key)) {
+                continue;
+            }
+            generator.addProperty(key, entry.getValue());
+            count++;
+        }
+        if(count  == 0) {
+            return null;
+        }
+        return generator;
+    }
+
+    /**
+     * 创建继承于原始Bean的新对象
+     * @param source 原始Bean
+     * @param properties 附加属性
+     * @return 新对象
+     */
+    public static <X> X  createProxyBean(X source, String... properties) {
+        BeanGenerator generator = createBeanGenerator(source,properties);
+        if(generator == null) {
+            return source;
+        }
+        return createProxyBean(generator,source);
+    }
+
+    /**
+     * 创建继承于原始Bean的新对象
+     * @param source 原始Bean
+     * @param propertyMap 附加属性
+     * @return 新对象
+     */
+    public static <X> X  createProxyBean(X source, Map<String, Class<?>> propertyMap) {
+        BeanGenerator generator = createBeanGenerator(source,propertyMap);
+        if(generator == null) {
+            return source;
+        }
+        return createProxyBean(generator,source);
+    }
+
+    /**
+     * 抽取的公共方法
+     * @param generator 新的bean生成器
+     * @param source 复制扩展源对象
+     * @param <X> 源类型
+     * @return 新对象
+     */
+    private static <X> X createProxyBean(BeanGenerator generator,X source){
+        BeanMap sourceMap = BeanMap.create(source);
+        @SuppressWarnings("unchecked")
+        X target = (X) generator.create();
+        BeanMap targetMap = BeanMap.create(target);
+        targetMap.putAll(sourceMap);
+        return target;
+    }
+}

+ 111 - 0
dc-common/src/main/java/com/persagy/dc/common/utils/ResultHelper.java

@@ -0,0 +1,111 @@
+package com.persagy.dc.common.utils;
+
+
+import cn.hutool.core.collection.CollUtil;
+import com.persagy.dc.common.constant.ResponseCode;
+import com.persagy.dc.common.exception.BusinessException;
+import com.persagy.dc.common.model.response.CommonResult;
+import com.persagy.dc.common.model.response.PageList;
+
+import java.util.List;
+
+/**
+ * 响应结果包装助手
+ * @author Charlie Yu
+ * @date 2021-03-25
+ */
+public class ResultHelper {
+
+    /**
+     * 成功消息
+     * @return
+     */
+    public static CommonResult success(){
+        return new CommonResult(ResponseCode.A00000.getCode(), ResponseCode.A00000.getDesc());
+    }
+
+    /**
+     * 成功消息
+     * @return
+     */
+    public static CommonResult success(String respMsg){
+        return new CommonResult(ResponseCode.A00000.getCode(), respMsg);
+    }
+
+    /**
+     * 失败消息
+     * @param respCode
+     * @param respMsg
+     * @return
+     */
+    public static CommonResult failure(String respCode, String respMsg) {
+        return new CommonResult(respCode, respMsg);
+    }
+
+    /**
+     * 单个数据
+     * @param content
+     * @param <T>
+     * @return
+     */
+    public static <T> CommonResult<T> single(T content) {
+        return new CommonResult(ResponseCode.A00000.getCode(), ResponseCode.A00000.getDesc(), content);
+    }
+
+    /**
+     * 单个数据
+     * @param content
+     * @param <T>
+     * @return
+     */
+    public static <T> CommonResult<T> single(T content, String desc) {
+        return new CommonResult(ResponseCode.A00000.getCode(), desc, content);
+    }
+
+    /**
+     * 多个数据 - 列表
+     * @param records
+     * @param <T>
+     * @return
+     */
+    public static <T> CommonResult<PageList<T>> multi(List<T> records) {
+        return multi(records, CollUtil.size(records));
+    }
+
+    /**
+     * 多个数据 - 分页
+     * @param page
+     * @param <T>
+     * @return
+     */
+//    public static <T> CommonResult<PageList<T>> multi(IPage<T> page) {
+//        List<T> records = page == null ? null : page.getRecords();
+//        long total = page == null ? 0 : page.getTotal();
+//        return multi(records, total);
+//    }
+
+    /**
+     * 多个数据
+     * @param records
+     * @param total
+     * @param <T>
+     * @return
+     */
+    private static <T> CommonResult<PageList<T>> multi(List<T> records, long total) {
+        PageList<T> pageList = new PageList<>(records, total);
+        return new CommonResult(ResponseCode.A00000.getCode(), ResponseCode.A00000.getDesc(), pageList);
+    }
+
+    /**
+     * 读取结果
+     * @param result
+     * @param <T>
+     * @return 对应的结果
+     */
+    public static <T> T getContent(CommonResult<T> result) {
+        if(!ResponseCode.A00000.getCode().equals(result.getRespCode())) {
+            throw new BusinessException(result.getRespCode(), result.getRespMsg());
+        }
+        return result.getContent();
+    }
+}

+ 338 - 0
dc-common/src/main/java/com/persagy/dc/common/utils/TypeConvertUtil.java

@@ -0,0 +1,338 @@
+package com.persagy.dc.common.utils;
+
+import cn.hutool.core.util.StrUtil;
+import com.persagy.dc.common.model.data.DataType;
+import lombok.extern.slf4j.Slf4j;
+
+import java.math.BigDecimal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * 数据类型转换器
+ * @author Charlie Yu
+ * @version 1.0 2021-03-03
+ */
+@Slf4j
+public class TypeConvertUtil {
+
+    /**
+     * 私有构造方法,防止实例化
+     */
+    private TypeConvertUtil() {
+
+    }
+
+    /**
+     * 将Object对象解析成字符串类型
+     * @param obj 需要转换的对象
+     * @return 字符串
+     */
+    public static final String parseString(Object obj) {
+        if (obj == null) {
+            return null;
+        }
+        return obj.toString();
+    }
+
+    /**
+     * 将对象解析成字节类型
+     * @param obj 需要转换的对象
+     * @return 字节值
+     */
+    public static final byte parseByte(Object obj) {
+        if (obj == null) {
+            return 0;
+        }
+        if (obj instanceof Number) {
+            return ((Number) obj).byteValue();
+        }
+        if (obj instanceof Boolean) {
+            return (byte) (((Boolean) obj).booleanValue() ? 1 : 0);
+        }
+        String s;
+        if ("".equals(s = obj.toString())) {
+            return 0;
+        } else {
+            return Byte.parseByte(s);
+        }
+    }
+
+    /**
+     * 将对象解析成字节类型
+     * @param obj 需要转换的对象
+     * @return 字节值
+     */
+    public static final short parseShort(Object obj) {
+        if (obj == null) {
+            return 0;
+        }
+        if (obj instanceof Number) {
+            return ((Number) obj).shortValue();
+        }
+        if (obj instanceof Boolean) {
+            return (short) (((Boolean) obj).booleanValue() ? 1 : 0);
+        }
+        String s;
+        if ("".equals(s = obj.toString())) {
+            return 0;
+        } else {
+            return Short.parseShort(s);
+        }
+    }
+
+    /**
+     * 将对象解析成整数类型
+     * @param obj 需转换的对象
+     * @return 整数值
+     */
+    public static final int parseInt(Object obj) {
+        if (obj == null) {
+            return 0;
+        }
+        if (obj instanceof Number) {
+            return ((Number) obj).intValue();
+        }
+        if (obj instanceof Boolean) {
+            return !((Boolean) obj).booleanValue() ? 0 : 1;
+        }
+        String s;
+        if ("".equals(s = obj.toString())) {
+            return 0;
+        } else {
+            return Integer.parseInt(s);
+        }
+    }
+
+    /**
+     * 将对象解析成长整类型
+     * @param obj 需转换的对象
+     * @return 长整数值
+     */
+    public static final long parseLong(Object obj) {
+        if (obj == null) {
+            return 0;
+        }
+        if (obj instanceof Number) {
+            return ((Number) obj).longValue();
+        }
+        if (obj instanceof Boolean) {
+            return !((Boolean) obj).booleanValue() ? 0L : 1L;
+        }
+        if (obj instanceof Date) {
+            return ((Date) obj).getTime();
+        }
+        String s;
+        if ("".equals(s = obj.toString())) {
+            return 0;
+        } else {
+            return Long.parseLong(s);
+        }
+    }
+
+    /**
+     * 将对象解析成布尔类型数据
+     * @param obj 需转换的对象
+     * @return 布尔值
+     */
+    public static final boolean parseBoolean(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (obj instanceof Boolean) {
+            return ((Boolean) obj).booleanValue();
+        } else {
+            String s = obj.toString();
+            return "true".equalsIgnoreCase(s) || "1".equals(s) || "Y".equalsIgnoreCase(s) || "YES".equalsIgnoreCase(s);
+        }
+    }
+
+    /**
+     * 将对象解析成浮点型数据
+     * @param obj 需转换的对象
+     * @return 浮点数值
+     */
+    public static final float parseFloat(Object obj) {
+        if (obj == null) {
+            return 0.0f;
+        }
+        if (obj instanceof Number) {
+            return ((Number) obj).floatValue();
+        }
+        if (obj instanceof Boolean) {
+            return !((Boolean) obj).booleanValue() ? 0.0F : 1.0F;
+        }
+        String s;
+        if ("".equals(s = obj.toString())) {
+            return 0.0f;
+        } else {
+            return Float.parseFloat(s);
+        }
+    }
+
+    /**
+     * 将对象解析成双浮点型数据
+     * @param obj 需转换的对象
+     * @return 双浮点值
+     */
+    public static final double parseDouble(Object obj) {
+        if (obj == null) {
+            return 0.0d;
+        }
+        if (obj instanceof Number) {
+            return ((Number) obj).doubleValue();
+        }
+        if (obj instanceof Boolean) {
+            return !((Boolean) obj).booleanValue() ? 0.0D : 1.0D;
+        }
+        String s;
+        if ("".equals(s = obj.toString())) {
+            return 0.0d;
+        } else {
+            return Double.parseDouble(s);
+        }
+    }
+
+    /**
+     * 解析对象成大数字类型数据
+     * @param obj 需要转换的对象
+     * @return 大数字对象
+     */
+    public static final BigDecimal parseBigDecimal(Object obj) {
+        if (obj == null) {
+            return BigDecimal.valueOf(0L);
+        }
+        if (obj instanceof BigDecimal) {
+            return (BigDecimal) obj;
+        }
+        if (obj instanceof Number) {
+            return BigDecimal.valueOf(((Number) obj).doubleValue());
+        }
+        if (obj instanceof Boolean) {
+            return BigDecimal.valueOf(((Boolean) obj).booleanValue() ? 1L : 0L);
+        }
+        String s;
+        if ("".equals(s = obj.toString())) {
+            return BigDecimal.valueOf(0L);
+        } else {
+            return new BigDecimal(s);
+        }
+    }
+
+    /**
+     * 解析对象成日期数据类型
+     *
+     * @param obj 需转换的对象
+     * @return 日期对象
+     */
+    public static Date parseDate(Object obj) {
+        if (obj == null) {
+            return null;
+        }
+        if (obj instanceof Date) {
+            return (Date) obj;
+        }
+        if (obj instanceof Number) {
+            return new Date(((Number) obj).longValue());
+        }
+        String objString = String.valueOf(obj);
+        if (StrUtil.isBlank(objString)) {
+            return null;
+        }
+        if (isNumeric(objString)) {
+            long time = Long.parseLong(objString);
+            return new Date(time);
+        }
+        int len = objString.length();
+        if (len >= 19) {
+            return null;
+        }
+        boolean isDate = objString.indexOf("-") > 0;
+        boolean isTime = objString.indexOf(":") > 0;
+        try {
+            if (isDate && isTime) {
+                return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(objString);
+            }
+            if (isDate) {
+                return new SimpleDateFormat("yyyy-MM-dd").parse(objString);
+            }
+            if (isTime) {
+                return new SimpleDateFormat("HH:mm:ss").parse(objString);
+            }
+        } catch (ParseException e) {
+            log.error("TypeConvert parse: Date parse error", e);
+        }
+        return null;
+    }
+
+    /**
+     * 将未知类型的对象转换成指定类型的对象
+     * @param clazz 数据类型
+     * @param obj  未知类型对象
+     * @return 目标类型对象
+     */
+    public static final Object translate(Class clazz, Object obj) {
+        return translate(DataType.parse(clazz), obj);
+    }
+
+    /**
+     * 将未知类型的对象转换成指定类型的对象
+     * @param dataType 数据类型
+     * @param obj      未知类型对象
+     * @return 目标类型对象
+     */
+    public static final Object translate(DataType dataType, Object obj) {
+        boolean isBlank = obj == null || (obj instanceof String) && ((String) obj).length() == 0;
+        if (isBlank) {
+            if (dataType == DataType.STRING) {
+                return obj;
+            } else {
+                return null;
+            }
+        }
+        switch (dataType) {
+            case STRING:
+                return parseString(obj);
+            case INT:
+                return parseInt(obj);
+            case LONG:
+                return parseLong(obj);
+            case BIGDECIMAL:
+                return parseBigDecimal(obj);
+            case BOOLEAN:
+                return parseBoolean(obj);
+            case FLOAT:
+                return parseFloat(obj);
+            case DOUBLE:
+                return parseDouble(obj);
+            case DATE:
+            case TIME:
+            case DATETIME:
+                return parseDate(obj);
+            case BYTE:
+                return parseByte(obj);
+            case SHORT:
+                return parseShort(obj);
+            default:
+                return obj;
+        }
+    }
+
+    /**
+     * 判断字符串是否是有效的数组
+     * @param s 需要判断的字符串
+     * @return 布尔值
+     */
+    public static boolean isNumeric(String s) {
+        int length = s.length();
+        for (int i = 0; i < length; i++) {
+            char c = s.charAt(i);
+            boolean isNumberChar = (c < '0' || c > '9') && c != '.' && (i != 0 || c != '-');
+            if (isNumberChar) {
+                return false;
+            }
+        }
+        return true;
+    }
+}

+ 24 - 0
dc-comp/dc-amqp-starter/pom.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>dc-comp</artifactId>
+        <groupId>com.persagy</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>dc-amqp-starter</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>dc-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 27 - 0
dc-comp/dc-amqp-starter/src/main/java/com/persagy/dc/amqp/config/RabbitBindingConfig.java

@@ -0,0 +1,27 @@
+package com.persagy.dc.amqp.config;
+
+import org.springframework.amqp.rabbit.annotation.EnableRabbit;
+import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
+import org.springframework.amqp.support.converter.MessageConverter;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * RabbitMQ配置
+ * 绑定Exchange-RoutingKey-Queue
+ * @author Charlie Yu
+ * @date 2021-05-20
+ */
+@EnableRabbit
+@Configuration
+public class RabbitBindingConfig {
+
+    /**
+     * 消息转换器
+     * @return
+     */
+    @Bean
+    public MessageConverter customMessageConvert() {
+        return new Jackson2JsonMessageConverter();
+    }
+}

+ 78 - 0
dc-comp/dc-amqp-starter/src/main/java/com/persagy/dc/amqp/handler/AbstractRabbitHandlerTemplate.java

@@ -0,0 +1,78 @@
+package com.persagy.dc.amqp.handler;
+
+import com.rabbitmq.client.Channel;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.Charsets;
+import org.apache.commons.codec.binary.StringUtils;
+import org.springframework.amqp.core.Message;
+import org.springframework.amqp.rabbit.annotation.RabbitHandler;
+
+import java.io.IOException;
+
+/**
+ * RabbitMQ消息Handler 模板类
+ * 继承模板类实现handler方法实现业务,并添加RabbitListener注解即可接收队列中的消息
+ * @author Charlie Yu
+ * @date 2021-05-20
+ */
+@Slf4j
+public abstract class AbstractRabbitHandlerTemplate {
+
+    /**
+     * 消息处理
+     * 公共拦截,处理消息确认、异常处理
+     * @param message 消息体字符串
+     * @param channel 通道对象
+     * @param vo     消息对象
+     */
+    @RabbitHandler
+    public void process(String message, Channel channel, Message vo) {
+        log.info("启动消息处理:{}", message);
+        try {
+            handler(message, channel, vo);
+            // 手动确认消息已消费
+            channel.basicAck(vo.getMessageProperties().getDeliveryTag(), false);
+            log.info("消息处理成功,{}", message);
+        } catch (Exception e) {
+            try {
+                log.error("消息处理失败:{}", message, e);
+                Boolean redelivered = vo.getMessageProperties().getRedelivered();
+                // 消息重复处理失败
+                if (redelivered) {
+                    // 拒绝消息,requeue=false 表示不再重新入队,如果配置了死信队列则进入死信队列
+                    channel.basicReject(vo.getMessageProperties().getDeliveryTag(), false);
+                    // TODO 后续考虑写入DB,扩展运维手动处理失败消息
+                    // ...
+                } else {
+                    // 如果是第一次失败则再次放入队列 - requeue为是否重新回到队列,true重新入队
+                    channel.basicNack(vo.getMessageProperties().getDeliveryTag(), false, true);
+                }
+                log.error("消息入队成功:{}", message);
+            } catch (IOException e1) {
+                log.error("消息入队失败:{}", message, e1);
+            }
+        }
+    }
+
+    /**
+     * 消息处理
+     * 公共拦截,处理消息确认、异常处理
+     * @param body 消息体
+     * @param channel 通道对象
+     * @param vo     消息对象
+     */
+    @RabbitHandler
+    public void process(byte[] body, Channel channel, Message vo) {
+        String message = StringUtils.newString(body, Charsets.UTF_8.name());
+        process(message, channel, vo);
+    }
+
+    /**
+     * 消息处理
+     * 只需要做实际的业务处理
+     * @param message 消息体字符串
+     * @param channel 通道对象
+     * @param vo     消息对象
+     */
+    protected abstract void handler(String message, Channel channel, Message vo);
+}

+ 48 - 0
dc-comp/dc-amqp-starter/src/main/java/com/persagy/dc/amqp/handler/RabbitMessageSender.java

@@ -0,0 +1,48 @@
+package com.persagy.dc.amqp.handler;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
+/**
+ * 发送MQ消息
+ * 需要配置Binding
+ * @author Charlie Yu
+ * @date 2021-05-20
+ */
+@Component
+@Slf4j
+public class RabbitMessageSender {
+
+    @Autowired
+    private RabbitTemplate rabbitTemplate;
+
+    /** 读取配置不发消息的RoutingKey */
+    @Value("#{'${persagy.common.amqp.disablekey:?}'.split(',')}")
+    private Set<String> disableKeys;
+
+    /**
+     * 发送消息
+     * @param exchange 发送的交换机
+     * @param routingKey 路由Key
+     * @param message
+     */
+    public void send(String exchange, String routingKey, String message) {
+        log.info("开始发送消息,routingKey={},message={}", routingKey, message);
+        // 配置了不发此类消息
+        if(CollUtil.isNotEmpty(disableKeys) &&
+                StrUtil.isNotBlank(routingKey) &&
+                disableKeys.contains(routingKey)) {
+            return;
+        }
+        // 发送 异常不捕获直接抛出
+        rabbitTemplate.convertAndSend(exchange, routingKey, message);
+        log.info("消息发送成功,routingKey={}", routingKey);
+    }
+}

+ 48 - 0
dc-comp/dc-file-starter/README.md

@@ -0,0 +1,48 @@
+文件服务客户端
+============ 
+文件服务客户端
+- 支持MinIO文件服务器
+- 支持文件管理
+- 图片自动压缩(压缩至200K以下)
+文件管理服务只存储文件信息,文件上传由客户端直接上传至文件服务器中,不在文件管理服务做额外中转。
+
+说明
+---------------
+文件助手使用
+* 示例代码
+  ```
+  FileInfo fileInfo = FileInfoCreator.of(groupCode, businessId, appName, fileName);
+  // 可对fileInfo属性额外配置。如有效期等
+  FileStorageHelper.uploadFile(fileInfo, inputStream);
+  ```
+
+配置说明
+* 参数配置
+  ```
+  # 使用的文件服务器,默认为MinIO。详见FileStorageEnum
+  persagy.common.file.storage=0
+  # MinIO服务地址、账号、密码
+  persagy.common.file.url=http://192.168.25.129:9000/
+  persagy.common.file.accessKey=admin
+  persagy.common.file.secretKey=********
+  # typeHandler扫描
+  mybatis-plus.typeHandlersPackage=com.persagy.**.typehandler
+  ```
+  
+安装说明
+* MinIO安装
+  ```
+  # 拉镜像
+  docker pull minio/minio
+  # 启动
+  docker run -p 9000:9000 --name minio \
+  -d --restart=always \
+  -e "MINIO_ACCESS_KEY=admin" \
+  -e "MINIO_SECRET_KEY=********" \
+  -v /home/data:/data \
+  -v /home/config:/root/.minio \
+  minio/minio server /usr/local/minio/files
+  ```
+最新变化
+---------------
+

+ 38 - 0
dc-comp/dc-file-starter/pom.xml

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>dc-comp</artifactId>
+        <groupId>com.persagy</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>dc-file-starter</artifactId>
+    <version>1.0.0</version>
+
+    <dependencies>
+        <!-- MinIO -->
+        <dependency>
+            <groupId>io.minio</groupId>
+            <artifactId>minio</artifactId>
+            <version>7.0.2</version>
+        </dependency>
+        <!-- fm 工具包 -->
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>dc-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mybatis</groupId>
+            <artifactId>mybatis-spring</artifactId>
+            <version>2.0.3</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+
+</project>

+ 55 - 0
dc-comp/dc-file-starter/src/main/java/com/persagy/dc/file/client/FileClientFacade.java

@@ -0,0 +1,55 @@
+package com.persagy.dc.file.client;
+
+import cn.hutool.extra.spring.SpringUtil;
+import com.persagy.dc.file.model.FileInfo;
+import com.persagy.dc.common.model.response.CommonResult;
+import com.persagy.dc.common.utils.ResultHelper;
+
+/**
+ * 文件管理Client门面
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+public class FileClientFacade {
+
+    private static FileManageClient client = SpringUtil.getBean(FileManageClient.class);
+
+    /**
+     * 新增
+     * @param fileInfo
+     * @return
+     */
+    public static FileInfo insert(FileInfo fileInfo) {
+        CommonResult<FileInfo> result = client.insert(fileInfo);
+        return ResultHelper.getContent(result);
+    }
+
+    /**
+     * 修改
+     * @param fileInfo
+     * @return
+     */
+    public static FileInfo update(FileInfo fileInfo) {
+        CommonResult<FileInfo> result = client.update(fileInfo);
+        return ResultHelper.getContent(result);
+    }
+
+    /**
+     * 删除
+     * @param fileId
+     */
+    public static void delete(String fileId) {
+        CommonResult<String> result = client.delete(fileId);
+        ResultHelper.getContent(result);
+    }
+
+    /**
+     * 查询
+     * @param fileId
+     * @return
+     */
+    public static FileInfo load(String fileId) {
+        CommonResult<FileInfo> result = client.load(fileId);
+        return ResultHelper.getContent(result);
+    }
+}

+ 49 - 0
dc-comp/dc-file-starter/src/main/java/com/persagy/dc/file/client/FileManageClient.java

@@ -0,0 +1,49 @@
+package com.persagy.dc.file.client;
+
+import com.persagy.dc.file.model.FileInfo;
+import com.persagy.dc.storage.config.FileFeignConfig;
+import com.persagy.dc.common.model.response.CommonResult;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+/**
+ * 文件管理Client
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+@FeignClient(name = "dc-file", configuration = FileFeignConfig.class)
+public interface FileManageClient {
+
+    /**
+     * 新增文件信息
+     * @param fileInfo 文件信息
+     * @return 文件信息
+     */
+    @PostMapping("/file/insert")
+    CommonResult<FileInfo> insert(@RequestBody FileInfo fileInfo);
+
+    /**
+     * 修改文件信息
+     * @param fileInfo 文件信息
+     * @return 文件信息
+     */
+    @PostMapping("/file/update")
+    CommonResult<FileInfo> update(@RequestBody FileInfo fileInfo);
+
+    /**
+     * 删除文件信息
+     * @param id 文件信息
+     * @return 文件信息
+     */
+    @PostMapping("/file/delete")
+    CommonResult<String> delete(@RequestBody String id);
+
+    /**
+     * 查询文件信息
+     * @param id 文件信息
+     * @return 文件信息
+     */
+    @PostMapping("/file/load")
+    CommonResult<FileInfo> load(@RequestBody String id);
+}

+ 31 - 0
dc-comp/dc-file-starter/src/main/java/com/persagy/dc/file/model/FileInfo.java

@@ -0,0 +1,31 @@
+package com.persagy.dc.file.model;
+
+import com.persagy.dc.common.lang.PsDate;
+import com.persagy.dc.common.model.entity.AuditableEntity;
+import lombok.Data;
+
+/**
+ * 文件对象
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+@Data
+public class FileInfo extends AuditableEntity {
+
+    /** 集团编码:租户 */
+    private String groupCode;
+    /** 文件名称:显示用 */
+    private String fileName;
+    /** 文件类型:0-文件,1-目录 */
+    private String fileType;
+    /** 文件分组:默认为应用名 */
+    private String fileBucket;
+    /** 文件路径:含文件名称 - 对应文件存储路径 */
+    private String filePath;
+    /** 文件大小 */
+    private Long fileSize;
+    /** 关联业务Id */
+    private String businessId;
+    /** 保留期限 */
+    private PsDate expireDate;
+}

+ 72 - 0
dc-comp/dc-file-starter/src/main/java/com/persagy/dc/file/model/FileInfoCreator.java

@@ -0,0 +1,72 @@
+package com.persagy.dc.file.model;
+
+import cn.hutool.core.io.file.FileNameUtil;
+import cn.hutool.core.util.ReUtil;
+import cn.hutool.core.util.StrUtil;
+import com.persagy.dc.storage.constant.FileCommonConst;
+import com.persagy.dc.common.exception.BusinessException;
+import com.persagy.dc.common.lang.PsDate;
+
+/**
+ * 文件信息组装器
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+public class FileInfoCreator {
+
+    /** 桶名校验正则 */
+    private static final String BUCKET_PATTERN = "^[a-z0-9][a-z0-9\\-]*[a-z0-9]$";
+
+    /**
+     * 创建FileInfo
+     * @param groupCode 集团编码 - 租户
+     * @param businessId 业务Id
+     * @param bucketName 桶 - 应用名
+     * @param fileName 文件名
+     * @return
+     */
+    public static FileInfo of(String groupCode, String businessId, String bucketName, String fileName){
+        FileInfo fileInfo = new FileInfo();
+        fileInfo.setGroupCode(groupCode);
+        fileInfo.setFileBucket(formatBucket(bucketName));
+        fileInfo.setFileName(fileName);
+        fileInfo.setFileType("0");
+        fileInfo.setBusinessId(businessId);
+        return fileInfo;
+    }
+
+    /**
+     * 桶名处理
+     * 将大写转换为小写。
+     * 校验字符:小写字母、连字符、数字,长度为[3,63],开头和结尾必须是字母或数字
+     * https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html
+     * @param bucketName 桶名
+     * @return
+     */
+    public static String formatBucket(String bucketName) {
+        if(StrUtil.isBlank(bucketName)) {
+            return FileCommonConst.DEFAULT_BUCKET;
+        }
+        // 格式转换
+        bucketName = bucketName.toLowerCase();
+        // 校验
+        if(StrUtil.length(bucketName) > 63 ||
+                !ReUtil.isMatch(BUCKET_PATTERN, bucketName)) {
+            throw new BusinessException("桶名有误,允许字符:小写字母、连字符、数字,长度为3至63,开头和结尾必须是字母或数字");
+        }
+        return bucketName;
+    }
+
+    /**
+     * 组装文件路径
+     * groupCode/dateStr/fileName
+     * @param fileInfo 文件管理信息
+     */
+    public static void buildFilePath(FileInfo fileInfo) {
+        String date = PsDate.parsePsDate(fileInfo.getCreationTime()).toString();
+        StringBuilder sb = new StringBuilder(fileInfo.getGroupCode());
+        sb.append(FileNameUtil.UNIX_SEPARATOR).append(date);
+        sb.append(FileNameUtil.UNIX_SEPARATOR).append(fileInfo.getFileName());
+        fileInfo.setFilePath(sb.toString());
+    }
+}

+ 43 - 0
dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/config/FileFeignConfig.java

@@ -0,0 +1,43 @@
+package com.persagy.dc.storage.config;
+
+import feign.Logger;
+import feign.codec.Encoder;
+import feign.form.spring.SpringFormEncoder;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.cloud.openfeign.support.SpringEncoder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.context.annotation.Scope;
+
+import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
+
+/**
+ * Feign配置
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+@MapperScan(value = "com.persagy.**.dao")
+@EnableFeignClients(basePackages="com.persagy.**.client")
+@Configuration
+public class FileFeignConfig {
+
+    @Autowired
+    private ObjectFactory<HttpMessageConverters> messageConverters;
+
+    @Bean
+    @Primary
+    @Scope(SCOPE_PROTOTYPE)
+    Encoder feignFormEncoder() {
+        return new SpringFormEncoder(new SpringEncoder(this.messageConverters));
+    }
+
+    @Bean
+    Logger.Level feignLevel(){
+        return Logger.Level.FULL;
+    }
+}

+ 36 - 0
dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/config/MinioConfig.java

@@ -0,0 +1,36 @@
+package com.persagy.dc.storage.config;
+
+import com.persagy.dc.common.exception.BusinessException;
+import io.minio.MinioClient;
+import io.minio.errors.MinioException;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * MinIO配置
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+@Configuration
+public class MinioConfig {
+
+    /** 文件服务器url */
+    @Value("${persagy.common.file.url}")
+    private String url;
+    /** 文件服务器accessKey */
+    @Value("${persagy.common.file.accessKey}")
+    private String accessKey;
+    /** 文件服务器secretKey */
+    @Value("${persagy.common.file.secretKey}")
+    private String secretKey;
+
+    @Bean
+    public MinioClient minioClient() {
+        try {
+            return new MinioClient(url, accessKey, secretKey);
+        } catch (MinioException e) {
+            throw new BusinessException(e.getMessage(), e);
+        }
+    }
+}

+ 14 - 0
dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/constant/FileCommonConst.java

@@ -0,0 +1,14 @@
+package com.persagy.dc.storage.constant;
+
+/**
+ * 文件管理常量
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+public interface FileCommonConst {
+
+    /** 默认桶名 */
+    String DEFAULT_BUCKET = "default";
+    /** 链接有效期(秒):7天 */
+    int URL_EXPIRES = 7 * 24 * 3600;
+}

+ 38 - 0
dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/constant/FileStorageEnum.java

@@ -0,0 +1,38 @@
+package com.persagy.dc.storage.constant;
+
+import lombok.Getter;
+
+/**
+ * 文件管理服务
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+@Getter
+public enum FileStorageEnum {
+    /** MinIO */
+    MINIO("0", "minioStorageServiceImpl"),
+    /** FastDFS */
+    FAST_DFS("1", "fastDfsStorageServiceImpl");
+
+    private String index;
+    private String name;
+
+    FileStorageEnum(String index, String name) {
+        this.index = index;
+        this.name = name;
+    }
+
+    /**
+     * 获取Enum
+     * @param index
+     * @return
+     */
+    public static FileStorageEnum load(String index) {
+        for (FileStorageEnum value: values()) {
+            if (value.getIndex().equals(index)) {
+                return value;
+            }
+        }
+        return null;
+    }
+}

+ 27 - 0
dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/service/FileStorageFactory.java

@@ -0,0 +1,27 @@
+package com.persagy.dc.storage.service;
+
+import com.persagy.dc.storage.constant.FileStorageEnum;
+import com.persagy.dc.common.helper.SpringHelper;
+
+/**
+ * 文件存储管理工厂
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+public class FileStorageFactory {
+
+    private static IFileStorageService storageService;
+
+    /**
+     * 获取文件存储服务
+     * @return
+     */
+    public static IFileStorageService getService() {
+        if(storageService == null) {
+            String storageType = SpringHelper.getString("persagy.common.file.storage", "0");
+            String beanName = FileStorageEnum.load(storageType).getName();
+            storageService = SpringHelper.getBean(beanName);
+        }
+        return storageService;
+    }
+}

+ 56 - 0
dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/service/IFileStorageService.java

@@ -0,0 +1,56 @@
+package com.persagy.dc.storage.service;
+
+import java.io.InputStream;
+
+/**
+ * 文件存储服务接口
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+public interface IFileStorageService {
+
+    /**
+     * 文件上传
+     * @param bucketName 桶名
+     * @param fileName 文件名 - 同名文件则覆盖
+     * @param input 输入流
+     */
+    void upload(String bucketName, String fileName, InputStream input);
+
+    /**
+     * 文件下载
+     * @param bucketName 桶名
+     * @param fileName 文件名
+     * @return
+     */
+    InputStream download(String bucketName, String fileName);
+
+    /**
+     * 获取下载Url
+     * @param bucketName 桶名
+     * @param fileName 文件名
+     * @return
+     */
+    String fetchUrl(String bucketName, String fileName);
+
+    /**
+     * 文件是否存在
+     * @param bucketName 桶名
+     * @param fileName 文件名
+     * @return
+     */
+    boolean exists(String bucketName, String fileName);
+
+    /**
+     * 删除文件
+     * @param bucketName 桶名
+     * @param fileName 文件名
+     */
+    void delete(String bucketName, String fileName);
+
+    /**
+     * 删除桶
+     * @param bucketName 桶名
+     */
+    void deletePath(String bucketName);
+}

+ 54 - 0
dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/service/impl/MinioExceptionHandler.java

@@ -0,0 +1,54 @@
+package com.persagy.dc.storage.service.impl;
+
+import com.persagy.dc.common.exception.BusinessException;
+import io.minio.errors.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * MinIO异常处理
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+@Slf4j
+public class MinioExceptionHandler {
+
+    /**
+     * Minio异常处理
+     * @param e 异常
+     */
+    public static void handleException(Exception e) {
+        log.error(e.getMessage(), e);
+        if(e instanceof InvalidKeyException) {
+            throw new BusinessException("文件服务器-密钥有误");
+        }
+        if(e instanceof NoSuchAlgorithmException) {
+            throw new BusinessException("文件服务器密钥有误");
+        }
+        if(e instanceof InvalidResponseException) {
+            throw new BusinessException("文件服务器密钥有误");
+        }
+        if(e instanceof InvalidBucketNameException) {
+            throw new BusinessException("文件服务器-不合法的存储桶名称。");
+        }
+        if(e instanceof ErrorResponseException) {
+            throw new BusinessException("文件服务器-执行失败异常。");
+        }
+        if(e instanceof InsufficientDataException) {
+            throw new BusinessException("文件服务器密钥有误");
+        }
+        if(e instanceof XmlParserException) {
+            throw new BusinessException("文件服务器-解析返回的XML异常。");
+        }
+        if(e instanceof InternalException) {
+            throw new BusinessException("文件服务器-内部错误。");
+        }
+        if(e instanceof IOException) {
+            throw new BusinessException("文件服务器-连接异常。");
+        }
+        throw new BusinessException(e.getMessage(), e);
+    }
+}

+ 150 - 0
dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/service/impl/MinioStorageServiceImpl.java

@@ -0,0 +1,150 @@
+package com.persagy.dc.storage.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.persagy.dc.storage.constant.FileCommonConst;
+import com.persagy.dc.storage.service.IFileStorageService;
+import com.persagy.dc.common.exception.BusinessException;
+import io.minio.MinioClient;
+import io.minio.ObjectStat;
+import io.minio.PutObjectOptions;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.io.InputStream;
+
+/**
+ * MinIO存储服务实现类
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+@Service
+@Slf4j
+public class MinioStorageServiceImpl implements IFileStorageService {
+
+    @Resource
+    private MinioClient minioClient;
+
+    @Override
+    public void upload(String bucketName, String fileName, InputStream input) {
+        // 容错判断
+        if(StrUtil.isBlank(fileName) || input == null) {
+            throw new BusinessException("上传文件参数有误!");
+        }
+        try {
+            // 确认桶是否存在
+            bucketName = ensureBucket(bucketName);
+            // 上传文件
+            PutObjectOptions options = new PutObjectOptions(input.available(), -1);
+            minioClient.putObject(bucketName, fileName, input, options);
+        } catch (Exception e) {
+            MinioExceptionHandler.handleException(e);
+        }
+    }
+
+    @Override
+    public InputStream download(String bucketName, String fileName) {
+        // 容错判断
+        if(StrUtil.isBlank(fileName)) {
+            throw new BusinessException("下载文件参数有误!");
+        }
+        try {
+            // 确认桶是否存在
+            bucketName = ensureBucket(bucketName);
+            // 下载文件
+            return minioClient.getObject(bucketName, fileName);
+        } catch (Exception e) {
+            MinioExceptionHandler.handleException(e);
+        }
+        return null;
+    }
+
+    @Override
+    public String fetchUrl(String bucketName, String fileName) {
+        // 容错判断
+        if(StrUtil.isBlank(fileName)) {
+            throw new BusinessException("下载文件参数有误!");
+        }
+        try {
+            // 确认桶是否存在
+            bucketName = ensureBucket(bucketName);
+            // 下载文件
+            return minioClient.presignedGetObject(bucketName, fileName, FileCommonConst.URL_EXPIRES);
+        } catch (Exception e) {
+            MinioExceptionHandler.handleException(e);
+        }
+        return null;
+    }
+
+    @Override
+    public boolean exists(String bucketName, String fileName) {
+        // 容错判断
+        if(StrUtil.isBlank(fileName)) {
+            throw new BusinessException("检查文件参数有误!");
+        }
+        try {
+            // 确认桶是否存在
+            bucketName = ensureBucket(bucketName);
+            // 查看文件元数据
+            ObjectStat stat = minioClient.statObject(bucketName, fileName);
+            return stat != null;
+        } catch (Exception e) {
+            MinioExceptionHandler.handleException(e);
+        }
+        return false;
+    }
+
+    @Override
+    public void delete(String bucketName, String fileName) {
+        // 容错判断
+        if(StrUtil.isBlank(fileName)) {
+            throw new BusinessException("删除文件参数有误!");
+        }
+        try {
+            // 确认桶是否存在
+            bucketName = ensureBucket(bucketName);
+            // 删除文件
+            minioClient.removeObject(bucketName, fileName);
+        } catch (Exception e) {
+            MinioExceptionHandler.handleException(e);
+        }
+    }
+
+    @Override
+    public void deletePath(String bucketName) {
+        // 容错判断 桶名为空时也不让删
+        if(StrUtil.isBlank(bucketName)) {
+            throw new BusinessException("删除文件参数有误!");
+        }
+        try {
+            // 确认桶是否存在
+            bucketName = ensureBucket(bucketName);
+            // 删除桶
+            minioClient.removeBucket(bucketName);
+        } catch (Exception e) {
+            MinioExceptionHandler.handleException(e);
+        }
+    }
+
+    /**
+     * 桶ensure
+     * @param bucketName 桶名
+     * @return 确认后的桶名
+     * @throws Exception
+     */
+    private String ensureBucket(String bucketName) throws Exception{
+        // 桶名为空时,使用默认桶
+        if(StrUtil.isBlank(bucketName)) {
+            bucketName = FileCommonConst.DEFAULT_BUCKET;
+        }
+        log.info("开始检查桶名:[{0}]是否存在", bucketName);
+        boolean isExist = minioClient.bucketExists(bucketName);
+        if(isExist) {
+            log.info("桶名:[{0}]已存在", bucketName);
+            return bucketName;
+        }
+        minioClient.makeBucket(bucketName);
+        log.info("桶名:[{0}]不存在,创建成功", bucketName);
+        return bucketName;
+    }
+}

+ 130 - 0
dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/utils/FileStorageHelper.java

@@ -0,0 +1,130 @@
+package com.persagy.dc.storage.utils;
+
+import com.persagy.dc.file.client.FileClientFacade;
+import com.persagy.dc.file.model.FileInfo;
+import com.persagy.dc.storage.service.FileStorageFactory;
+import com.persagy.dc.common.exception.BusinessException;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * 文件存储助手
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+@Slf4j
+public class FileStorageHelper {
+
+    /**
+     * 上传文件
+     * @param fileInfo
+     * @param in
+     * @return
+     */
+    public static String uploadFile(FileInfo fileInfo, InputStream in) {
+        fileInfo.setFileSize(getStreamSize(in));
+        fileInfo = FileClientFacade.insert(fileInfo);
+        FileStorageFactory.getService().upload(fileInfo.getFileBucket(), fileInfo.getFilePath(), in);
+        return fileInfo.getId();
+    }
+
+    /**
+     * 上传图片
+     * @param fileInfo
+     * @param in
+     * @return
+     */
+    public static String uploadImage(FileInfo fileInfo, InputStream in) {
+        // 图片压缩
+        InputStream cutIn = ImageHelper.cutImage(in);
+        return uploadFile(fileInfo, cutIn);
+    }
+
+    /**
+     * 修改文件
+     * @param fileId
+     * @param fileName
+     * @param in
+     * @return
+     */
+    public static String replaceFile(String fileId, String fileName, InputStream in) {
+        FileInfo fileInfo = FileClientFacade.load(fileId);
+        // 先删除文件
+        FileStorageFactory.getService().delete(fileInfo.getFileBucket(), fileInfo.getFilePath());
+        // 更新信息后上传
+        fileInfo.setFileName(fileName);
+        fileInfo.setFileSize(getStreamSize(in));
+        fileInfo = FileClientFacade.update(fileInfo);
+        FileStorageFactory.getService().upload(fileInfo.getFileBucket(), fileInfo.getFilePath(), in);
+        return fileInfo.getId();
+    }
+
+    /**
+     * 更新图片
+     * @param fileId
+     * @param fileName
+     * @param in
+     * @return
+     */
+    public static String replaceImage(String fileId, String fileName, InputStream in) {
+        // 图片压缩
+        InputStream cutIn = ImageHelper.cutImage(in);
+        return replaceFile(fileId, fileName, cutIn);
+    }
+
+    /**
+     * 删除文件及图片
+     * @param fileId
+     */
+    public static void deleteFile(String fileId) {
+        FileInfo fileInfo = FileClientFacade.load(fileId);
+        FileClientFacade.delete(fileId);
+        FileStorageFactory.getService().delete(fileInfo.getFileBucket(), fileInfo.getFilePath());
+    }
+
+    /**
+     * 下载
+     * @param fileId 文件id
+     * @return 文件下载地址
+     */
+    public static String downloadUrl(String fileId) {
+        FileInfo fileInfo = FileClientFacade.load(fileId);
+        return FileStorageFactory.getService().fetchUrl(fileInfo.getFileBucket(), fileInfo.getFilePath());
+    }
+
+    /**
+     * 下载
+     * @param fileId 文件id
+     * @return 文件流
+     */
+    public static InputStream downloadStream(String fileId) {
+        FileInfo fileInfo = FileClientFacade.load(fileId);
+        return FileStorageFactory.getService().download(fileInfo.getFileBucket(), fileInfo.getFilePath());
+    }
+
+    /**
+     * 下载
+     * @param fileId 文件id
+     * @return 文件内容
+     */
+    public static byte[] downloadContent(String fileId) {
+        // TODO 暂不提供,依赖前端显示规则
+        return null;
+    }
+
+    /**
+     * 获取文件大小
+     * @param stream
+     * @return
+     */
+    private static Long getStreamSize(InputStream stream) {
+        try {
+            return Long.valueOf(stream.available());
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+            throw new BusinessException("文件上传失败!");
+        }
+    }
+}

+ 55 - 0
dc-comp/dc-file-starter/src/main/java/com/persagy/dc/storage/utils/ImageHelper.java

@@ -0,0 +1,55 @@
+package com.persagy.dc.storage.utils;
+
+import cn.hutool.core.img.ImgUtil;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+/**
+ * 图片工具
+ * @author Charlie Yu
+ * @date 2021-05-15
+ */
+@Slf4j
+public class ImageHelper {
+
+    /** 压缩后大小(b) */
+    private static final BigDecimal IMAGE_SIZE = BigDecimal.valueOf(100 * 1024);
+
+    /**
+     * 压缩裁剪图片
+     * @param in 原图片
+     * @return 图片压缩至IMAGE_SIZE以下
+     */
+    public static InputStream cutImage(InputStream in) {
+        try {
+            // 文件大小
+            BigDecimal fileSize = BigDecimal.valueOf(in.available());
+            // 文件小于压缩后大小,不必处理
+            if(fileSize.compareTo(IMAGE_SIZE) <= 0) {
+                return in;
+            }
+            // 文件压缩比例
+            BigDecimal fileScaleValue = IMAGE_SIZE.divide(fileSize, 8, RoundingMode.DOWN);
+            // 图片压缩比例 = 根号 文件压缩比例
+            float scaleValue = BigDecimal.valueOf(Math.sqrt(fileScaleValue.doubleValue())).floatValue();
+            // 容错处理,如果图片过大,会导致压缩比例过小为0,采用最小压缩比
+            if(scaleValue <= 0) {
+                scaleValue = 0.001f;
+            }
+            try(ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+                // 压缩
+                ImgUtil.scale(in, out, scaleValue);
+                return new ByteArrayInputStream(out.toByteArray());
+            }
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+        }
+        return in;
+    }
+}

+ 176 - 0
dc-comp/dc-mybatis/README.md

@@ -0,0 +1,176 @@
+关系数据库
+============ 
+mybatis-plus封装
+- 支持多数据源自动切换
+- 支持预置建库脚本
+- 提供树型内部码生成工具
+
+说明
+---------------
+多数据源
+* 配置文件参考:com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties
+* [配置文件示例](https://github.com/dynamic-datasource/dynamic-datasource-doc/blob/master/docs/guide/integration/Druid.md)
+```
+spring:
+  datasource:
+    druid:
+      stat-view-servlet:
+        enabled: true
+        loginUsername: admin
+        loginPassword: 123456
+    dynamic:
+      druid: #以下是支持的全局默认值
+        initial-size:
+        max-active:
+        min-idle:
+        max-wait:
+        time-between-eviction-runs-millis:
+        time-between-log-stats-millis:
+        stat-sqlmax-size:
+        min-evictable-idle-time-millis:
+        max-evictable-idle-time-millis:
+        test-while-idle:
+        test-on-borrow:
+        test-on-return:
+        validation-query:
+        validation-query-timeout:
+        use-global-datasource-stat:
+        async-init:
+        clear-filters-enable:
+        reset-stat-enable:
+        not-full-timeout-retry-count:
+        max-wait-thread-count:
+        fail-fast:
+        phyTimeout-millis:
+        keep-alive:
+        pool-prepared-statements:
+        init-variants:
+        init-global-variants:
+        use-unfair-lock:
+        kill-when-socket-read-timeout:
+        connection-properties:
+        max-pool-prepared-statement-per-connection-size:
+        init-connection-sqls:
+        share-prepared-statements:
+        connection-errorretry-attempts:
+        break-after-acquire-failure:
+        filters: stat,wall # 注意这个值和druid原生不一致,默认启动了stat,wall
+        wall:
+            noneBaseStatementAllow:
+            callAllow:
+            selectAllow:
+            selectIntoAllow:
+            selectIntoOutfileAllow:
+            selectWhereAlwayTrueCheck:
+            selectHavingAlwayTrueCheck:
+            selectUnionCheck:
+            selectMinusCheck:
+            selectExceptCheck:
+            selectIntersectCheck:
+            createTableAllow:
+            dropTableAllow:
+            alterTableAllow:
+            renameTableAllow:
+            hintAllow:
+            lockTableAllow:
+            startTransactionAllow:
+            blockAllow:
+            conditionAndAlwayTrueAllow:
+            conditionAndAlwayFalseAllow:
+            conditionDoubleConstAllow:
+            conditionLikeTrueAllow:
+            selectAllColumnAllow:
+            deleteAllow:
+            deleteWhereAlwayTrueCheck:
+            deleteWhereNoneCheck:
+            updateAllow:
+            updateWhereAlayTrueCheck:
+            updateWhereNoneCheck:
+            insertAllow:
+            mergeAllow:
+            minusAllow:
+            intersectAllow:
+            replaceAllow:
+            setAllow:
+            commitAllow:
+            rollbackAllow:
+            useAllow:
+            multiStatementAllow:
+            truncateAllow:
+            commentAllow:
+            strictSyntaxCheck:
+            constArithmeticAllow:
+            limitZeroAllow:
+            describeAllow:
+            showAllow:
+            schemaCheck:
+            tableCheck:
+            functionCheck:
+            objectCheck:
+            variantCheck:
+            mustParameterized:
+            doPrivilegedAllow:
+            dir:
+            tenantTablePattern:
+            tenantColumn:
+            wrapAllow:
+            metadataAllow:
+            conditionOpXorAllow:
+            conditionOpBitwseAllow:
+            caseConditionConstAllow:
+            completeInsertValuesCheck:
+            insertValuesCheckSize:
+            selectLimit:
+        stat:
+          merge-sql:
+          log-slow-sql:
+          slow-sql-millis: 
+      datasource:
+        master:
+          username: root
+          password: 123456
+          driver-class-name: com.mysql.jdbc.Driver
+          url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic?characterEncoding=utf8&useSSL=false
+          druid: # 以下是独立参数,每个库可以重新设置
+            initial-size:
+            validation-query: select 1 FROM DUAL #比如oracle就需要重新设置这个
+            public-key: #(非全局参数)设置即表示启用加密,底层会自动帮你配置相关的连接参数和filter,推荐使用本项目自带的加密方法。
+#           ......
+
+# 生成 publickey 和密码,推荐使用本项目自带的加密方法。
+# java -cp druid-1.1.10.jar com.alibaba.druid.filter.config.ConfigTools youpassword
+```
+
+预置建库脚本
+* [参考配置](https://github.com/dynamic-datasource/dynamic-datasource-doc/blob/master/docs/guide/advance/Init-Schema-Data.md)
+* 脚本文件放在resources目录,在默认数据源中增加配置:
+  ```
+  spring:
+    datasource:
+      dynamic:
+        primary: order
+        datasource:
+          order:
+            # 基础配置省略...
+            schema: db/order/schema.sql # 配置则生效,自动初始化表结构。
+            data: db/order/data.sql # 配置则生效,自动初始化数据。
+            continue-on-error: true # 默认true,初始化失败是否继续
+            separator: ";" # sql默认分号分隔符,一般无需更改
+  ```
+* 注意事项:
+  - schema: 建立连接时会执行,因此建表应使用CREATE TABLE IF NOT EXISTS确保不会重复创建。
+  - data: 建立连接时会执行,应确保数据不会重复插入。
+
+树型内部码工具
+* 可对实现ITreeEntity的model自动生成innerCode内部码。
+	
+自定义日期类型转换
+* 全局处理日期类型转换char,需要增加配置
+  ```
+  mybatis-plus:
+    typeHandlersPackage: com.persagy.**.typehandler
+  ```
+
+最新变化
+---------------
+

+ 29 - 0
dc-comp/dc-mybatis/pom.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>dc-comp</artifactId>
+        <groupId>com.persagy</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>dc-mybatis</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>dc-common</artifactId>
+        </dependency>
+        <!-- mybatisPlus -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 16 - 0
dc-comp/dc-mybatis/src/main/java/com/persagy/dc/mybatis/annotations/FormatMybatisQueryParams.java

@@ -0,0 +1,16 @@
+package com.persagy.fm.mybatis.annotations;
+
+import java.lang.annotation.*;
+
+/**
+ * mybatis查询参数格式化注解
+ *
+ * @author lixing
+ * @version V1.0 2021/4/23 3:58 下午
+ **/
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@Inherited
+@Documented
+public @interface FormatMybatisQueryParams {
+}

+ 68 - 0
dc-comp/dc-mybatis/src/main/java/com/persagy/dc/mybatis/aop/FormatMybatisQueryParamsAspect.java

@@ -0,0 +1,68 @@
+package com.persagy.fm.mybatis.aop;
+
+import cn.hutool.core.util.StrUtil;
+import com.persagy.fm.mybatis.utils.MyBatisStringUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * 格式化mybatis请求参数切面
+ *
+ * @author lixing
+ * @version V1.0 2021/4/23 4:00 下午
+ **/
+@Component
+@Aspect
+@Slf4j
+public class FormatMybatisQueryParamsAspect {
+    @Pointcut("@annotation(com.persagy.fm.mybatis.annotations.FormatMybatisQueryParams)")
+    private void pointCutMethod() {
+    }
+
+    @Before("pointCutMethod()")
+    public void process(JoinPoint point) throws Throwable {
+        // 获取方法的参数
+        Object[] args = point.getArgs();
+        for (Object arg : args) {
+            // 获取方法入参的属性
+            Field[] declaredFields = arg.getClass().getDeclaredFields();
+            for (Field declaredField : declaredFields) {
+                if (declaredField.getType().equals(String.class)) {
+                    Method getMethod = arg.getClass().getDeclaredMethod(
+                            "get" + MyBatisStringUtils.firstCharUpperCase(declaredField.getName()));
+                    String s = (String) getMethod.invoke(arg);
+                    s = formatUnderLine(s);
+                    Method setMethod = arg.getClass().getDeclaredMethod(
+                            "set" + MyBatisStringUtils.firstCharUpperCase(declaredField.getName()), String.class);
+                    setMethod.invoke(arg, s);
+                }
+            }
+        }
+    }
+
+    /**
+     * 格式化下划线,如果字符串中包含下划线,将下划线替换为"\_"
+     * mysql中下划线代表任意字符
+     *
+     * @param s 格式化前的字符串
+     * @return 格式化后的字符串
+     * @author lixing
+     * @version V1.0 2021/4/23 4:14 下午
+     */
+    private String formatUnderLine(String s) {
+        if (StrUtil.isBlank(s)) {
+            return s;
+        }
+        if (s.contains("_")) {
+            s = s.replaceAll("_", "\\\\_");
+        }
+        return s;
+    }
+}

+ 29 - 0
dc-comp/dc-mybatis/src/main/java/com/persagy/dc/mybatis/config/MyBatisWebConfigurer.java

@@ -0,0 +1,29 @@
+package com.persagy.fm.mybatis.config;
+
+import com.persagy.fm.mybatis.handler.DynamicDataSourceHandler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * MyBatis配置
+ * @author Charlie Yu
+ * @date 2021-03-29
+ */
+@Configuration
+@Order(2)
+public class MyBatisWebConfigurer implements WebMvcConfigurer {
+
+    @Bean
+    public DynamicDataSourceHandler dynamicDataSourceHandler() {
+        return new DynamicDataSourceHandler();
+    }
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        // 设置拦截的路径、不拦截的路径、优先级等等 -- 在contextHandler之后执行
+        registry.addInterceptor(dynamicDataSourceHandler()).order(20).addPathPatterns("/**");
+    }
+}

+ 36 - 0
dc-comp/dc-mybatis/src/main/java/com/persagy/dc/mybatis/config/MybatisPlusConfig.java

@@ -0,0 +1,36 @@
+package com.persagy.fm.mybatis.config;
+
+import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+/**
+ * <p>
+ * MybatisPlusConfig
+ * </p>
+ *
+ * @author lixing
+ * @description 由 Mybatisplus Code Generator 创建
+ * @since 2020-09-09 09:13:27
+ **/
+@Configuration
+@EnableTransactionManagement
+public class MybatisPlusConfig {
+    /**
+     * @param
+     * @description:配置分页插件 不配置分页插件,MP提供的分页方法会查询全部,分页不生效
+     * 使用实例:
+     * IPage<Member> memberPage = new Page<>(1, 2,false) true查询总记录数量
+     * @exception:
+     * @author: LuoGuangyi
+     * @company: Persagy Technology Co.,Ltd
+     * @return: com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor
+     * @since: 2020/09/30 10:33
+     * @version: V1.0
+     */
+    @Bean
+    public PaginationInterceptor paginationInterceptor() {
+        return new PaginationInterceptor();
+    }
+}

+ 17 - 0
dc-comp/dc-mybatis/src/main/java/com/persagy/dc/mybatis/dao/DbDao.java

@@ -0,0 +1,17 @@
+package com.persagy.fm.mybatis.dao;
+
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 数据库操作dao
+ * @author Charlie Yu
+ * @date 2021-03-30
+ */
+public interface DbDao {
+
+    /**
+     * 创建schema
+     * @param schema 实例名
+     */
+    void createDataBase(@Param("schema") String schema);
+}

+ 30 - 0
dc-comp/dc-mybatis/src/main/java/com/persagy/dc/mybatis/dao/SimpleTreeCodeDao.java

@@ -0,0 +1,30 @@
+package com.persagy.fm.mybatis.dao;
+
+import org.apache.ibatis.annotations.Param;
+
+
+/**
+ * 树编码使用的dao
+ * @author Charlie Yu
+ * @date 2021-03-29
+ */
+public interface SimpleTreeCodeDao {
+
+    /**
+     * 取得指定表指定树层次的最大编码
+     * @param tableName      表名
+     * @param parentCode     父节点编码
+     * @param innerCodeField 内码字段名
+     * @return 最大编码
+     */
+    String getMaxCode(@Param("tableName") String tableName, @Param("parentCode") String parentCode, @Param("innerCodeField") String innerCodeField);
+
+    /**
+     * 更新指定表的指定上级的实体的树编码
+     * @param tableName     表名
+     * @param parentCode    新的上级编码
+     * @param oldParentCode 旧的上级编码
+     * @param innerCodeField 内码字段名
+     */
+    void updateSubTreeCode(@Param("tableName") String tableName, @Param("parentCode") String parentCode, @Param("oldParentCode") String oldParentCode, @Param("innerCodeField") String innerCodeField);
+}

+ 0 - 0
dc-comp/dc-mybatis/src/main/java/com/persagy/dc/mybatis/handler/CommonMetaObjectHandler.java


Some files were not shown because too many files changed in this diff