Commit 150ccda62aa35cee64d38b9cca06a701b26ec68c

Authored by huanggang
0 parents

assistant project init

Showing 95 changed files with 4741 additions and 0 deletions

Too many changes to show.

To preserve performance only 95 of 181 files are displayed.

.gitignore 0 → 100644
  1 +++ a/.gitignore
  1 +# Compiled class file
  2 +*.class
  3 +
  4 +# Log file
  5 +*.log
  6 +
  7 +# BlueJ files
  8 +*.ctxt
  9 +
  10 +# Mobile Tools for Java (J2ME)
  11 +.mtj.tmp/
  12 +
  13 +# Package Files #
  14 +*.war
  15 +*.nar
  16 +*.ear
  17 +*.zip
  18 +*.tar.gz
  19 +*.rar
  20 +
  21 +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
  22 +hs_err_pid*
  23 +replay_pid*
... ...
README.md 0 → 100644
  1 +++ a/README.md
  1 +dili-uap
  2 + uap-shared 基础设施和共享模块(解决循环依赖问题,工具类引入,中间件服务mq/redis/mysql等配置,第三方框架的封装和配置,全局异常拦截配置)
  3 + uap-rpc 远程调用模块(openfeign集成,访问其他平台服务)
  4 + uap-boss 管理模块,维护用户/角色/权限等
  5 + uap-auth 用户认证授权模块,用于用户登陆和权限控制,集成uap-security权限框架
  6 + uap-security 参考spring-security自研的一套权限验证框架,用于uap及其他子系统进行用户认证授权,类似于uap sdk
  7 + uap-boot 父工程(springboot打包,系统对外提供开放接口)
  8 +
  9 +项目依赖
  10 +uap-shared -> etrade-rpc -> uap-boss -> uap-auth -> uap-boot
  11 +uap-security
  12 +
  13 +项目结构
  14 + com.diligrp.uap.xxxx - 模块spring配置xxxxConfiguration(Spring组件扫描配置/MybatisMapper扫描配置)ErrorCode Constants
  15 + com.diligrp.uap.xxxx.controller - 后台接口
  16 + com.diligrp.uap.xxxx.api - 移动端接口
  17 + com.diligrp.uap.xxxx.service
  18 + com.diligrp.uap.xxxx.dao
  19 + com.diligrp.uap.xxxx.exception
  20 + com.diligrp.uap.xxxx.domain
  21 + com.diligrp.uap.xxxx.model
  22 + com.diligrp.uap.xxxx.type
  23 + com.diligrp.uap.xxxx.util
  24 + resource/com.diligrp.uap.dao.mapper - mybatis mapper文件
  25 +
  26 + 系统对第三方系统提供接口通过uap-boot controller包
  27 + 所有数据模型类放入com.diligrp.uap.xxxx.model下,所有域模型类(VO DTO)放入com.diligrp.uap.xxxx.domain下
  28 + 所有数据模型类须继承BaseDo类,进一步规范数据表设计:需包含id version created_time modified_time
  29 + 所有枚举类型放入com.diligrp.uap.xxxx.type下,枚举类定义请提供code/name属性,参见com.diligrp.uap.shared.type.Gender
  30 + 所有自定义工具类放入com.diligrp.uap.xxxx.util下,如果大家都能公用请放uap-shared模块下
  31 + 所有异常类继承PlatformServiceException(提供了错误码和是否打印异常栈信息功能),并放入com.diligrp.uap.xxxx.exception下
  32 + 每个模块的常量类请放在模块根目录下,如通用常量请放入uap-shared模块下
  33 + 错误码为6位,每个模块的错误类ErrorCode且放入模块根目录,错误码应唯一且独特如前三位为模块标识,公共错误码参见com.diligrp.uap.shared.ErrorCode
  34 +
  35 +工具类
  36 + 参见:com.diligrp.uap.shared.util.* com.diligrp.uap.shared.security.*
  37 + 包括:JsonUtils CurrencyUtils DateUtils RandomUtils AssertUtils HexUtils AesCipher RsaCipher ShaCipher KeyStoreUtils等等
  38 +
  39 +技术要求
  40 + JDK17 SpringCould SpringBoot 3版本
  41 + 编译工具:gradle
  42 + 第三方库尽量使用springboot默认推荐,如:Jackson Lettuce;springboot工具集中没有推荐的第三方库,引入时请在合适模块中进行
  43 + 已在uap-shared中完成Jackson配置,包括Spring DataBinding,且额外提供了Jackson工具类JsonUtils
  44 + 已在uap-shared中已完成Redis基础配置Lettuce,可直接使用StringRedisTemplate,如需进行进一步封装配置请在合适的模块中配置,如需Redis分布式锁,可考虑引入Redission
  45 + 已在uap-shared中已完成Mybatis基础配置,使用MapperScan完成mapper文件的扫描,不用plus,可用mybatis分页插件
  46 + 已在uap-shared中完成MQ基础配置RabbitMQ,可直接进行使用RabbitTemplate且可进行Queue Exchange和消息监听器的配置
  47 + 外部第三方jar放入dili-uap/libs
  48 + 新技术框架的引入不以个人熟悉为重点考量标准,以技术框架的通用型和稳定性为考量标准
  49 +
  50 +数据库脚本要求
  51 + 维护全量(dili-uap/scripts)和增量脚本(scripts/upgrade)
  52 + 维护增量脚本,需同时修改权量脚本
  53 + 所有建表SQL,每个字段需填写备注
  54 + 通常情况下,每个表都需要包含三个字段id,version,created_time,modified_time
  55 + 每个模块的数据表,建议统一的前缀uap_****
0 56 \ No newline at end of file
... ...
assistant-boot/build.gradle 0 → 100644
  1 +++ a/assistant-boot/build.gradle
  1 +group = 'com.diligrp'
  2 +archivesBaseName = 'assistant-boot'
  3 +
  4 +jar.enabled = false
  5 +bootJar {
  6 + enabled = true
  7 + archiveBaseName = 'assistant-boot'
  8 +}
  9 +
  10 +dependencies {
  11 + implementation project(':assistant-dfs')
  12 + implementation project(':assistant-sms')
  13 + implementation project(':assistant-data')
  14 + implementation project(':assistant-product')
  15 + implementation project(':assistant-logging')
  16 + implementation project(':assistant-uid')
  17 + implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery:2022.0.0.0-RC2'
  18 + implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config:2022.0.0.0-RC2'
  19 +}
0 20 \ No newline at end of file
... ...
assistant-boot/src/main/java/com/diligrp/assistant/boot/AssistantServiceBootstrap.java 0 → 100644
  1 +++ a/assistant-boot/src/main/java/com/diligrp/assistant/boot/AssistantServiceBootstrap.java
  1 +package com.diligrp.assistant.boot;
  2 +
  3 +import com.diligrp.assistant.data.DataConfiguration;
  4 +import com.diligrp.assistant.dfs.DfsConfiguration;
  5 +import com.diligrp.assistant.logging.LoggingConfiguration;
  6 +import com.diligrp.assistant.product.ProductConfiguration;
  7 +import com.diligrp.assistant.shared.SharedConfiguration;
  8 +import com.diligrp.assistant.sms.SmsConfiguration;
  9 +import com.diligrp.assistant.uid.UidConfiguration;
  10 +import org.springframework.boot.SpringApplication;
  11 +import org.springframework.boot.SpringBootConfiguration;
  12 +import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  13 +import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  14 +import org.springframework.context.annotation.Import;
  15 +
  16 +@SpringBootConfiguration
  17 +@EnableAutoConfiguration
  18 +@Import({BootConfiguration.class, SharedConfiguration.class, DfsConfiguration.class, SmsConfiguration.class,
  19 + DataConfiguration.class, ProductConfiguration.class, LoggingConfiguration.class, UidConfiguration.class})
  20 +@EnableDiscoveryClient
  21 +public class AssistantServiceBootstrap {
  22 + public static void main(String[] args) {
  23 + SpringApplication.run(AssistantServiceBootstrap.class, args);
  24 + }
  25 +}
0 26 \ No newline at end of file
... ...
assistant-boot/src/main/java/com/diligrp/assistant/boot/BootConfiguration.java 0 → 100644
  1 +++ a/assistant-boot/src/main/java/com/diligrp/assistant/boot/BootConfiguration.java
  1 +package com.diligrp.assistant.boot;
  2 +
  3 +import org.springframework.context.annotation.ComponentScan;
  4 +import org.springframework.context.annotation.Configuration;
  5 +
  6 +@Configuration
  7 +@ComponentScan("com.diligrp.assistant.boot")
  8 +public class BootConfiguration {
  9 +}
... ...
assistant-boot/src/main/java/com/diligrp/assistant/boot/controller/TestOpenApiController.java 0 → 100644
  1 +++ a/assistant-boot/src/main/java/com/diligrp/assistant/boot/controller/TestOpenApiController.java
  1 +package com.diligrp.assistant.boot.controller;
  2 +
  3 +import jakarta.servlet.http.HttpServletRequest;
  4 +import org.springframework.web.bind.annotation.RequestMapping;
  5 +import org.springframework.web.bind.annotation.RestController;
  6 +
  7 +@RestController
  8 +@RequestMapping(value = "/assistant")
  9 +public class TestOpenApiController {
  10 + @RequestMapping(value = "/test.do")
  11 + public String staticTest(HttpServletRequest request) {
  12 + return "I'm test url";
  13 + }
  14 +}
... ...
assistant-boot/src/main/resources/application.properties 0 → 100644
  1 +++ a/assistant-boot/src/main/resources/application.properties
  1 +#Logback configuration
  2 +logging.config=classpath:logback-spring.xml
  3 +
  4 +#Mybatis configuration
  5 +mybatis.configuration.cache-enabled=true
  6 +mybatis.configuration.multiple-result-sets-enabled=true
  7 +mybatis.configuration.map-underscore-to-camel-case=true
  8 +mybatis.configuration.use-column-label=true
  9 +mybatis.configuration.default-statement-timeout=25000
  10 +mybatis.mapper-locations=classpath*:com/diligrp/assistant/dao/mapper/**/*.xml
  11 +mybatis.configuration.default-enum-type-handler=com.diligrp.assistant.shared.mybatis.GenericEnumTypeHandler
  12 +
  13 +#Datasource configuration
  14 +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  15 +spring.datasource.url=jdbc:mysql://mysql.diligrp.com:3306/dili_assistant?useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2B8
  16 +spring.datasource.username=root
  17 +spring.datasource.password=123456
  18 +spring.datasource.type=com.zaxxer.hikari.HikariDataSource
  19 +spring.datasource.hikari.pool-name=AssistantHikariPool
  20 +spring.datasource.hikari.minimum-idle=4
  21 +spring.datasource.hikari.maximum-pool-size=60
  22 +spring.datasource.hikari.idle-timeout=120000
  23 +spring.datasource.hikari.max-lifetime=900000
  24 +spring.datasource.hikari.connection-timeout=15000
  25 +spring.datasource.hikari.connection-test-query=SELECT 1
  26 +
  27 +#Redis configuration
  28 +spring.data.redis.host=redis.diligrp.com
  29 +spring.data.redis.port=6379
  30 +spring.data.redis.database=8
  31 +#spring.data.redis.username=
  32 +#spring.data.redis.password=
  33 +spring.data.redis.connect-timeout=15000
  34 +spring.data.redis.timeout=30000
  35 +#spring.data.redis.lettuce.pool.enabled=false
  36 +
  37 +#RabbitMQ configuration
  38 +spring.rabbitmq.host=rabbitmq.diligrp.com
  39 +spring.rabbitmq.port=5672
  40 +spring.rabbitmq.username=admin
  41 +spring.rabbitmq.password=123456
  42 +spring.rabbitmq.virtual-host=/
  43 +
  44 +#DFS configuration
  45 +spring.servlet.multipart.max-file-size=40MB
  46 +spring.servlet.multipart.max-request-size=100MB
  47 +spring.servlet.multipart.file-size-threshold=20MB
  48 +dfs.private-key=MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAvRtBBrQX5di1jQPbUh+Lu5pMwrg6/H9/XX7qBU7dsGA/yygQAH7AYb/fpHQ1GQDolU3LVgYt3IE43QacLo09MwIDAQABAkAJ8U5kb8e0U2J+CmIJedRZO0GtX+MeD1uX51iCNJqYvbI/tKAgqd9ulc07it7tW0vGhDDj+WaVLp1R5D7bgRcpAiEA6Vc1xjoMYmT+OL+DZfipOeMTUwEePCg0Eq8DnVtalgsCIQDPeGSQ+lVijjNTEF7swM6rH5Ofa1E+ry5VRAw1ywI2eQIgdNFuYIErNg9tnqdydxiYUBy4zfNfWaqe90ObQao8naUCIQComhNIClgXZq5pA3XQ+wM458llFaaJxX1mx40QrjDXKQIgB+x7Fz2MT/GdIUhN6s1Rpfb5IIAR51ztiVEJlJ+wpdo=
  49 +dfs.public-key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL0bQQa0F+XYtY0D21Ifi7uaTMK4Ovx/f11+6gVO3bBgP8soEAB+wGG/36R0NRkA6JVNy1YGLdyBON0GnC6NPTMCAwEAAQ==
  50 +dfs.oss.uri=https://oss-cn-hangzhou.aliyuncs.com
  51 +dfs.oss.access-key-id=LTAI5tS2xUrriUfti9pvDTwM
  52 +dfs.oss.access-key-secret=NIa9FQMDc5zSAtvfOeOK4J1dp7aotg
  53 +
  54 +#Sms configuration
  55 +sms.private-key=MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAvRtBBrQX5di1jQPbUh+Lu5pMwrg6/H9/XX7qBU7dsGA/yygQAH7AYb/fpHQ1GQDolU3LVgYt3IE43QacLo09MwIDAQABAkAJ8U5kb8e0U2J+CmIJedRZO0GtX+MeD1uX51iCNJqYvbI/tKAgqd9ulc07it7tW0vGhDDj+WaVLp1R5D7bgRcpAiEA6Vc1xjoMYmT+OL+DZfipOeMTUwEePCg0Eq8DnVtalgsCIQDPeGSQ+lVijjNTEF7swM6rH5Ofa1E+ry5VRAw1ywI2eQIgdNFuYIErNg9tnqdydxiYUBy4zfNfWaqe90ObQao8naUCIQComhNIClgXZq5pA3XQ+wM458llFaaJxX1mx40QrjDXKQIgB+x7Fz2MT/GdIUhN6s1Rpfb5IIAR51ztiVEJlJ+wpdo=
  56 +sms.public-key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL0bQQa0F+XYtY0D21Ifi7uaTMK4Ovx/f11+6gVO3bBgP8soEAB+wGG/36R0NRkA6JVNy1YGLdyBON0GnC6NPTMCAwEAAQ==
  57 +sms.smschinese.uri=http://utf8.api.smschinese.cn
  58 +sms.smschinese.uid=zhuxuegang@diligrp.com
  59 +sms.smschinese.secret-key=c0978121c3893cf9ddbc
0 60 \ No newline at end of file
... ...
assistant-boot/src/main/resources/bootstrap.properties 0 → 100644
  1 +++ a/assistant-boot/src/main/resources/bootstrap.properties
  1 +server.port=8080
  2 +server.servlet.context-path=/
  3 +server.servlet.encoding.charset=UTF-8
  4 +server.servlet.encoding.force=true
  5 +
  6 +spring.profiles.active=dev
  7 +spring.application.name=uap-service
  8 +
  9 +spring.cloud.nacos.discovery.enabled=true
  10 +spring.cloud.nacos.discovery.group=WEB_APPLICATION
  11 +spring.cloud.nacos.discovery.server-addr=nacos.diligrp.com:8848
  12 +spring.cloud.nacos.discovery.namespace=54c39cfe-d1c4-4022-a94b-a3486c5927fc
  13 +
  14 +spring.cloud.nacos.config.enabled=true
  15 +spring.cloud.nacos.config.group=WEB_APPLICATION
  16 +spring.cloud.nacos.config.server-addr=nacos.diligrp.com:8848
  17 +spring.cloud.nacos.config.namespace=54c39cfe-d1c4-4022-a94b-a3486c5927fc
0 18 \ No newline at end of file
... ...
assistant-boot/src/main/resources/logback-spring.xml 0 → 100644
  1 +++ a/assistant-boot/src/main/resources/logback-spring.xml
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<configuration>
  3 + <!-- 日志名称 -->
  4 + <property name="LOG_NAME" value="uap-boot" />
  5 + <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 -->
  6 + <property name="LOG_HOME" value="logs" />
  7 +
  8 + <!-- springProperty读取springboot配置属性 -->
  9 + <springProperty scope="context" name="build.profile.id" source="spring.profiles.active" />
  10 +
  11 + <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
  12 +
  13 + <!-- 日志控制台输出 -->
  14 + <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
  15 + <encoder>
  16 + <pattern>%d %-5level [${LOG_NAME}-${build.profile.id}] [%t] [%c:%L] -| %msg%n</pattern>
  17 + </encoder>
  18 + </appender>
  19 +
  20 + <!-- 日志文件输出 -->
  21 + <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  22 + <file>${LOG_HOME}/${LOG_NAME}.log</file>
  23 + <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
  24 + <!--日志文件输出的文件名 -->
  25 + <fileNamePattern>${LOG_HOME}/%d{yyyy-MM-dd}/${LOG_NAME}_%i.log.zip</fileNamePattern>
  26 + <!--日志文件保留天数(FileNamePattern中的%d 格式有关,如果yyyy-MM-dd 则是天数) -->
  27 + <maxHistory>10</maxHistory>
  28 + <!--日志文件最大的大小 -->
  29 + <maxFileSize>10MB</maxFileSize>
  30 + </rollingPolicy>
  31 + <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  32 + <pattern>%d %-5level [${LOG_NAME}-${build.profile.id}] [%t] [%c:%L]-| %msg%n</pattern>
  33 + </encoder>
  34 + </appender>
  35 +
  36 + <!-- 开发环境 -->
  37 + <springProfile name="dev">
  38 + <root level="INFO">
  39 + <appender-ref ref="CONSOLE" />
  40 + </root>
  41 +
  42 + <logger name="com.diligrp.assistant" level="DEBUG" additivity="false">
  43 + <appender-ref ref="CONSOLE" />
  44 + </logger>
  45 +
  46 + <logger name="com.alibaba" level="ERROR" additivity="false">
  47 + <appender-ref ref="CONSOLE" />
  48 + </logger>
  49 + </springProfile>
  50 +
  51 + <!-- 测试环境 -->
  52 + <springProfile name="test">
  53 + <root level="INFO">
  54 + <appender-ref ref="CONSOLE" />
  55 + </root>
  56 +
  57 + <logger name="com.diligrp.assistant" level="DEBUG">
  58 + <appender-ref ref="FILE" />
  59 + </logger>
  60 +
  61 + <logger name="com.alibaba" level="ERROR" additivity="false">
  62 + <appender-ref ref="CONSOLE" />
  63 + <appender-ref ref="FILE" />
  64 + </logger>
  65 + </springProfile>
  66 +
  67 + <!-- 灰度、生产环境 -->
  68 + <springProfile name="pre,prod">
  69 + <root level="INFO">
  70 + <appender-ref ref="CONSOLE" />
  71 + </root>
  72 +
  73 + <logger name="com.diligrp.assistant" level="DEBUG">
  74 + <appender-ref ref="FILE" />
  75 + </logger>
  76 +
  77 + <logger name="com.alibaba" level="ERROR" additivity="false">
  78 + <appender-ref ref="CONSOLE" />
  79 + <appender-ref ref="FILE" />
  80 + </logger>
  81 +
  82 + <!-- 单独给子模块指定日志配置时,请注意additivity的使用
  83 + <logger name="com.diligrp.assistant.boss" level="debug" additivity="false">
  84 + <appender-ref ref="CONSOLE" />
  85 + <appender-ref ref="FILE" />
  86 + </logger>
  87 + -->
  88 + </springProfile>
  89 +</configuration>
0 90 \ No newline at end of file
... ...
assistant-data/build.gradle 0 → 100644
  1 +++ a/assistant-data/build.gradle
  1 +group = 'com.diligrp'
  2 +archivesBaseName = 'assistant-data'
  3 +
  4 +dependencies {
  5 + api project(':assistant-shared')
  6 +}
0 7 \ No newline at end of file
... ...
assistant-data/src/main/java/com/diligrp/assistant/data/DataConfiguration.java 0 → 100644
  1 +++ a/assistant-data/src/main/java/com/diligrp/assistant/data/DataConfiguration.java
  1 +package com.diligrp.assistant.data;
  2 +
  3 +import com.diligrp.assistant.shared.mybatis.MybatisMapperSupport;
  4 +import org.mybatis.spring.annotation.MapperScan;
  5 +import org.springframework.context.annotation.ComponentScan;
  6 +import org.springframework.context.annotation.Configuration;
  7 +
  8 +@Configuration
  9 +@ComponentScan("com.diligrp.assistant.data")
  10 +@MapperScan(basePackages = {"com.diligrp.assistant.data.dao"}, markerInterface = MybatisMapperSupport.class)
  11 +public class DataConfiguration {
  12 +}
... ...
assistant-data/src/main/java/com/diligrp/assistant/data/controller/DataDictionaryController.java 0 → 100644
  1 +++ a/assistant-data/src/main/java/com/diligrp/assistant/data/controller/DataDictionaryController.java
  1 +package com.diligrp.assistant.data.controller;
  2 +
  3 +import com.diligrp.assistant.data.domain.DataDictionaryDTO;
  4 +import com.diligrp.assistant.data.model.DataDictionary;
  5 +import com.diligrp.assistant.data.service.DataDictionaryService;
  6 +import com.diligrp.assistant.shared.domain.Message;
  7 +import com.diligrp.assistant.shared.util.AssertUtils;
  8 +import jakarta.annotation.Resource;
  9 +import org.springframework.web.bind.annotation.RequestBody;
  10 +import org.springframework.web.bind.annotation.RequestMapping;
  11 +import org.springframework.web.bind.annotation.RestController;
  12 +
  13 +import java.time.LocalDateTime;
  14 +import java.util.List;
  15 +import java.util.stream.Collectors;
  16 +
  17 +@RestController
  18 +@RequestMapping(value = "/dictionary")
  19 +public class DataDictionaryController {
  20 +
  21 + @Resource
  22 + private DataDictionaryService dataDictionaryService;
  23 +
  24 + @RequestMapping(value = "/save.do")
  25 + public Message save(@RequestBody DataDictionaryDTO request) {
  26 + AssertUtils.notNull(request.getType(), "type missed");
  27 + AssertUtils.notEmpty(request.getGroupCode(), "groupCode missed");
  28 + AssertUtils.notEmpty(request.getCode(), "code missed");
  29 + AssertUtils.notEmpty(request.getValue(), "value missed");
  30 +
  31 + LocalDateTime now = LocalDateTime.now();
  32 + DataDictionary.Builder dictionary = DataDictionary.builder();
  33 + dictionary.type(request.getType()).groupCode(request.getGroupCode()).code(request.getCode())
  34 + .name(request.getName()).value(request.getValue()).description(request.getDescription())
  35 + .createdTime(now).modifiedTime(now);
  36 + dataDictionaryService.insertDataDictionary(dictionary.build());
  37 + return Message.success();
  38 + }
  39 +
  40 + @RequestMapping(value = "/findByCode.do")
  41 + public Message<DataDictionaryDTO> findByCode(@RequestBody DataDictionaryDTO request) {
  42 + AssertUtils.notEmpty(request.getGroupCode(), "groupCode missed");
  43 + AssertUtils.notEmpty(request.getCode(), "code missed");
  44 +
  45 + DataDictionary dictionary = dataDictionaryService.findDataDictionaryByCode(request.getGroupCode(), request.getCode());
  46 + return Message.success(DataDictionaryDTO.from(dictionary.getType(), dictionary.getGroupCode(),
  47 + dictionary.getCode(), dictionary.getName(), dictionary.getValue(), dictionary.getDescription()));
  48 + }
  49 +
  50 + @RequestMapping(value = "/findByGroupCode.do")
  51 + public Message<List<DataDictionaryDTO>> findByGroupCode(@RequestBody DataDictionaryDTO request) {
  52 + AssertUtils.notEmpty(request.getGroupCode(), "groupCode missed");
  53 +
  54 + List<DataDictionary> dictionaries = dataDictionaryService.findDataDictionaries(request.getGroupCode());
  55 + List<DataDictionaryDTO> data = dictionaries.stream().map(dictionary -> DataDictionaryDTO.from(dictionary.getType(),
  56 + dictionary.getGroupCode(), dictionary.getCode(), dictionary.getName(), dictionary.getValue(),
  57 + dictionary.getDescription())).collect(Collectors.toList());
  58 +
  59 + return Message.success(data);
  60 + }
  61 +
  62 + @RequestMapping(value = "/update.do")
  63 + public Message update(@RequestBody DataDictionaryDTO request) {
  64 + AssertUtils.notEmpty(request.getGroupCode(), "groupCode missed");
  65 + AssertUtils.notEmpty(request.getCode(), "code missed");
  66 + AssertUtils.notEmpty(request.getValue(), "value missed");
  67 +
  68 + DataDictionary.Builder dictionary = DataDictionary.builder().groupCode(request.getGroupCode())
  69 + .code(request.getCode()).name(request.getName()).value(request.getValue())
  70 + .description(request.getDescription()).modifiedTime(LocalDateTime.now());
  71 + dataDictionaryService.updateDataDictionary(dictionary.build());
  72 +
  73 + return Message.success();
  74 + }
  75 +}
... ...
assistant-data/src/main/java/com/diligrp/assistant/data/controller/DataDistrictController.java 0 → 100644
  1 +++ a/assistant-data/src/main/java/com/diligrp/assistant/data/controller/DataDistrictController.java
  1 +package com.diligrp.assistant.data.controller;
  2 +
  3 +import com.diligrp.assistant.data.domain.DataDistrictDTO;
  4 +import com.diligrp.assistant.data.domain.DistrictPageQuery;
  5 +import com.diligrp.assistant.data.domain.ListDataDistrict;
  6 +import com.diligrp.assistant.data.model.DataDistrict;
  7 +import com.diligrp.assistant.data.service.DataDistrictService;
  8 +import com.diligrp.assistant.shared.domain.Message;
  9 +import com.diligrp.assistant.shared.util.AssertUtils;
  10 +import jakarta.annotation.Resource;
  11 +import org.springframework.web.bind.annotation.RequestBody;
  12 +import org.springframework.web.bind.annotation.RequestMapping;
  13 +import org.springframework.web.bind.annotation.RequestParam;
  14 +import org.springframework.web.bind.annotation.RestController;
  15 +
  16 +import java.util.List;
  17 +import java.util.stream.Collectors;
  18 +
  19 +@RestController
  20 +@RequestMapping(value = "/api/district")
  21 +public class DataDistrictController {
  22 +
  23 + @Resource
  24 + private DataDistrictService dataDistrictService;
  25 +
  26 + @RequestMapping(value = "/findById.do")
  27 + public Message<DataDistrictDTO> findById(@RequestParam("id") Long id) {
  28 + DataDistrict district = dataDistrictService.findDataDistrictById(id);
  29 + return Message.success(DataDistrictDTO.of(district.getId(), district.getParentId(),
  30 + district.getName(), district.getLevel(), district.getFullName(), district.getPath()));
  31 + }
  32 +
  33 + @RequestMapping(value = "/findParentById.do")
  34 + public Message<DataDistrictDTO> findParentById(@RequestParam("id") Long id) {
  35 + DataDistrict district = dataDistrictService.findParentDistrictById(id);
  36 + return Message.success(DataDistrictDTO.of(district.getId(), district.getParentId(),
  37 + district.getName(), district.getLevel(), district.getFullName(), district.getPath()));
  38 + }
  39 +
  40 + @RequestMapping(value = "/listParentsById.do")
  41 + public Message<List<DataDistrictDTO>> listParentsById(@RequestParam("id") Long id) {
  42 + List<DataDistrictDTO> districts = dataDistrictService.listParentsById(id).stream().map(d ->
  43 + DataDistrictDTO.of(d.getId(), d.getParentId(), d.getName(), d.getLevel(), d.getFullName(), d.getPath()))
  44 + .collect(Collectors.toList());
  45 + return Message.success(districts);
  46 + }
  47 +
  48 + @RequestMapping(value = "/listChildrenById.do")
  49 + public Message<List<DataDistrictDTO>> listChildrenById(@RequestBody ListDataDistrict request) {
  50 + AssertUtils.notNull(request.getId(), "id missed");
  51 + AssertUtils.notNull(request.getPageNo(), "pageNo missed");
  52 + AssertUtils.notNull(request.getPageSize(), "pageSize missed");
  53 +
  54 + DistrictPageQuery query = new DistrictPageQuery();
  55 + query.setId(request.getId());
  56 + query.from(request.getPageNo(), request.getPageSize());
  57 +
  58 + List<DataDistrictDTO> districts = dataDistrictService.listChildrenById(query).stream().map(d ->
  59 + DataDistrictDTO.of(d.getId(), d.getParentId(), d.getName(), d.getLevel(), d.getFullName(), d.getPath()))
  60 + .collect(Collectors.toList());
  61 + return Message.success(districts);
  62 + }
  63 +
  64 + @RequestMapping(value = "/listByLevel.do")
  65 + public Message<List<DataDistrictDTO>> listByLevel(@RequestBody ListDataDistrict request) {
  66 + AssertUtils.notNull(request.getLevel(), "level missed");
  67 + AssertUtils.notNull(request.getPageNo(), "pageNo missed");
  68 + AssertUtils.notNull(request.getPageSize(), "pageSize missed");
  69 +
  70 + DistrictPageQuery query = new DistrictPageQuery();
  71 + query.setLevel(request.getLevel());
  72 + query.from(request.getPageNo(), request.getPageSize());
  73 +
  74 + List<DataDistrictDTO> districts = dataDistrictService.listDataDistrictsByLevel(query).stream().map(d ->
  75 + DataDistrictDTO.of(d.getId(), d.getParentId(), d.getName(), d.getLevel(), d.getFullName(), d.getPath()))
  76 + .collect(Collectors.toList());
  77 + return Message.success(districts);
  78 + }
  79 +
  80 + @RequestMapping(value = "/listByDistance.do")
  81 + public Message<List<DataDistrictDTO>> listByDistance(@RequestBody ListDataDistrict request) {
  82 + AssertUtils.notEmpty(request.getLongitude(), "longitude missed");
  83 + AssertUtils.notEmpty(request.getLatitude(), "latitude missed");
  84 + AssertUtils.notNull(request.getPageNo(), "pageNo missed");
  85 + AssertUtils.notNull(request.getPageSize(), "pageSize missed");
  86 +
  87 + DistrictPageQuery query = new DistrictPageQuery();
  88 + query.setLongitude(request.getLongitude());
  89 + query.setLatitude(request.getLatitude());
  90 + query.from(request.getPageNo(), request.getPageSize());
  91 +
  92 + List<DataDistrictDTO> districts = dataDistrictService.listDataDistrictsByDistance(query).stream().map(d ->
  93 + DataDistrictDTO.of(d.getId(), d.getParentId(), d.getName(), d.getLevel(), d.getFullName(), d.getPath()))
  94 + .collect(Collectors.toList());
  95 + return Message.success(districts);
  96 + }
  97 +}
... ...
assistant-data/src/main/java/com/diligrp/assistant/data/dao/DataDictionaryDao.java 0 → 100644
  1 +++ a/assistant-data/src/main/java/com/diligrp/assistant/data/dao/DataDictionaryDao.java
  1 +package com.diligrp.assistant.data.dao;
  2 +
  3 +import com.diligrp.assistant.data.model.DataDictionary;
  4 +import com.diligrp.assistant.shared.mybatis.MybatisMapperSupport;
  5 +import org.apache.ibatis.annotations.Param;
  6 +import org.springframework.stereotype.Repository;
  7 +
  8 +import java.util.List;
  9 +import java.util.Optional;
  10 +
  11 +/**
  12 + * 数据字典设计:参数配置唯一约束:code + group_code, 允许出现重复code的配置参数;但不允许
  13 + * 在某个参数分组中(group_code)重复出现code的配置参数,这样设计的目的是允许不同商户下某个参数
  14 + * 配置有不同的值; 根据业务场景使用不同的数据字典API(返回一条还是多条字典配置)
  15 + */
  16 +@Repository("dataDictionaryDao")
  17 +public interface DataDictionaryDao extends MybatisMapperSupport {
  18 + /**
  19 + * 新增数据字典配置
  20 + *
  21 + * @param dictionary - 数据字典
  22 + */
  23 + void insertDataDictionary(DataDictionary dictionary);
  24 +
  25 + /**
  26 + * 根据编码(groupCode和Code)查询数据字典配置
  27 + *
  28 + * @param groupCode - 分组编码,必填
  29 + * @param code - 参数编码,必填
  30 + * @return DataDictionary - 查询结果大于一条记录将抛出异常
  31 + */
  32 + Optional<DataDictionary> findDataDictionaryByCode(@Param("groupCode") String groupCode, @Param("code") String code);
  33 +
  34 + /**
  35 + * 根据编码查询数据字典列表
  36 + *
  37 + * @param groupCode - 分组编码,非必填
  38 + * @return List<DataDictionary> - 数据字典列表
  39 + */
  40 + List<DataDictionary> findDataDictionaries(@Param("groupCode") String groupCode);
  41 +
  42 + /**
  43 + * 根据编码(groupCode和Code)修改数据字典配置
  44 + *
  45 + * @param dictionary - 数据字典
  46 + * @return int - 更新条数
  47 + */
  48 + int updateDataDictionary(DataDictionary dictionary);
  49 +}
... ...
assistant-data/src/main/java/com/diligrp/assistant/data/dao/DataDistrictDao.java 0 → 100644
  1 +++ a/assistant-data/src/main/java/com/diligrp/assistant/data/dao/DataDistrictDao.java
  1 +package com.diligrp.assistant.data.dao;
  2 +
  3 +import com.diligrp.assistant.data.domain.DistrictPageQuery;
  4 +import com.diligrp.assistant.data.model.DataDistrict;
  5 +import com.diligrp.assistant.shared.mybatis.MybatisMapperSupport;
  6 +import org.springframework.stereotype.Repository;
  7 +
  8 +import java.util.List;
  9 +import java.util.Optional;
  10 +
  11 +@Repository("dataDistrictDao")
  12 +public interface DataDistrictDao extends MybatisMapperSupport {
  13 + Optional<DataDistrict> findDataDistrictById(Long id);
  14 +
  15 + Optional<DataDistrict> findParentDistrictById(Long id);
  16 +
  17 + List<DataDistrict> findDataDistrictsByIds(List<Long> ids);
  18 +
  19 + List<DataDistrict> listChildrenById(DistrictPageQuery query);
  20 +
  21 + List<DataDistrict> listDataDistrictsByLevel(DistrictPageQuery query);
  22 +
  23 + List<DataDistrict> listDataDistrictsByDistance(DistrictPageQuery query);
  24 +}
... ...
assistant-data/src/main/java/com/diligrp/assistant/data/domain/DataDictionaryDTO.java 0 → 100644
  1 +++ a/assistant-data/src/main/java/com/diligrp/assistant/data/domain/DataDictionaryDTO.java
  1 +package com.diligrp.assistant.data.domain;
  2 +
  3 +public class DataDictionaryDTO {
  4 + // 类型
  5 + private Integer type;
  6 + // 分组编码
  7 + private String groupCode;
  8 + // 编码
  9 + private String code;
  10 + // 名称
  11 + private String name;
  12 + // 字典值
  13 + private String value;
  14 + // 描述
  15 + private String description;
  16 +
  17 + public static DataDictionaryDTO from(Integer type, String groupCode, String code, String name,
  18 + String value, String description) {
  19 + DataDictionaryDTO dictionary = new DataDictionaryDTO();
  20 + dictionary.type = type;
  21 + dictionary.groupCode = groupCode;
  22 + dictionary.code = code;
  23 + dictionary.name = name;
  24 + dictionary.value = value;
  25 + dictionary.description = description;
  26 + return dictionary;
  27 + }
  28 +
  29 + public Integer getType() {
  30 + return type;
  31 + }
  32 +
  33 + public void setType(Integer type) {
  34 + this.type = type;
  35 + }
  36 +
  37 + public String getGroupCode() {
  38 + return groupCode;
  39 + }
  40 +
  41 + public void setGroupCode(String groupCode) {
  42 + this.groupCode = groupCode;
  43 + }
  44 +
  45 + public String getCode() {
  46 + return code;
  47 + }
  48 +
  49 + public void setCode(String code) {
  50 + this.code = code;
  51 + }
  52 +
  53 + public String getName() {
  54 + return name;
  55 + }
  56 +
  57 + public void setName(String name) {
  58 + this.name = name;
  59 + }
  60 +
  61 + public String getValue() {
  62 + return value;
  63 + }
  64 +
  65 + public void setValue(String value) {
  66 + this.value = value;
  67 + }
  68 +
  69 + public String getDescription() {
  70 + return description;
  71 + }
  72 +
  73 + public void setDescription(String description) {
  74 + this.description = description;
  75 + }
  76 +}
... ...
assistant-data/src/main/java/com/diligrp/assistant/data/domain/DataDistrictDTO.java 0 → 100644
  1 +++ a/assistant-data/src/main/java/com/diligrp/assistant/data/domain/DataDistrictDTO.java
  1 +package com.diligrp.assistant.data.domain;
  2 +
  3 +public class DataDistrictDTO {
  4 + // ID
  5 + private Long id;
  6 + // 父区域ID
  7 + private Long parentId;
  8 + // 名称
  9 + private String name;
  10 + // 级别
  11 + private Integer level;
  12 + // 全称
  13 + private String fullName;
  14 + // 路径
  15 + private String path;
  16 +
  17 + public static DataDistrictDTO of(Long id, Long parentId, String name, Integer level, String fullName, String path) {
  18 + DataDistrictDTO district = new DataDistrictDTO();
  19 + district.id = id;
  20 + district.parentId = parentId;
  21 + district.name = name;
  22 + district.level = level;
  23 + district.fullName = fullName;
  24 + district.path = path;
  25 +
  26 + return district;
  27 + }
  28 +
  29 + public Long getId() {
  30 + return id;
  31 + }
  32 +
  33 + public void setId(Long id) {
  34 + this.id = id;
  35 + }
  36 +
  37 + public Long getParentId() {
  38 + return parentId;
  39 + }
  40 +
  41 + public void setParentId(Long parentId) {
  42 + this.parentId = parentId;
  43 + }
  44 +
  45 + public String getName() {
  46 + return name;
  47 + }
  48 +
  49 + public void setName(String name) {
  50 + this.name = name;
  51 + }
  52 +
  53 + public Integer getLevel() {
  54 + return level;
  55 + }
  56 +
  57 + public void setLevel(Integer level) {
  58 + this.level = level;
  59 + }
  60 +
  61 + public String getFullName() {
  62 + return fullName;
  63 + }
  64 +
  65 + public void setFullName(String fullName) {
  66 + this.fullName = fullName;
  67 + }
  68 +
  69 + public String getPath() {
  70 + return path;
  71 + }
  72 +
  73 + public void setPath(String path) {
  74 + this.path = path;
  75 + }
  76 +}
... ...
assistant-data/src/main/java/com/diligrp/assistant/data/domain/DistrictPageQuery.java 0 → 100644
  1 +++ a/assistant-data/src/main/java/com/diligrp/assistant/data/domain/DistrictPageQuery.java
  1 +package com.diligrp.assistant.data.domain;
  2 +
  3 +import com.diligrp.assistant.shared.domain.PageQuery;
  4 +
  5 +public class DistrictPageQuery extends PageQuery {
  6 + // 区域ID
  7 + private Long id;
  8 + // 区域级别
  9 + private Integer level;
  10 + // 经度
  11 + private String longitude;
  12 + // 纬度
  13 + private String latitude;
  14 +
  15 + public Long getId() {
  16 + return id;
  17 + }
  18 +
  19 + public void setId(Long id) {
  20 + this.id = id;
  21 + }
  22 +
  23 + public Integer getLevel() {
  24 + return level;
  25 + }
  26 +
  27 + public void setLevel(Integer level) {
  28 + this.level = level;
  29 + }
  30 +
  31 + public String getLongitude() {
  32 + return longitude;
  33 + }
  34 +
  35 + public void setLongitude(String longitude) {
  36 + this.longitude = longitude;
  37 + }
  38 +
  39 + public String getLatitude() {
  40 + return latitude;
  41 + }
  42 +
  43 + public void setLatitude(String latitude) {
  44 + this.latitude = latitude;
  45 + }
  46 +}
... ...
assistant-data/src/main/java/com/diligrp/assistant/data/domain/ListDataDistrict.java 0 → 100644
  1 +++ a/assistant-data/src/main/java/com/diligrp/assistant/data/domain/ListDataDistrict.java
  1 +package com.diligrp.assistant.data.domain;
  2 +
  3 +public class ListDataDistrict {
  4 + // 页号
  5 + private Integer pageNo = 1;
  6 + // 每页记录数
  7 + private Integer pageSize = 20;
  8 +
  9 + // 区域ID
  10 + private Long id;
  11 + // 区域级别
  12 + private Integer level;
  13 + // 经度
  14 + private String longitude;
  15 + // 纬度
  16 + private String latitude;
  17 +
  18 + public Integer getPageNo() {
  19 + return pageNo;
  20 + }
  21 +
  22 + public void setPageNo(Integer pageNo) {
  23 + this.pageNo = pageNo;
  24 + }
  25 +
  26 + public Integer getPageSize() {
  27 + return pageSize;
  28 + }
  29 +
  30 + public void setPageSize(Integer pageSize) {
  31 + this.pageSize = pageSize;
  32 + }
  33 +
  34 + public Long getId() {
  35 + return id;
  36 + }
  37 +
  38 + public void setId(Long id) {
  39 + this.id = id;
  40 + }
  41 +
  42 + public Integer getLevel() {
  43 + return level;
  44 + }
  45 +
  46 + public void setLevel(Integer level) {
  47 + this.level = level;
  48 + }
  49 +
  50 + public String getLongitude() {
  51 + return longitude;
  52 + }
  53 +
  54 + public void setLongitude(String longitude) {
  55 + this.longitude = longitude;
  56 + }
  57 +
  58 + public String getLatitude() {
  59 + return latitude;
  60 + }
  61 +
  62 + public void setLatitude(String latitude) {
  63 + this.latitude = latitude;
  64 + }
  65 +}
... ...
assistant-data/src/main/java/com/diligrp/assistant/data/exception/DataAccessException.java 0 → 100644
  1 +++ a/assistant-data/src/main/java/com/diligrp/assistant/data/exception/DataAccessException.java
  1 +package com.diligrp.assistant.data.exception;
  2 +
  3 +import com.diligrp.assistant.shared.exception.PlatformServiceException;
  4 +
  5 +public class DataAccessException extends PlatformServiceException {
  6 + public DataAccessException(String message) {
  7 + super(message);
  8 + }
  9 +
  10 + public DataAccessException(int code, String message) {
  11 + super(code, message);
  12 + }
  13 +
  14 + public DataAccessException(String message, Throwable ex) {
  15 + super(message, ex);
  16 + }
  17 +}
... ...
assistant-data/src/main/java/com/diligrp/assistant/data/model/DataDictionary.java 0 → 100644
  1 +++ a/assistant-data/src/main/java/com/diligrp/assistant/data/model/DataDictionary.java
  1 +package com.diligrp.assistant.data.model;
  2 +
  3 +import com.diligrp.assistant.shared.domain.BaseDo;
  4 +
  5 +import java.time.LocalDateTime;
  6 +
  7 +public class DataDictionary extends BaseDo {
  8 + // 类型
  9 + private Integer type;
  10 + // 分组编码
  11 + private String groupCode;
  12 + // 编码
  13 + private String code;
  14 + // 名称
  15 + private String name;
  16 + // 字典值
  17 + private String value;
  18 + // 描述
  19 + private String description;
  20 +
  21 + public Integer getType() {
  22 + return type;
  23 + }
  24 +
  25 + public void setType(Integer type) {
  26 + this.type = type;
  27 + }
  28 +
  29 + public String getGroupCode() {
  30 + return groupCode;
  31 + }
  32 +
  33 + public void setGroupCode(String groupCode) {
  34 + this.groupCode = groupCode;
  35 + }
  36 +
  37 + public String getCode() {
  38 + return code;
  39 + }
  40 +
  41 + public void setCode(String code) {
  42 + this.code = code;
  43 + }
  44 +
  45 + public String getName() {
  46 + return name;
  47 + }
  48 +
  49 + public void setName(String name) {
  50 + this.name = name;
  51 + }
  52 +
  53 + public String getValue() {
  54 + return value;
  55 + }
  56 +
  57 + public void setValue(String value) {
  58 + this.value = value;
  59 + }
  60 +
  61 + public String getDescription() {
  62 + return description;
  63 + }
  64 +
  65 + public void setDescription(String description) {
  66 + this.description = description;
  67 + }
  68 +
  69 + public LocalDateTime getCreatedTime() {
  70 + return createdTime;
  71 + }
  72 +
  73 + public void setCreatedTime(LocalDateTime createdTime) {
  74 + this.createdTime = createdTime;
  75 + }
  76 +
  77 + public LocalDateTime getModifiedTime() {
  78 + return modifiedTime;
  79 + }
  80 +
  81 + public void setModifiedTime(LocalDateTime modifiedTime) {
  82 + this.modifiedTime = modifiedTime;
  83 + }
  84 +
  85 + public static Builder builder() {
  86 + return new DataDictionary().new Builder();
  87 + }
  88 +
  89 + public class Builder {
  90 + public Builder type(int type) {
  91 + DataDictionary.this.type = type;
  92 + return this;
  93 + }
  94 +
  95 + public Builder groupCode(String groupCode) {
  96 + DataDictionary.this.groupCode = groupCode;
  97 + return this;
  98 + }
  99 +
  100 + public Builder code(String code) {
  101 + DataDictionary.this.code = code;
  102 + return this;
  103 + }
  104 +
  105 + public Builder name(String name) {
  106 + DataDictionary.this.name = name;
  107 + return this;
  108 + }
  109 +
  110 + public Builder value(String value) {
  111 + DataDictionary.this.value = value;
  112 + return this;
  113 + }
  114 +
  115 + public Builder description(String description) {
  116 + DataDictionary.this.description = description;
  117 + return this;
  118 + }
  119 +
  120 + public Builder createdTime(LocalDateTime createdTime) {
  121 + DataDictionary.this.createdTime = createdTime;
  122 + return this;
  123 + }
  124 +
  125 + public Builder modifiedTime(LocalDateTime modifiedTime) {
  126 + DataDictionary.this.modifiedTime = modifiedTime;
  127 + return this;
  128 + }
  129 +
  130 + public DataDictionary build() {
  131 + return DataDictionary.this;
  132 + }
  133 + }
  134 +}
... ...
assistant-data/src/main/java/com/diligrp/assistant/data/model/DataDistrict.java 0 → 100644
  1 +++ a/assistant-data/src/main/java/com/diligrp/assistant/data/model/DataDistrict.java
  1 +package com.diligrp.assistant.data.model;
  2 +
  3 +import com.diligrp.assistant.shared.domain.BaseDo;
  4 +
  5 +public class DataDistrict extends BaseDo {
  6 + // 父区域ID
  7 + private Long parentId;
  8 + // 名称
  9 + private String name;
  10 + // 简称
  11 + private String shortName;
  12 + // 级别
  13 + private Integer level;
  14 + // 全称
  15 + private String fullName;
  16 + // 区号
  17 + private String areaCode;
  18 + // 拼音
  19 + private String pyCode;
  20 + // 简拼
  21 + private String shortPy;
  22 + // 路径
  23 + private String path;
  24 + // 路径名称
  25 + private String pathName;
  26 + // 经度
  27 + private String longitude;
  28 + // 纬度
  29 + private String latitude;
  30 + // 状态
  31 + private Integer state;
  32 +
  33 + public Long getParentId() {
  34 + return parentId;
  35 + }
  36 +
  37 + public void setParentId(Long parentId) {
  38 + this.parentId = parentId;
  39 + }
  40 +
  41 + public String getName() {
  42 + return name;
  43 + }
  44 +
  45 + public void setName(String name) {
  46 + this.name = name;
  47 + }
  48 +
  49 + public String getShortName() {
  50 + return shortName;
  51 + }
  52 +
  53 + public void setShortName(String shortName) {
  54 + this.shortName = shortName;
  55 + }
  56 +
  57 + public Integer getLevel() {
  58 + return level;
  59 + }
  60 +
  61 + public void setLevel(Integer level) {
  62 + this.level = level;
  63 + }
  64 +
  65 + public String getFullName() {
  66 + return fullName;
  67 + }
  68 +
  69 + public void setFullName(String fullName) {
  70 + this.fullName = fullName;
  71 + }
  72 +
  73 + public String getAreaCode() {
  74 + return areaCode;
  75 + }
  76 +
  77 + public void setAreaCode(String areaCode) {
  78 + this.areaCode = areaCode;
  79 + }
  80 +
  81 + public String getPyCode() {
  82 + return pyCode;
  83 + }
  84 +
  85 + public void setPyCode(String pyCode) {
  86 + this.pyCode = pyCode;
  87 + }
  88 +
  89 + public String getShortPy() {
  90 + return shortPy;
  91 + }
  92 +
  93 + public void setShortPy(String shortPy) {
  94 + this.shortPy = shortPy;
  95 + }
  96 +
  97 + public String getPath() {
  98 + return path;
  99 + }
  100 +
  101 + public void setPath(String path) {
  102 + this.path = path;
  103 + }
  104 +
  105 + public String getPathName() {
  106 + return pathName;
  107 + }
  108 +
  109 + public void setPathName(String pathName) {
  110 + this.pathName = pathName;
  111 + }
  112 +
  113 + public String getLongitude() {
  114 + return longitude;
  115 + }
  116 +
  117 + public void setLongitude(String longitude) {
  118 + this.longitude = longitude;
  119 + }
  120 +
  121 + public String getLatitude() {
  122 + return latitude;
  123 + }
  124 +
  125 + public void setLatitude(String latitude) {
  126 + this.latitude = latitude;
  127 + }
  128 +
  129 + public Integer getState() {
  130 + return state;
  131 + }
  132 +
  133 + public void setState(Integer state) {
  134 + this.state = state;
  135 + }
  136 +}
... ...
assistant-data/src/main/java/com/diligrp/assistant/data/service/DataDictionaryService.java 0 → 100644
  1 +++ a/assistant-data/src/main/java/com/diligrp/assistant/data/service/DataDictionaryService.java
  1 +package com.diligrp.assistant.data.service;
  2 +
  3 +import com.diligrp.assistant.data.model.DataDictionary;
  4 +import org.apache.ibatis.annotations.Param;
  5 +
  6 +import java.util.List;
  7 +
  8 +public interface DataDictionaryService {
  9 + /**
  10 + * 新增数据字典配置
  11 + *
  12 + * @param dictionary - 数据字典
  13 + */
  14 + void insertDataDictionary(DataDictionary dictionary);
  15 +
  16 + /**
  17 + * 根据编码(groupCode和Code)查询数据字典配置
  18 + *
  19 + * @param groupCode - 分组编码,必填
  20 + * @param code - 参数编码,必填
  21 + * @return DataDictionary - 查询结果大于一条记录将抛出异常
  22 + */
  23 + DataDictionary findDataDictionaryByCode(@Param("groupCode") String groupCode, @Param("code") String code);
  24 +
  25 + /**
  26 + * 根据编码查询数据字典列表
  27 + *
  28 + * @param groupCode - 分组编码,非必填
  29 + * @return List<DataDictionary> - 数据字典列表
  30 + */
  31 + List<DataDictionary> findDataDictionaries(@Param("groupCode") String groupCode);
  32 +
  33 + /**
  34 + * 根据编码(groupCode和Code)修改数据字典配置
  35 + *
  36 + * @param dictionary - 数据字典
  37 + * @return int - 更新条数
  38 + */
  39 + void updateDataDictionary(DataDictionary dictionary);
  40 +}
... ...
assistant-data/src/main/java/com/diligrp/assistant/data/service/DataDistrictService.java 0 → 100644
  1 +++ a/assistant-data/src/main/java/com/diligrp/assistant/data/service/DataDistrictService.java
  1 +package com.diligrp.assistant.data.service;
  2 +
  3 +import com.diligrp.assistant.data.domain.DistrictPageQuery;
  4 +import com.diligrp.assistant.data.model.DataDistrict;
  5 +
  6 +import java.util.List;
  7 +
  8 +public interface DataDistrictService {
  9 + /**
  10 + * 根据ID查询区域
  11 + *
  12 + * @param id - 区域ID
  13 + * @return 区域
  14 + */
  15 + DataDistrict findDataDistrictById(Long id);
  16 +
  17 + /**
  18 + * 根据ID查询父级区域
  19 + *
  20 + * @param id - 区域ID
  21 + * @return 父级区域
  22 + */
  23 + DataDistrict findParentDistrictById(Long id);
  24 +
  25 + /**
  26 + * 根据ID查询所有的祖先区域,查询结果包含自身且按照等级降序排序
  27 + *
  28 + * @param id - 区域ID
  29 + * @return 祖先区域
  30 + */
  31 + List<DataDistrict> listParentsById(Long id);
  32 +
  33 + /**
  34 + * 根据父区域ID分页查询子区域
  35 + *
  36 + * @param query 查询条件
  37 + * @return 子区域
  38 + */
  39 + List<DataDistrict> listChildrenById(DistrictPageQuery query);
  40 +
  41 + /**
  42 + * 根据区域级别分页查询区域
  43 + *
  44 + * @param query 查询条件
  45 + * @return 区域
  46 + */
  47 + List<DataDistrict> listDataDistrictsByLevel(DistrictPageQuery query);
  48 +
  49 + /**
  50 + * 根据经纬度坐标分页查询区域,距离由近到远排序
  51 + *
  52 + * @param query 查询条件
  53 + * @return 区域
  54 + */
  55 + List<DataDistrict> listDataDistrictsByDistance(DistrictPageQuery query);
  56 +}
... ...
assistant-data/src/main/java/com/diligrp/assistant/data/service/impl/DataDictionaryServiceImpl.java 0 → 100644
  1 +++ a/assistant-data/src/main/java/com/diligrp/assistant/data/service/impl/DataDictionaryServiceImpl.java
  1 +package com.diligrp.assistant.data.service.impl;
  2 +
  3 +import com.diligrp.assistant.data.dao.DataDictionaryDao;
  4 +import com.diligrp.assistant.data.exception.DataAccessException;
  5 +import com.diligrp.assistant.data.model.DataDictionary;
  6 +import com.diligrp.assistant.data.service.DataDictionaryService;
  7 +import com.diligrp.assistant.shared.ErrorCode;
  8 +import jakarta.annotation.Resource;
  9 +import org.springframework.stereotype.Service;
  10 +import org.springframework.transaction.annotation.Transactional;
  11 +
  12 +import java.util.List;
  13 +import java.util.Optional;
  14 +
  15 +@Service("dataDictionaryService")
  16 +public class DataDictionaryServiceImpl implements DataDictionaryService {
  17 +
  18 + @Resource
  19 + private DataDictionaryDao dataDictionaryDao;
  20 +
  21 + @Override
  22 + @Transactional(rollbackFor = Exception.class)
  23 + public void insertDataDictionary(DataDictionary dictionary) {
  24 + dataDictionaryDao.insertDataDictionary(dictionary);
  25 + }
  26 +
  27 + @Override
  28 + public DataDictionary findDataDictionaryByCode(String groupCode, String code) {
  29 + //TODO: Use redis or local cache?
  30 + Optional<DataDictionary> optional = dataDictionaryDao.findDataDictionaryByCode(groupCode, code);
  31 + return optional.orElseThrow(() -> new DataAccessException(ErrorCode.OBJECT_NOT_FOUND, "数据字典不存在"));
  32 + }
  33 +
  34 + @Override
  35 + public List<DataDictionary> findDataDictionaries(String groupCode) {
  36 + return dataDictionaryDao.findDataDictionaries(groupCode);
  37 + }
  38 +
  39 + @Override
  40 + @Transactional(rollbackFor = Exception.class)
  41 + public void updateDataDictionary(DataDictionary dictionary) {
  42 + int result = dataDictionaryDao.updateDataDictionary(dictionary);
  43 + if (result == 0) {
  44 + throw new DataAccessException(ErrorCode.OBJECT_NOT_FOUND, "数据字典不存在");
  45 + }
  46 + }
  47 +}
... ...
assistant-data/src/main/java/com/diligrp/assistant/data/service/impl/DataDistrictServiceImpl.java 0 → 100644
  1 +++ a/assistant-data/src/main/java/com/diligrp/assistant/data/service/impl/DataDistrictServiceImpl.java
  1 +package com.diligrp.assistant.data.service.impl;
  2 +
  3 +import com.diligrp.assistant.data.dao.DataDistrictDao;
  4 +import com.diligrp.assistant.data.domain.DistrictPageQuery;
  5 +import com.diligrp.assistant.data.exception.DataAccessException;
  6 +import com.diligrp.assistant.data.model.DataDistrict;
  7 +import com.diligrp.assistant.data.service.DataDistrictService;
  8 +import com.diligrp.assistant.shared.ErrorCode;
  9 +import jakarta.annotation.Resource;
  10 +import org.springframework.stereotype.Service;
  11 +
  12 +import java.util.*;
  13 +
  14 +@Service("dataDistrictService")
  15 +public class DataDistrictServiceImpl implements DataDistrictService {
  16 +
  17 + @Resource
  18 + private DataDistrictDao dataDistrictDao;
  19 +
  20 + @Override
  21 + public DataDistrict findDataDistrictById(Long id) {
  22 + return dataDistrictDao.findDataDistrictById(id).orElseThrow(() ->
  23 + new DataAccessException(ErrorCode.OBJECT_NOT_FOUND, "区域不存在"));
  24 + }
  25 +
  26 + @Override
  27 + public DataDistrict findParentDistrictById(Long id) {
  28 + return dataDistrictDao.findParentDistrictById(id).orElseThrow(() ->
  29 + new DataAccessException(ErrorCode.OBJECT_NOT_FOUND, "区域不存在"));
  30 + }
  31 +
  32 + @Override
  33 + public List<DataDistrict> listParentsById(Long id) {
  34 + DataDistrict self = findDataDistrictById(id);
  35 + String path = self.getPath();
  36 +
  37 + if (Objects.nonNull(path)) {
  38 + List<Long> ids = new ArrayList<>();
  39 + StringTokenizer tokenizer = new StringTokenizer(path, ",");
  40 +
  41 + while (tokenizer.hasMoreTokens()) {
  42 + ids.add(Long.parseLong(tokenizer.nextToken()));
  43 + }
  44 + if (ids.isEmpty()) {
  45 + ids.add(id);
  46 + }
  47 +
  48 + return dataDistrictDao.findDataDistrictsByIds(ids);
  49 + } else {
  50 + return Collections.singletonList(self);
  51 + }
  52 + }
  53 +
  54 + @Override
  55 + public List<DataDistrict> listChildrenById(DistrictPageQuery query) {
  56 + return dataDistrictDao.listChildrenById(query);
  57 + }
  58 +
  59 + @Override
  60 + public List<DataDistrict> listDataDistrictsByLevel(DistrictPageQuery query) {
  61 + return dataDistrictDao.listDataDistrictsByLevel(query);
  62 + }
  63 +
  64 + @Override
  65 + public List<DataDistrict> listDataDistrictsByDistance(DistrictPageQuery query) {
  66 + return dataDistrictDao.listDataDistrictsByDistance(query);
  67 + }
  68 +}
... ...
assistant-data/src/main/resources/com/diligrp/assistant/dao/mapper/DataDictionaryDao.xml 0 → 100644
  1 +++ a/assistant-data/src/main/resources/com/diligrp/assistant/dao/mapper/DataDictionaryDao.xml
  1 +<?xml version="1.0" encoding="UTF-8" ?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3 + "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4 +
  5 +<mapper namespace="com.diligrp.assistant.data.dao.DataDictionaryDao">
  6 + <resultMap id="DataDictionaryMap" type="com.diligrp.assistant.data.model.DataDictionary">
  7 + <id column="id" property="id"/>
  8 + <result column="type" property="type"/>
  9 + <result column="group_code" property="groupCode"/>
  10 + <result column="code" property="code"/>
  11 + <result column="name" property="name"/>
  12 + <result column="value" property="value"/>
  13 + <result column="description" property="description"/>
  14 + <result column="created_time" property="createdTime"/>
  15 + <result column="modified_time" property="modifiedTime"/>
  16 + </resultMap>
  17 +
  18 + <insert id="insertDataDictionary" parameterType="com.diligrp.assistant.data.model.DataDictionary">
  19 + INSERT INTO data_dictionary
  20 + (type, group_code, code, name, value, description, created_time, modified_time)
  21 + VALUES
  22 + (#{type}, #{groupCode}, #{code}, #{name}, #{value}, #{description}, #{createdTime}, #{modifiedTime})
  23 + </insert>
  24 +
  25 + <select id="findDataDictionaryByCode" resultMap="DataDictionaryMap">
  26 + SELECT * FROM data_dictionary WHERE group_code = #{groupCode} AND code = #{code}
  27 + </select>
  28 +
  29 + <select id="findDataDictionaries" resultMap="DataDictionaryMap">
  30 + SELECT * FROM data_dictionary WHERE group_code = #{groupCode}
  31 + ORDER BY id
  32 + </select>
  33 +
  34 + <update id="updateDataDictionary">
  35 + UPDATE data_dictionary SET value = #{value}, modified_time = #{modifiedTime}
  36 + <if test="name != null">
  37 + , name = #{name}
  38 + </if>
  39 + <if test="description != null">
  40 + , description = #{description}
  41 + </if>
  42 + WHERE group_code = #{groupCode} AND code = #{code}
  43 + </update>
  44 +</mapper>
... ...
assistant-data/src/main/resources/com/diligrp/assistant/dao/mapper/DataDistrictDao.xml 0 → 100644
  1 +++ a/assistant-data/src/main/resources/com/diligrp/assistant/dao/mapper/DataDistrictDao.xml
  1 +<?xml version="1.0" encoding="UTF-8" ?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3 + "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4 +
  5 +<mapper namespace="com.diligrp.assistant.data.dao.DataDistrictDao">
  6 + <resultMap id="DataDistrictMap" type="com.diligrp.assistant.data.model.DataDistrict">
  7 + <id column="id" property="id"/>
  8 + <result column="parent_id" property="parentId"/>
  9 + <result column="name" property="name"/>
  10 + <result column="short_name" property="shortName"/>
  11 + <result column="level" property="level"/>
  12 + <result column="full_name" property="fullName"/>
  13 + <result column="area_code" property="areaCode"/>
  14 + <result column="py_code" property="pyCode"/>
  15 + <result column="short_py" property="shortPy"/>
  16 + <result column="path" property="path"/>
  17 + <result column="path_name" property="pathName"/>
  18 + <result column="longitude" property="longitude"/>
  19 + <result column="latitude" property="latitude"/>
  20 + <result column="state" property="state"/>
  21 + <result column="created_time" property="createdTime"/>
  22 + </resultMap>
  23 +
  24 + <select id="findDataDistrictById" parameterType="long" resultMap="DataDistrictMap">
  25 + SELECT * FROM data_district WHERE id = #{id} AND state &gt; 0
  26 + </select>
  27 +
  28 + <select id="findParentDistrictById" parameterType="long" resultMap="DataDistrictMap">
  29 + SELECT
  30 + parent.*
  31 + FROM data_district self
  32 + INNER JOIN data_district parent ON self.parent_id = parent.id
  33 + WHERE self.id = #{id} AND self.state &gt; 0
  34 + </select>
  35 +
  36 + <select id="findDataDistrictsByIds" resultMap="DataDistrictMap">
  37 + SELECT * FROM data_district WHERE id IN
  38 + <foreach item="id" collection="list" open="(" separator="," close=")">
  39 + #{id}
  40 + </foreach>
  41 + AND state &gt; 0 ORDER BY level DESC, id
  42 + </select>
  43 +
  44 + <select id="listChildrenById" parameterType="com.diligrp.assistant.data.domain.DistrictPageQuery" resultMap="DataDistrictMap">
  45 + SELECT * FROM data_district WHERE parent_Id = #{id} AND state &gt; 0
  46 + ORDER BY ID LIMIT #{start}, #{limit}
  47 + </select>
  48 +
  49 + <select id="listDataDistrictsByLevel" parameterType="com.diligrp.assistant.data.domain.DistrictPageQuery" resultMap="DataDistrictMap">
  50 + SELECT * FROM data_district WHERE level = #{level} AND state &gt; 0
  51 + ORDER BY ID LIMIT #{start}, #{limit}
  52 + </select>
  53 +
  54 + <select id="listDataDistrictsByDistance" parameterType="com.diligrp.assistant.data.domain.DistrictPageQuery" resultMap="DataDistrictMap">
  55 + SELECT * FROM data_district
  56 + WHERE longitude IS NOT NULL AND latitude IS NOT NULL AND state &gt; 0
  57 + ORDER BY ST_Distance(ST_GeomFromText('POINT(${longitude} ${latitude})'), ST_GeomFromText(CONCAT('POINT(', longitude, ' ', latitude, ')')))
  58 + LIMIT #{start}, #{limit}
  59 + </select>
  60 +</mapper>
... ...
assistant-dfs/build.gradle 0 → 100644
  1 +++ a/assistant-dfs/build.gradle
  1 +group = 'com.diligrp'
  2 +archivesBaseName = 'assistant-dfs'
  3 +
  4 +dependencies {
  5 + api project(':assistant-shared')
  6 + implementation 'com.aliyun.oss:aliyun-sdk-oss:3.15.1'
  7 +}
0 8 \ No newline at end of file
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/Constants.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/Constants.java
  1 +package com.diligrp.assistant.dfs;
  2 +
  3 +public final class Constants {
  4 + public static final String HEADER_AUTHORIZATION = "Dfs-Authorization";
  5 +
  6 + public static final String FILE_METADATA_NAME = "name";
  7 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/DfsConfiguration.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/DfsConfiguration.java
  1 +package com.diligrp.assistant.dfs;
  2 +
  3 +import com.diligrp.assistant.dfs.pipeline.DefaultDfsPipelineManager;
  4 +import com.diligrp.assistant.dfs.pipeline.DfsPipeline;
  5 +import com.diligrp.assistant.dfs.pipeline.DfsPipelineManager;
  6 +import com.diligrp.assistant.dfs.pipeline.OssPipeline;
  7 +import com.diligrp.assistant.shared.mybatis.MybatisMapperSupport;
  8 +import org.mybatis.spring.annotation.MapperScan;
  9 +import org.springframework.boot.context.properties.ConfigurationProperties;
  10 +import org.springframework.context.annotation.Bean;
  11 +import org.springframework.context.annotation.ComponentScan;
  12 +import org.springframework.context.annotation.Configuration;
  13 +
  14 +@Configuration
  15 +@ComponentScan("com.diligrp.assistant.dfs")
  16 +@MapperScan(basePackages = {"com.diligrp.assistant.dfs.dao"}, markerInterface = MybatisMapperSupport.class)
  17 +public class DfsConfiguration {
  18 +
  19 + @Bean
  20 + @ConfigurationProperties("dfs")
  21 + public DfsProperties dfsProperties() {
  22 + return new DfsProperties();
  23 + }
  24 +
  25 + @Bean
  26 + public DfsPipelineManager dfsPipelineManager(DfsProperties properties) {
  27 + DfsPipelineManager pipelineManager = new DefaultDfsPipelineManager();
  28 + DfsProperties.Oss oss = properties.getOss();
  29 + if (oss != null) {
  30 + // 可利用数据库进行通道配置, 前期并没有必要
  31 + DfsPipeline pipeline = new OssPipeline(1, "OSS文件存储服务", oss.getUri(),
  32 + oss.getAccessKeyId(), oss.getAccessKeySecret());
  33 + pipelineManager.registerPipeline(pipeline);
  34 + }
  35 + return pipelineManager;
  36 + }
  37 +}
0 38 \ No newline at end of file
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/DfsProperties.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/DfsProperties.java
  1 +package com.diligrp.assistant.dfs;
  2 +
  3 +import java.security.PrivateKey;
  4 +import java.security.PublicKey;
  5 +
  6 +public class DfsProperties {
  7 + // 私钥
  8 + private PrivateKey privateKey;
  9 + // 公钥
  10 + private PublicKey publicKey;
  11 + // OSS配置
  12 + private Oss oss;
  13 +
  14 + public PrivateKey getPrivateKey() {
  15 + return privateKey;
  16 + }
  17 +
  18 + public void setPrivateKey(PrivateKey privateKey) {
  19 + this.privateKey = privateKey;
  20 + }
  21 +
  22 + public PublicKey getPublicKey() {
  23 + return publicKey;
  24 + }
  25 +
  26 + public void setPublicKey(PublicKey publicKey) {
  27 + this.publicKey = publicKey;
  28 + }
  29 +
  30 + public Oss getOss() {
  31 + return oss;
  32 + }
  33 +
  34 + public void setOss(Oss oss) {
  35 + this.oss = oss;
  36 + }
  37 +
  38 + public static class Oss {
  39 + private String uri;
  40 + private String accessKeyId;
  41 + private String accessKeySecret;
  42 +
  43 + public String getUri() {
  44 + return uri;
  45 + }
  46 +
  47 + public void setUri(String uri) {
  48 + this.uri = uri;
  49 + }
  50 +
  51 + public String getAccessKeyId() {
  52 + return accessKeyId;
  53 + }
  54 +
  55 + public void setAccessKeyId(String accessKeyId) {
  56 + this.accessKeyId = accessKeyId;
  57 + }
  58 +
  59 + public String getAccessKeySecret() {
  60 + return accessKeySecret;
  61 + }
  62 +
  63 + public void setAccessKeySecret(String accessKeySecret) {
  64 + this.accessKeySecret = accessKeySecret;
  65 + }
  66 + }
  67 +}
0 68 \ No newline at end of file
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/client/FileRepositoryClient.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/client/FileRepositoryClient.java
  1 +package com.diligrp.assistant.dfs.client;
  2 +
  3 +import com.diligrp.assistant.dfs.domain.DfsFile;
  4 +
  5 +public interface FileRepositoryClient {
  6 + /**
  7 + * 创建文件仓库,并返回仓库唯一标识:名称
  8 + *
  9 + * @param repositoryId - 仓库ID
  10 + * @return 仓库名称,同仓库ID
  11 + */
  12 + String createFileRepository(String repositoryId);
  13 +
  14 + /**
  15 + * 删除文件仓库 - 空仓库才允许删除
  16 + *
  17 + * @param repositoryId - 仓库ID
  18 + * @return true/false
  19 + */
  20 + boolean deleteFileRepository(String repositoryId);
  21 +
  22 + /**
  23 + * 存放文件到仓库
  24 + *
  25 + * @param repositoryId - 仓库ID
  26 + * @param file - 文件
  27 + */
  28 + void putFile(String repositoryId, DfsFile file);
  29 +
  30 + /**
  31 + * 从文件仓库中获取文件
  32 + *
  33 + * @param repositoryId - 仓库ID
  34 + * @param fileId - 文件标识
  35 + * @param style - 图片处理参数, 参见https://help.aliyun.com/zh/oss/user-guide/adjust-image-quality?spm=a2c4g.11186623.0.0.1c2d2759lemWct
  36 + * @return - 文件
  37 + */
  38 + DfsFile getFile(String repositoryId, String fileId, String style);
  39 +
  40 + /**
  41 + * 从文件仓库中删除文件
  42 + *
  43 + * @param repositoryId - 仓库ID
  44 + * @param fileId - 文件标识
  45 + * @return - true/false
  46 + */
  47 + boolean deleteFile(String repositoryId, String fileId);
  48 +
  49 + void destroy();
  50 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/client/OssFileRepositoryClient.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/client/OssFileRepositoryClient.java
  1 +package com.diligrp.assistant.dfs.client;
  2 +
  3 +import com.aliyun.oss.ClientBuilderConfiguration;
  4 +import com.aliyun.oss.OSS;
  5 +import com.aliyun.oss.OSSClientBuilder;
  6 +import com.aliyun.oss.OSSException;
  7 +import com.aliyun.oss.common.auth.CredentialsProvider;
  8 +import com.aliyun.oss.common.auth.DefaultCredentialProvider;
  9 +import com.aliyun.oss.model.*;
  10 +import com.diligrp.assistant.dfs.Constants;
  11 +import com.diligrp.assistant.dfs.domain.DfsFile;
  12 +import com.diligrp.assistant.dfs.domain.FileMetadata;
  13 +import com.diligrp.assistant.dfs.exception.DfsServiceException;
  14 +import com.diligrp.assistant.shared.ErrorCode;
  15 +import org.slf4j.Logger;
  16 +import org.slf4j.LoggerFactory;
  17 +
  18 +import java.nio.charset.StandardCharsets;
  19 +import java.util.Base64;
  20 +
  21 +public class OssFileRepositoryClient implements FileRepositoryClient {
  22 + private final static Logger LOGGER = LoggerFactory.getLogger(OssFileRepositoryClient.class);
  23 +
  24 + private OSS client;
  25 +
  26 + public OssFileRepositoryClient(String uri, String accessKeyId, String accessKeySecret) {
  27 + CredentialsProvider credentialsProvider = new DefaultCredentialProvider(accessKeyId, accessKeySecret);
  28 + ClientBuilderConfiguration conf = new ClientBuilderConfiguration();
  29 + // 设置OSSClient允许打开的最大HTTP连接数,默认为1024个。
  30 + conf.setMaxConnections(200);
  31 + // 设置Socket层传输数据的超时时间,默认为50000毫秒。
  32 + conf.setSocketTimeout(20000);
  33 + // 设置建立连接的超时时间,默认为50000毫秒。
  34 + conf.setConnectionTimeout(10000);
  35 + // 设置从连接池中获取连接的超时时间(单位:毫秒),默认不超时。
  36 + conf.setConnectionRequestTimeout(15000);
  37 + // 设置连接空闲超时时间。超时则关闭连接,默认为60000毫秒。
  38 + conf.setIdleConnectionTime(30000);
  39 + // 设置失败请求重试次数,默认为3次。
  40 + conf.setMaxErrorRetry(5);
  41 + client = new OSSClientBuilder().build(uri, credentialsProvider, conf);
  42 + }
  43 +
  44 + @Override
  45 + public String createFileRepository(String repositoryId) {
  46 + CreateBucketRequest request = new CreateBucketRequest(repositoryId);
  47 + try {
  48 + Bucket bucket = client.createBucket(request);
  49 + return bucket.getName();
  50 + } catch (OSSException oex) {
  51 + LOGGER.error("Failed to create oss bucket", oex);
  52 + throw new DfsServiceException(ErrorCode.SERVICE_ACCESS_ERROR, "创建存储空间失败: " + oex.getErrorMessage());
  53 + } catch (Exception ex) {
  54 + LOGGER.error("Failed to create oss bucket", ex);
  55 + throw new DfsServiceException(ErrorCode.SERVICE_ACCESS_ERROR, "创建存储空间失败: " + ex.getMessage());
  56 + }
  57 + }
  58 +
  59 + @Override
  60 + public boolean deleteFileRepository(String repositoryId) {
  61 + try {
  62 + client.deleteBucket(repositoryId);
  63 + return true;
  64 + } catch (OSSException oex) {
  65 + LOGGER.error("Failed to delete oss bucket", oex);
  66 + throw new DfsServiceException(ErrorCode.SERVICE_ACCESS_ERROR, "文件仓库删除失败: " + oex.getErrorMessage());
  67 + } catch (Exception ex) {
  68 + LOGGER.error("Failed to delete oss bucket", ex);
  69 + throw new DfsServiceException(ErrorCode.SERVICE_ACCESS_ERROR, "文件仓库删除失败: " + ex.getMessage());
  70 + }
  71 + }
  72 +
  73 + @Override
  74 + public void putFile(String repositoryId, DfsFile file) {
  75 + FileMetadata option = file.getMetadata();
  76 + ObjectMetadata metadata = new ObjectMetadata();
  77 + metadata.setContentType(option.getMimeType());
  78 + // 处理中文名称无法验证签名导致无法完成文件上传
  79 + String encodedFileName = Base64.getEncoder().encodeToString(file.getName().getBytes(StandardCharsets.UTF_8));
  80 + metadata.addUserMetadata(Constants.FILE_METADATA_NAME, encodedFileName);
  81 + PutObjectRequest request = new PutObjectRequest(repositoryId, file.getId(), file.getStream(), metadata);
  82 +
  83 + try {
  84 + client.putObject(request);
  85 + } catch (OSSException oex) {
  86 + LOGGER.error("Failed to put file into bucket", oex);
  87 + throw new DfsServiceException(ErrorCode.SERVICE_ACCESS_ERROR, "文件上传失败: " + oex.getErrorMessage());
  88 + } catch (Exception ex) {
  89 + LOGGER.error("Failed to put file into bucket", ex);
  90 + throw new DfsServiceException(ErrorCode.SERVICE_ACCESS_ERROR, "文件上传失败: " + ex.getMessage());
  91 + }
  92 + }
  93 +
  94 + @Override
  95 + public DfsFile getFile(String repositoryId, String fileId, String style) {
  96 + GetObjectRequest request = new GetObjectRequest(repositoryId, fileId);
  97 + if (style != null) {
  98 + request.setProcess(style);
  99 + }
  100 + OSSObject ossObject;
  101 +
  102 + try {
  103 + ossObject = client.getObject(request);
  104 + ObjectMetadata metadata = ossObject.getObjectMetadata();
  105 + String encodedFileName = metadata.getUserMetadata().get(Constants.FILE_METADATA_NAME);
  106 + String fileName = new String(Base64.getDecoder().decode(encodedFileName), StandardCharsets.UTF_8);
  107 +
  108 + FileMetadata fileMetadata = new FileMetadata(metadata.getContentType());
  109 + return new DfsFile(fileId, fileName, ossObject.getObjectContent(), fileMetadata);
  110 + } catch (OSSException oex) {
  111 + LOGGER.error("Failed to get file from bucket", oex);
  112 + throw new DfsServiceException(ErrorCode.SERVICE_ACCESS_ERROR, "文件下载失败: " + oex.getErrorMessage());
  113 + } catch (Exception ex) {
  114 + LOGGER.error("Failed to get file from bucket", ex);
  115 + throw new DfsServiceException(ErrorCode.SERVICE_ACCESS_ERROR, "文件下载失败: " + ex.getMessage());
  116 + }
  117 + }
  118 +
  119 + @Override
  120 + public boolean deleteFile(String repositoryId, String fileId) {
  121 + try {
  122 + client.deleteObject(repositoryId, fileId);
  123 + return true;
  124 + } catch (OSSException oex) {
  125 + LOGGER.error("Failed to delete file from bucket", oex);
  126 + throw new DfsServiceException(ErrorCode.SERVICE_ACCESS_ERROR, "文件删除失败: " + oex.getErrorMessage());
  127 + } catch (Exception ex) {
  128 + LOGGER.error("Failed to delete file from bucket", ex);
  129 + throw new DfsServiceException(ErrorCode.SERVICE_ACCESS_ERROR, "文件删除失败: " + ex.getMessage());
  130 + }
  131 + }
  132 +
  133 + @Override
  134 + public void destroy() {
  135 + client.shutdown();
  136 + }
  137 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/controller/FileObjectController.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/controller/FileObjectController.java
  1 +package com.diligrp.assistant.dfs.controller;
  2 +
  3 +import com.diligrp.assistant.dfs.Constants;
  4 +import com.diligrp.assistant.dfs.DfsProperties;
  5 +import com.diligrp.assistant.dfs.domain.DfsAccessToken;
  6 +import com.diligrp.assistant.dfs.domain.DfsFile;
  7 +import com.diligrp.assistant.dfs.domain.FileMetadata;
  8 +import com.diligrp.assistant.dfs.exception.DfsServiceException;
  9 +import com.diligrp.assistant.dfs.service.FileRepositoryService;
  10 +import com.diligrp.assistant.shared.ErrorCode;
  11 +import com.diligrp.assistant.shared.domain.Message;
  12 +import com.diligrp.assistant.shared.util.ObjectUtils;
  13 +import jakarta.annotation.Resource;
  14 +import jakarta.servlet.http.HttpServletResponse;
  15 +import org.apache.commons.io.IOUtils;
  16 +import org.springframework.http.MediaType;
  17 +import org.springframework.http.MediaTypeFactory;
  18 +import org.springframework.util.MimeType;
  19 +import org.springframework.web.bind.annotation.*;
  20 +import org.springframework.web.multipart.MultipartFile;
  21 +
  22 +import java.io.ByteArrayInputStream;
  23 +import java.io.IOException;
  24 +import java.io.InputStream;
  25 +import java.net.URLDecoder;
  26 +import java.net.URLEncoder;
  27 +import java.nio.charset.StandardCharsets;
  28 +import java.util.ArrayList;
  29 +import java.util.Base64;
  30 +import java.util.List;
  31 +import java.util.Optional;
  32 +
  33 +@RestController
  34 +@RequestMapping(value = "/dfs")
  35 +public class FileObjectController {
  36 +
  37 + @Resource
  38 + private DfsProperties dfsProperties;
  39 +
  40 + @Resource
  41 + private FileRepositoryService fileRepositoryService;
  42 +
  43 + @RequestMapping(value = "/file/upload.do")
  44 + public Message<?> fileUpload(@RequestPart("file") MultipartFile file,
  45 + @RequestHeader(Constants.HEADER_AUTHORIZATION) String authorization) throws IOException {
  46 + DfsAccessToken accessToken = accessAuthorization(authorization);
  47 +
  48 + Optional<MediaType> optional = MediaTypeFactory.getMediaType(file.getOriginalFilename());
  49 + MimeType mimeType = optional.orElse(MediaType.APPLICATION_OCTET_STREAM);
  50 + FileMetadata metadata = new FileMetadata(mimeType.toString());
  51 +
  52 + String fileName = ObjectUtils.isNotEmpty(file.getOriginalFilename()) ? file.getOriginalFilename() : "unknown";
  53 + DfsFile fileObject = new DfsFile(fileName, file.getInputStream(), metadata);
  54 + String fileId = fileRepositoryService.uploadFile(accessToken, fileObject);
  55 + return Message.success(fileId);
  56 + }
  57 +
  58 + @RequestMapping(value = "/files/upload.do")
  59 + public Message<?> fileUploads(@RequestPart(value = "files") MultipartFile[] files,
  60 + @RequestHeader(Constants.HEADER_AUTHORIZATION) String authorization) throws IOException {
  61 + DfsAccessToken accessToken = accessAuthorization(authorization);
  62 +
  63 + List<String> fileIds = new ArrayList<>();
  64 + for (MultipartFile file : files) {
  65 + Optional<MediaType> optional = MediaTypeFactory.getMediaType(file.getOriginalFilename());
  66 + MimeType mimeType = optional.orElse(MediaType.APPLICATION_OCTET_STREAM);
  67 + FileMetadata metadata = new FileMetadata(mimeType.toString());
  68 +
  69 + DfsFile fileObject = new DfsFile(file.getOriginalFilename(), file.getInputStream(), metadata);
  70 + String fileId = fileRepositoryService.uploadFile(accessToken, fileObject);
  71 + fileIds.add(fileId);
  72 + }
  73 +
  74 + return Message.success(fileIds);
  75 + }
  76 +
  77 + @RequestMapping(value = "/file/uploadByBase64.do")
  78 + public Message<?> uploadByBase64(@RequestParam("file") String file, @RequestParam(value = "name") String name,
  79 + @RequestHeader(Constants.HEADER_AUTHORIZATION) String authorization) {
  80 + DfsAccessToken accessToken = accessAuthorization(authorization);
  81 +
  82 + Optional<MediaType> optional = MediaTypeFactory.getMediaType(name);
  83 + MimeType mimeType = optional.orElse(MediaType.APPLICATION_OCTET_STREAM);
  84 + FileMetadata metadata = new FileMetadata(mimeType.toString());
  85 +
  86 + ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(file));
  87 + DfsFile fileObject = new DfsFile(name, inputStream, metadata);
  88 + String fileId = fileRepositoryService.uploadFile(accessToken, fileObject);
  89 + return Message.success(fileId);
  90 + }
  91 +
  92 + @RequestMapping(value = "/file/preview.do")
  93 + public void preview(@RequestParam("fileId") String fileId, @RequestParam(name = "style", required = false) String style,
  94 + @RequestHeader(Constants.HEADER_AUTHORIZATION) String authorization, HttpServletResponse response) throws IOException {
  95 + // style参数为oss图片处理设置,比如:image/resize,m_fixed,h_300,w_300
  96 + DfsAccessToken accessToken = accessAuthorization(authorization);
  97 + DfsFile file = fileRepositoryService.downloadFile(accessToken, fileId, style);
  98 + InputStream fileStream = file.getStream();
  99 + response.setContentType(file.getMetadata().getMimeType());
  100 + // oss sdk存在bug, fileStream.available()和真实字节数存在差异
  101 + // response.setContentLength(fileStream.available());
  102 + try {
  103 + IOUtils.copy(fileStream, response.getOutputStream());
  104 + } finally {
  105 + IOUtils.closeQuietly(fileStream);
  106 + }
  107 + response.flushBuffer();
  108 + }
  109 +
  110 + @RequestMapping(value = "/file/download.do")
  111 + public void download(@RequestParam("fileId") String fileId, @RequestHeader(Constants.HEADER_AUTHORIZATION) String authorization,
  112 + HttpServletResponse response) throws IOException {
  113 + DfsAccessToken accessToken = accessAuthorization(authorization);
  114 + DfsFile file = fileRepositoryService.downloadFile(accessToken, fileId, null);
  115 + InputStream fileStream = file.getStream();
  116 + response.setContentType(file.getMetadata().getMimeType());
  117 + // oss sdk存在bug, fileStream.available()和真实字节数存在差异
  118 + // response.setContentLength(fileStream.available());
  119 + System.out.println(file.getName());
  120 + String fileName = URLEncoder.encode(file.getName(), StandardCharsets.UTF_8);
  121 + System.out.println(URLDecoder.decode(fileName, StandardCharsets.UTF_8));
  122 + response.addHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName);
  123 + try {
  124 + IOUtils.copy(fileStream, response.getOutputStream());
  125 + } finally {
  126 + IOUtils.closeQuietly(fileStream);
  127 + }
  128 + response.flushBuffer();
  129 + }
  130 +
  131 + @RequestMapping(value = "/file/delete.do")
  132 + public Message<?> deleteFile(@RequestParam("fileId") String fileId, @RequestHeader(Constants.HEADER_AUTHORIZATION) String authorization) {
  133 + DfsAccessToken accessToken = accessAuthorization(authorization);
  134 + fileRepositoryService.deleteFile(accessToken, fileId);
  135 + return Message.success();
  136 + }
  137 +
  138 + private DfsAccessToken accessAuthorization(String authorization) {
  139 + if (authorization == null) {
  140 + throw new DfsServiceException(ErrorCode.UNAUTHORIZED_ACCESS_ERROR, ErrorCode.MESSAGE_ACCESS_DENIED);
  141 + }
  142 + return DfsAccessToken.of(authorization, dfsProperties.getPublicKey());
  143 + }
  144 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/controller/FileRepositoryController.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/controller/FileRepositoryController.java
  1 +package com.diligrp.assistant.dfs.controller;
  2 +
  3 +import com.diligrp.assistant.dfs.Constants;
  4 +import com.diligrp.assistant.dfs.DfsProperties;
  5 +import com.diligrp.assistant.dfs.domain.DfsAccessToken;
  6 +import com.diligrp.assistant.dfs.domain.FileRepositoryDTO;
  7 +import com.diligrp.assistant.dfs.exception.DfsServiceException;
  8 +import com.diligrp.assistant.dfs.model.FileRepository;
  9 +import com.diligrp.assistant.dfs.service.FileRepositoryService;
  10 +import com.diligrp.assistant.shared.ErrorCode;
  11 +import com.diligrp.assistant.shared.domain.Message;
  12 +import com.diligrp.assistant.shared.util.AssertUtils;
  13 +import com.diligrp.assistant.shared.util.RandomUtils;
  14 +import jakarta.annotation.Resource;
  15 +import org.springframework.web.bind.annotation.RequestBody;
  16 +import org.springframework.web.bind.annotation.RequestHeader;
  17 +import org.springframework.web.bind.annotation.RequestMapping;
  18 +import org.springframework.web.bind.annotation.RestController;
  19 +
  20 +@RestController
  21 +@RequestMapping(value = "/dfs")
  22 +public class FileRepositoryController {
  23 +
  24 + @Resource
  25 + private DfsProperties dfsProperties;
  26 +
  27 + @Resource
  28 + private FileRepositoryService fileRepositoryService;
  29 +
  30 + @RequestMapping(value = "/repository/create.do")
  31 + public Message<?> create(@RequestBody FileRepositoryDTO request) {
  32 + AssertUtils.notEmpty(request.getName(), "name missed");
  33 + AssertUtils.notNull(request.getPipeline(), "pipeline missed");
  34 +
  35 + DfsAccessToken accessToken = new DfsAccessToken(request.getPipeline(), RandomUtils.randomUUID(false));
  36 + FileRepository repository = FileRepository.builder().repositoryId(accessToken.getRepositoryId())
  37 + .name(request.getName()).pipeline(request.getPipeline()).description(request.getDescription()).build();
  38 + fileRepositoryService.createFileRepository(accessToken, repository);
  39 + return Message.success(accessToken.toString(dfsProperties.getPrivateKey()));
  40 + }
  41 +
  42 + @RequestMapping(value = "/repository/delete.do")
  43 + public Message<?> deleteRepository(@RequestHeader(Constants.HEADER_AUTHORIZATION) String authorization) {
  44 + DfsAccessToken accessToken = accessAuthorization(authorization);
  45 + fileRepositoryService.deleteFileRepository(accessToken);
  46 + return Message.success();
  47 + }
  48 +
  49 + private DfsAccessToken accessAuthorization(String authorization) {
  50 + if (authorization == null) {
  51 + throw new DfsServiceException(ErrorCode.UNAUTHORIZED_ACCESS_ERROR, ErrorCode.MESSAGE_ACCESS_DENIED);
  52 + }
  53 + return DfsAccessToken.of(authorization, dfsProperties.getPublicKey());
  54 + }
  55 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/dao/FileObjectDao.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/dao/FileObjectDao.java
  1 +package com.diligrp.assistant.dfs.dao;
  2 +
  3 +import com.diligrp.assistant.dfs.model.FileObject;
  4 +import com.diligrp.assistant.shared.mybatis.MybatisMapperSupport;
  5 +import org.apache.ibatis.annotations.Param;
  6 +import org.springframework.stereotype.Repository;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +@Repository("fileObjectDao")
  11 +public interface FileObjectDao extends MybatisMapperSupport {
  12 + void insertFileObject(FileObject file);
  13 +
  14 + int hitFileObject(@Param("fileId") String fileId, @Param("modifiedTime") LocalDateTime modifiedTime);
  15 +
  16 + int deleteFileObject(String fileId);
  17 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/dao/FileRepositoryDao.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/dao/FileRepositoryDao.java
  1 +package com.diligrp.assistant.dfs.dao;
  2 +
  3 +import com.diligrp.assistant.dfs.model.FileRepository;
  4 +import com.diligrp.assistant.shared.mybatis.MybatisMapperSupport;
  5 +import org.springframework.stereotype.Repository;
  6 +
  7 +import java.util.Optional;
  8 +
  9 +@Repository("fileRepositoryDao")
  10 +public interface FileRepositoryDao extends MybatisMapperSupport {
  11 + void insertFileRepository(FileRepository repository);
  12 +
  13 + Optional<FileRepository> findFileRepositoryById(String repositoryId);
  14 +
  15 + int deleteFileRepository(String repositoryId);
  16 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/domain/DfsAccessToken.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/domain/DfsAccessToken.java
  1 +package com.diligrp.assistant.dfs.domain;
  2 +
  3 +import com.diligrp.assistant.dfs.exception.DfsServiceException;
  4 +import com.diligrp.assistant.shared.Constants;
  5 +import com.diligrp.assistant.shared.ErrorCode;
  6 +import com.diligrp.assistant.shared.codec.StringCodec;
  7 +
  8 +import java.io.Serializable;
  9 +import java.nio.ByteBuffer;
  10 +import java.security.PrivateKey;
  11 +import java.security.PublicKey;
  12 +import java.security.SecureRandom;
  13 +import java.security.Signature;
  14 +import java.util.Base64;
  15 +import java.util.StringTokenizer;
  16 +
  17 +public class DfsAccessToken implements Serializable {
  18 + // 文件存储通道
  19 + private int pipeline;
  20 + // 文件仓库ID
  21 + private String repositoryId;
  22 +
  23 + public DfsAccessToken(int pipeline, String repositoryId) {
  24 + this.repositoryId = repositoryId;
  25 + this.pipeline = pipeline;
  26 + }
  27 +
  28 + public int getPipeline() {
  29 + return pipeline;
  30 + }
  31 +
  32 + public String getRepositoryId() {
  33 + return repositoryId;
  34 + }
  35 +
  36 + public String toString(PrivateKey privateKey) {
  37 + try {
  38 + byte[] bytes = StringCodec.getEncoder().encode(repositoryId);
  39 + ByteBuffer packet = ByteBuffer.allocate(bytes.length + Integer.BYTES);
  40 + packet.putInt(pipeline);
  41 + packet.put(bytes);
  42 +
  43 + Signature signature = Signature.getInstance(Constants.SIGN_ALGORITHM);
  44 + signature.initSign(privateKey, new SecureRandom());
  45 + signature.update(packet.array());
  46 + byte[] sign = signature.sign();
  47 + return String.format("%s.%s", Base64.getEncoder().encodeToString(packet.array()), Base64.getEncoder().encodeToString(sign));
  48 + } catch (Exception ex) {
  49 + throw new DfsServiceException(ErrorCode.ILLEGAL_ARGUMENT_ERROR, "accessToken sign failed");
  50 + }
  51 + }
  52 +
  53 + public static DfsAccessToken of(String authorization, PublicKey publicKey) {
  54 + StringTokenizer tokenizer = new StringTokenizer(authorization, ".");
  55 + if (tokenizer.countTokens() != 2) {
  56 + throw new DfsServiceException(ErrorCode.ILLEGAL_ARGUMENT_ERROR, "Invalid accessToken format");
  57 + }
  58 + byte[] data = Base64.getDecoder().decode(tokenizer.nextToken());
  59 + byte[] sign = Base64.getDecoder().decode(tokenizer.nextToken());
  60 +
  61 + try {
  62 + Signature signature = Signature.getInstance(Constants.SIGN_ALGORITHM);
  63 + signature.initVerify(publicKey);
  64 + signature.update(data);
  65 + boolean result = signature.verify(sign);
  66 + if (!result) {
  67 + throw new DfsServiceException(ErrorCode.UNAUTHORIZED_ACCESS_ERROR, ErrorCode.MESSAGE_ACCESS_DENIED);
  68 + }
  69 +
  70 + ByteBuffer packet = ByteBuffer.wrap(data);
  71 + int pipeline = packet.getInt();
  72 + byte[] bytes = new byte[packet.remaining()];
  73 + for (int i = 0; packet.hasRemaining(); i++) {
  74 + bytes[i] = packet.get();
  75 + }
  76 + String repositoryId = StringCodec.getDecoder().decode(bytes);
  77 + return new DfsAccessToken(pipeline, repositoryId);
  78 + } catch (Exception ex) {
  79 + throw new DfsServiceException(ErrorCode.ILLEGAL_ARGUMENT_ERROR, "Invalid accessToken data");
  80 + }
  81 + }
  82 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/domain/DfsFile.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/domain/DfsFile.java
  1 +package com.diligrp.assistant.dfs.domain;
  2 +
  3 +import com.diligrp.assistant.shared.util.RandomUtils;
  4 +
  5 +import java.io.InputStream;
  6 +
  7 +public class DfsFile {
  8 + // 文件唯一标识
  9 + private String id;
  10 + // 文件名称
  11 + private String name;
  12 + // 文件流
  13 + private InputStream stream;
  14 + // option参数
  15 + private FileMetadata metadata;
  16 +
  17 + public DfsFile(String name, InputStream stream, FileMetadata metadata) {
  18 + this(RandomUtils.randomUUID(false), name, stream, metadata);
  19 + }
  20 +
  21 + public DfsFile(String id, String name, InputStream stream, FileMetadata metadata) {
  22 + this.id = id;
  23 + this.name = name;
  24 + this.stream = stream;
  25 + this.metadata = metadata;
  26 + }
  27 +
  28 + public String getId() {
  29 + return id;
  30 + }
  31 +
  32 + public String getName() {
  33 + return name;
  34 + }
  35 +
  36 + public InputStream getStream() {
  37 + return stream;
  38 + }
  39 +
  40 + public FileMetadata getMetadata() {
  41 + return metadata;
  42 + }
  43 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/domain/FileMetadata.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/domain/FileMetadata.java
  1 +package com.diligrp.assistant.dfs.domain;
  2 +
  3 +public class FileMetadata {
  4 + // MimeType or MediaType
  5 + private String mimeType;
  6 +
  7 + public FileMetadata(String mimeType) {
  8 + this.mimeType = mimeType;
  9 + }
  10 +
  11 + public String getMimeType() {
  12 + return mimeType;
  13 + }
  14 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/domain/FileRepositoryDTO.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/domain/FileRepositoryDTO.java
  1 +package com.diligrp.assistant.dfs.domain;
  2 +
  3 +public class FileRepositoryDTO {
  4 + // 名称
  5 + private String name;
  6 + // 文件存储通道
  7 + private Integer pipeline;
  8 + // 描述
  9 + private String description;
  10 +
  11 + public String getName() {
  12 + return name;
  13 + }
  14 +
  15 + public void setName(String name) {
  16 + this.name = name;
  17 + }
  18 +
  19 + public Integer getPipeline() {
  20 + return pipeline;
  21 + }
  22 +
  23 + public void setPipeline(Integer pipeline) {
  24 + this.pipeline = pipeline;
  25 + }
  26 +
  27 + public String getDescription() {
  28 + return description;
  29 + }
  30 +
  31 + public void setDescription(String description) {
  32 + this.description = description;
  33 + }
  34 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/exception/DfsServiceException.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/exception/DfsServiceException.java
  1 +package com.diligrp.assistant.dfs.exception;
  2 +
  3 +import com.diligrp.assistant.shared.exception.PlatformServiceException;
  4 +
  5 +public class DfsServiceException extends PlatformServiceException {
  6 + public DfsServiceException(String message) {
  7 + super(message);
  8 + }
  9 +
  10 + public DfsServiceException(int code, String message) {
  11 + super(code, message);
  12 + }
  13 +
  14 + public DfsServiceException(String message, Throwable ex) {
  15 + super(message, ex);
  16 + }
  17 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/model/FileObject.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/model/FileObject.java
  1 +package com.diligrp.assistant.dfs.model;
  2 +
  3 +import com.diligrp.assistant.shared.domain.BaseDo;
  4 +
  5 +import java.time.LocalDateTime;
  6 +
  7 +public class FileObject extends BaseDo {
  8 + // 仓库ID
  9 + private String repositoryId;
  10 + // 服务通道
  11 + private Integer pipeline;
  12 + // 文件ID
  13 + private String fileId;
  14 + // 文件名称
  15 + private String fileName;
  16 + // MIME类型
  17 + private String mimeType;
  18 + // 访问次数
  19 + private Integer hits;
  20 + // 文件状态
  21 + private Integer state;
  22 +
  23 + public String getRepositoryId() {
  24 + return repositoryId;
  25 + }
  26 +
  27 + public void setRepositoryId(String repositoryId) {
  28 + this.repositoryId = repositoryId;
  29 + }
  30 +
  31 + public Integer getPipeline() {
  32 + return pipeline;
  33 + }
  34 +
  35 + public void setPipeline(Integer pipeline) {
  36 + this.pipeline = pipeline;
  37 + }
  38 +
  39 + public String getFileId() {
  40 + return fileId;
  41 + }
  42 +
  43 + public void setFileId(String fileId) {
  44 + this.fileId = fileId;
  45 + }
  46 +
  47 + public String getFileName() {
  48 + return fileName;
  49 + }
  50 +
  51 + public void setFileName(String fileName) {
  52 + this.fileName = fileName;
  53 + }
  54 +
  55 + public String getMimeType() {
  56 + return mimeType;
  57 + }
  58 +
  59 + public void setMimeType(String mimeType) {
  60 + this.mimeType = mimeType;
  61 + }
  62 +
  63 + public Integer getHits() {
  64 + return hits;
  65 + }
  66 +
  67 + public void setHits(Integer hits) {
  68 + this.hits = hits;
  69 + }
  70 +
  71 + public Integer getState() {
  72 + return state;
  73 + }
  74 +
  75 + public void setState(Integer state) {
  76 + this.state = state;
  77 + }
  78 +
  79 + public static Builder builder() {
  80 + return new FileObject().new Builder();
  81 + }
  82 +
  83 + public class Builder {
  84 + public Builder repositoryId(String repositoryId) {
  85 + FileObject.this.repositoryId = repositoryId;
  86 + return this;
  87 + }
  88 +
  89 + public Builder pipeline(int pipeline) {
  90 + FileObject.this.pipeline = pipeline;
  91 + return this;
  92 + }
  93 +
  94 + public Builder fileId(String fileId) {
  95 + FileObject.this.fileId = fileId;
  96 + return this;
  97 + }
  98 +
  99 + public Builder fileName(String fileName) {
  100 + FileObject.this.fileName = fileName;
  101 + return this;
  102 + }
  103 +
  104 + public Builder mimeType(String mimeType) {
  105 + FileObject.this.mimeType = mimeType;
  106 + return this;
  107 + }
  108 +
  109 + public Builder hits(int hits) {
  110 + FileObject.this.hits = hits;
  111 + return this;
  112 + }
  113 +
  114 + public Builder state(int state) {
  115 + FileObject.this.state = state;
  116 + return this;
  117 + }
  118 +
  119 + public Builder createdTime(LocalDateTime createdTime) {
  120 + FileObject.this.createdTime = createdTime;
  121 + return this;
  122 + }
  123 +
  124 + public Builder modifiedTime(LocalDateTime modifiedTime) {
  125 + FileObject.this.modifiedTime = modifiedTime;
  126 + return this;
  127 + }
  128 +
  129 + public FileObject build() {
  130 + return FileObject.this;
  131 + }
  132 + }
  133 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/model/FileRepository.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/model/FileRepository.java
  1 +package com.diligrp.assistant.dfs.model;
  2 +
  3 +import com.diligrp.assistant.shared.domain.BaseDo;
  4 +
  5 +import java.time.LocalDateTime;
  6 +
  7 +public class FileRepository extends BaseDo {
  8 + // 文件仓库
  9 + private String repositoryId;
  10 + // 名称
  11 + private String name;
  12 + // 服务通道
  13 + private Integer pipeline;
  14 + // 描述
  15 + private String description;
  16 +
  17 + public String getRepositoryId() {
  18 + return repositoryId;
  19 + }
  20 +
  21 + public void setRepositoryId(String repositoryId) {
  22 + this.repositoryId = repositoryId;
  23 + }
  24 +
  25 + public Integer getPipeline() {
  26 + return pipeline;
  27 + }
  28 +
  29 + public void setPipeline(Integer pipeline) {
  30 + this.pipeline = pipeline;
  31 + }
  32 +
  33 + public String getName() {
  34 + return name;
  35 + }
  36 +
  37 + public void setName(String name) {
  38 + this.name = name;
  39 + }
  40 +
  41 + public String getDescription() {
  42 + return description;
  43 + }
  44 +
  45 + public void setDescription(String description) {
  46 + this.description = description;
  47 + }
  48 +
  49 + public static Builder builder() {
  50 + return new FileRepository().new Builder();
  51 + }
  52 +
  53 + public class Builder {
  54 + public Builder repositoryId(String repositoryId) {
  55 + FileRepository.this.repositoryId = repositoryId;
  56 + return this;
  57 + }
  58 +
  59 + public Builder name(String name) {
  60 + FileRepository.this.name = name;
  61 + return this;
  62 + }
  63 +
  64 + public Builder pipeline(int pipeline) {
  65 + FileRepository.this.pipeline = pipeline;
  66 + return this;
  67 + }
  68 +
  69 + public Builder description(String description) {
  70 + FileRepository.this.description = description;
  71 + return this;
  72 + }
  73 +
  74 + public Builder createdTime(LocalDateTime createdTime) {
  75 + FileRepository.this.createdTime = createdTime;
  76 + return this;
  77 + }
  78 +
  79 + public FileRepository build() {
  80 + return FileRepository.this;
  81 + }
  82 + }
  83 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/pipeline/DefaultDfsPipelineManager.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/pipeline/DefaultDfsPipelineManager.java
  1 +package com.diligrp.assistant.dfs.pipeline;
  2 +
  3 +import org.springframework.beans.factory.DisposableBean;
  4 +
  5 +import java.util.ArrayList;
  6 +import java.util.List;
  7 +
  8 +public class DefaultDfsPipelineManager implements DfsPipelineManager, DisposableBean {
  9 + private List<DfsPipeline> pipelines;
  10 +
  11 + public DefaultDfsPipelineManager() {
  12 + this.pipelines = new ArrayList<>();
  13 + }
  14 +
  15 + @Override
  16 + public void registerPipeline(DfsPipeline pipeline) {
  17 + this.pipelines.add(pipeline);
  18 + }
  19 +
  20 + @Override
  21 + public List<DfsPipeline> pipelines() {
  22 + return this.pipelines;
  23 + }
  24 +
  25 + @Override
  26 + public void destroy() {
  27 + for (DfsPipeline pipeline : pipelines) {
  28 + pipeline.destroy();
  29 + }
  30 + }
  31 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/pipeline/DfsPipeline.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/pipeline/DfsPipeline.java
  1 +package com.diligrp.assistant.dfs.pipeline;
  2 +
  3 +import com.diligrp.assistant.dfs.domain.DfsFile;
  4 +import com.diligrp.assistant.dfs.exception.DfsServiceException;
  5 +import com.diligrp.assistant.dfs.type.PipelineType;
  6 +import com.diligrp.assistant.shared.ErrorCode;
  7 +
  8 +public abstract class DfsPipeline {
  9 + // 通道编码
  10 + protected int code;
  11 + // 通道名称
  12 + protected String name;
  13 + // 通道类型
  14 + protected PipelineType type;
  15 +
  16 + public DfsPipeline(int code, String name, PipelineType type) {
  17 + this.code = code;
  18 + this.name = name;
  19 + this.type = type;
  20 + }
  21 +
  22 + /**
  23 + * 创建文件仓库,并返回仓库唯一标识:名称
  24 + *
  25 + * @param repositoryId - 仓库ID
  26 + * @return 仓库名称,同仓库ID
  27 + */
  28 + public String createFileRepository(String repositoryId) {
  29 + throw new DfsServiceException(ErrorCode.OPERATION_NOT_ALLOWED, "文件存储服务通道不支持此操作");
  30 + }
  31 +
  32 + /**
  33 + * 删除文件仓库 - 空仓库才允许删除
  34 + *
  35 + * @param repositoryId - 仓库ID
  36 + * @return true/false
  37 + */
  38 + public boolean deleteFileRepository(String repositoryId) {
  39 + throw new DfsServiceException(ErrorCode.OPERATION_NOT_ALLOWED, "文件存储服务通道不支持此操作");
  40 + }
  41 +
  42 + /**
  43 + * 存放文件到仓库
  44 + *
  45 + * @param repositoryId - 仓库ID
  46 + * @param file - 文件
  47 + */
  48 + public abstract void putFile(String repositoryId, DfsFile file);
  49 +
  50 + /**
  51 + * 从文件仓库中获取文件
  52 + *
  53 + * @param repositoryId - 仓库ID
  54 + * @param fileId - 文件标识
  55 + * @param style- OSS图片处理参数, 参见https://help.aliyun.com/zh/oss/user-guide/adjust-image-quality?spm=a2c4g.11186623.0.0.1c2d2759lemWct
  56 + * @return - 文件
  57 + */
  58 + public abstract DfsFile getFile(String repositoryId, String fileId, String style);
  59 +
  60 + /**
  61 + * 从文件仓库中删除文件
  62 + *
  63 + * @param repositoryId - 仓库ID
  64 + * @param fileId - 文件标识
  65 + * @return - true/false
  66 + */
  67 + public abstract boolean deleteFile(String repositoryId, String fileId);
  68 +
  69 + public void destroy() {
  70 + }
  71 +
  72 + /**
  73 + * 获取通道code
  74 + */
  75 + public int getCode() {
  76 + return this.code;
  77 + }
  78 +
  79 + /**
  80 + * 获取通道编码
  81 + */
  82 + public String getName() {
  83 + return this.name;
  84 + }
  85 +
  86 + /**
  87 + * 获取通道类型
  88 + */
  89 + public PipelineType getType() {
  90 + return this.type;
  91 + }
  92 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/pipeline/DfsPipelineManager.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/pipeline/DfsPipelineManager.java
  1 +package com.diligrp.assistant.dfs.pipeline;
  2 +
  3 +import com.diligrp.assistant.dfs.exception.DfsServiceException;
  4 +import com.diligrp.assistant.shared.ErrorCode;
  5 +
  6 +import java.util.List;
  7 +import java.util.Optional;
  8 +
  9 +public interface DfsPipelineManager {
  10 +
  11 + void registerPipeline(DfsPipeline pipeline);
  12 +
  13 + List<DfsPipeline> pipelines();
  14 +
  15 + default DfsPipeline findPipelineByCode(int code) {
  16 + Optional<DfsPipeline> pipeline = pipelines().stream().filter(p -> p.code == code).findAny();
  17 + return pipeline.orElseThrow(() -> new DfsServiceException(ErrorCode.OPERATION_NOT_ALLOWED, "系统未配置此文件存储服务通道"));
  18 + }
  19 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/pipeline/OssPipeline.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/pipeline/OssPipeline.java
  1 +package com.diligrp.assistant.dfs.pipeline;
  2 +
  3 +import com.diligrp.assistant.dfs.client.FileRepositoryClient;
  4 +import com.diligrp.assistant.dfs.client.OssFileRepositoryClient;
  5 +import com.diligrp.assistant.dfs.domain.DfsFile;
  6 +import com.diligrp.assistant.dfs.type.PipelineType;
  7 +
  8 +/**
  9 + * 阿里OSS文件存储服务通道
  10 + */
  11 +// TODO: 数据库中进行通道配置
  12 +public class OssPipeline extends DfsPipeline {
  13 +
  14 + private FileRepositoryClient fileRepositoryClient;
  15 +
  16 + public OssPipeline(int code, String name, String uri, String accessKeyId, String accessKeySecret) {
  17 + super(code, name, PipelineType.DFS_OSS);
  18 + this.fileRepositoryClient = new OssFileRepositoryClient(uri, accessKeyId, accessKeySecret);
  19 + }
  20 +
  21 + @Override
  22 + public String createFileRepository(String repositoryId) {
  23 + return fileRepositoryClient.createFileRepository(repositoryId);
  24 + }
  25 +
  26 + @Override
  27 + public boolean deleteFileRepository(String repositoryId) {
  28 + return fileRepositoryClient.deleteFileRepository(repositoryId);
  29 + }
  30 +
  31 + @Override
  32 + public void putFile(String repositoryId, DfsFile file) {
  33 + fileRepositoryClient.putFile(repositoryId, file);
  34 + }
  35 +
  36 + @Override
  37 + public DfsFile getFile(String repositoryId, String fileId, String style) {
  38 + return fileRepositoryClient.getFile(repositoryId, fileId, style);
  39 + }
  40 +
  41 + @Override
  42 + public boolean deleteFile(String repositoryId, String fileId) {
  43 + return fileRepositoryClient.deleteFile(repositoryId, fileId);
  44 + }
  45 +
  46 + public void destroy() {
  47 + fileRepositoryClient.destroy();
  48 + }
  49 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/service/FileRepositoryService.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/service/FileRepositoryService.java
  1 +package com.diligrp.assistant.dfs.service;
  2 +
  3 +import com.diligrp.assistant.dfs.domain.DfsAccessToken;
  4 +import com.diligrp.assistant.dfs.domain.DfsFile;
  5 +import com.diligrp.assistant.dfs.model.FileRepository;
  6 +
  7 +public interface FileRepositoryService {
  8 + String createFileRepository(DfsAccessToken accessToken, FileRepository repository);
  9 +
  10 + void deleteFileRepository(DfsAccessToken accessToken);
  11 +
  12 + String uploadFile(DfsAccessToken accessToken, DfsFile file);
  13 +
  14 + DfsFile downloadFile(DfsAccessToken accessToken, String fileId, String style);
  15 +
  16 + void deleteFile(DfsAccessToken accessToken, String fileId);
  17 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/service/impl/FileRepositoryServiceImpl.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/service/impl/FileRepositoryServiceImpl.java
  1 +package com.diligrp.assistant.dfs.service.impl;
  2 +
  3 +import com.diligrp.assistant.dfs.dao.FileObjectDao;
  4 +import com.diligrp.assistant.dfs.dao.FileRepositoryDao;
  5 +import com.diligrp.assistant.dfs.domain.DfsAccessToken;
  6 +import com.diligrp.assistant.dfs.domain.DfsFile;
  7 +import com.diligrp.assistant.dfs.model.FileObject;
  8 +import com.diligrp.assistant.dfs.model.FileRepository;
  9 +import com.diligrp.assistant.dfs.pipeline.DfsPipeline;
  10 +import com.diligrp.assistant.dfs.pipeline.DfsPipelineManager;
  11 +import com.diligrp.assistant.dfs.service.FileRepositoryService;
  12 +import com.diligrp.assistant.shared.service.ThreadPollService;
  13 +import jakarta.annotation.Resource;
  14 +import org.springframework.stereotype.Service;
  15 +import org.springframework.transaction.annotation.Transactional;
  16 +
  17 +import java.time.LocalDateTime;
  18 +
  19 +@Service("fileRepositoryService")
  20 +public class FileRepositoryServiceImpl implements FileRepositoryService {
  21 +
  22 + @Resource
  23 + private FileRepositoryDao fileRepositoryDao;
  24 +
  25 + @Resource
  26 + private FileObjectDao fileObjectDao;
  27 +
  28 + @Resource
  29 + private DfsPipelineManager dfsPipelineManager;
  30 +
  31 + /**
  32 + * 创建文件仓库,调用三方文件服务(如阿里OSS)创建Bucket存储
  33 + */
  34 + @Override
  35 + @Transactional(rollbackFor = Exception.class)
  36 + public String createFileRepository(DfsAccessToken accessToken, FileRepository repository) {
  37 + DfsPipeline pipeline = dfsPipelineManager.findPipelineByCode(accessToken.getPipeline());
  38 + fileRepositoryDao.insertFileRepository(repository);
  39 +
  40 + return pipeline.createFileRepository(repository.getRepositoryId());
  41 + }
  42 +
  43 + @Override
  44 + @Transactional(rollbackFor = Exception.class)
  45 + public void deleteFileRepository(DfsAccessToken accessToken) {
  46 + String repositoryId = accessToken.getRepositoryId();
  47 + DfsPipeline pipeline = dfsPipelineManager.findPipelineByCode(accessToken.getPipeline());
  48 + if (pipeline.deleteFileRepository(repositoryId)) {
  49 + fileRepositoryDao.deleteFileRepository(repositoryId);
  50 + }
  51 + }
  52 +
  53 + @Override
  54 + public String uploadFile(DfsAccessToken accessToken, DfsFile file) {
  55 + DfsPipeline pipeline = dfsPipelineManager.findPipelineByCode(accessToken.getPipeline());
  56 + String repositoryId = accessToken.getRepositoryId();
  57 + pipeline.putFile(repositoryId, file);
  58 + // 为提高文件上传性能,异步持久化文件对象
  59 + ThreadPollService.getInstance().submit(() -> {
  60 + LocalDateTime now = LocalDateTime.now();
  61 + FileObject fileObject = FileObject.builder().repositoryId(repositoryId).pipeline(pipeline.getCode())
  62 + .fileId(file.getId()).fileName(file.getName()).mimeType(file.getMetadata().getMimeType()).hits(0)
  63 + .state(0).createdTime(now).modifiedTime(now).build();
  64 + fileObjectDao.insertFileObject(fileObject);
  65 + });
  66 + return file.getId();
  67 + }
  68 +
  69 + @Override
  70 + public DfsFile downloadFile(DfsAccessToken accessToken, String fileId, String style) {
  71 + DfsPipeline pipeline = dfsPipelineManager.findPipelineByCode(accessToken.getPipeline());
  72 + DfsFile file = pipeline.getFile(accessToken.getRepositoryId(), fileId, style);
  73 + // 异步增加文件访问次数
  74 + ThreadPollService.getInstance().submit(() -> {
  75 + fileObjectDao.hitFileObject(fileId, LocalDateTime.now());
  76 + });
  77 + return file;
  78 + }
  79 +
  80 + @Override
  81 + @Transactional(rollbackFor = Exception.class)
  82 + public void deleteFile(DfsAccessToken accessToken, String fileId) {
  83 + DfsPipeline pipeline = dfsPipelineManager.findPipelineByCode(accessToken.getPipeline());
  84 + if (pipeline.deleteFile(accessToken.getRepositoryId(), fileId)) {
  85 + fileObjectDao.deleteFileObject(fileId);
  86 + }
  87 + }
  88 +}
... ...
assistant-dfs/src/main/java/com/diligrp/assistant/dfs/type/PipelineType.java 0 → 100644
  1 +++ a/assistant-dfs/src/main/java/com/diligrp/assistant/dfs/type/PipelineType.java
  1 +package com.diligrp.assistant.dfs.type;
  2 +
  3 +import com.diligrp.assistant.shared.type.IEnumType;
  4 +
  5 +import java.util.Arrays;
  6 +import java.util.List;
  7 +import java.util.Optional;
  8 +import java.util.stream.Stream;
  9 +
  10 +public enum PipelineType implements IEnumType {
  11 + DFS_OSS("阿里云对象存储服务", 1),
  12 +
  13 + DFS_MINIO("MINIO对象存储服务", 2);
  14 +
  15 + private String name;
  16 + private int code;
  17 +
  18 + PipelineType(String name, int code) {
  19 + this.name = name;
  20 + this.code = code;
  21 + }
  22 +
  23 + public static Optional<PipelineType> getType(int code) {
  24 + Stream<PipelineType> GENDERS = Arrays.stream(values());
  25 + return GENDERS.filter(type -> type.getCode() == code).findFirst();
  26 + }
  27 +
  28 + public static String getName(int code) {
  29 + Stream<PipelineType> TYPES = Arrays.stream(values());
  30 + Optional<String> result = TYPES.filter(type -> type.getCode() == code)
  31 + .map(PipelineType::getName).findFirst();
  32 + return result.isPresent() ? result.get() : null;
  33 + }
  34 +
  35 + public static List<PipelineType> getTypes() {
  36 + return Arrays.asList(values());
  37 + }
  38 +
  39 + public String getName() {
  40 + return this.name;
  41 + }
  42 +
  43 + public int getCode() {
  44 + return this.code;
  45 + }
  46 +
  47 + public String toString() {
  48 + return this.name;
  49 + }
  50 +}
0 51 \ No newline at end of file
... ...
assistant-dfs/src/main/resources/com/diligrp/assistant/dao/mapper/FileObjectDao.xml 0 → 100644
  1 +++ a/assistant-dfs/src/main/resources/com/diligrp/assistant/dao/mapper/FileObjectDao.xml
  1 +<?xml version="1.0" encoding="UTF-8" ?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3 + "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4 +
  5 +<mapper namespace="com.diligrp.assistant.dfs.dao.FileObjectDao">
  6 + <resultMap id="FileObjectMap" type="com.diligrp.assistant.dfs.model.FileObject">
  7 + <id column="id" property="id"/>
  8 + <result column="repository_id" property="repositoryId"/>
  9 + <result column="pipeline" property="pipeline"/>
  10 + <result column="file_id" property="fileId"/>
  11 + <result column="file_name" property="fileName"/>
  12 + <result column="mime_type" property="mimeType"/>
  13 + <result column="hits" property="hits"/>
  14 + <result column="state" property="state"/>
  15 + <result column="created_time" property="createdTime"/>
  16 + <result column="modified_time" property="modifiedTime"/>
  17 + </resultMap>
  18 +
  19 + <insert id="insertFileObject" parameterType="com.diligrp.assistant.dfs.model.FileObject">
  20 + INSERT INTO dfs_file_object
  21 + (repository_id, pipeline, file_id, file_name, mime_type, hits, state, created_time, modified_time)
  22 + VALUES
  23 + (#{repositoryId}, #{pipeline}, #{fileId}, #{fileName}, #{mimeType}, #{hits}, #{state}, #{createdTime}, #{modifiedTime})
  24 + </insert>
  25 +
  26 + <update id="hitFileObject">
  27 + UPDATE dfs_file_object SET hits = hits + 1, modified_time = #{modifiedTime}
  28 + WHERE file_id = #{fileId}
  29 + </update>
  30 +
  31 + <update id="deleteFileObject" parameterType="string">
  32 + DELETE FROM dfs_file_object WHERE file_id = #{fileId}
  33 + </update>
  34 +</mapper>
... ...
assistant-dfs/src/main/resources/com/diligrp/assistant/dao/mapper/FileRepositoryDao.xml 0 → 100644
  1 +++ a/assistant-dfs/src/main/resources/com/diligrp/assistant/dao/mapper/FileRepositoryDao.xml
  1 +<?xml version="1.0" encoding="UTF-8" ?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3 + "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4 +
  5 +<mapper namespace="com.diligrp.assistant.dfs.dao.FileRepositoryDao">
  6 + <resultMap id="FileRepositoryMap" type="com.diligrp.assistant.dfs.model.FileRepository">
  7 + <id column="id" property="id"/>
  8 + <result column="repository_id" property="repositoryId"/>
  9 + <result column="name" property="name"/>
  10 + <result column="pipeline" property="pipeline"/>
  11 + <result column="description" property="description"/>
  12 + <result column="created_time" property="createdTime"/>
  13 + </resultMap>
  14 +
  15 + <insert id="insertFileRepository" parameterType="com.diligrp.assistant.dfs.model.FileRepository">
  16 + INSERT INTO dfs_file_repository
  17 + (repository_id, name, pipeline, description, created_time)
  18 + VALUES
  19 + (#{repositoryId}, #{name}, #{pipeline}, #{description}, #{createdTime})
  20 + </insert>
  21 +
  22 + <select id="findFileRepositoryById" parameterType="string" resultMap="FileRepositoryMap">
  23 + SELECT * FROM dfs_file_repository WHERE repository_id = #{repositoryId}
  24 + </select>
  25 +
  26 + <update id="deleteFileRepository" parameterType="string">
  27 + DELETE FROM dfs_file_repository WHERE repository_id = #{repositoryId}
  28 + </update>
  29 +</mapper>
... ...
assistant-logging/build.gradle 0 → 100644
  1 +++ a/assistant-logging/build.gradle
  1 +group = 'com.diligrp'
  2 +archivesBaseName = 'assistant-logging'
  3 +
  4 +dependencies {
  5 + api project(':assistant-shared')
  6 +}
0 7 \ No newline at end of file
... ...
assistant-logging/src/main/java/com/diligrp/assistant/logging/LoggingConfiguration.java 0 → 100644
  1 +++ a/assistant-logging/src/main/java/com/diligrp/assistant/logging/LoggingConfiguration.java
  1 +package com.diligrp.assistant.logging;
  2 +
  3 +import org.springframework.context.annotation.ComponentScan;
  4 +import org.springframework.context.annotation.Configuration;
  5 +
  6 +@Configuration
  7 +@ComponentScan("com.diligrp.assistant.logging")
  8 +public class LoggingConfiguration {
  9 +}
... ...
assistant-product/build.gradle 0 → 100644
  1 +++ a/assistant-product/build.gradle
  1 +group = 'com.diligrp'
  2 +archivesBaseName = 'assistant-product'
  3 +
  4 +dependencies {
  5 + api project(':assistant-shared')
  6 +}
0 7 \ No newline at end of file
... ...
assistant-product/src/main/java/com/diligrp/assistant/product/ProductConfiguration.java 0 → 100644
  1 +++ a/assistant-product/src/main/java/com/diligrp/assistant/product/ProductConfiguration.java
  1 +package com.diligrp.assistant.product;
  2 +
  3 +import com.diligrp.assistant.shared.mybatis.MybatisMapperSupport;
  4 +import org.mybatis.spring.annotation.MapperScan;
  5 +import org.springframework.context.annotation.ComponentScan;
  6 +import org.springframework.context.annotation.Configuration;
  7 +
  8 +@Configuration
  9 +@ComponentScan("com.diligrp.assistant.product")
  10 +@MapperScan(basePackages = {"com.diligrp.assistant.product.dao"}, markerInterface = MybatisMapperSupport.class)
  11 +public class ProductConfiguration {
  12 +}
0 13 \ No newline at end of file
... ...
assistant-product/src/main/java/com/diligrp/assistant/product/controller/CategoryOpenApiController.java 0 → 100644
  1 +++ a/assistant-product/src/main/java/com/diligrp/assistant/product/controller/CategoryOpenApiController.java
  1 +package com.diligrp.assistant.product.controller;
  2 +
  3 +import com.diligrp.assistant.product.domain.CategoryVO;
  4 +import com.diligrp.assistant.product.domain.CategoryPageQuery;
  5 +import com.diligrp.assistant.product.domain.ListCategory;
  6 +import com.diligrp.assistant.product.model.Category;
  7 +import com.diligrp.assistant.product.service.ProductCategoryService;
  8 +import com.diligrp.assistant.shared.domain.Message;
  9 +import com.diligrp.assistant.shared.util.AssertUtils;
  10 +import jakarta.annotation.Resource;
  11 +import org.springframework.web.bind.annotation.RequestBody;
  12 +import org.springframework.web.bind.annotation.RequestMapping;
  13 +import org.springframework.web.bind.annotation.RequestParam;
  14 +import org.springframework.web.bind.annotation.RestController;
  15 +
  16 +import java.util.List;
  17 +import java.util.stream.Collectors;
  18 +
  19 +/**
  20 + * 对外接口控制器
  21 + */
  22 +@RestController
  23 +@RequestMapping(value = "/api/category")
  24 +public class CategoryOpenApiController {
  25 +
  26 + @Resource
  27 + private ProductCategoryService productCategoryService;
  28 +
  29 + @RequestMapping(value = "/findById.do")
  30 + public Message<CategoryVO> findById(@RequestParam("id") Long id) {
  31 + Category category = productCategoryService.findCategoryById(id);
  32 + return Message.success(CategoryVO.of(category.getId(), category.getParentId(),
  33 + category.getName(), category.getAlias(), category.getLevel(), category.getIcon(), category.getPath()));
  34 + }
  35 +
  36 + @RequestMapping(value = "/findParentById.do")
  37 + public Message<CategoryVO> findParentById(@RequestParam("id") Long id) {
  38 + Category category = productCategoryService.findParentCategoryById(id);
  39 + return Message.success(CategoryVO.of(category.getId(), category.getParentId(),
  40 + category.getName(), category.getAlias(), category.getLevel(), category.getIcon(), category.getPath()));
  41 + }
  42 +
  43 + @RequestMapping(value = "/listParentsById.do")
  44 + public Message<List<CategoryVO>> listParentsById(@RequestParam("id") Long id) {
  45 + List<CategoryVO> categories = productCategoryService.listParentsById(id).stream().map(category ->
  46 + CategoryVO.of(category.getId(), category.getParentId(), category.getName(), category.getAlias(),
  47 + category.getLevel(), category.getIcon(), category.getPath())).collect(Collectors.toList());
  48 + return Message.success(categories);
  49 + }
  50 +
  51 + @RequestMapping(value = "/listChildrenById.do")
  52 + public Message<List<CategoryVO>> findChildrenById(@RequestBody ListCategory request) {
  53 + AssertUtils.notNull(request.getId(), "id missed");
  54 + AssertUtils.notNull(request.getPageNo(), "pageNo missed");
  55 + AssertUtils.notNull(request.getPageSize(), "pageSize missed");
  56 +
  57 + CategoryPageQuery query = new CategoryPageQuery();
  58 + query.setId(request.getId());
  59 + query.from(request.getPageNo(), request.getPageSize());
  60 +
  61 + List<CategoryVO> categories = productCategoryService.listChildrenById(query).stream().map(category ->
  62 + CategoryVO.of(category.getId(), category.getParentId(), category.getName(), category.getAlias(),
  63 + category.getLevel(), category.getIcon(), category.getPath())).collect(Collectors.toList());
  64 + return Message.success(categories);
  65 + }
  66 +
  67 + @RequestMapping(value = "/listByLevel.do")
  68 + public Message<List<CategoryVO>> findByLevel(@RequestBody ListCategory request) {
  69 + AssertUtils.notNull(request.getLevel(), "level missed");
  70 + AssertUtils.notNull(request.getPageNo(), "pageNo missed");
  71 + AssertUtils.notNull(request.getPageSize(), "pageSize missed");
  72 +
  73 + CategoryPageQuery query = new CategoryPageQuery();
  74 + query.setLevel(request.getLevel());
  75 + query.from(request.getPageNo(), request.getPageSize());
  76 +
  77 + List<CategoryVO> categories = productCategoryService.listCategoriesByLevel(query).stream().map(category ->
  78 + CategoryVO.of(category.getId(), category.getParentId(), category.getName(), category.getAlias(),
  79 + category.getLevel(), category.getIcon(), category.getPath())).collect(Collectors.toList());
  80 + return Message.success(categories);
  81 + }
  82 +}
... ...
assistant-product/src/main/java/com/diligrp/assistant/product/controller/ProductCategoryController.java 0 → 100644
  1 +++ a/assistant-product/src/main/java/com/diligrp/assistant/product/controller/ProductCategoryController.java
  1 +package com.diligrp.assistant.product.controller;
  2 +
  3 +import com.diligrp.assistant.product.domain.CategoryDTO;
  4 +import com.diligrp.assistant.product.service.ProductCategoryService;
  5 +import com.diligrp.assistant.shared.domain.Message;
  6 +import com.diligrp.assistant.shared.util.AssertUtils;
  7 +import jakarta.annotation.Resource;
  8 +import org.springframework.web.bind.annotation.RequestBody;
  9 +import org.springframework.web.bind.annotation.RequestMapping;
  10 +import org.springframework.web.bind.annotation.RequestParam;
  11 +import org.springframework.web.bind.annotation.RestController;
  12 +
  13 +@RestController
  14 +@RequestMapping(value = "/category")
  15 +public class ProductCategoryController {
  16 +
  17 + @Resource
  18 + private ProductCategoryService productCategoryService;
  19 +
  20 + @RequestMapping(value = "/create.do")
  21 + public Message<?> create(@RequestBody CategoryDTO request) {
  22 + AssertUtils.notNull(request.getParentId(), "parentId missed");
  23 + AssertUtils.notEmpty(request.getName(), "name missed");
  24 +
  25 + Long id = productCategoryService.createCategory(request);
  26 + return Message.success(id);
  27 + }
  28 +
  29 + @RequestMapping(value = "/update.do")
  30 + public Message<?> update(@RequestBody CategoryDTO request) {
  31 + AssertUtils.notNull(request.getId(), "id missed");
  32 +
  33 + productCategoryService.updateCategory(request);
  34 + return Message.success();
  35 + }
  36 +
  37 + @RequestMapping(value = "/delete.do")
  38 + public Message<?> delete(@RequestParam Long id) {
  39 + productCategoryService.deleteCategory(id);
  40 + return Message.success();
  41 + }
  42 +}
... ...
assistant-product/src/main/java/com/diligrp/assistant/product/dao/ProductCategoryDao.java 0 → 100644
  1 +++ a/assistant-product/src/main/java/com/diligrp/assistant/product/dao/ProductCategoryDao.java
  1 +package com.diligrp.assistant.product.dao;
  2 +
  3 +import com.diligrp.assistant.product.domain.CategoryPageQuery;
  4 +import com.diligrp.assistant.product.model.Category;
  5 +import com.diligrp.assistant.shared.mybatis.MybatisMapperSupport;
  6 +import org.springframework.stereotype.Repository;
  7 +
  8 +import java.util.List;
  9 +import java.util.Optional;
  10 +
  11 +@Repository("productCategoryDao")
  12 +public interface ProductCategoryDao extends MybatisMapperSupport {
  13 + void insertCategory(Category category);
  14 +
  15 + int updateCategory(Category category);
  16 +
  17 + Optional<Category> findCategoryById(Long id);
  18 +
  19 + Optional<Category> findParentCategoryById(Long id);
  20 +
  21 + List<Category> findCategoriesByIds(List<Long> ids);
  22 +
  23 + List<Category> listChildrenById(CategoryPageQuery query);
  24 +
  25 + Long countChildrenById(Long id);
  26 +
  27 + List<Category> listCategoriesByLevel(CategoryPageQuery query);
  28 +}
... ...
assistant-product/src/main/java/com/diligrp/assistant/product/domain/CategoryDTO.java 0 → 100644
  1 +++ a/assistant-product/src/main/java/com/diligrp/assistant/product/domain/CategoryDTO.java
  1 +package com.diligrp.assistant.product.domain;
  2 +
  3 +public class CategoryDTO {
  4 + // 品类ID
  5 + private Long id;
  6 + // 父品类ID
  7 + private Long parentId;
  8 + // 名称
  9 + private String name;
  10 + // 别名
  11 + private String alias;
  12 + // 拼音
  13 + private String pyCode;
  14 + // 简拼
  15 + private String shortCode;
  16 + // 路径
  17 + private String path;
  18 + // 图标
  19 + private String icon;
  20 +
  21 + public Long getId() {
  22 + return id;
  23 + }
  24 +
  25 + public void setId(Long id) {
  26 + this.id = id;
  27 + }
  28 +
  29 + public Long getParentId() {
  30 + return parentId;
  31 + }
  32 +
  33 + public void setParentId(Long parentId) {
  34 + this.parentId = parentId;
  35 + }
  36 +
  37 + public String getName() {
  38 + return name;
  39 + }
  40 +
  41 + public void setName(String name) {
  42 + this.name = name;
  43 + }
  44 +
  45 + public String getAlias() {
  46 + return alias;
  47 + }
  48 +
  49 + public void setAlias(String alias) {
  50 + this.alias = alias;
  51 + }
  52 +
  53 + public String getPyCode() {
  54 + return pyCode;
  55 + }
  56 +
  57 + public void setPyCode(String pyCode) {
  58 + this.pyCode = pyCode;
  59 + }
  60 +
  61 + public String getShortCode() {
  62 + return shortCode;
  63 + }
  64 +
  65 + public void setShortCode(String shortCode) {
  66 + this.shortCode = shortCode;
  67 + }
  68 +
  69 + public String getPath() {
  70 + return path;
  71 + }
  72 +
  73 + public void setPath(String path) {
  74 + this.path = path;
  75 + }
  76 +
  77 + public String getIcon() {
  78 + return icon;
  79 + }
  80 +
  81 + public void setIcon(String icon) {
  82 + this.icon = icon;
  83 + }
  84 +}
... ...
assistant-product/src/main/java/com/diligrp/assistant/product/domain/CategoryPageQuery.java 0 → 100644
  1 +++ a/assistant-product/src/main/java/com/diligrp/assistant/product/domain/CategoryPageQuery.java
  1 +package com.diligrp.assistant.product.domain;
  2 +
  3 +import com.diligrp.assistant.shared.domain.PageQuery;
  4 +
  5 +public class CategoryPageQuery extends PageQuery {
  6 + // 区域ID
  7 + private Long id;
  8 + // 区域级别
  9 + private Integer level;
  10 +
  11 + public Long getId() {
  12 + return id;
  13 + }
  14 +
  15 + public void setId(Long id) {
  16 + this.id = id;
  17 + }
  18 +
  19 + public Integer getLevel() {
  20 + return level;
  21 + }
  22 +
  23 + public void setLevel(Integer level) {
  24 + this.level = level;
  25 + }
  26 +}
... ...
assistant-product/src/main/java/com/diligrp/assistant/product/domain/CategoryVO.java 0 → 100644
  1 +++ a/assistant-product/src/main/java/com/diligrp/assistant/product/domain/CategoryVO.java
  1 +package com.diligrp.assistant.product.domain;
  2 +
  3 +public class CategoryVO {
  4 + // ID
  5 + private Long id;
  6 + // 父品类ID
  7 + private Long parentId;
  8 + // 名称
  9 + private String name;
  10 + // 别名
  11 + private String alias;
  12 + // 级别
  13 + private Integer level;
  14 + // 图标
  15 + private String icon;
  16 + // 路径
  17 + private String path;
  18 +
  19 + public static CategoryVO of(Long id, Long parentId, String name, String alias, Integer level, String icon, String path) {
  20 + CategoryVO category = new CategoryVO();
  21 + category.id = id;
  22 + category.parentId = parentId;
  23 + category.name = name;
  24 + category.alias = alias;
  25 + category.level = level;
  26 + category.icon = icon;
  27 + category.path = path;
  28 +
  29 + return category;
  30 + }
  31 +
  32 + public Long getId() {
  33 + return id;
  34 + }
  35 +
  36 + public void setId(Long id) {
  37 + this.id = id;
  38 + }
  39 +
  40 + public Long getParentId() {
  41 + return parentId;
  42 + }
  43 +
  44 + public void setParentId(Long parentId) {
  45 + this.parentId = parentId;
  46 + }
  47 +
  48 + public String getName() {
  49 + return name;
  50 + }
  51 +
  52 + public void setName(String name) {
  53 + this.name = name;
  54 + }
  55 +
  56 + public String getAlias() {
  57 + return alias;
  58 + }
  59 +
  60 + public void setAlias(String alias) {
  61 + this.alias = alias;
  62 + }
  63 +
  64 + public Integer getLevel() {
  65 + return level;
  66 + }
  67 +
  68 + public void setLevel(Integer level) {
  69 + this.level = level;
  70 + }
  71 +
  72 + public String getIcon() {
  73 + return icon;
  74 + }
  75 +
  76 + public void setIcon(String icon) {
  77 + this.icon = icon;
  78 + }
  79 +
  80 + public String getPath() {
  81 + return path;
  82 + }
  83 +
  84 + public void setPath(String path) {
  85 + this.path = path;
  86 + }
  87 +}
... ...
assistant-product/src/main/java/com/diligrp/assistant/product/domain/ListCategory.java 0 → 100644
  1 +++ a/assistant-product/src/main/java/com/diligrp/assistant/product/domain/ListCategory.java
  1 +package com.diligrp.assistant.product.domain;
  2 +
  3 +public class ListCategory {
  4 + // 页号
  5 + private Integer pageNo = 1;
  6 + // 每页记录数
  7 + private Integer pageSize = 20;
  8 +
  9 + // 品类ID
  10 + private Long id;
  11 + // 品类级别
  12 + private Integer level;
  13 +
  14 + public Integer getPageNo() {
  15 + return pageNo;
  16 + }
  17 +
  18 + public void setPageNo(Integer pageNo) {
  19 + this.pageNo = pageNo;
  20 + }
  21 +
  22 + public Integer getPageSize() {
  23 + return pageSize;
  24 + }
  25 +
  26 + public void setPageSize(Integer pageSize) {
  27 + this.pageSize = pageSize;
  28 + }
  29 +
  30 + public Long getId() {
  31 + return id;
  32 + }
  33 +
  34 + public void setId(Long id) {
  35 + this.id = id;
  36 + }
  37 +
  38 + public Integer getLevel() {
  39 + return level;
  40 + }
  41 +
  42 + public void setLevel(Integer level) {
  43 + this.level = level;
  44 + }
  45 +}
... ...
assistant-product/src/main/java/com/diligrp/assistant/product/exception/CategoryServiceException.java 0 → 100644
  1 +++ a/assistant-product/src/main/java/com/diligrp/assistant/product/exception/CategoryServiceException.java
  1 +package com.diligrp.assistant.product.exception;
  2 +
  3 +import com.diligrp.assistant.shared.exception.PlatformServiceException;
  4 +
  5 +public class CategoryServiceException extends PlatformServiceException {
  6 + public CategoryServiceException(String message) {
  7 + super(message);
  8 + }
  9 +
  10 + public CategoryServiceException(int code, String message) {
  11 + super(code, message);
  12 + }
  13 +
  14 + public CategoryServiceException(String message, Throwable ex) {
  15 + super(message, ex);
  16 + }
  17 +}
... ...
assistant-product/src/main/java/com/diligrp/assistant/product/model/Category.java 0 → 100644
  1 +++ a/assistant-product/src/main/java/com/diligrp/assistant/product/model/Category.java
  1 +package com.diligrp.assistant.product.model;
  2 +
  3 +import com.diligrp.assistant.shared.domain.BaseDo;
  4 +
  5 +import java.time.LocalDateTime;
  6 +
  7 +public class Category extends BaseDo {
  8 + // 父品类ID
  9 + private Long parentId;
  10 + // 名称
  11 + private String name;
  12 + // 别名
  13 + private String alias;
  14 + // 级别
  15 + private Integer level;
  16 + // 拼音
  17 + private String pyCode;
  18 + // 简拼
  19 + private String shortCode;
  20 + // 路径
  21 + private String path;
  22 + // 图标
  23 + private String icon;
  24 + // 状态
  25 + private Integer state;
  26 +
  27 + public Long getParentId() {
  28 + return parentId;
  29 + }
  30 +
  31 + public void setParentId(Long parentId) {
  32 + this.parentId = parentId;
  33 + }
  34 +
  35 + public String getName() {
  36 + return name;
  37 + }
  38 +
  39 + public void setName(String name) {
  40 + this.name = name;
  41 + }
  42 +
  43 + public String getAlias() {
  44 + return alias;
  45 + }
  46 +
  47 + public void setAlias(String alias) {
  48 + this.alias = alias;
  49 + }
  50 +
  51 + public Integer getLevel() {
  52 + return level;
  53 + }
  54 +
  55 + public void setLevel(Integer level) {
  56 + this.level = level;
  57 + }
  58 +
  59 + public String getPyCode() {
  60 + return pyCode;
  61 + }
  62 +
  63 + public void setPyCode(String pyCode) {
  64 + this.pyCode = pyCode;
  65 + }
  66 +
  67 + public String getShortCode() {
  68 + return shortCode;
  69 + }
  70 +
  71 + public void setShortCode(String shortCode) {
  72 + this.shortCode = shortCode;
  73 + }
  74 +
  75 + public String getPath() {
  76 + return path;
  77 + }
  78 +
  79 + public void setPath(String path) {
  80 + this.path = path;
  81 + }
  82 +
  83 + public String getIcon() {
  84 + return icon;
  85 + }
  86 +
  87 + public void setIcon(String icon) {
  88 + this.icon = icon;
  89 + }
  90 +
  91 + public Integer getState() {
  92 + return state;
  93 + }
  94 +
  95 + public void setState(Integer state) {
  96 + this.state = state;
  97 + }
  98 +
  99 + public static Builder builder() {
  100 + return new Category().new Builder();
  101 + }
  102 +
  103 + public class Builder {
  104 + public Builder parentId(Long parentId) {
  105 + Category.this.parentId = parentId;
  106 + return this;
  107 + }
  108 +
  109 + public Builder name(String name) {
  110 + Category.this.name = name;
  111 + return this;
  112 + }
  113 +
  114 + public Builder alias(String alias) {
  115 + Category.this.alias = alias;
  116 + return this;
  117 + }
  118 +
  119 + public Builder level(Integer level) {
  120 + Category.this.level = level;
  121 + return this;
  122 + }
  123 +
  124 + public Builder pyCode(String pyCode) {
  125 + Category.this.pyCode = pyCode;
  126 + return this;
  127 + }
  128 +
  129 + public Builder shortCode(String shortCode) {
  130 + Category.this.shortCode = shortCode;
  131 + return this;
  132 + }
  133 +
  134 + public Builder path(String path) {
  135 + Category.this.path = path;
  136 + return this;
  137 + }
  138 +
  139 + public Builder icon(String icon) {
  140 + Category.this.icon = icon;
  141 + return this;
  142 + }
  143 +
  144 + public Builder state(Integer state) {
  145 + Category.this.state = state;
  146 + return this;
  147 + }
  148 +
  149 + public Builder version(Integer version) {
  150 + Category.this.version = version;
  151 + return this;
  152 + }
  153 +
  154 + public Builder createdTime(LocalDateTime createdTime) {
  155 + Category.this.createdTime = createdTime;
  156 + return this;
  157 + }
  158 +
  159 + public Builder modifiedTime(LocalDateTime modifiedTime) {
  160 + Category.this.modifiedTime = modifiedTime;
  161 + return this;
  162 + }
  163 +
  164 + public Category build() {
  165 + return Category.this;
  166 + }
  167 + }
  168 +}
... ...
assistant-product/src/main/java/com/diligrp/assistant/product/service/ProductCategoryService.java 0 → 100644
  1 +++ a/assistant-product/src/main/java/com/diligrp/assistant/product/service/ProductCategoryService.java
  1 +package com.diligrp.assistant.product.service;
  2 +
  3 +import com.diligrp.assistant.product.domain.CategoryDTO;
  4 +import com.diligrp.assistant.product.domain.CategoryPageQuery;
  5 +import com.diligrp.assistant.product.model.Category;
  6 +
  7 +import java.util.List;
  8 +
  9 +public interface ProductCategoryService {
  10 + /**
  11 + * 创建品类
  12 + *
  13 + * @param request - 品类
  14 + * @return 品类ID
  15 + */
  16 + long createCategory(CategoryDTO request);
  17 +
  18 + /**
  19 + * 修改品类
  20 + *
  21 + * @param request - 品类
  22 + */
  23 + void updateCategory(CategoryDTO request);
  24 +
  25 + /**
  26 + * 删除指定的品类
  27 + *
  28 + * @param id - 品类ID
  29 + */
  30 + void deleteCategory(Long id);
  31 +
  32 + /**
  33 + * 根据ID查询品类
  34 + *
  35 + * @param id - 品类ID
  36 + * @return 品类
  37 + */
  38 + Category findCategoryById(Long id);
  39 +
  40 + /**
  41 + * 根据ID查询父级品类
  42 + *
  43 + * @param id - 品类ID
  44 + * @return 父级品类
  45 + */
  46 + Category findParentCategoryById(Long id);
  47 +
  48 + /**
  49 + * 根据ID查询所有的祖先品类,查询结果包含自身且按照等级降序排序
  50 + *
  51 + * @param id - 品类ID
  52 + * @return 祖先品类
  53 + */
  54 + List<Category> listParentsById(Long id);
  55 +
  56 + /**
  57 + * 根据父品类ID分页查询子品类
  58 + *
  59 + * @param query 查询条件
  60 + * @return 子品类
  61 + */
  62 + List<Category> listChildrenById(CategoryPageQuery query);
  63 +
  64 + /**
  65 + * 根据品类级别分页查询品类
  66 + *
  67 + * @param query 查询条件
  68 + * @return 品类
  69 + */
  70 + List<Category> listCategoriesByLevel(CategoryPageQuery query);
  71 +}
... ...
assistant-product/src/main/java/com/diligrp/assistant/product/service/impl/ProductCategoryServiceImpl.java 0 → 100644
  1 +++ a/assistant-product/src/main/java/com/diligrp/assistant/product/service/impl/ProductCategoryServiceImpl.java
  1 +package com.diligrp.assistant.product.service.impl;
  2 +
  3 +import com.diligrp.assistant.product.dao.ProductCategoryDao;
  4 +import com.diligrp.assistant.product.domain.CategoryDTO;
  5 +import com.diligrp.assistant.product.domain.CategoryPageQuery;
  6 +import com.diligrp.assistant.product.exception.CategoryServiceException;
  7 +import com.diligrp.assistant.product.model.Category;
  8 +import com.diligrp.assistant.product.service.ProductCategoryService;
  9 +import com.diligrp.assistant.shared.ErrorCode;
  10 +import jakarta.annotation.Resource;
  11 +import org.springframework.stereotype.Service;
  12 +import org.springframework.transaction.annotation.Transactional;
  13 +
  14 +import java.time.LocalDateTime;
  15 +import java.util.*;
  16 +
  17 +@Service("productCategoryService")
  18 +public class ProductCategoryServiceImpl implements ProductCategoryService {
  19 +
  20 + @Resource
  21 + private ProductCategoryDao productCategoryDao;
  22 +
  23 + @Override
  24 + @Transactional(rollbackFor = Exception.class)
  25 + public long createCategory(CategoryDTO request) {
  26 + LocalDateTime now = LocalDateTime.now();
  27 + Category parent = findCategoryById(request.getParentId());
  28 + Category category = Category.builder().parentId(request.getParentId()).name(request.getName())
  29 + .alias(request.getAlias()).level(parent.getLevel() + 1).pyCode(request.getPyCode())
  30 + .shortCode(request.getShortCode()).path(parent.getPath()).icon(request.getIcon()).state(1).version(0)
  31 + .createdTime(now).modifiedTime(now).build();
  32 + productCategoryDao.insertCategory(category);
  33 +
  34 + // 更新路径信息, 将自身ID添加到路径信息中
  35 + String path = parent.getPath() + "," + category.getId();
  36 + Category update = new Category();
  37 + update.setId(category.getId());
  38 + update.setPath(path);
  39 + productCategoryDao.updateCategory(update);
  40 + return category.getId();
  41 + }
  42 +
  43 + @Override
  44 + @Transactional(rollbackFor = Exception.class)
  45 + public void updateCategory(CategoryDTO request) {
  46 + LocalDateTime now = LocalDateTime.now();
  47 + Category category = Category.builder().name(request.getName())
  48 + .alias(request.getAlias()).pyCode(request.getPyCode()).shortCode(request.getShortCode())
  49 + .icon(request.getIcon()).modifiedTime(now).build();
  50 + category.setId(request.getId());
  51 + if (productCategoryDao.updateCategory(category) == 0) {
  52 + throw new CategoryServiceException(ErrorCode.OBJECT_NOT_FOUND, "品类不存在");
  53 + }
  54 + }
  55 +
  56 + @Override
  57 + @Transactional(rollbackFor = Exception.class)
  58 + public void deleteCategory(Long id) {
  59 + LocalDateTime now = LocalDateTime.now();
  60 + long children = productCategoryDao.countChildrenById(id);
  61 + if (children > 0) {
  62 + throw new CategoryServiceException(ErrorCode.OPERATION_NOT_ALLOWED, "该品类不能被删除: 存在子品类");
  63 + }
  64 +
  65 + Category update = new Category();
  66 + update.setId(id);
  67 + update.setState(0);
  68 + update.setModifiedTime(now);
  69 + if (productCategoryDao.updateCategory(update) == 0) {
  70 + throw new CategoryServiceException(ErrorCode.OBJECT_NOT_FOUND, "该品类删除失败: 品类不存在");
  71 + }
  72 + }
  73 +
  74 + @Override
  75 + public Category findCategoryById(Long id) {
  76 + return productCategoryDao.findCategoryById(id).orElseThrow(() ->
  77 + new CategoryServiceException(ErrorCode.OBJECT_NOT_FOUND, "品类不存在"));
  78 + }
  79 +
  80 + @Override
  81 + public Category findParentCategoryById(Long id) {
  82 + return productCategoryDao.findParentCategoryById(id).orElseThrow(() ->
  83 + new CategoryServiceException(ErrorCode.OBJECT_NOT_FOUND, "品类不存在"));
  84 + }
  85 +
  86 + @Override
  87 + public List<Category> listParentsById(Long id) {
  88 + Category self = findCategoryById(id);
  89 + String path = self.getPath();
  90 +
  91 + if (Objects.nonNull(path)) {
  92 + List<Long> ids = new ArrayList<>();
  93 + StringTokenizer tokenizer = new StringTokenizer(path, ",");
  94 +
  95 + while (tokenizer.hasMoreTokens()) {
  96 + ids.add(Long.parseLong(tokenizer.nextToken()));
  97 + }
  98 + if (ids.isEmpty()) {
  99 + ids.add(id);
  100 + }
  101 +
  102 + return productCategoryDao.findCategoriesByIds(ids);
  103 + } else {
  104 + return Collections.singletonList(self);
  105 + }
  106 + }
  107 +
  108 + @Override
  109 + public List<Category> listChildrenById(CategoryPageQuery query) {
  110 + return productCategoryDao.listChildrenById(query);
  111 + }
  112 +
  113 + @Override
  114 + public List<Category> listCategoriesByLevel(CategoryPageQuery query) {
  115 + return productCategoryDao.listCategoriesByLevel(query);
  116 + }
  117 +}
... ...
assistant-product/src/main/resources/com/diligrp/assistant/dao/mapper/ProductCategoryDao.xml 0 → 100644
  1 +++ a/assistant-product/src/main/resources/com/diligrp/assistant/dao/mapper/ProductCategoryDao.xml
  1 +<?xml version="1.0" encoding="UTF-8" ?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3 + "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4 +
  5 +<mapper namespace="com.diligrp.assistant.product.dao.ProductCategoryDao">
  6 + <resultMap id="CategoryMap" type="com.diligrp.assistant.product.model.Category">
  7 + <id column="id" property="id"/>
  8 + <result column="parent_id" property="parentId"/>
  9 + <result column="name" property="name"/>
  10 + <result column="alias" property="alias"/>
  11 + <result column="level" property="level"/>
  12 + <result column="py_code" property="pyCode"/>
  13 + <result column="short_code" property="shortCode"/>
  14 + <result column="path" property="path"/>
  15 + <result column="icon" property="icon"/>
  16 + <result column="state" property="state"/>
  17 + <result column="version" property="version"/>
  18 + <result column="created_time" property="createdTime"/>
  19 + <result column="modified_time" property="modifiedTime"/>
  20 + </resultMap>
  21 +
  22 + <insert id="insertCategory" parameterType="com.diligrp.assistant.product.model.Category" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
  23 + INSERT INTO product_category(`parent_id`, `name`, `alias`, `level`, `py_code`, `short_code`, `path`, `icon`, `state`, `version`, `created_time`, `modified_time`)
  24 + VALUES (#{parentId}, #{name}, #{alias}, #{level}, #{pyCode}, #{shortCode}, #{path}, #{icon}, #{state}, #{version}, #{createdTime}, #{modifiedTime})
  25 + </insert>
  26 +
  27 + <update id="updateCategory" parameterType="com.diligrp.assistant.product.model.Category">
  28 + UPDATE product_category SET version = version + 1
  29 + <if test="name != null">
  30 + , name = #{name}
  31 + </if>
  32 + <if test="alias != null">
  33 + , alias = #{alias}
  34 + </if>
  35 + <if test="pyCode != null">
  36 + , py_code = #{pyCode}
  37 + </if>
  38 + <if test="shortCode != null">
  39 + , short_code = #{shortCode}
  40 + </if>
  41 + <if test="path != null">
  42 + , path = #{path}
  43 + </if>
  44 + <if test="icon != null">
  45 + , icon = #{icon}
  46 + </if>
  47 + <if test="state != null">
  48 + , state = #{state}
  49 + </if>
  50 + <if test="modifiedTime != null">
  51 + , modified_time = #{modifiedTime}
  52 + </if>
  53 + WHERE id = #{id}
  54 + </update>
  55 +
  56 + <select id="findCategoryById" parameterType="long" resultMap="CategoryMap">
  57 + SELECT * FROM product_category WHERE id = #{id} AND state &gt; 0
  58 + </select>
  59 +
  60 + <select id="findParentCategoryById" parameterType="long" resultMap="CategoryMap">
  61 + SELECT
  62 + parent.*
  63 + FROM product_category self
  64 + INNER JOIN product_category parent ON self.parent_id = parent.id
  65 + WHERE self.id = #{id} AND self.state &gt; 0
  66 + </select>
  67 +
  68 + <select id="findCategoriesByIds" resultMap="CategoryMap">
  69 + SELECT * FROM product_category WHERE id IN
  70 + <foreach item="id" collection="list" open="(" separator="," close=")">
  71 + #{id}
  72 + </foreach>
  73 + AND state &gt; 0 ORDER BY level DESC, id
  74 + </select>
  75 +
  76 + <select id="listChildrenById" parameterType="com.diligrp.assistant.product.domain.CategoryPageQuery" resultMap="CategoryMap">
  77 + SELECT * FROM product_category WHERE parent_Id = #{id} AND state &gt; 0
  78 + ORDER BY ID LIMIT #{start}, #{limit}
  79 + </select>
  80 +
  81 + <select id="countChildrenById" parameterType="long" resultType="long">
  82 + SELECT COUNT(*) FROM product_category WHERE parent_Id = #{id} AND state &gt; 0
  83 + </select>
  84 +
  85 + <select id="listCategoriesByLevel" parameterType="com.diligrp.assistant.product.domain.CategoryPageQuery" resultMap="CategoryMap">
  86 + SELECT * FROM product_category WHERE level = #{level} AND state &gt; 0
  87 + ORDER BY ID LIMIT #{start}, #{limit}
  88 + </select>
  89 +</mapper>
... ...
assistant-shared/build.gradle 0 → 100644
  1 +++ a/assistant-shared/build.gradle
  1 + group = 'com.diligrp'
  2 +archivesBaseName = 'assistant-shared'
  3 +
  4 +dependencies {
  5 +// api 'org.springframework.boot:spring-boot-starter-data-mongodb'
  6 + api 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.2'
  7 + api 'org.springframework.boot:spring-boot-starter-data-redis'
  8 + //api 'org.redisson:redisson-spring-boot-starter:3.23.1'
  9 + api 'org.springframework.boot:spring-boot-starter-amqp'
  10 + api 'org.springframework.cloud:spring-cloud-starter-loadbalancer'
  11 + api 'org.springframework.cloud:spring-cloud-starter-openfeign'
  12 + api 'com.github.ben-manes.caffeine:caffeine:3.1.8'
  13 +
  14 + runtimeOnly 'mysql:mysql-connector-java:8.0.33'
  15 +}
0 16 \ No newline at end of file
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/Constants.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/Constants.java
  1 +package com.diligrp.assistant.shared;
  2 +
  3 +public final class Constants {
  4 + public static final String SIGN_ALGORITHM = "SHA1WithRSA";
  5 +
  6 + public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
  7 +
  8 + public static final String DATE_FORMAT = "yyyy-MM-dd";
  9 +
  10 + public static final String TIME_FORMAT = "HH:mm:ss";
  11 +
  12 + public static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() + 1;
  13 +
  14 + public static final int MAX_POOL_SIZE = 200;
  15 +}
0 16 \ No newline at end of file
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/ErrorCode.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/ErrorCode.java
  1 +package com.diligrp.assistant.shared;
  2 +
  3 +/**
  4 + * 系统错误码列表 - 错误码前三位用于区分模块
  5 + */
  6 +public class ErrorCode {
  7 + // 系统未知异常
  8 + public static final int SYSTEM_UNKNOWN_ERROR = 100000;
  9 + // 无效参数错误
  10 + public static final int ILLEGAL_ARGUMENT_ERROR = 100001;
  11 + // 访问未授权
  12 + public static final int UNAUTHORIZED_ACCESS_ERROR = 100002;
  13 + // 操作不允许
  14 + public static final int OPERATION_NOT_ALLOWED = 100003;
  15 + // 对象不存在
  16 + public static final int OBJECT_NOT_FOUND = 100004;
  17 + // 对象已存在
  18 + public static final int OBJECT_ALREADY_EXISTS = 100005;
  19 + // 远程服务访问错误
  20 + public static final int SERVICE_ACCESS_ERROR = 101000;
  21 +
  22 + public static final String MESSAGE_UNKNOWN_ERROR = "基础服务系统未知异常,请联系管理员";
  23 +
  24 + public static final String MESSAGE_ACCESS_DENIED = "未授权的系统访问";
  25 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/SharedConfiguration.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/SharedConfiguration.java
  1 +package com.diligrp.assistant.shared;
  2 +
  3 +import com.diligrp.assistant.shared.exception.PlatformServiceException;
  4 +import com.diligrp.assistant.shared.security.RsaCipher;
  5 +import com.diligrp.assistant.shared.util.JsonUtils;
  6 +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
  7 +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
  8 +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
  9 +import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
  10 +import org.springframework.context.annotation.Bean;
  11 +import org.springframework.context.annotation.ComponentScan;
  12 +import org.springframework.context.annotation.Configuration;
  13 +import org.springframework.core.convert.converter.Converter;
  14 +import org.springframework.data.redis.connection.RedisConnectionFactory;
  15 +import org.springframework.data.redis.core.RedisTemplate;
  16 +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
  17 +import org.springframework.data.redis.serializer.StringRedisSerializer;
  18 +import org.springframework.stereotype.Component;
  19 +import org.springframework.util.StringUtils;
  20 +
  21 +import java.security.PrivateKey;
  22 +import java.security.PublicKey;
  23 +import java.text.SimpleDateFormat;
  24 +import java.time.LocalDate;
  25 +import java.time.LocalDateTime;
  26 +import java.time.LocalTime;
  27 +import java.time.format.DateTimeFormatter;
  28 +import java.util.Date;
  29 +
  30 +@Configuration
  31 +@ComponentScan("com.diligrp.assistant.shared")
  32 +public class SharedConfiguration {
  33 + @Bean
  34 + public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
  35 + RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
  36 + template.setConnectionFactory(factory);
  37 + template.setKeySerializer(new StringRedisSerializer());
  38 + template.setHashKeySerializer(new StringRedisSerializer());
  39 + template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
  40 + template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
  41 + template.afterPropertiesSet();
  42 + return template;
  43 + }
  44 +
  45 + // Jackson DataBinding所有配置
  46 + @Bean
  47 + @ConditionalOnClass(JavaTimeModule.class)
  48 + public Jackson2ObjectMapperBuilderCustomizer customizeJacksonConfig() {
  49 + return JsonUtils::initObjectMapperBuilder;
  50 + }
  51 +
  52 + @Bean
  53 + public Converter<String, LocalDateTime> localDateTimeConverter() {
  54 + // 不能使用lambda表达式,否则导致springboot启动问题
  55 + return new Converter<String, LocalDateTime>() {
  56 + @Override
  57 + public LocalDateTime convert(String source) {
  58 + try {
  59 + return StringUtils.hasText(source) ? LocalDateTime.parse(source, DateTimeFormatter.ofPattern(Constants.DATE_TIME_FORMAT)) : null;
  60 + } catch (Exception ex) {
  61 + throw new IllegalArgumentException(String.format("Error parse %s to LocalDateTime", source), ex);
  62 + }
  63 + }
  64 + };
  65 + }
  66 +
  67 + @Bean
  68 + public Converter<String, LocalDate> localDateConverter() {
  69 + // 不能使用lambda表达式,否则导致springboot启动问题
  70 + return new Converter<String, LocalDate>() {
  71 + @Override
  72 + public LocalDate convert(String source) {
  73 + try {
  74 + return StringUtils.hasText(source) ? LocalDate.parse(source, DateTimeFormatter.ofPattern(Constants.DATE_FORMAT)) : null;
  75 + } catch (Exception ex) {
  76 + throw new IllegalArgumentException(String.format("Error parse %s to LocalDate", source), ex);
  77 + }
  78 + }
  79 + };
  80 + }
  81 +
  82 + @Bean
  83 + public Converter<String, LocalTime> localTimeConverter() {
  84 + // 不能使用lambda表达式,否则导致springboot启动问题
  85 + return new Converter<String, LocalTime>() {
  86 + @Override
  87 + public LocalTime convert(String source) {
  88 + try {
  89 + return StringUtils.hasText(source) ? LocalTime.parse(source, DateTimeFormatter.ofPattern(Constants.TIME_FORMAT)) : null;
  90 + } catch (Exception ex) {
  91 + throw new IllegalArgumentException(String.format("Error parse %s to LocalTime", source), ex);
  92 + }
  93 + }
  94 + };
  95 + }
  96 +
  97 + @Bean
  98 + public Converter<String, Date> dateConverter() {
  99 + // 不能使用lambda表达式,否则导致springboot启动问题
  100 + return new Converter<String, Date>() {
  101 + @Override
  102 + public Date convert(String source) {
  103 + try {
  104 + return StringUtils.hasText(source) ? new SimpleDateFormat(Constants.DATE_TIME_FORMAT).parse(source) : null;
  105 + } catch (Exception ex) {
  106 + throw new IllegalArgumentException(String.format("Error parse %s to Date", source), ex);
  107 + }
  108 + }
  109 + };
  110 + }
  111 +
  112 + @Component
  113 + @ConfigurationPropertiesBinding
  114 + public class PrivateKeyConverter implements Converter<String, PrivateKey> {
  115 + @Override
  116 + public PrivateKey convert(String source) {
  117 + try {
  118 + return RsaCipher.getPrivateKey(source);
  119 + } catch (Exception ex) {
  120 + throw new PlatformServiceException("privateKey configuration failed", ex);
  121 + }
  122 + }
  123 + }
  124 +
  125 + @Component
  126 + @ConfigurationPropertiesBinding
  127 + public class PublicKeyConverter implements Converter<String, PublicKey> {
  128 + @Override
  129 + public PublicKey convert(String source) {
  130 + try {
  131 + return RsaCipher.getPublicKey(source);
  132 + } catch (Exception ex) {
  133 + throw new PlatformServiceException("publicKey configuration failed", ex);
  134 + }
  135 + }
  136 + }
  137 +}
0 138 \ No newline at end of file
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/codec/ByteDecoder.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/codec/ByteDecoder.java
  1 +package com.diligrp.assistant.shared.codec;
  2 +
  3 +import java.io.IOException;
  4 +
  5 +public interface ByteDecoder<T> {
  6 + T decode(byte[] payload) throws IOException;
  7 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/codec/ByteEncoder.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/codec/ByteEncoder.java
  1 +package com.diligrp.assistant.shared.codec;
  2 +
  3 +import java.io.IOException;
  4 +
  5 +public interface ByteEncoder<T> {
  6 + byte[] encode(T payload) throws IOException;
  7 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/codec/StringCodec.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/codec/StringCodec.java
  1 +package com.diligrp.assistant.shared.codec;
  2 +
  3 +import java.nio.charset.StandardCharsets;
  4 +
  5 +public final class StringCodec {
  6 + public static ByteEncoder<String> getEncoder() {
  7 + return StringEncoder.INSTANCE;
  8 + }
  9 +
  10 + public static ByteDecoder<String> getDecoder() {
  11 + return StringDecoder.INSTANCE;
  12 + }
  13 +
  14 + static class StringEncoder implements ByteEncoder<String> {
  15 +
  16 + static final ByteEncoder<String> INSTANCE = new StringEncoder();
  17 +
  18 + @Override
  19 + public byte[] encode(String payload) {
  20 + return payload.getBytes(StandardCharsets.UTF_8);
  21 + }
  22 + }
  23 +
  24 + static class StringDecoder implements ByteDecoder<String> {
  25 +
  26 + static final ByteDecoder<String> INSTANCE = new StringDecoder();
  27 +
  28 + @Override
  29 + public String decode(byte[] payload) {
  30 + return new String(payload, StandardCharsets.UTF_8);
  31 + }
  32 + }
  33 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/domain/AsyncMessage.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/domain/AsyncMessage.java
  1 +package com.diligrp.assistant.shared.domain;
  2 +
  3 +import com.diligrp.assistant.shared.util.JsonUtils;
  4 +
  5 +/**
  6 + * MQ异步消息模型
  7 + */
  8 +public class AsyncMessage {
  9 + // 消息类型
  10 + private Integer type;
  11 + // 消息体
  12 + private String payload;
  13 + // 消息参数
  14 + private String params;
  15 +
  16 + public static AsyncMessage of(Integer type, String payload, String params) {
  17 + AsyncMessage message = new AsyncMessage();
  18 + message.type = type;
  19 + message.payload = payload;
  20 + message.params = params;
  21 + return message;
  22 + }
  23 +
  24 + public static AsyncMessage from(String message) {
  25 + return JsonUtils.fromJsonString(message, AsyncMessage.class);
  26 + }
  27 +
  28 + public Integer getType() {
  29 + return type;
  30 + }
  31 +
  32 + public String getPayload() {
  33 + return payload;
  34 + }
  35 +
  36 + public String getParams() {
  37 + return params;
  38 + }
  39 +
  40 + public String toString() {
  41 + return JsonUtils.toJsonString(this);
  42 + }
  43 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/domain/BaseDo.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/domain/BaseDo.java
  1 +package com.diligrp.assistant.shared.domain;
  2 +
  3 +import java.time.LocalDateTime;
  4 +
  5 +public class BaseDo {
  6 + // 数据库主键
  7 + protected Long id;
  8 + // 数据版本
  9 + protected Integer version;
  10 + // 创建时间
  11 + protected LocalDateTime createdTime;
  12 + // 修改时间
  13 + protected LocalDateTime modifiedTime;
  14 +
  15 + public Long getId() {
  16 + return id;
  17 + }
  18 +
  19 + public void setId(Long id) {
  20 + this.id = id;
  21 + }
  22 +
  23 + public Integer getVersion() {
  24 + return version;
  25 + }
  26 +
  27 + public void setVersion(Integer version) {
  28 + this.version = version;
  29 + }
  30 +
  31 + public LocalDateTime getCreatedTime() {
  32 + return createdTime;
  33 + }
  34 +
  35 + public void setCreatedTime(LocalDateTime createdTime) {
  36 + this.createdTime = createdTime;
  37 + }
  38 +
  39 + public LocalDateTime getModifiedTime() {
  40 + return modifiedTime;
  41 + }
  42 +
  43 + public void setModifiedTime(LocalDateTime modifiedTime) {
  44 + this.modifiedTime = modifiedTime;
  45 + }
  46 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/domain/ContainerSupport.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/domain/ContainerSupport.java
  1 +package com.diligrp.assistant.shared.domain;
  2 +
  3 +import java.util.HashMap;
  4 +import java.util.List;
  5 +import java.util.Optional;
  6 +
  7 +public abstract class ContainerSupport extends HashMap<String, Object> {
  8 + public ContainerSupport attach(Object object) {
  9 + put(object.getClass().getName(), object);
  10 + return this;
  11 + }
  12 +
  13 + public ContainerSupport attach(String key, Object object) {
  14 + put(key, object);
  15 + return this;
  16 + }
  17 +
  18 + public Long getLong(String param) {
  19 + Object value = get(param);
  20 + if (value != null) {
  21 + return value instanceof Long ? (Long)value : Long.parseLong(value.toString());
  22 + }
  23 + return null;
  24 + }
  25 +
  26 + public Integer getInteger(String param) {
  27 + Object value = get(param);
  28 + if (value != null) {
  29 + return value instanceof Integer ? (Integer)value : Integer.parseInt(value.toString());
  30 + }
  31 + return null;
  32 + }
  33 +
  34 + public String getString(String param) {
  35 + Object value = get(param);
  36 + return value != null ? value.toString() : null;
  37 + }
  38 +
  39 + public <T> T getObject(String param, Class<T> type) {
  40 + Object value = get(param);
  41 + return value == null ? null : type.cast(value);
  42 + }
  43 +
  44 + public <T> T getObject(Class<T> type) {
  45 + Object value = get(type.getName());
  46 + return value == null ? null : type.cast(value);
  47 + }
  48 +
  49 + @SuppressWarnings("unchecked")
  50 + public <T> Optional<T> getObject(String param) {
  51 + Object value = get(param);
  52 + return Optional.ofNullable ((T) value);
  53 + }
  54 +
  55 + @SuppressWarnings("unchecked")
  56 + public <T> Optional<List<T>> getObjects(String param) {
  57 + Object value = get(param);
  58 + return Optional.ofNullable ((List<T>) value);
  59 + }
  60 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/domain/Message.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/domain/Message.java
  1 +package com.diligrp.assistant.shared.domain;
  2 +
  3 +import com.diligrp.assistant.shared.ErrorCode;
  4 +
  5 +public class Message<T> {
  6 + protected static final int CODE_SUCCESS = 200;
  7 + protected static final int CODE_FAILURE = ErrorCode.SYSTEM_UNKNOWN_ERROR;
  8 + protected static final String MSG_SUCCESS = "success";
  9 +
  10 + private Integer code;
  11 + private String message;
  12 + private T data;
  13 +
  14 + public Message() {
  15 + }
  16 +
  17 + public Integer getCode() {
  18 + return this.code;
  19 + }
  20 +
  21 + public void setCode(Integer code) {
  22 + this.code = code;
  23 + }
  24 +
  25 + public String getMessage() {
  26 + return this.message;
  27 + }
  28 +
  29 + public void setMessage(String message) {
  30 + this.message = message;
  31 + }
  32 +
  33 + public T getData() {
  34 + return this.data;
  35 + }
  36 +
  37 + public void setData(T data) {
  38 + this.data = data;
  39 + }
  40 +
  41 + public static Message<?> success() {
  42 + Message<?> result = new Message();
  43 + result.code = 200;
  44 + result.message = "success";
  45 + return result;
  46 + }
  47 +
  48 + public static <E> Message<E> success(E data) {
  49 + Message<E> result = new Message();
  50 + result.code = 200;
  51 + result.data = data;
  52 + result.message = "success";
  53 + return result;
  54 + }
  55 +
  56 + public static Message<?> failure(String message) {
  57 + Message<?> result = new Message();
  58 + result.code = 1000;
  59 + result.message = message;
  60 + return result;
  61 + }
  62 +
  63 + public static Message<?> failure(int code, String message) {
  64 + Message<?> result = new Message();
  65 + result.code = code;
  66 + result.message = message;
  67 + return result;
  68 + }
  69 +}
0 70 \ No newline at end of file
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/domain/PageMessage.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/domain/PageMessage.java
  1 +package com.diligrp.assistant.shared.domain;
  2 +
  3 +import java.util.List;
  4 +
  5 +/**
  6 + * 分页数据模型
  7 + */
  8 +public class PageMessage<E> extends Message<List<E>>{
  9 + // 总记录数
  10 + private long total;
  11 +
  12 + public long getTotal() {
  13 + return total;
  14 + }
  15 +
  16 + public void setTotal(long total) {
  17 + this.total = total;
  18 + }
  19 +
  20 + public static <T> PageMessage<T> success(long total, List<T> data) {
  21 + PageMessage<T> page = new PageMessage<>();
  22 + page.setCode(CODE_SUCCESS);
  23 + page.setTotal(total);
  24 + page.setData(data);
  25 + page.setMessage(MSG_SUCCESS);
  26 + return page;
  27 + }
  28 +
  29 + public static PageMessage<?> failure(String message) {
  30 + PageMessage<?> page = new PageMessage<>();
  31 + page.setCode(CODE_FAILURE);
  32 + page.setTotal(0);
  33 + page.setData(null);
  34 + page.setMessage(message);
  35 + return page;
  36 + }
  37 +
  38 + public static PageMessage<?> failure(int code, String message) {
  39 + PageMessage<?> page = new PageMessage<>();
  40 + page.setCode(code);
  41 + page.setTotal(0);
  42 + page.setData(null);
  43 + page.setMessage(message);
  44 + return page;
  45 + }
  46 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/domain/PageQuery.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/domain/PageQuery.java
  1 +package com.diligrp.assistant.shared.domain;
  2 +
  3 +/**
  4 + * 分页查询领域模型
  5 + */
  6 +public class PageQuery {
  7 + // 起始行下标
  8 + protected Integer start;
  9 + // 获取的记录行数
  10 + protected Integer limit;
  11 +
  12 + public Integer getStart() {
  13 + return start;
  14 + }
  15 +
  16 + public void setStart(Integer start) {
  17 + this.start = start;
  18 + }
  19 +
  20 + public Integer getLimit() {
  21 + return limit;
  22 + }
  23 +
  24 + public void setLimit(Integer limit) {
  25 + this.limit = limit;
  26 + }
  27 +
  28 + /**
  29 + * 通过页号、每页记录数计算起始行下标
  30 + */
  31 + public void from(int pageNo, int pageSize) {
  32 + this.start = (pageNo - 1) * pageSize;
  33 + this.limit = pageSize;
  34 + }
  35 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/exception/DefaultExceptionHandler.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/exception/DefaultExceptionHandler.java
  1 +package com.diligrp.assistant.shared.exception;
  2 +
  3 +import com.diligrp.assistant.shared.domain.Message;
  4 +import com.diligrp.assistant.shared.ErrorCode;
  5 +import org.slf4j.Logger;
  6 +import org.slf4j.LoggerFactory;
  7 +import org.springframework.web.bind.annotation.ExceptionHandler;
  8 +import org.springframework.web.bind.annotation.RestControllerAdvice;
  9 +
  10 +@RestControllerAdvice
  11 +public class DefaultExceptionHandler {
  12 + private final Logger LOG = LoggerFactory.getLogger(this.getClass());
  13 +
  14 + @ExceptionHandler(PlatformServiceException.class)
  15 + public Message<?> platformServiceException(PlatformServiceException ex) {
  16 + LOG.warn("assistant platform service exception", ex);
  17 + return Message.failure(ex.getCode(), ex.getMessage());
  18 + }
  19 +
  20 + @ExceptionHandler(IllegalArgumentException.class)
  21 + public Message<?> illegalArgumentException(IllegalArgumentException ex) {
  22 + LOG.warn("assistant platform service exception", ex);
  23 + return Message.failure(ErrorCode.ILLEGAL_ARGUMENT_ERROR, ex.getMessage());
  24 + }
  25 +
  26 + @ExceptionHandler(Exception.class)
  27 + public Message<?> defaultExceptionHandler(Exception ex) {
  28 + LOG.warn("assistant platform service exception", ex);
  29 + return Message.failure(ErrorCode.SYSTEM_UNKNOWN_ERROR, ErrorCode.MESSAGE_UNKNOWN_ERROR);
  30 + }
  31 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/exception/PlatformServiceException.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/exception/PlatformServiceException.java
  1 +package com.diligrp.assistant.shared.exception;
  2 +
  3 +import com.diligrp.assistant.shared.ErrorCode;
  4 +
  5 +/**
  6 + * 所有模块异常类的基类
  7 + */
  8 +public class PlatformServiceException extends RuntimeException {
  9 + /**
  10 + * 错误码
  11 + */
  12 + private int code = ErrorCode.SYSTEM_UNKNOWN_ERROR;
  13 +
  14 + /**
  15 + * 是否打印异常栈
  16 + */
  17 + private boolean stackTrace = true;
  18 +
  19 + public PlatformServiceException(String message) {
  20 + super(message);
  21 + }
  22 +
  23 + public PlatformServiceException(int code, String message) {
  24 + super(message);
  25 + this.code = code;
  26 + this.stackTrace = false;
  27 + }
  28 +
  29 + public PlatformServiceException(String message, Throwable ex) {
  30 + super(message, ex);
  31 + }
  32 +
  33 + @Override
  34 + public Throwable fillInStackTrace() {
  35 + return stackTrace ? super.fillInStackTrace() : this;
  36 + }
  37 +
  38 + public int getCode() {
  39 + return code;
  40 + }
  41 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/exception/ServiceAccessException.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/exception/ServiceAccessException.java
  1 +package com.diligrp.assistant.shared.exception;
  2 +
  3 +/**
  4 + * 远程服务访问异常
  5 + */
  6 +public class ServiceAccessException extends PlatformServiceException {
  7 + public ServiceAccessException(String message) {
  8 + super(message);
  9 + }
  10 +
  11 + public ServiceAccessException(int code, String message) {
  12 + super(code, message);
  13 + }
  14 +
  15 + public ServiceAccessException(String message, Throwable ex) {
  16 + super(message, ex);
  17 + }
  18 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/exception/ServiceConnectException.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/exception/ServiceConnectException.java
  1 +package com.diligrp.assistant.shared.exception;
  2 +
  3 +/**
  4 + * 远程服务连接异常
  5 + */
  6 +public class ServiceConnectException extends ServiceAccessException {
  7 +
  8 + public ServiceConnectException(String message) {
  9 + super(message);
  10 + }
  11 +
  12 + public ServiceConnectException(int code, String message) {
  13 + super(code, message);
  14 + }
  15 +
  16 + public ServiceConnectException(String message, Throwable ex) {
  17 + super(message, ex);
  18 + }
  19 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/exception/ServiceTimeoutException.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/exception/ServiceTimeoutException.java
  1 +package com.diligrp.assistant.shared.exception;
  2 +
  3 +/**
  4 + * 远程服务访问超时异常
  5 + */
  6 +public class ServiceTimeoutException extends ServiceAccessException {
  7 +
  8 + public ServiceTimeoutException(String message) {
  9 + super(message);
  10 + }
  11 +
  12 + public ServiceTimeoutException(int code, String message) {
  13 + super(code, message);
  14 + }
  15 +
  16 + public ServiceTimeoutException(String message, Throwable ex) {
  17 + super(message, ex);
  18 + }
  19 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/mybatis/GenericEnumTypeHandler.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/mybatis/GenericEnumTypeHandler.java
  1 +package com.diligrp.assistant.shared.mybatis;
  2 +
  3 +import com.diligrp.assistant.shared.type.IEnumType;
  4 +import org.apache.ibatis.type.BaseTypeHandler;
  5 +import org.apache.ibatis.type.JdbcType;
  6 +
  7 +import java.sql.CallableStatement;
  8 +import java.sql.PreparedStatement;
  9 +import java.sql.ResultSet;
  10 +import java.sql.SQLException;
  11 +import java.util.Arrays;
  12 +
  13 +public class GenericEnumTypeHandler<E extends IEnumType> extends BaseTypeHandler<E> {
  14 + private final E[] enums;
  15 +
  16 + public GenericEnumTypeHandler(Class<E> type) {
  17 + if (type == null) {
  18 + throw new IllegalArgumentException("Type argument cannot be null");
  19 + } else {
  20 + this.enums = type.getEnumConstants();
  21 + if (this.enums == null) {
  22 + throw new IllegalArgumentException(type.getSimpleName() + " does not represent an enum type.");
  23 + }
  24 + }
  25 + }
  26 +
  27 + @Override
  28 + public void setNonNullParameter(PreparedStatement preparedStatement, int i, E e, JdbcType jdbcType) throws SQLException {
  29 + preparedStatement.setInt(i, e.getCode());
  30 + }
  31 +
  32 + @Override
  33 + public E getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
  34 + int code = resultSet.getInt(columnName);
  35 + if (resultSet.wasNull()) {
  36 + return null;
  37 + } else {
  38 + return getEnumType(code);
  39 + }
  40 + }
  41 +
  42 + @Override
  43 + public E getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {
  44 + int code = resultSet.getInt(columnIndex);
  45 + if (resultSet.wasNull()) {
  46 + return null;
  47 + } else {
  48 + return getEnumType(code);
  49 + }
  50 + }
  51 +
  52 + @Override
  53 + public E getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {
  54 + int code = callableStatement.getInt(columnIndex);
  55 + if (callableStatement.wasNull()) {
  56 + return null;
  57 + } else {
  58 + return getEnumType(code);
  59 + }
  60 + }
  61 +
  62 + private E getEnumType(int code) {
  63 + return Arrays.stream(enums).filter(item -> item.getCode() == code).findFirst().orElse(null);
  64 + }
  65 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/mybatis/MybatisMapperSupport.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/mybatis/MybatisMapperSupport.java
  1 +package com.diligrp.assistant.shared.mybatis;
  2 +
  3 +public interface MybatisMapperSupport {
  4 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/security/AesCipher.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/security/AesCipher.java
  1 +package com.diligrp.assistant.shared.security;
  2 +
  3 +import javax.crypto.Cipher;
  4 +import javax.crypto.KeyGenerator;
  5 +import javax.crypto.SecretKey;
  6 +import javax.crypto.spec.SecretKeySpec;
  7 +import java.security.Key;
  8 +import java.util.Base64;
  9 +
  10 +/**
  11 + * AES算法工具类
  12 + */
  13 +public class AesCipher {
  14 + private static final String KEY_ALGORITHM = "AES";
  15 +
  16 + private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
  17 +
  18 + public static String generateSecretKey() throws Exception {
  19 + KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
  20 + keyGenerator.init(128);
  21 +
  22 + SecretKey secretKey = keyGenerator.generateKey();
  23 + return Base64.getEncoder().encodeToString(secretKey.getEncoded());
  24 + }
  25 +
  26 + public static byte[] encrypt(byte[] data, String secretKey) throws Exception {
  27 + Key key = toKey(secretKey);
  28 + Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
  29 + cipher.init(Cipher.ENCRYPT_MODE, key);
  30 +
  31 + return cipher.doFinal(data);
  32 + }
  33 +
  34 + public static byte[] decrypt(byte[] data, String secretKey) throws Exception {
  35 + Key key = toKey(secretKey);
  36 + Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
  37 + cipher.init(Cipher.DECRYPT_MODE, key);
  38 +
  39 + return cipher.doFinal(data);
  40 + }
  41 +
  42 + private static Key toKey(String secretKey) {
  43 + byte[] key = Base64.getDecoder().decode(secretKey);
  44 + return new SecretKeySpec(key, KEY_ALGORITHM);
  45 + }
  46 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/security/HexUtils.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/security/HexUtils.java
  1 +package com.diligrp.assistant.shared.security;
  2 +
  3 +/**
  4 + * 字节数组与十六进制字符串转化工具类
  5 + */
  6 +public class HexUtils {
  7 +
  8 + private static final int GUARD_CHAR = 0x01;
  9 +
  10 + private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5',
  11 + '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
  12 +
  13 + private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5',
  14 + '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
  15 +
  16 + public static char[] encodeHex(byte[] data) {
  17 + return encodeHex(data, true);
  18 + }
  19 +
  20 + public static char[] encodeHex(byte[] data, boolean toLowerCase) {
  21 + return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
  22 + }
  23 +
  24 + public static String encodeHexStr(byte[] data) {
  25 + return encodeHexStr(data, true);
  26 + }
  27 +
  28 + public static String encodeHexStr(byte[] data, boolean toLowerCase) {
  29 + return encodeHexStr(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
  30 + }
  31 +
  32 + public static byte[] decodeHex(String data) {
  33 + return decodeHex(data.toCharArray());
  34 + }
  35 +
  36 + public static byte[] decodeHex(char[] data) {
  37 + int len = data.length;
  38 + if ((len & GUARD_CHAR) != 0) {
  39 + throw new RuntimeException("Unknown char");
  40 + }
  41 +
  42 + byte[] out = new byte[len >> 1];
  43 + for (int i = 0, j = 0; j < len; i++) {
  44 + int f = toDigit(data[j], j) << 4;
  45 + j++;
  46 + f = f | toDigit(data[j], j);
  47 + j++;
  48 + out[i] = (byte) (f & 0xFF);
  49 + }
  50 +
  51 + return out;
  52 + }
  53 +
  54 + private static char[] encodeHex(byte[] data, char[] toDigits) {
  55 + int l = data.length;
  56 + char[] out = new char[l << 1];
  57 + for (int i = 0, j = 0; i < l; i++) {
  58 + out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
  59 + out[j++] = toDigits[0x0F & data[i]];
  60 + }
  61 + return out;
  62 + }
  63 +
  64 + private static String encodeHexStr(byte[] data, char[] toDigits) {
  65 + return new String(encodeHex(data, toDigits));
  66 + }
  67 +
  68 + private static int toDigit(char ch, int index) {
  69 + int digit = Character.digit(ch, 16);
  70 + if (digit == -1) {
  71 + throw new RuntimeException("Invalid hex char " + ch + ", index at " + index);
  72 + }
  73 + return digit;
  74 + }
  75 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/security/KeyStoreUtils.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/security/KeyStoreUtils.java
  1 +package com.diligrp.assistant.shared.security;
  2 +
  3 +import com.diligrp.assistant.shared.util.ClassUtils;
  4 +
  5 +import java.io.InputStream;
  6 +import java.security.KeyStore;
  7 +import java.security.PrivateKey;
  8 +import java.security.PublicKey;
  9 +import java.security.cert.Certificate;
  10 +import java.security.cert.CertificateFactory;
  11 +import java.util.Base64;
  12 +
  13 +/**
  14 + * 数字证书工具类
  15 + */
  16 +public class KeyStoreUtils {
  17 + public static String getPrivateKeyStr(String keyStorePath, String storeType, String storePass,
  18 + String alias, String keyPass) throws Exception {
  19 + InputStream in = ClassUtils.getDefaultClassLoader().getResourceAsStream(keyStorePath);
  20 + PrivateKey privateKey = getPrivateKey(in, storeType, storePass, alias, keyPass);
  21 + return Base64.getEncoder().encodeToString(privateKey.getEncoded());
  22 + }
  23 +
  24 + public static PrivateKey getPrivateKey(String keyStorePath, String storeType, String storePass,
  25 + String alias, String keyPass) throws Exception {
  26 + InputStream in = ClassUtils.getDefaultClassLoader().getResourceAsStream(keyStorePath);
  27 + return getPrivateKey(in, storeType, storePass, alias, keyPass);
  28 + }
  29 +
  30 + public static PrivateKey getPrivateKey(InputStream in, String storeType, String storePass, String alias, String keyPass) throws Exception {
  31 + KeyStore ks = getKeyStore(in, storeType, storePass);
  32 + PrivateKey key = (PrivateKey) ks.getKey(alias, keyPass.toCharArray());
  33 + return key;
  34 + }
  35 +
  36 + public static String getPublicKeyStr(String keyStorePath, String storeType, String storePass, String alias) throws Exception {
  37 + InputStream in = ClassUtils.getDefaultClassLoader().getResourceAsStream(keyStorePath);
  38 + PublicKey publicKey = getPublicKey(in, storeType, storePass, alias);
  39 + return Base64.getEncoder().encodeToString(publicKey.getEncoded());
  40 + }
  41 +
  42 + public static PublicKey getPublicKey(String keyStorePath, String storeType, String storePass, String alias) throws Exception {
  43 + InputStream in = ClassUtils.getDefaultClassLoader().getResourceAsStream(keyStorePath);
  44 + return getPublicKey(in, storeType, storePass, alias);
  45 + }
  46 +
  47 + public static PublicKey getPublicKey(InputStream in, String storeType, String storePass, String alias) throws Exception {
  48 + KeyStore ks = getKeyStore(in, storeType, storePass);
  49 + Certificate cert = ks.getCertificate(alias);
  50 + return cert.getPublicKey();
  51 + }
  52 +
  53 + public static String getPublicKeyStr(String certificatePath) throws Exception {
  54 + PublicKey publicKey = getPublicKey(certificatePath);
  55 + return Base64.getEncoder().encodeToString(publicKey.getEncoded());
  56 + }
  57 +
  58 + public static PublicKey getPublicKey(String certificatePath) throws Exception {
  59 + InputStream in = ClassUtils.getDefaultClassLoader().getResourceAsStream(certificatePath);
  60 + Certificate x509Cert = CertificateFactory.getInstance("X509").generateCertificate(in);
  61 + return x509Cert.getPublicKey();
  62 + }
  63 +
  64 + public static KeyStore getKeyStore(InputStream in, String storeType, String storePass) throws Exception {
  65 + KeyStore ks = KeyStore.getInstance(storeType);
  66 + ks.load(in, storePass.toCharArray());
  67 + in.close();
  68 + return ks;
  69 + }
  70 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/security/Md5Cipher.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/security/Md5Cipher.java
  1 +package com.diligrp.assistant.shared.security;
  2 +
  3 +import java.security.MessageDigest;
  4 +
  5 +/**
  6 + * MD5算法工具类
  7 + */
  8 +public class Md5Cipher {
  9 + private static final String KEY_MD5 = "MD5";
  10 +
  11 + public static byte[] encrypt(byte[] data) throws Exception {
  12 + MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
  13 + md5.update(data);
  14 + return md5.digest();
  15 + }
  16 +}
... ...
assistant-shared/src/main/java/com/diligrp/assistant/shared/security/PasswordUtils.java 0 → 100644
  1 +++ a/assistant-shared/src/main/java/com/diligrp/assistant/shared/security/PasswordUtils.java
  1 +package com.diligrp.assistant.shared.security;
  2 +
  3 +import java.nio.charset.StandardCharsets;
  4 +
  5 +/**
  6 + * 密码加密散列工具类
  7 + */
  8 +public class PasswordUtils {
  9 + public static String generateSecretKey() {
  10 + try {
  11 + return AesCipher.generateSecretKey();
  12 + } catch (Exception ex) {
  13 + throw new RuntimeException("Generate password secret key error", ex);
  14 + }
  15 + }
  16 +
  17 + public static String encrypt(String password, String secretKey) {
  18 + try {
  19 + byte[] data = password.getBytes(StandardCharsets.UTF_8);
  20 + return HexUtils.encodeHexStr(ShaCipher.encrypt(AesCipher.encrypt(data, secretKey)));
  21 + } catch (Exception ex) {
  22 + throw new RuntimeException("Encrypt password error", ex);
  23 + }
  24 + }
  25 +}
... ...