Commit f16598b43691b7b1ffc9d294bf7ffaefaafac946

Authored by huanggang
0 parents

boss project init

Showing 90 changed files with 4498 additions and 0 deletions
.gitignore 0 → 100644
  1 +++ a/.gitignore
  1 +### Java template
  2 +# Compiled class file
  3 +*.class
  4 +
  5 +# Log file
  6 +*.log
  7 +
  8 +# BlueJ files
  9 +*.ctxt
  10 +
  11 +# Mobile Tools for Java (J2ME)
  12 +.mtj.tmp/
  13 +
  14 +# Package Files #
  15 +*.jar
  16 +*.war
  17 +*.nar
  18 +*.ear
  19 +*.zip
  20 +*.tar.gz
  21 +*.rar
  22 +
  23 +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
  24 +hs_err_pid*
  25 +replay_pid*
  26 +
  27 +.idea/
  28 +*/target/
  29 +logs/
  30 +*.iml
  31 +
  32 +
  33 +### Gradle template
  34 +.gradle
  35 +**/build/
  36 +!src/**/build/
  37 +
  38 +# Ignore Gradle GUI config
  39 +gradle-app.setting
  40 +
  41 +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
  42 +!gradle-wrapper.jar
  43 +
  44 +# Avoid ignore Gradle wrappper properties
  45 +!gradle-wrapper.properties
  46 +
  47 +# Cache of project
  48 +.gradletasknamecache
  49 +
  50 +# Eclipse Gradle plugin generated files
  51 +# Eclipse Core
  52 +.project
  53 +# JDT-specific (Eclipse Java Development Tools)
  54 +.classpath
  55 +
... ...
README.md 0 → 100644
  1 +++ a/README.md
  1 +dili-boss模块及功能范围: boss.diligrp.com
  2 + boss-shared 基础设施和共享模块(解决循环依赖问题,工具类引入,中间件服务mq/redis/mysql等配置,第三方框架的封装和配置,全局异常拦截配置 ID生成器)
  3 + boss-auth 用户认证授权,系统用户管理等
  4 + boss-admin 基础数据维护, 运营参数设置,帮助中心
  5 + boss-support 运营支持类功能:审核功能,用户数据查询
  6 + boss-report 运营数据报表
  7 + boss-ui 前端VUE页面
  8 + boss-boot 父工程(springboot打包,系统对外提供开放接口)
  9 +
  10 +模块依赖: 后期根据实际需求调整模块依赖
  11 + boss-auth -> boss-shared
  12 + boss-admin -> boss-auth
  13 + boss-support -> boss-admin
  14 + boss-report -> boss-admin
  15 + boss-boot -> boss-admin boss-support boss-report
  16 +
  17 +项目结构
  18 + com.diligrp.boss.xxxx - 模块spring配置xxxxConfiguration(Spring组件扫描配置/MybatisMapper扫描配置)ErrorCode Constants
  19 + com.diligrp.boss.xxxx.controller - 后台接口
  20 + com.diligrp.boss.xxxx.api - 移动端接口
  21 + com.diligrp.boss.xxxx.service
  22 + com.diligrp.boss.xxxx.dao
  23 + com.diligrp.boss.xxxx.exception
  24 + com.diligrp.boss.xxxx.domain
  25 + com.diligrp.boss.xxxx.model
  26 + com.diligrp.boss.xxxx.type
  27 + com.diligrp.boss.xxxx.util
  28 + resource/com.diligrp.boss.dao.mapper - mybatis mapper文件
  29 +
  30 + 系统对第三方系统提供接口通过erp-boot模块api包
  31 + 模块内部后台服务接口使用controller包
  32 + 所有数据模型类放入com.diligrp.boss.xxxx.model下,所有域模型类(VO DTO)放入com.diligrp.boss.xxxx.domain下
  33 + 所有数据模型类须继承BaseDo类,进一步规范数据表设计:需包含id version created_time modified_time
  34 + 所有枚举类型放入com.diligrp.boss.xxxx.type下,枚举类定义请提供code/name属性,参见com.diligrp.boss.shared.type.Gender
  35 + 所有自定义工具类放入com.diligrp.boss.xxxx.util下,如果各模块共用则放erp-shared模块下
  36 + 所有异常类继承PlatformServiceException(提供了错误码和是否打印异常栈信息功能),并放入com.diligrp.boss.xxxx.exception下
  37 + 每个模块的常量类请放在模块根目录下,如通用常量请放入boss-shared模块下
  38 + 错误码为6位,每个模块的错误类ErrorCode且放入模块根目录,错误码应唯一且独特如前三位为模块标识,公共错误码参见com.diligrp.boss.shared.ErrorCode
  39 +
  40 +工具类
  41 + 参见:com.diligrp.boss.shared.util.* com.diligrp.boss.shared.security.*
  42 + 包括:JsonUtils CurrencyUtils DateUtils RandomUtils AssertUtils HexUtils AesCipher RsaCipher ShaCipher KeyStoreUtils等等
  43 +
  44 +技术要求
  45 + JDK17 SpringCould SpringBoot 3版本
  46 + 编译工具:gradle
  47 + 第三方库尽量使用springboot默认推荐,如:Jackson Lettuce;springboot工具集中没有推荐的第三方库,引入时请在合适模块中进行
  48 + 已在boss-shared中完成Jackson配置,包括Spring DataBinding,且额外提供了Jackson工具类JsonUtils
  49 + 已在boss-shared中已完成Redis基础配置Lettuce,可直接使用StringRedisTemplate,如需进行进一步封装配置请在合适的模块中配置,如需Redis分布式锁,可考虑引入Redission
  50 + 已在boss-shared中已完成Mybatis基础配置,使用MapperScan完成mapper文件的扫描,不用plus,可用mybatis分页插件
  51 + 已在boss-shared中完成MQ基础配置RabbitMQ,可直接进行使用RabbitTemplate且可进行Queue Exchange和消息监听器的配置
  52 + 外部第三方jar放入dili-boss/libs
  53 + 新技术框架的引入不以个人熟悉为重点考量标准,以技术框架的通用型和稳定性为考量标准
  54 +
  55 +数据库脚本要求
  56 + 维护全量(dili-boss/scripts)和增量脚本(scripts/upgrade)
  57 + 维护增量脚本,需同时修改全量脚本
  58 + 所有建表SQL,每个字段需填写备注
  59 + 通常情况下,每个表都需要包含三个字段id,version,created_time,modified_time
  60 + 每个模块的数据表,建议统一的前缀boss_****
  61 +
  62 +mkdir -p src/main/java/com/diligrp/erp/$ModuleName/api
  63 +mkdir -p src/main/java/com/diligrp/erp/$ModuleName/controller
  64 +mkdir -p src/main/java/com/diligrp/erp/$ModuleName/service
  65 +mkdir -p src/main/java/com/diligrp/erp/$ModuleName/dao
  66 +mkdir -p src/main/java/com/diligrp/erp/$ModuleName/exception
  67 +mkdir -p src/main/java/com/diligrp/erp/$ModuleName/domain
  68 +mkdir -p src/main/java/com/diligrp/erp/$ModuleName/model
  69 +mkdir -p src/main/java/com/diligrp/erp/$ModuleName/type
  70 +mkdir -p src/main/java/com/diligrp/erp/$ModuleName/util
  71 +mkdir -p src/main/resources/com/diligrp/erp/dao/mapper
0 72 \ No newline at end of file
... ...
boss-admin/build.gradle 0 → 100644
  1 +++ a/boss-admin/build.gradle
  1 +group = 'com.diligrp'
  2 +archivesBaseName = 'boss-admin'
  3 +
  4 +dependencies {
  5 + api project(':boss-auth')
  6 +}
0 7 \ No newline at end of file
... ...
boss-admin/src/main/java/com/diligrp/boss/admin/AdminConfiguration.java 0 → 100644
  1 +++ a/boss-admin/src/main/java/com/diligrp/boss/admin/AdminConfiguration.java
  1 +package com.diligrp.boss.admin;
  2 +
  3 +import com.diligrp.boss.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.boss.admin")
  10 +@MapperScan(basePackages = {"com.diligrp.boss.admin.dao"}, markerInterface = MybatisMapperSupport.class)
  11 +public class AdminConfiguration {
  12 +}
... ...
boss-auth/build.gradle 0 → 100644
  1 +++ a/boss-auth/build.gradle
  1 +group = 'com.diligrp'
  2 +archivesBaseName = 'boss-auth'
  3 +
  4 +dependencies {
  5 + api project(':boss-shared')
  6 +}
0 7 \ No newline at end of file
... ...
boss-auth/src/main/java/com/diligrp/boss/auth/AuthConfiguration.java 0 → 100644
  1 +++ a/boss-auth/src/main/java/com/diligrp/boss/auth/AuthConfiguration.java
  1 +package com.diligrp.boss.auth;
  2 +
  3 +import com.diligrp.boss.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.boss.auth")
  10 +@MapperScan(basePackages = {"com.diligrp.boss.auth.dao"}, markerInterface = MybatisMapperSupport.class)
  11 +public class AuthConfiguration {
  12 +}
... ...
boss-boot/build.gradle 0 → 100644
  1 +++ a/boss-boot/build.gradle
  1 +group = 'com.diligrp'
  2 +archivesBaseName = 'boss-boot'
  3 +
  4 +jar.enabled = false
  5 +bootJar {
  6 + enabled = true
  7 + archiveBaseName = 'boss-boot'
  8 +}
  9 +
  10 +dependencies {
  11 + implementation project(':boss-admin')
  12 + implementation project(':boss-support')
  13 + implementation project(':boss-report')
  14 +}
0 15 \ No newline at end of file
... ...
boss-boot/src/main/java/com/diligrp/boss/boot/BootConfiguration.java 0 → 100644
  1 +++ a/boss-boot/src/main/java/com/diligrp/boss/boot/BootConfiguration.java
  1 +package com.diligrp.boss.boot;
  2 +
  3 +import org.springframework.context.annotation.ComponentScan;
  4 +import org.springframework.context.annotation.Configuration;
  5 +
  6 +@Configuration
  7 +@ComponentScan("com.diligrp.boss.boot")
  8 +public class BootConfiguration {
  9 +}
... ...
boss-boot/src/main/java/com/diligrp/boss/boot/BossServiceBootstrap.java 0 → 100644
  1 +++ a/boss-boot/src/main/java/com/diligrp/boss/boot/BossServiceBootstrap.java
  1 +package com.diligrp.boss.boot;
  2 +
  3 +import com.diligrp.boss.admin.AdminConfiguration;
  4 +import com.diligrp.boss.auth.AuthConfiguration;
  5 +import com.diligrp.boss.report.ReportConfiguration;
  6 +import com.diligrp.boss.shared.SharedConfiguration;
  7 +import com.diligrp.boss.support.SupportConfiguration;
  8 +import org.springframework.boot.SpringApplication;
  9 +import org.springframework.boot.SpringBootConfiguration;
  10 +import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  11 +import org.springframework.context.annotation.Import;
  12 +
  13 +@SpringBootConfiguration
  14 +@EnableAutoConfiguration
  15 +@Import({BootConfiguration.class, SharedConfiguration.class, AuthConfiguration.class, AdminConfiguration.class,
  16 + SupportConfiguration.class, ReportConfiguration.class})
  17 +public class BossServiceBootstrap {
  18 + public static void main(String[] args) {
  19 + SpringApplication.run(BossServiceBootstrap.class, args);
  20 + }
  21 +}
0 22 \ No newline at end of file
... ...
boss-boot/src/main/java/com/diligrp/boss/boot/controller/BossOpenApiController.java 0 → 100644
  1 +++ a/boss-boot/src/main/java/com/diligrp/boss/boot/controller/BossOpenApiController.java
  1 +package com.diligrp.boss.boot.controller;
  2 +
  3 +import com.diligrp.boss.shared.domain.Message;
  4 +import com.diligrp.boss.shared.uid.KeyGenerator;
  5 +import com.diligrp.boss.shared.uid.KeyGeneratorManager;
  6 +import jakarta.annotation.Resource;
  7 +import org.springframework.web.bind.annotation.RequestMapping;
  8 +import org.springframework.web.bind.annotation.RestController;
  9 +
  10 +@RestController
  11 +@RequestMapping(value = "/api")
  12 +public class BossOpenApiController {
  13 + @Resource
  14 + private KeyGeneratorManager keyGeneratorManager;
  15 +
  16 + @RequestMapping(value = "/uid/get.do")
  17 + public Message<?> testUid() {
  18 + KeyGenerator keyGenerator = keyGeneratorManager.getKeyGenerator("TEST_KEY");
  19 + return Message.success(keyGenerator.nextId());
  20 + }
  21 +}
... ...
boss-boot/src/main/resources/application-dev.properties 0 → 100644
  1 +++ a/boss-boot/src/main/resources/application-dev.properties
  1 +#Datasource configuration
  2 +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  3 +spring.datasource.url=jdbc:mysql://mysql.diligrp.com:3306/dili_boss?useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2B8
  4 +spring.datasource.username=root
  5 +spring.datasource.password=123456
  6 +spring.datasource.type=com.zaxxer.hikari.HikariDataSource
  7 +spring.datasource.hikari.pool-name=BossHikariPool
  8 +spring.datasource.hikari.minimum-idle=4
  9 +spring.datasource.hikari.maximum-pool-size=60
  10 +spring.datasource.hikari.idle-timeout=120000
  11 +spring.datasource.hikari.max-lifetime=900000
  12 +spring.datasource.hikari.connection-timeout=15000
  13 +spring.datasource.hikari.connection-test-query=SELECT 1
  14 +
  15 +#Redis configuration
  16 +spring.data.redis.host=redis.diligrp.com
  17 +spring.data.redis.port=6379
  18 +spring.data.redis.database=8
  19 +#spring.data.redis.username=
  20 +#spring.data.redis.password=
  21 +spring.data.redis.connect-timeout=15000
  22 +spring.data.redis.timeout=30000
  23 +#spring.data.redis.lettuce.pool.enabled=false
  24 +
  25 +#RabbitMQ configuration
  26 +spring.rabbitmq.host=rabbitmq.diligrp.com
  27 +spring.rabbitmq.port=5672
  28 +spring.rabbitmq.username=admin
  29 +spring.rabbitmq.password=123456
  30 +spring.rabbitmq.virtual-host=/
0 31 \ No newline at end of file
... ...
boss-boot/src/main/resources/application-prod.properties 0 → 100644
  1 +++ a/boss-boot/src/main/resources/application-prod.properties
  1 +#Datasource configuration
  2 +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  3 +spring.datasource.url=jdbc:mysql://mysql.diligrp.com:3306/dili_boss?useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2B8
  4 +spring.datasource.username=root
  5 +spring.datasource.password=123456
  6 +spring.datasource.type=com.zaxxer.hikari.HikariDataSource
  7 +spring.datasource.hikari.pool-name=BossHikariPool
  8 +spring.datasource.hikari.minimum-idle=4
  9 +spring.datasource.hikari.maximum-pool-size=60
  10 +spring.datasource.hikari.idle-timeout=120000
  11 +spring.datasource.hikari.max-lifetime=900000
  12 +spring.datasource.hikari.connection-timeout=15000
  13 +spring.datasource.hikari.connection-test-query=SELECT 1
  14 +
  15 +#Redis configuration
  16 +spring.data.redis.host=redis.diligrp.com
  17 +spring.data.redis.port=6379
  18 +spring.data.redis.database=8
  19 +#spring.data.redis.username=
  20 +#spring.data.redis.password=
  21 +spring.data.redis.connect-timeout=15000
  22 +spring.data.redis.timeout=30000
  23 +#spring.data.redis.lettuce.pool.enabled=false
  24 +
  25 +#RabbitMQ configuration
  26 +spring.rabbitmq.host=rabbitmq.diligrp.com
  27 +spring.rabbitmq.port=5672
  28 +spring.rabbitmq.username=admin
  29 +spring.rabbitmq.password=123456
  30 +spring.rabbitmq.virtual-host=/
0 31 \ No newline at end of file
... ...
boss-boot/src/main/resources/application-test.properties 0 → 100644
  1 +++ a/boss-boot/src/main/resources/application-test.properties
  1 +#Datasource configuration
  2 +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  3 +spring.datasource.url=jdbc:mysql://mysql.diligrp.com:3306/dili_boss?useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2B8
  4 +spring.datasource.username=root
  5 +spring.datasource.password=123456
  6 +spring.datasource.type=com.zaxxer.hikari.HikariDataSource
  7 +spring.datasource.hikari.pool-name=BossHikariPool
  8 +spring.datasource.hikari.minimum-idle=4
  9 +spring.datasource.hikari.maximum-pool-size=60
  10 +spring.datasource.hikari.idle-timeout=120000
  11 +spring.datasource.hikari.max-lifetime=900000
  12 +spring.datasource.hikari.connection-timeout=15000
  13 +spring.datasource.hikari.connection-test-query=SELECT 1
  14 +
  15 +#Redis configuration
  16 +spring.data.redis.host=redis.diligrp.com
  17 +spring.data.redis.port=6379
  18 +spring.data.redis.database=8
  19 +#spring.data.redis.username=
  20 +#spring.data.redis.password=
  21 +spring.data.redis.connect-timeout=15000
  22 +spring.data.redis.timeout=30000
  23 +#spring.data.redis.lettuce.pool.enabled=false
  24 +
  25 +#RabbitMQ configuration
  26 +spring.rabbitmq.host=rabbitmq.diligrp.com
  27 +spring.rabbitmq.port=5672
  28 +spring.rabbitmq.username=admin
  29 +spring.rabbitmq.password=123456
  30 +spring.rabbitmq.virtual-host=/
0 31 \ No newline at end of file
... ...
boss-boot/src/main/resources/application.properties 0 → 100644
  1 +++ a/boss-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/boss/dao/mapper/**/*.xml
  11 +mybatis.configuration.default-enum-type-handler=com.diligrp.boss.shared.mybatis.GenericEnumTypeHandler
0 12 \ No newline at end of file
... ...
boss-boot/src/main/resources/bootstrap.properties 0 → 100644
  1 +++ a/boss-boot/src/main/resources/bootstrap.properties
  1 +server.port=8684
  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=boss-service
0 8 \ No newline at end of file
... ...
boss-boot/src/main/resources/logback-spring.xml 0 → 100644
  1 +++ a/boss-boot/src/main/resources/logback-spring.xml
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<configuration>
  3 + <!-- 日志名称 -->
  4 + <property name="LOG_NAME" value="boss-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.boss" 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.boss" 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.boss" 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.boss.admin" 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
... ...
boss-report/build.gradle 0 → 100644
  1 +++ a/boss-report/build.gradle
  1 +group = 'com.diligrp'
  2 +archivesBaseName = 'boss-report'
  3 +
  4 +dependencies {
  5 + api project(':boss-admin')
  6 +}
0 7 \ No newline at end of file
... ...
boss-report/src/main/java/com/diligrp/boss/report/ReportConfiguration.java 0 → 100644
  1 +++ a/boss-report/src/main/java/com/diligrp/boss/report/ReportConfiguration.java
  1 +package com.diligrp.boss.report;
  2 +
  3 +import com.diligrp.boss.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.boss.report")
  10 +@MapperScan(basePackages = {"com.diligrp.boss.report.dao"}, markerInterface = MybatisMapperSupport.class)
  11 +public class ReportConfiguration {
  12 +}
... ...
boss-shared/build.gradle 0 → 100644
  1 +++ a/boss-shared/build.gradle
  1 + group = 'com.diligrp'
  2 +archivesBaseName = 'boss-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
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/Constants.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/Constants.java
  1 +package com.diligrp.boss.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
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/ErrorCode.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/ErrorCode.java
  1 +package com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/SharedConfiguration.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/SharedConfiguration.java
  1 +package com.diligrp.boss.shared;
  2 +
  3 +import com.diligrp.boss.shared.exception.PlatformServiceException;
  4 +import com.diligrp.boss.shared.mybatis.MybatisMapperSupport;
  5 +import com.diligrp.boss.shared.security.RsaCipher;
  6 +import com.diligrp.boss.shared.util.JsonUtils;
  7 +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
  8 +import org.mybatis.spring.annotation.MapperScan;
  9 +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
  10 +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
  11 +import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
  12 +import org.springframework.context.annotation.Bean;
  13 +import org.springframework.context.annotation.ComponentScan;
  14 +import org.springframework.context.annotation.Configuration;
  15 +import org.springframework.core.convert.converter.Converter;
  16 +import org.springframework.data.redis.connection.RedisConnectionFactory;
  17 +import org.springframework.data.redis.core.RedisTemplate;
  18 +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
  19 +import org.springframework.data.redis.serializer.StringRedisSerializer;
  20 +import org.springframework.stereotype.Component;
  21 +import org.springframework.util.StringUtils;
  22 +
  23 +import java.security.PrivateKey;
  24 +import java.security.PublicKey;
  25 +import java.text.SimpleDateFormat;
  26 +import java.time.LocalDate;
  27 +import java.time.LocalDateTime;
  28 +import java.time.LocalTime;
  29 +import java.time.format.DateTimeFormatter;
  30 +import java.util.Date;
  31 +
  32 +@Configuration
  33 +@ComponentScan("com.diligrp.boss.shared")
  34 +@MapperScan(basePackages = {"com.diligrp.boss.shared.dao"}, markerInterface = MybatisMapperSupport.class)
  35 +public class SharedConfiguration {
  36 + @Bean
  37 + public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
  38 + RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
  39 + template.setConnectionFactory(factory);
  40 + template.setKeySerializer(new StringRedisSerializer());
  41 + template.setHashKeySerializer(new StringRedisSerializer());
  42 + template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
  43 + template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
  44 + template.afterPropertiesSet();
  45 + return template;
  46 + }
  47 +
  48 + // Jackson DataBinding所有配置
  49 + @Bean
  50 + @ConditionalOnClass(JavaTimeModule.class)
  51 + public Jackson2ObjectMapperBuilderCustomizer customizeJacksonConfig() {
  52 + return JsonUtils::initObjectMapperBuilder;
  53 + }
  54 +
  55 + @Bean
  56 + public Converter<String, LocalDateTime> localDateTimeConverter() {
  57 + // 不能使用lambda表达式,否则导致springboot启动问题
  58 + return new Converter<String, LocalDateTime>() {
  59 + @Override
  60 + public LocalDateTime convert(String source) {
  61 + try {
  62 + return StringUtils.hasText(source) ? LocalDateTime.parse(source, DateTimeFormatter.ofPattern(Constants.DATE_TIME_FORMAT)) : null;
  63 + } catch (Exception ex) {
  64 + throw new IllegalArgumentException(String.format("Error parse %s to LocalDateTime", source), ex);
  65 + }
  66 + }
  67 + };
  68 + }
  69 +
  70 + @Bean
  71 + public Converter<String, LocalDate> localDateConverter() {
  72 + // 不能使用lambda表达式,否则导致springboot启动问题
  73 + return new Converter<String, LocalDate>() {
  74 + @Override
  75 + public LocalDate convert(String source) {
  76 + try {
  77 + return StringUtils.hasText(source) ? LocalDate.parse(source, DateTimeFormatter.ofPattern(Constants.DATE_FORMAT)) : null;
  78 + } catch (Exception ex) {
  79 + throw new IllegalArgumentException(String.format("Error parse %s to LocalDate", source), ex);
  80 + }
  81 + }
  82 + };
  83 + }
  84 +
  85 + @Bean
  86 + public Converter<String, LocalTime> localTimeConverter() {
  87 + // 不能使用lambda表达式,否则导致springboot启动问题
  88 + return new Converter<String, LocalTime>() {
  89 + @Override
  90 + public LocalTime convert(String source) {
  91 + try {
  92 + return StringUtils.hasText(source) ? LocalTime.parse(source, DateTimeFormatter.ofPattern(Constants.TIME_FORMAT)) : null;
  93 + } catch (Exception ex) {
  94 + throw new IllegalArgumentException(String.format("Error parse %s to LocalTime", source), ex);
  95 + }
  96 + }
  97 + };
  98 + }
  99 +
  100 + @Bean
  101 + public Converter<String, Date> dateConverter() {
  102 + // 不能使用lambda表达式,否则导致springboot启动问题
  103 + return new Converter<String, Date>() {
  104 + @Override
  105 + public Date convert(String source) {
  106 + try {
  107 + return StringUtils.hasText(source) ? new SimpleDateFormat(Constants.DATE_TIME_FORMAT).parse(source) : null;
  108 + } catch (Exception ex) {
  109 + throw new IllegalArgumentException(String.format("Error parse %s to Date", source), ex);
  110 + }
  111 + }
  112 + };
  113 + }
  114 +
  115 + @Component
  116 + @ConfigurationPropertiesBinding
  117 + public class PrivateKeyConverter implements Converter<String, PrivateKey> {
  118 + @Override
  119 + public PrivateKey convert(String source) {
  120 + try {
  121 + return RsaCipher.getPrivateKey(source);
  122 + } catch (Exception ex) {
  123 + throw new PlatformServiceException("privateKey configuration failed", ex);
  124 + }
  125 + }
  126 + }
  127 +
  128 + @Component
  129 + @ConfigurationPropertiesBinding
  130 + public class PublicKeyConverter implements Converter<String, PublicKey> {
  131 + @Override
  132 + public PublicKey convert(String source) {
  133 + try {
  134 + return RsaCipher.getPublicKey(source);
  135 + } catch (Exception ex) {
  136 + throw new PlatformServiceException("publicKey configuration failed", ex);
  137 + }
  138 + }
  139 + }
  140 +}
0 141 \ No newline at end of file
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/codec/ByteDecoder.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/codec/ByteDecoder.java
  1 +package com.diligrp.boss.shared.codec;
  2 +
  3 +import java.io.IOException;
  4 +
  5 +public interface ByteDecoder<T> {
  6 + T decode(byte[] payload) throws IOException;
  7 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/codec/ByteEncoder.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/codec/ByteEncoder.java
  1 +package com.diligrp.boss.shared.codec;
  2 +
  3 +import java.io.IOException;
  4 +
  5 +public interface ByteEncoder<T> {
  6 + byte[] encode(T payload) throws IOException;
  7 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/codec/StringCodec.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/codec/StringCodec.java
  1 +package com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/dao/SequenceKeyDao.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/dao/SequenceKeyDao.java
  1 +package com.diligrp.boss.shared.dao;
  2 +
  3 +import com.diligrp.boss.shared.domain.PersistentSequenceKey;
  4 +import com.diligrp.boss.shared.mybatis.MybatisMapperSupport;
  5 +import org.springframework.stereotype.Repository;
  6 +
  7 +import java.util.Optional;
  8 +
  9 +/**
  10 + * KeySequence数据操作
  11 + *
  12 + */
  13 +@Repository("sequenceKeyDao")
  14 +public interface SequenceKeyDao extends MybatisMapperSupport {
  15 + /**
  16 + * 注册SequenceKey
  17 + */
  18 + void insertSequenceKey(PersistentSequenceKey sequenceKey);
  19 +
  20 + /**
  21 + * 查询指定的SequenceKey
  22 + *
  23 + * @param key - SequenceKey唯一标识
  24 + * @return SequenceKey
  25 + */
  26 + Optional<PersistentSequenceKey> findSequenceKey(String key);
  27 +
  28 + /**
  29 + * 根据KeyId查询SequenceKey
  30 + *
  31 + * @param id - KeyId
  32 + * @return SequenceKey
  33 + */
  34 + Optional<PersistentSequenceKey> findSequenceKeyById(Long id);
  35 +
  36 + /**
  37 + * 悲观锁实现 - 根据数据库主键锁定数据记录
  38 + *
  39 + * @param id - KeyId
  40 + * @return SequenceKey
  41 + */
  42 + Optional<PersistentSequenceKey> lockSequenceKey(Long id);
  43 +
  44 + /**
  45 + * 悲观锁解锁实现 - 根据数据库主键解锁数据记录
  46 + *
  47 + * @param sequenceKey - 参数列表:id/newValue/expiredOn
  48 + * @return 1-更新成功,0-更新失败
  49 + */
  50 + int unlockSequenceKey(PersistentSequenceKey sequenceKey);
  51 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/domain/AsyncMessage.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/domain/AsyncMessage.java
  1 +package com.diligrp.boss.shared.domain;
  2 +
  3 +import com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/domain/BaseDo.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/domain/BaseDo.java
  1 +package com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/domain/ContainerSupport.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/domain/ContainerSupport.java
  1 +package com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/domain/Message.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/domain/Message.java
  1 +package com.diligrp.boss.shared.domain;
  2 +
  3 +import com.diligrp.boss.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
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/domain/PageMessage.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/domain/PageMessage.java
  1 +package com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/domain/PageQuery.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/domain/PageQuery.java
  1 +package com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/domain/PersistentSequenceKey.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/domain/PersistentSequenceKey.java
  1 +package com.diligrp.boss.shared.domain;
  2 +
  3 +import java.time.LocalDate;
  4 +
  5 +/**
  6 + * KEY-ID生成器数据库模型
  7 + */
  8 +public class PersistentSequenceKey {
  9 + /**
  10 + * ID主键
  11 + */
  12 + private Long id;
  13 + /**
  14 + * KEY标识
  15 + */
  16 + private String key;
  17 + /**
  18 + * 名称
  19 + */
  20 + private String name;
  21 + /**
  22 + * 起始值
  23 + */
  24 + private Long value;
  25 + /**
  26 + * 步长
  27 + */
  28 + private Integer step;
  29 + /**
  30 + * ID格式
  31 + */
  32 + private String pattern;
  33 + /**
  34 + * 有效日期
  35 + */
  36 + private LocalDate expiredOn;
  37 + /**
  38 + * 当前日期-循环ID生成器使用
  39 + */
  40 + private LocalDate today;
  41 + /**
  42 + * 数据版本
  43 + */
  44 + private Integer version;
  45 +
  46 + public Long getId() {
  47 + return id;
  48 + }
  49 +
  50 + public void setId(Long id) {
  51 + this.id = id;
  52 + }
  53 +
  54 + public String getKey() {
  55 + return key;
  56 + }
  57 +
  58 + public void setKey(String key) {
  59 + this.key = key;
  60 + }
  61 +
  62 + public String getName() {
  63 + return name;
  64 + }
  65 +
  66 + public void setName(String name) {
  67 + this.name = name;
  68 + }
  69 +
  70 + public Long getValue() {
  71 + return value;
  72 + }
  73 +
  74 + public void setValue(Long value) {
  75 + this.value = value;
  76 + }
  77 +
  78 + public Integer getStep() {
  79 + return step;
  80 + }
  81 +
  82 + public void setStep(Integer step) {
  83 + this.step = step;
  84 + }
  85 +
  86 + public String getPattern() {
  87 + return pattern;
  88 + }
  89 +
  90 + public void setPattern(String pattern) {
  91 + this.pattern = pattern;
  92 + }
  93 +
  94 + public LocalDate getExpiredOn() {
  95 + return expiredOn;
  96 + }
  97 +
  98 + public void setExpiredOn(LocalDate expiredOn) {
  99 + this.expiredOn = expiredOn;
  100 + }
  101 +
  102 + public LocalDate getToday() {
  103 + return today;
  104 + }
  105 +
  106 + public void setToday(LocalDate today) {
  107 + this.today = today;
  108 + }
  109 +
  110 + public Integer getVersion() {
  111 + return version;
  112 + }
  113 +
  114 + public void setVersion(Integer version) {
  115 + this.version = version;
  116 + }
  117 +
  118 + public static Builder builder() {
  119 + return new PersistentSequenceKey().new Builder();
  120 + }
  121 +
  122 + public class Builder {
  123 + public Builder key(String key) {
  124 + PersistentSequenceKey.this.key = key;
  125 + return this;
  126 + }
  127 +
  128 + public Builder name(String name) {
  129 + PersistentSequenceKey.this.name = name;
  130 + return this;
  131 + }
  132 +
  133 + public Builder value(Long value) {
  134 + PersistentSequenceKey.this.value = value;
  135 + return this;
  136 + }
  137 +
  138 + public Builder step(Integer step) {
  139 + PersistentSequenceKey.this.step = step;
  140 + return this;
  141 + }
  142 +
  143 + public Builder pattern(String pattern) {
  144 + PersistentSequenceKey.this.pattern = pattern;
  145 + return this;
  146 + }
  147 +
  148 + public Builder expiredOn(LocalDate expiredOn) {
  149 + PersistentSequenceKey.this.expiredOn = expiredOn;
  150 + return this;
  151 + }
  152 +
  153 + public Builder version(Integer version) {
  154 + PersistentSequenceKey.this.version = version;
  155 + return this;
  156 + }
  157 +
  158 + public PersistentSequenceKey build() {
  159 + return PersistentSequenceKey.this;
  160 + }
  161 + }
  162 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/domain/SequenceKey.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/domain/SequenceKey.java
  1 +package com.diligrp.boss.shared.domain;
  2 +
  3 +import java.time.LocalDate;
  4 +
  5 +public class SequenceKey {
  6 + private long sequence;
  7 + private LocalDate when;
  8 +
  9 + public SequenceKey(long sequence, LocalDate when) {
  10 + this.sequence = sequence;
  11 + this.when = when;
  12 + }
  13 +
  14 + public long getSequence() {
  15 + return sequence;
  16 + }
  17 +
  18 + public LocalDate getWhen() {
  19 + return when;
  20 + }
  21 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/exception/DefaultExceptionHandler.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/exception/DefaultExceptionHandler.java
  1 +package com.diligrp.boss.shared.exception;
  2 +
  3 +import com.diligrp.boss.shared.domain.Message;
  4 +import com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/exception/PlatformServiceException.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/exception/PlatformServiceException.java
  1 +package com.diligrp.boss.shared.exception;
  2 +
  3 +import com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/exception/ServiceAccessException.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/exception/ServiceAccessException.java
  1 +package com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/exception/ServiceConnectException.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/exception/ServiceConnectException.java
  1 +package com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/exception/ServiceTimeoutException.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/exception/ServiceTimeoutException.java
  1 +package com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/mybatis/GenericEnumTypeHandler.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/mybatis/GenericEnumTypeHandler.java
  1 +package com.diligrp.boss.shared.mybatis;
  2 +
  3 +import com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/mybatis/MybatisMapperSupport.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/mybatis/MybatisMapperSupport.java
  1 +package com.diligrp.boss.shared.mybatis;
  2 +
  3 +public interface MybatisMapperSupport {
  4 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/security/AesCipher.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/security/AesCipher.java
  1 +package com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/security/HexUtils.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/security/HexUtils.java
  1 +package com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/security/KeyStoreUtils.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/security/KeyStoreUtils.java
  1 +package com.diligrp.boss.shared.security;
  2 +
  3 +import com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/security/Md5Cipher.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/security/Md5Cipher.java
  1 +package com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/security/PasswordUtils.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/security/PasswordUtils.java
  1 +package com.diligrp.boss.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 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/security/PbeCipher.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/security/PbeCipher.java
  1 +package com.diligrp.boss.shared.security;
  2 +
  3 +import javax.crypto.Cipher;
  4 +import javax.crypto.SecretKey;
  5 +import javax.crypto.SecretKeyFactory;
  6 +import javax.crypto.spec.PBEKeySpec;
  7 +import javax.crypto.spec.PBEParameterSpec;
  8 +import java.security.Key;
  9 +import java.util.Random;
  10 +
  11 +/**
  12 + * PBE算法工具类
  13 + */
  14 +public class PbeCipher {
  15 + private static final String ALGORITHM = "PBEWithSHA1AndDESede";
  16 +
  17 + public static byte[] encrypt(byte[] data, String password, byte[] salt) throws Exception {
  18 + Key key = toKey(password);
  19 +
  20 + PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
  21 + Cipher cipher = Cipher.getInstance(ALGORITHM);
  22 + cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
  23 +
  24 + return cipher.doFinal(data);
  25 + }
  26 +
  27 + public static byte[] decrypt(byte[] data, String password, byte[] salt) throws Exception {
  28 + Key key = toKey(password);
  29 +
  30 + PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
  31 + Cipher cipher = Cipher.getInstance(ALGORITHM);
  32 + cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
  33 +
  34 + return cipher.doFinal(data);
  35 + }
  36 +
  37 + private static byte[] initSalt() throws Exception {
  38 + byte[] salt = new byte[8];
  39 + Random random = new Random();
  40 + random.nextBytes(salt);
  41 + return salt;
  42 + }
  43 +
  44 + private static Key toKey(String password) throws Exception {
  45 + PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
  46 + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
  47 + SecretKey secretKey = keyFactory.generateSecret(keySpec);
  48 +
  49 + return secretKey;
  50 + }
  51 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/security/RsaCipher.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/security/RsaCipher.java
  1 +package com.diligrp.boss.shared.security;
  2 +
  3 +import javax.crypto.Cipher;
  4 +import java.security.*;
  5 +import java.security.spec.PKCS8EncodedKeySpec;
  6 +import java.security.spec.X509EncodedKeySpec;
  7 +import java.util.Base64;
  8 +
  9 +/**
  10 + * RSA算法工具类
  11 + */
  12 +public class RsaCipher {
  13 + private static final String KEY_ALGORITHM = "RSA";
  14 +
  15 + private static final String SIGN_ALGORITHMS = "SHA1WithRSA";
  16 +
  17 + public static String[] generateRSAKeyPair() throws Exception {
  18 + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
  19 + keyPairGen.initialize(512, new SecureRandom());
  20 + KeyPair keyPair = keyPairGen.generateKeyPair();
  21 + PrivateKey privateKey = keyPair.getPrivate();
  22 + PublicKey publicKey = keyPair.getPublic();
  23 +
  24 + String[] keyPairArray = new String[2];
  25 + keyPairArray[0] = Base64.getEncoder().encodeToString(privateKey.getEncoded());
  26 + keyPairArray[1] = Base64.getEncoder().encodeToString(publicKey.getEncoded());
  27 + return keyPairArray;
  28 + }
  29 +
  30 + public static byte[] encrypt(byte[] data, Key secretKey) throws Exception {
  31 + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
  32 + cipher.init(Cipher.ENCRYPT_MODE, secretKey);
  33 + return cipher.doFinal(data);
  34 + }
  35 +
  36 + public static byte[] decrypt(byte[] data, Key secretKey) throws Exception {
  37 + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
  38 + cipher.init(Cipher.DECRYPT_MODE, secretKey);
  39 + return cipher.doFinal(data);
  40 + }
  41 +
  42 + public static byte[] sign(byte[] data, PrivateKey privateKey) throws Exception {
  43 + Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
  44 + signature.initSign(privateKey, new SecureRandom());
  45 + signature.update(data);
  46 + return signature.sign();
  47 + }
  48 +
  49 + public static boolean verify(byte[] data, byte[] sign, PublicKey publicKey) throws Exception {
  50 + Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
  51 + signature.initVerify(publicKey);
  52 + signature.update(data);
  53 + return signature.verify(sign);
  54 + }
  55 +
  56 + public static PrivateKey getPrivateKey(String key) throws Exception {
  57 + byte[] keyBytes = Base64.getDecoder().decode(key);
  58 + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
  59 + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
  60 + return keyFactory.generatePrivate(keySpec);
  61 + }
  62 +
  63 + public static PublicKey getPublicKey(String key) throws Exception {
  64 + byte[] keyBytes = Base64.getDecoder().decode(key);
  65 + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
  66 + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
  67 + return keyFactory.generatePublic(keySpec);
  68 + }
  69 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/security/ShaCipher.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/security/ShaCipher.java
  1 +package com.diligrp.boss.shared.security;
  2 +
  3 +import java.security.MessageDigest;
  4 +
  5 +/**
  6 + * SHA散列算法工具类
  7 + */
  8 +public class ShaCipher {
  9 + private static final String KEY_SHA = "SHA";
  10 +
  11 + public static byte[] encrypt(byte[] data) throws Exception {
  12 + MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
  13 + sha.update(data);
  14 +
  15 + return sha.digest();
  16 + }
  17 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/service/SequenceKeyService.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/service/SequenceKeyService.java
  1 +package com.diligrp.boss.shared.service;
  2 +
  3 +import com.diligrp.boss.shared.domain.PersistentSequenceKey;
  4 +
  5 +/**
  6 + * SequenceKey数据同步基础类
  7 + *
  8 + * @author: brenthuang
  9 + * @date: 2020/03/24
  10 + */
  11 +public interface SequenceKeyService {
  12 + /**
  13 + * 注册SequenceKey
  14 + */
  15 + void registerSequenceKey(PersistentSequenceKey sequenceKey);
  16 +
  17 + /**
  18 + * 查找指定的SequenceKey
  19 + *
  20 + * @param key - SequenceKey的唯一标识
  21 + * @return SequenceKey
  22 + */
  23 + PersistentSequenceKey findSequenceKey(String key);
  24 +
  25 + /**
  26 + * 根据KeyId查询SequenceKey
  27 + *
  28 + * @param id - KeyId
  29 + * @return SequenceKey
  30 + */
  31 + PersistentSequenceKey findSequenceKeyById(Long id);
  32 +
  33 + /**
  34 + * 通过悲观锁实现同步从数据库获取基于过期日期的SequenceKey
  35 + *
  36 + * 根据数据库主键锁定数据记录(加行锁),根据SequenceKey的过期日期更新下一个startWith值
  37 + * 当SequenceKey过期时value将重新设置为1,否则value + 1
  38 + *
  39 + * @param id - KeyId
  40 + * @return SequenceKey
  41 + */
  42 + PersistentSequenceKey synchronizeSequenceKey(Long id);
  43 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/service/SequenceKeyServiceImpl.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/service/SequenceKeyServiceImpl.java
  1 +package com.diligrp.boss.shared.service;
  2 +
  3 +import com.diligrp.boss.shared.ErrorCode;
  4 +import com.diligrp.boss.shared.dao.SequenceKeyDao;
  5 +import com.diligrp.boss.shared.domain.PersistentSequenceKey;
  6 +import com.diligrp.boss.shared.exception.PlatformServiceException;
  7 +import org.springframework.stereotype.Service;
  8 +import org.springframework.transaction.annotation.Transactional;
  9 +
  10 +import java.time.LocalDate;
  11 +
  12 +/**
  13 + * SequenceKey数据同步的实现类
  14 + */
  15 +@Service("sequenceKeyService")
  16 +public class SequenceKeyServiceImpl implements SequenceKeyService {
  17 +
  18 + private final SequenceKeyDao sequenceKeyDao;
  19 +
  20 + public SequenceKeyServiceImpl(SequenceKeyDao sequenceKeyDao) {
  21 + this.sequenceKeyDao = sequenceKeyDao;
  22 + }
  23 +
  24 + @Override
  25 + @Transactional(rollbackFor = Exception.class)
  26 + public void registerSequenceKey(PersistentSequenceKey sequenceKey) {
  27 + sequenceKeyDao.insertSequenceKey(sequenceKey);
  28 + }
  29 +
  30 + @Override
  31 + public PersistentSequenceKey findSequenceKey(String key) {
  32 + return sequenceKeyDao.findSequenceKey(key).orElseThrow(() ->
  33 + new PlatformServiceException(ErrorCode.OBJECT_NOT_FOUND, "没有配置该SequenceKey"));
  34 + }
  35 +
  36 + @Override
  37 + public PersistentSequenceKey findSequenceKeyById(Long id) {
  38 + return sequenceKeyDao.findSequenceKeyById(id).orElseThrow(() ->
  39 + new PlatformServiceException(ErrorCode.OBJECT_NOT_FOUND, "没有配置该SequenceKey"));
  40 + }
  41 +
  42 + /**
  43 + * 数据库的行锁只有在事务提交之后才会释放,这里使用业务层的Spring事务,因此行锁将不能很快释放,这样势必会降低此代码块的并发性能。
  44 + * 如果新开一个Spring事务Propagation.REQUIRES_NEW,与业务层事务独立将无法保证ID的连续性(不能随着业务层的失败回滚生成的ID)
  45 + */
  46 + @Override
  47 +// @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
  48 + @Transactional(rollbackFor = Exception.class)
  49 + public PersistentSequenceKey synchronizeSequenceKey(Long id) {
  50 + // 悲观锁添加行锁 - 多JVM多线程场景下自动实现线程同步
  51 + // 通过SELECT FOR UPDATE锁定了行,当事务提交时将自动释放行锁
  52 + PersistentSequenceKey persistentKey = sequenceKeyDao.lockSequenceKey(id).orElseThrow(() ->
  53 + new PlatformServiceException(ErrorCode.OBJECT_NOT_FOUND, "没有配置该SequenceKey"));
  54 +
  55 + // 当PersistentKey设置了过期时间并且已经过期时, 则value重新设置成1并刷新过期日期为今天,否则设置value+=step
  56 + LocalDate today = persistentKey.getToday();
  57 + LocalDate expiredDay = persistentKey.getExpiredOn();
  58 + if (expiredDay != null && today.isAfter(persistentKey.getExpiredOn())) {
  59 + persistentKey.setValue(1L);
  60 + expiredDay = today;
  61 + }
  62 +
  63 + PersistentSequenceKey newKey = new PersistentSequenceKey();
  64 + newKey.setId(persistentKey.getId());
  65 + newKey.setValue(persistentKey.getValue() + persistentKey.getStep());
  66 + newKey.setExpiredOn(expiredDay);
  67 +
  68 + sequenceKeyDao.unlockSequenceKey(newKey);
  69 +
  70 + return persistentKey;
  71 + }
  72 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/service/ServiceEndpointSupport.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/service/ServiceEndpointSupport.java
  1 +package com.diligrp.boss.shared.service;
  2 +
  3 +import com.diligrp.boss.shared.exception.ServiceAccessException;
  4 +import com.diligrp.boss.shared.exception.ServiceConnectException;
  5 +import com.diligrp.boss.shared.exception.ServiceTimeoutException;
  6 +import com.diligrp.boss.shared.util.ObjectUtils;
  7 +
  8 +import javax.net.ssl.SSLContext;
  9 +import javax.net.ssl.SSLParameters;
  10 +import java.net.ConnectException;
  11 +import java.net.URI;
  12 +import java.net.URLEncoder;
  13 +import java.net.http.*;
  14 +import java.nio.charset.StandardCharsets;
  15 +import java.time.Duration;
  16 +import java.util.Arrays;
  17 +import java.util.List;
  18 +import java.util.Map;
  19 +import java.util.Optional;
  20 +import java.util.concurrent.Executor;
  21 +
  22 +public abstract class ServiceEndpointSupport {
  23 + protected static final int MAX_CONNECT_TIMEOUT_TIME = 10000;
  24 +
  25 + protected static final int MAX_REQUEST_TIMEOUT_TIME = 30000;
  26 +
  27 + protected static final String CONTENT_TYPE = "Content-Type";
  28 +
  29 + protected static final String CONTENT_TYPE_JSON = "application/json;charset=UTF-8";
  30 +
  31 + protected static final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded;charset=UTF-8";
  32 +
  33 + protected static final String CONTENT_TYPE_XML = "text/xml;charset=UTF-8";
  34 +
  35 + protected volatile HttpClient httpClient;
  36 +
  37 + protected Object lock = new Object();
  38 +
  39 + /**
  40 + * @throws ServiceConnectException, ServiceAccessException, ServiceTimeoutException
  41 + */
  42 + public HttpResult send(String requestUrl, String body) {
  43 + return send(requestUrl, null, body);
  44 + }
  45 +
  46 + /**
  47 + * @throws ServiceConnectException, ServiceAccessException, ServiceTimeoutException
  48 + */
  49 + public HttpResult send(String requestUrl, HttpHeader[] headers, String body) {
  50 + if (ObjectUtils.isEmpty(requestUrl)) {
  51 + throw new IllegalArgumentException("Invalid http request url, url=" + requestUrl);
  52 + }
  53 +
  54 + HttpRequest.Builder request = HttpRequest.newBuilder().uri(URI.create(requestUrl))
  55 + .version(HttpClient.Version.HTTP_2)
  56 + .timeout(Duration.ofMillis(MAX_REQUEST_TIMEOUT_TIME))
  57 + .header(CONTENT_TYPE, CONTENT_TYPE_JSON)
  58 + .POST(HttpRequest.BodyPublishers.ofString(body));
  59 + // Wrap the HTTP headers
  60 + if (headers != null && headers.length > 0) {
  61 + // request.headers(Arrays.stream(headers).flatMap(h -> Stream.of(h.param, h.value)).toArray(String[]::new));
  62 + Arrays.stream(headers).filter(h -> h != null).forEach(h -> request.header(h.param, h.value));
  63 + }
  64 +
  65 + return execute(request.build());
  66 + }
  67 +
  68 + /**
  69 + * @throws ServiceConnectException, ServiceAccessException, ServiceTimeoutException
  70 + */
  71 + public HttpResult send(String requestUrl, HttpParam[] params) {
  72 + return send(requestUrl, null, params);
  73 + }
  74 +
  75 + /**
  76 + * @throws ServiceConnectException, ServiceAccessException, ServiceTimeoutException
  77 + */
  78 + public HttpResult send(String requestUrl, HttpHeader[] headers, HttpParam[] params) {
  79 + if (ObjectUtils.isEmpty(requestUrl)) {
  80 + throw new IllegalArgumentException("Invalid http request url, url=" + requestUrl);
  81 + }
  82 +
  83 + HttpRequest.Builder request = HttpRequest.newBuilder().uri(URI.create(requestUrl))
  84 + .version(HttpClient.Version.HTTP_2)
  85 + .timeout(Duration.ofMillis(MAX_REQUEST_TIMEOUT_TIME))
  86 + .header(CONTENT_TYPE, CONTENT_TYPE_FORM);
  87 + // Wrap the HTTP headers
  88 + if (headers != null && headers.length > 0) {
  89 + Arrays.stream(headers).filter(h -> h != null).forEach(h -> request.header(h.param, h.value));
  90 + }
  91 + if (params != null && params.length > 0) {
  92 + // [key1, value1, key2, value2] -> key1=value1&key2=value2
  93 + String body = Arrays.stream(params).filter(p -> p != null)
  94 + .map(p -> "".concat(p.param).concat("=").concat(URLEncoder.encode(p.value, StandardCharsets.UTF_8)))
  95 + .reduce((value1, value2) -> "".concat(value1).concat("&").concat(value2)).get();
  96 + request.POST(HttpRequest.BodyPublishers.ofString(body));
  97 + }
  98 +
  99 + return execute(request.build());
  100 + }
  101 +
  102 + /**
  103 + * @throws ServiceConnectException, ServiceAccessException, ServiceTimeoutException
  104 + */
  105 + public HttpResult sendXml(String requestUrl, HttpHeader[] headers, String xml) {
  106 + if (ObjectUtils.isEmpty(requestUrl)) {
  107 + throw new IllegalArgumentException("Invalid http request url, url=" + requestUrl);
  108 + }
  109 +
  110 + HttpRequest.Builder request = HttpRequest.newBuilder().uri(URI.create(requestUrl))
  111 + .version(HttpClient.Version.HTTP_2)
  112 + .timeout(Duration.ofMillis(MAX_REQUEST_TIMEOUT_TIME))
  113 + .header(CONTENT_TYPE, CONTENT_TYPE_XML)
  114 + .POST(HttpRequest.BodyPublishers.ofString(xml));
  115 + // Wrap the HTTP headers
  116 + if (headers != null && headers.length > 0) {
  117 + Arrays.stream(headers).filter(h -> h != null).forEach(h -> request.header(h.param, h.value));
  118 + }
  119 +
  120 + return execute(request.build());
  121 + }
  122 +
  123 + protected HttpClient getHttpClient() {
  124 + if (httpClient == null) {
  125 + synchronized (lock) {
  126 + // Double check for performance purpose
  127 + if (httpClient == null) {
  128 + HttpClient.Builder builder = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2)
  129 + // 认证,默认情况下Authenticator.getDefault()是null值,会报错
  130 +// .authenticator(Authenticator.getDefault())
  131 + // 缓存,默认情况下 CookieHandler.getDefault()是null值,会报错
  132 +// .cookieHandler(CookieHandler.getDefault())
  133 + .connectTimeout(Duration.ofMillis(MAX_CONNECT_TIMEOUT_TIME))
  134 + .followRedirects(HttpClient.Redirect.NEVER);
  135 + // Build SSL
  136 + Optional<SSLContext> sslContext = buildSSLContext();
  137 + sslContext.ifPresent(builder::sslContext);
  138 + Optional<SSLParameters> parameters = buildSslParameters();
  139 + parameters.ifPresent(builder::sslParameters);
  140 + // Build thread pool
  141 + Optional<Executor> executor = buildThreadPool();
  142 + executor.ifPresent(builder::executor);
  143 + httpClient = builder.build();
  144 + }
  145 + }
  146 + }
  147 +
  148 + return httpClient;
  149 + }
  150 +
  151 + protected Optional<SSLContext> buildSSLContext() {
  152 + return Optional.ofNullable(null);
  153 + }
  154 +
  155 + protected Optional<SSLParameters> buildSslParameters() {
  156 + return Optional.ofNullable(null);
  157 + }
  158 +
  159 + protected Optional<Executor> buildThreadPool() {
  160 + return Optional.ofNullable(null);
  161 + }
  162 +
  163 + /**
  164 + * @throws ServiceConnectException, ServiceAccessException, ServiceTimeoutException
  165 + */
  166 + protected HttpResult execute(HttpRequest request) {
  167 + try {
  168 + HttpResponse<String> response = getHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
  169 +
  170 + HttpResult result = HttpResult.create();
  171 + result.statusCode = response.statusCode();
  172 + result.responseText = response.body();
  173 + result.headers = response.headers() == null ? null : response.headers().map();
  174 + return result;
  175 + } catch (ConnectException | HttpConnectTimeoutException cex) {
  176 + throw new ServiceConnectException("Remote service connection exception", cex);
  177 + } catch (HttpTimeoutException hex) {
  178 + throw new ServiceTimeoutException("Remote service access timeout", hex);
  179 + } catch (Exception ex) {
  180 + throw new ServiceAccessException("Remote service access exception", ex);
  181 + }
  182 + }
  183 +
  184 + public static class HttpParam {
  185 + public String param;
  186 + public String value;
  187 +
  188 + private HttpParam(String param, String value) {
  189 + this.param = param;
  190 + this.value = value;
  191 + }
  192 +
  193 + public static HttpParam create(String param, String value) {
  194 + return new HttpParam(param, value);
  195 + }
  196 + }
  197 +
  198 + public static class HttpHeader {
  199 + public String param;
  200 + public String value;
  201 +
  202 + private HttpHeader(String param, String value) {
  203 + this.param = param;
  204 + this.value = value;
  205 + }
  206 +
  207 + public static HttpHeader create(String param, String value) {
  208 + return new HttpHeader(param, value);
  209 + }
  210 + }
  211 +
  212 + public static class HttpResult {
  213 + public int statusCode = -1;
  214 + public String responseText;
  215 + public Map<String, List<String>> headers;
  216 +
  217 + public static HttpResult create() {
  218 + return new HttpResult();
  219 + }
  220 +
  221 + public String header(String key) {
  222 + if (headers == null) {
  223 + return null;
  224 + }
  225 +
  226 + List<String> values = headers.get(key);
  227 + if (values == null || values.isEmpty()) {
  228 + return null;
  229 + }
  230 +
  231 + return values.get(0);
  232 + }
  233 + }
  234 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/service/ThreadPollService.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/service/ThreadPollService.java
  1 +package com.diligrp.boss.shared.service;
  2 +
  3 +import com.diligrp.boss.shared.Constants;
  4 +
  5 +import java.util.concurrent.ExecutorService;
  6 +import java.util.concurrent.LinkedBlockingQueue;
  7 +import java.util.concurrent.ThreadPoolExecutor;
  8 +import java.util.concurrent.TimeUnit;
  9 +
  10 +public final class ThreadPollService {
  11 +
  12 + private static volatile ExecutorService threadPoll;
  13 +
  14 + private ThreadPollService() {
  15 + }
  16 +
  17 + public static ExecutorService getInstance() {
  18 + if (threadPoll == null) {
  19 + synchronized (ThreadPollService.class) {
  20 + if (threadPoll == null) {
  21 + threadPoll = new ThreadPoolExecutor(Constants.CORE_POOL_SIZE, Constants.MAX_POOL_SIZE,
  22 + 1, TimeUnit.MINUTES, new LinkedBlockingQueue(1000),
  23 + new ThreadPoolExecutor.CallerRunsPolicy());
  24 + }
  25 + }
  26 + }
  27 + return threadPoll;
  28 + }
  29 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/type/Gender.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/type/Gender.java
  1 +package com.diligrp.boss.shared.type;
  2 +
  3 +import java.util.Arrays;
  4 +import java.util.List;
  5 +import java.util.Optional;
  6 +import java.util.stream.Stream;
  7 +
  8 +public enum Gender implements IEnumType {
  9 + MALE("男", 1),
  10 + FEMALE("女", 2);
  11 +
  12 + private String name;
  13 + private int code;
  14 +
  15 + Gender(String name, int code) {
  16 + this.name = name;
  17 + this.code = code;
  18 + }
  19 +
  20 + public static Optional<Gender> getGender(int code) {
  21 + Stream<Gender> GENDERS = Arrays.stream(values());
  22 + return GENDERS.filter((gender) -> gender.getCode() == code).findFirst();
  23 + }
  24 +
  25 + public static String getName(int code) {
  26 + Stream<Gender> GENDERS = Arrays.stream(values());
  27 + Optional<String> result = GENDERS.filter((gender) -> gender.getCode() == code)
  28 + .map(Gender::getName).findFirst();
  29 + return result.isPresent() ? result.get() : null;
  30 + }
  31 +
  32 + public static List<Gender> getGenders() {
  33 + return Arrays.asList(values());
  34 + }
  35 +
  36 + public String getName() {
  37 + return this.name;
  38 + }
  39 +
  40 + public int getCode() {
  41 + return this.code;
  42 + }
  43 +
  44 + public String toString() {
  45 + return this.name;
  46 + }
  47 +}
0 48 \ No newline at end of file
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/type/IEnumType.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/type/IEnumType.java
  1 +package com.diligrp.boss.shared.type;
  2 +
  3 +public interface IEnumType {
  4 + int getCode();
  5 +
  6 + String getName();
  7 +
  8 + String toString();
  9 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/uid/KeyGenerator.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/uid/KeyGenerator.java
  1 +package com.diligrp.boss.shared.uid;
  2 +
  3 +/**
  4 + * SequenceKey基础类
  5 + */
  6 +public interface KeyGenerator {
  7 + /**
  8 + * 获取下一个ID
  9 + *
  10 + * @return 下一个ID
  11 + */
  12 + String nextId();
  13 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/uid/KeyGeneratorManager.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/uid/KeyGeneratorManager.java
  1 +package com.diligrp.boss.shared.uid;
  2 +
  3 +import com.diligrp.boss.shared.domain.PersistentSequenceKey;
  4 +import com.diligrp.boss.shared.domain.SequenceKey;
  5 +import com.diligrp.boss.shared.service.SequenceKeyService;
  6 +import com.diligrp.boss.shared.uid.pattern.PatternLayout;
  7 +import com.diligrp.boss.shared.util.AssertUtils;
  8 +import org.springframework.stereotype.Service;
  9 +
  10 +import java.time.LocalDate;
  11 +import java.util.concurrent.ConcurrentHashMap;
  12 +import java.util.concurrent.ConcurrentMap;
  13 +import java.util.concurrent.TimeUnit;
  14 +import java.util.concurrent.locks.Lock;
  15 +import java.util.concurrent.locks.ReentrantLock;
  16 +
  17 +@Service("keyGeneratorManager")
  18 +public class KeyGeneratorManager {
  19 + private final SequenceKeyService sequenceKeyService;
  20 +
  21 + private final Lock locker = new ReentrantLock();
  22 +
  23 + private final ConcurrentMap<String, KeyGenerator> keyGenerators = new ConcurrentHashMap<>();
  24 +
  25 + public KeyGeneratorManager(SequenceKeyService sequenceKeyService) {
  26 + this.sequenceKeyService = sequenceKeyService;
  27 + }
  28 +
  29 + public KeyGenerator getKeyGenerator(String key) {
  30 + AssertUtils.notNull(key, "Miss key parameter");
  31 +
  32 + KeyGenerator keyGenerator = keyGenerators.get(key);
  33 + // First check, no need synchronize code block
  34 + if (keyGenerator == null) {
  35 + boolean result = false;
  36 + try {
  37 + result = locker.tryLock(15, TimeUnit.SECONDS);
  38 + if (!result) {
  39 + throw new RuntimeException("Timeout to get KeyGenerator for " + key);
  40 + }
  41 +
  42 + // Double check for performance purpose
  43 + if ((keyGenerator = keyGenerators.get(key)) == null) {
  44 + PersistentSequenceKey persistentKey = sequenceKeyService.findSequenceKey(key);
  45 + if (persistentKey.getExpiredOn() == null) {
  46 + keyGenerator = new KeyGeneratorImpl(persistentKey.getId(), key, persistentKey.getPattern());
  47 + } else {
  48 + keyGenerator = new ExpiredKeyGeneratorImpl(persistentKey.getId(), key, persistentKey.getPattern());
  49 + }
  50 + keyGenerators.put(key, keyGenerator);
  51 + }
  52 + } catch (InterruptedException iex) {
  53 + throw new RuntimeException("Interrupted to get KeyGenerator for " + key, iex);
  54 + } finally {
  55 + if (result) {
  56 + locker.unlock();
  57 + }
  58 + }
  59 + }
  60 +
  61 + return keyGenerator;
  62 + }
  63 +
  64 + private class KeyGeneratorImpl implements KeyGenerator {
  65 + private final long id;
  66 + private final String key;
  67 + private final PatternLayout layout;
  68 + private long startWith;
  69 + private long endWith;
  70 + private final Lock keyLocker = new ReentrantLock();
  71 +
  72 + public KeyGeneratorImpl(long id, String key, String pattern) {
  73 + this.id = id;
  74 + this.key = key;
  75 + this.startWith = 0;
  76 + this.endWith = -1;
  77 + if (pattern != null) {
  78 + this.layout = new PatternLayout(pattern);
  79 + } else {
  80 + this.layout = null;
  81 + }
  82 + }
  83 +
  84 + @Override
  85 + public String nextId() {
  86 + boolean result = false;
  87 + try {
  88 + result = keyLocker.tryLock(15L, TimeUnit.SECONDS);
  89 + if (!result) {
  90 + throw new RuntimeException("Timeout to get KeyGenerator for " + key);
  91 + }
  92 +
  93 + if (this.startWith <= this.endWith) {
  94 + if (this.layout != null) {
  95 + SequenceKey context = new SequenceKey(this.startWith++, LocalDate.now());
  96 + return layout.doLayout(context);
  97 + } else {
  98 + return String.valueOf(this.startWith++);
  99 + }
  100 + } else {
  101 + PersistentSequenceKey sequenceKey = sequenceKeyService.synchronizeSequenceKey(id);
  102 + long newValue = sequenceKey.getValue() + sequenceKey.getStep();
  103 + this.startWith = sequenceKey.getValue();
  104 + this.endWith = newValue - 1;
  105 +
  106 + // Then recursive call for a next ID
  107 + return nextId();
  108 + }
  109 + } catch (InterruptedException iex) {
  110 + throw new RuntimeException("Interrupted to get KeyGenerator for " + key, iex);
  111 + } finally {
  112 + if (result) {
  113 + keyLocker.unlock();
  114 + }
  115 + }
  116 + }
  117 + }
  118 +
  119 + private class ExpiredKeyGeneratorImpl implements KeyGenerator {
  120 + private final long id;
  121 + private final String key;
  122 + private final PatternLayout layout;
  123 +
  124 + private ExpiredKeyGeneratorImpl(long id, String key, String pattern) {
  125 + this.id = id;
  126 + this.key = key;
  127 + if (pattern != null) {
  128 + this.layout = new PatternLayout(pattern);
  129 + } else {
  130 + this.layout = null;
  131 + }
  132 + }
  133 +
  134 + @Override
  135 + public String nextId() {
  136 + //悲观锁添加行锁 - 多JVM多线程场景下自动实现线程同步
  137 + PersistentSequenceKey persistentKey = sequenceKeyService.synchronizeSequenceKey(id);
  138 + if (persistentKey == null) {
  139 + throw new RuntimeException("Unregistered service key generator: " + key);
  140 + }
  141 +
  142 + if (this.layout != null) {
  143 + SequenceKey context = new SequenceKey(persistentKey.getValue(), persistentKey.getToday());
  144 + return layout.doLayout(context);
  145 + } else {
  146 + return String.valueOf(persistentKey.getValue());
  147 + }
  148 + }
  149 + }
  150 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/uid/SnowflakeKeyManager.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/uid/SnowflakeKeyManager.java
  1 +package com.diligrp.boss.shared.uid;
  2 +
  3 +import com.diligrp.boss.shared.util.AssertUtils;
  4 +import com.diligrp.boss.shared.util.DateUtils;
  5 +import com.diligrp.boss.shared.util.RandomUtils;
  6 +import org.springframework.stereotype.Service;
  7 +
  8 +import java.time.LocalDateTime;
  9 +import java.time.ZoneOffset;
  10 +import java.util.concurrent.ConcurrentHashMap;
  11 +import java.util.concurrent.ConcurrentMap;
  12 +import java.util.concurrent.TimeUnit;
  13 +import java.util.concurrent.locks.Lock;
  14 +import java.util.concurrent.locks.ReentrantLock;
  15 +
  16 +/**
  17 + * 雪花算法KeyManager实现
  18 + */
  19 +@Service("snowflakeKeyManager")
  20 +public class SnowflakeKeyManager {
  21 +
  22 + private final Lock locker = new ReentrantLock();
  23 +
  24 + private final ConcurrentMap<String, KeyGenerator> keyGenerators = new ConcurrentHashMap<>();
  25 +
  26 + public KeyGenerator getKeyGenerator(SnowflakeKey key) {
  27 + AssertUtils.notNull(key, "Miss key parameter");
  28 +
  29 + String cachedKey = key.identifier();
  30 + KeyGenerator keyGenerator = keyGenerators.get(cachedKey);
  31 + // First check, no need synchronize code block
  32 + if (keyGenerator == null) {
  33 + boolean result = false;
  34 + try {
  35 + result = locker.tryLock(15, TimeUnit.SECONDS);
  36 + if (!result) {
  37 + throw new RuntimeException("Timeout to get SnowflakeKeyGenerator for " + cachedKey);
  38 + }
  39 +
  40 + // Double check for performance purpose
  41 + if ((keyGenerator = keyGenerators.get(cachedKey)) == null) {
  42 + keyGenerator = new SnowflakeKeyGenerator(key.timeBits(), key.workerBits(), key.seqBits());
  43 + keyGenerators.put(cachedKey, keyGenerator);
  44 + }
  45 + } catch (InterruptedException iex) {
  46 + throw new RuntimeException("Interrupted to get SnowflakeKeyGenerator for " + cachedKey, iex);
  47 + } finally {
  48 + if (result) {
  49 + locker.unlock();
  50 + }
  51 + }
  52 + }
  53 +
  54 + return keyGenerator;
  55 + }
  56 +
  57 + /**
  58 + * 雪花算法ID生成器实现
  59 + */
  60 + private static class SnowflakeKeyGenerator implements KeyGenerator {
  61 +
  62 + /**
  63 + * Customer based epoch, unit as second. until 2020-08-08 00:00:00
  64 + */
  65 + private final long epochSeconds = LocalDateTime.of(2020, 8, 8, 0, 0, 0)
  66 + .toEpochSecond(ZoneOffset.of("+8"));
  67 +
  68 + /**
  69 + * Stable fields after spring bean initializing
  70 + */
  71 + private final BitsAllocator bitsAllocator;
  72 + private final long workerId;
  73 +
  74 + /**
  75 + * Volatile fields caused by nextId()
  76 + */
  77 + private long sequence = 0L;
  78 + private long lastSecond = -1L;
  79 +
  80 + public SnowflakeKeyGenerator(int timeBits, int workerBits, int seqBits) {
  81 + this.bitsAllocator = new BitsAllocator(timeBits, workerBits, seqBits);
  82 + this.workerId = bitsAllocator.assignWorkerId();
  83 + }
  84 +
  85 + @Override
  86 + public synchronized String nextId() {
  87 + long currentSecond = getCurrentSecond();
  88 +
  89 + // Clock moved backwards, refuse to generate uid
  90 + if (currentSecond < lastSecond) {
  91 + long refusedSeconds = lastSecond - currentSecond;
  92 + throw new RuntimeException(String.format("Clock moved backwards. Refusing for %d seconds", refusedSeconds));
  93 + }
  94 +
  95 + // At the same second, increase service
  96 + if (currentSecond == lastSecond) {
  97 + sequence = (sequence + 1) & bitsAllocator.maxSequence;
  98 + // Exceed the max service, we wait the next second to generate uid
  99 + if (sequence == 0) {
  100 + currentSecond = getNextSecond(lastSecond);
  101 + }
  102 +
  103 + // At the different second, service restart from zero
  104 + } else {
  105 + sequence = 0L;
  106 + }
  107 +
  108 + lastSecond = currentSecond;
  109 +
  110 + // Allocate bits for UID
  111 + return String.valueOf(bitsAllocator.allocate(currentSecond - epochSeconds, workerId, sequence));
  112 + }
  113 +
  114 + public String parseId(long uid) {
  115 + long totalBits = BitsAllocator.TOTAL_BITS;
  116 + long signBits = bitsAllocator.signBits;
  117 + long timestampBits = bitsAllocator.timestampBits;
  118 + long workerIdBits = bitsAllocator.workerIdBits;
  119 + long sequenceBits = bitsAllocator.sequenceBits;
  120 +
  121 + // parse UID
  122 + long sequence = (uid << (totalBits - sequenceBits)) >>> (totalBits - sequenceBits);
  123 + long workerId = (uid << (timestampBits + signBits)) >>> (totalBits - workerIdBits);
  124 + long deltaSeconds = uid >>> (workerIdBits + sequenceBits);
  125 +
  126 + LocalDateTime when = LocalDateTime.ofEpochSecond(epochSeconds + deltaSeconds, 0, ZoneOffset.of("+8"));
  127 + String thatTime = DateUtils.formatDateTime(when);
  128 +
  129 + // format as string
  130 + return String.format("{\"UID\":\"%d\",\"timestamp\":\"%s\",\"workerId\":\"%d\",\"service\":\"%d\"}",
  131 + uid, thatTime, workerId, sequence);
  132 + }
  133 +
  134 + /**
  135 + * Get next millisecond
  136 + */
  137 + private long getNextSecond(long lastTimestamp) {
  138 + long timestamp = getCurrentSecond();
  139 + while (timestamp <= lastTimestamp) {
  140 + timestamp = getCurrentSecond();
  141 + }
  142 +
  143 + return timestamp;
  144 + }
  145 +
  146 + /**
  147 + * Get current second
  148 + */
  149 + private long getCurrentSecond() {
  150 + long currentSecond = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
  151 + if (currentSecond - epochSeconds > bitsAllocator.maxDeltaSeconds) {
  152 + throw new RuntimeException("Timestamp bits is exhausted. Refusing UID generate. Now: " + currentSecond);
  153 + }
  154 +
  155 + return currentSecond;
  156 + }
  157 + }
  158 +
  159 + /**
  160 + * Allocate 64 bits for the UID(long)<br>
  161 + * sign (fixed 1bit) -> deltaSecond -> workerId -> service(within the same second)
  162 + */
  163 + private static class BitsAllocator {
  164 + /**
  165 + * Total 64 bits
  166 + */
  167 + public static final int TOTAL_BITS = 1 << 6;
  168 +
  169 + /**
  170 + * Bits for [sign-> second-> workId-> service]
  171 + */
  172 + private final int signBits = 1;
  173 + private final int timestampBits;
  174 + private final int workerIdBits;
  175 + private final int sequenceBits;
  176 +
  177 + /**
  178 + * Max value for workId & service
  179 + */
  180 + private final long maxDeltaSeconds;
  181 + private final long maxWorkerId;
  182 + private final long maxSequence;
  183 +
  184 + /**
  185 + * Shift for timestamp & workerId
  186 + */
  187 + private final int timestampShift;
  188 + private final int workerIdShift;
  189 +
  190 + /**
  191 + * Constructor with timestampBits, workerIdBits, sequenceBits<br>
  192 + * The highest bit used for sign, so <code>63</code> bits for timestampBits, workerIdBits, sequenceBits
  193 + */
  194 + public BitsAllocator(int timestampBits, int workerIdBits, int sequenceBits) {
  195 + // make sure allocated 64 bits
  196 + int allocateTotalBits = signBits + timestampBits + workerIdBits + sequenceBits;
  197 + AssertUtils.isTrue(allocateTotalBits == TOTAL_BITS, "allocate not enough 64 bits");
  198 +
  199 + // initialize bits
  200 + this.timestampBits = timestampBits;
  201 + this.workerIdBits = workerIdBits;
  202 + this.sequenceBits = sequenceBits;
  203 +
  204 + // initialize max value
  205 + this.maxDeltaSeconds = ~(-1L << timestampBits);
  206 + this.maxWorkerId = ~(-1L << workerIdBits);
  207 + this.maxSequence = ~(-1L << sequenceBits);
  208 +
  209 + // initialize shift
  210 + this.timestampShift = workerIdBits + sequenceBits;
  211 + this.workerIdShift = sequenceBits;
  212 + }
  213 +
  214 + /**
  215 + * Allocate bits for UID according to delta seconds & workerId & service<br>
  216 + * <b>Note that: </b>The highest bit will always be 0 for sign
  217 + */
  218 + public long allocate(long deltaSeconds, long workerId, long sequence) {
  219 + return (deltaSeconds << timestampShift) | (workerId << workerIdShift) | sequence;
  220 + }
  221 +
  222 + /**
  223 + * Assign worker id using hash and random number, avoid worker id conflict
  224 + */
  225 + public long assignWorkerId() {
  226 + int h;
  227 + // First hash and index via maxWorkerId, see HashMap
  228 + String randomWorkerId = RandomUtils.randomUUID();
  229 + int hash = (h = randomWorkerId.hashCode()) ^ h >>> 16;
  230 + long workerId = (maxWorkerId - 1) & hash;
  231 +
  232 + // Secondly plus random number
  233 + int duration = (int)(maxWorkerId - workerId);
  234 + if (duration > 10) {
  235 + workerId += RandomUtils.randomInt(1, duration);
  236 + }
  237 + return workerId;
  238 + }
  239 + }
  240 +
  241 + public interface SnowflakeKey {
  242 + // 默认时间戳位数
  243 + int timeBits = 28;
  244 + // 默认机器标识位数
  245 + int workerBits = 22;
  246 + // 默认序号位数
  247 + int seqBits = 13;
  248 +
  249 + default int timeBits() {
  250 + return timeBits;
  251 + }
  252 +
  253 + default int workerBits() {
  254 + return workerBits;
  255 + }
  256 +
  257 + default int seqBits() {
  258 + return seqBits;
  259 + }
  260 +
  261 + default String identifier() {
  262 + return this.toString();
  263 + }
  264 + }
  265 +}
0 266 \ No newline at end of file
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/Converter.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/Converter.java
  1 +package com.diligrp.boss.shared.uid.pattern;
  2 +
  3 +public abstract class Converter<T> {
  4 + private Converter<T> next;
  5 +
  6 + public abstract String convert(T t);
  7 +
  8 + public Converter<T> getNext() {
  9 + return next;
  10 + }
  11 +
  12 + public void setNext(Converter<T> next) {
  13 + this.next = next;
  14 + }
  15 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/DateConverter.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/DateConverter.java
  1 +package com.diligrp.boss.shared.uid.pattern;
  2 +
  3 +import com.diligrp.boss.shared.ErrorCode;
  4 +import com.diligrp.boss.shared.domain.SequenceKey;
  5 +import com.diligrp.boss.shared.exception.PlatformServiceException;
  6 +
  7 +import java.time.format.DateTimeFormatter;
  8 +
  9 +public class DateConverter extends Converter<SequenceKey> {
  10 + private static final String DEFAULT_FORMAT = "yyyyMMdd";
  11 +
  12 + private final String format;
  13 +
  14 + public DateConverter(String format) {
  15 + if (format != null) {
  16 + try {
  17 + DateTimeFormatter.ofPattern(format);
  18 + } catch (Exception ex) {
  19 + throw new PlatformServiceException(ErrorCode.ILLEGAL_ARGUMENT_ERROR, "Invalid date format");
  20 + }
  21 + this.format = format;
  22 + } else {
  23 + this.format = DEFAULT_FORMAT;
  24 + }
  25 + }
  26 +
  27 + @Override
  28 + public String convert(SequenceKey context) {
  29 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
  30 + return context.getWhen().format(formatter);
  31 + }
  32 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/KeywordToken.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/KeywordToken.java
  1 +package com.diligrp.boss.shared.uid.pattern;
  2 +
  3 +import com.diligrp.boss.shared.ErrorCode;
  4 +import com.diligrp.boss.shared.domain.SequenceKey;
  5 +import com.diligrp.boss.shared.exception.PlatformServiceException;
  6 +
  7 +public class KeywordToken extends Token {
  8 + public KeywordToken(String token) {
  9 + super(token);
  10 + }
  11 +
  12 + @Override
  13 + Converter<SequenceKey> getConverter() {
  14 + if ("d".equals(token) || "date".equals(token)) {
  15 + return new DateConverter(option);
  16 + } else if ("n".equals(token)) {
  17 + return new SequenceConverter(option);
  18 + } else if ("r".equals(token)) {
  19 + return new RandomConverter(option);
  20 + } else {
  21 + throw new PlatformServiceException(ErrorCode.ILLEGAL_ARGUMENT_ERROR, "Unrecognized keyword " + token);
  22 + }
  23 + }
  24 +
  25 + public String toString() {
  26 + return String.format("keyword(%s)", token);
  27 + }
  28 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/LiteralConverter.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/LiteralConverter.java
  1 +package com.diligrp.boss.shared.uid.pattern;
  2 +
  3 +import com.diligrp.boss.shared.domain.SequenceKey;
  4 +
  5 +public class LiteralConverter extends Converter<SequenceKey> {
  6 + private final String literal;
  7 +
  8 + public LiteralConverter(String literal) {
  9 + this.literal = literal;
  10 + }
  11 +
  12 + @Override
  13 + public String convert(SequenceKey context) {
  14 + return literal;
  15 + }
  16 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/LiteralToken.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/LiteralToken.java
  1 +package com.diligrp.boss.shared.uid.pattern;
  2 +
  3 +import com.diligrp.boss.shared.domain.SequenceKey;
  4 +
  5 +public class LiteralToken extends Token {
  6 + public LiteralToken(String token) {
  7 + super(token);
  8 + }
  9 +
  10 + @Override
  11 + Converter<SequenceKey> getConverter() {
  12 + return new LiteralConverter(token);
  13 + }
  14 +
  15 + public String toString() {
  16 + return String.format("literal(%s)", token);
  17 + }
  18 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/OptionToken.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/OptionToken.java
  1 +package com.diligrp.boss.shared.uid.pattern;
  2 +
  3 +import com.diligrp.boss.shared.ErrorCode;
  4 +import com.diligrp.boss.shared.domain.SequenceKey;
  5 +import com.diligrp.boss.shared.exception.PlatformServiceException;
  6 +
  7 +public class OptionToken extends Token {
  8 + public OptionToken(String token) {
  9 + super(token);
  10 + }
  11 +
  12 + public String getToken() {
  13 + return this.token;
  14 + }
  15 +
  16 + @Override
  17 + Converter<SequenceKey> getConverter() {
  18 + throw new PlatformServiceException(ErrorCode.OPERATION_NOT_ALLOWED, "Not supported converter");
  19 + }
  20 +
  21 + public String toString() {
  22 + return String.format("option(%s)", token);
  23 + }
  24 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/PatternLayout.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/PatternLayout.java
  1 +package com.diligrp.boss.shared.uid.pattern;
  2 +
  3 +import com.diligrp.boss.shared.domain.SequenceKey;
  4 +
  5 +import java.util.ArrayList;
  6 +import java.util.List;
  7 +
  8 +public class PatternLayout {
  9 +
  10 + private final Converter<SequenceKey> head;
  11 +
  12 + public PatternLayout(String pattern) {
  13 + this.head = compile(pattern);
  14 + }
  15 +
  16 + public String doLayout(SequenceKey context) {
  17 + StringBuilder writer = new StringBuilder();
  18 + Converter<SequenceKey> converter = head;
  19 +
  20 + while (converter != null) {
  21 + writer.append(converter.convert(context));
  22 + converter = converter.getNext();
  23 + }
  24 + return writer.toString();
  25 + }
  26 +
  27 + private Converter<SequenceKey> compile(String pattern) {
  28 + PatternParser parser = new PatternParser(pattern);
  29 + List<Token> tokens = new ArrayList<>();
  30 + Token previous = null;
  31 + for (Token token : parser.parse()) {
  32 + if (token instanceof KeywordToken || token instanceof LiteralToken) {
  33 + previous = token;
  34 + tokens.add(token);
  35 + } else if (token instanceof OptionToken) {
  36 + if (previous != null) {
  37 + previous.setOption(((OptionToken) token).getToken());
  38 + }
  39 + }
  40 + }
  41 +
  42 + Converter<SequenceKey> first = tokens.get(0).getConverter();
  43 + Converter<SequenceKey> current = first;
  44 + for (int i = 1; i < tokens.size(); i++) {
  45 + Token token = tokens.get(i);
  46 + current.setNext(token.getConverter());
  47 + current = current.getNext();
  48 + }
  49 +
  50 + return first;
  51 + }
  52 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/PatternParser.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/PatternParser.java
  1 +package com.diligrp.boss.shared.uid.pattern;
  2 +
  3 +import com.diligrp.boss.shared.ErrorCode;
  4 +import com.diligrp.boss.shared.exception.PlatformServiceException;
  5 +
  6 +import java.util.ArrayList;
  7 +import java.util.List;
  8 +
  9 +public class PatternParser {
  10 + private final String pattern;
  11 + private final int length;
  12 + private TokenizerState state;
  13 + private int index;
  14 +
  15 + public PatternParser(String pattern) {
  16 + this.pattern = pattern;
  17 + this.length = pattern.length();
  18 + this.index = 0;
  19 + this.state = TokenizerState.LITERAL_STATE;
  20 + }
  21 +
  22 + public List<Token> parse() {
  23 + List<Token> tokens = new ArrayList<>();
  24 + StringBuilder buf = new StringBuilder();
  25 +
  26 + while(index < length) {
  27 + char c = pattern.charAt(index);
  28 + index ++;
  29 +
  30 + switch (this.state) {
  31 + case LITERAL_STATE:
  32 + handleLiteralState(c, tokens, buf);
  33 + break;
  34 + case KEYWORD_STATE:
  35 + handleKeywordState(c, tokens, buf);
  36 + break;
  37 + case OPTION_STATE:
  38 + handleOptionState(c, tokens, buf);
  39 + break;
  40 + }
  41 + }
  42 +
  43 + switch (state) {
  44 + case LITERAL_STATE:
  45 + handleLiteralState('%', tokens, buf);
  46 + break;
  47 + case KEYWORD_STATE:
  48 + handleKeywordState('%', tokens, buf);
  49 + break;
  50 + default:
  51 + throw new PlatformServiceException(ErrorCode.ILLEGAL_ARGUMENT_ERROR, "Unexpected end of pattern string");
  52 + }
  53 +
  54 + return tokens;
  55 + }
  56 +
  57 + private void handleLiteralState(char c, List<Token> tokens, StringBuilder buf) {
  58 + switch (c) {
  59 + case '%':
  60 + addLiteralToken(buf, tokens);
  61 + state = TokenizerState.KEYWORD_STATE;
  62 + break;
  63 + default:
  64 + buf.append(c);
  65 + }
  66 + }
  67 +
  68 + private void handleKeywordState(char c, List<Token> tokens, StringBuilder buf) {
  69 + switch (c) {
  70 + case '%':
  71 + addKeywordToken(buf, tokens);
  72 + state = TokenizerState.KEYWORD_STATE;
  73 + break;
  74 + case '{':
  75 + this.addKeywordToken(buf, tokens);
  76 + this.state = TokenizerState.OPTION_STATE;
  77 + break;
  78 + default:
  79 + buf.append(c);
  80 + }
  81 + }
  82 +
  83 + private void handleOptionState(char c, List<Token> tokens, StringBuilder buf) {
  84 + switch (c) {
  85 + case '}':
  86 + addOptionToken(buf, tokens);
  87 + state = TokenizerState.LITERAL_STATE;
  88 + break;
  89 + default:
  90 + buf.append(c);
  91 + }
  92 + }
  93 +
  94 + private void addLiteralToken(StringBuilder buf, List<Token> tokens) {
  95 + if (!buf.isEmpty()) {
  96 + tokens.add(new LiteralToken(buf.toString()));
  97 + buf.setLength(0);
  98 + }
  99 + }
  100 +
  101 + private void addKeywordToken(StringBuilder buf, List<Token> tokens) {
  102 + if (!buf.isEmpty()) {
  103 + tokens.add(new KeywordToken(buf.toString()));
  104 + buf.setLength(0);
  105 + }
  106 + }
  107 +
  108 + private void addOptionToken(StringBuilder buf, List<Token> tokens) {
  109 + if (!buf.isEmpty()) {
  110 + tokens.add(new OptionToken(buf.toString()));
  111 + buf.setLength(0);
  112 + }
  113 + }
  114 +
  115 + private enum TokenizerState {
  116 + LITERAL_STATE,
  117 + KEYWORD_STATE,
  118 + OPTION_STATE
  119 + }
  120 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/RandomConverter.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/RandomConverter.java
  1 +package com.diligrp.boss.shared.uid.pattern;
  2 +
  3 +import com.diligrp.boss.shared.ErrorCode;
  4 +import com.diligrp.boss.shared.domain.SequenceKey;
  5 +import com.diligrp.boss.shared.exception.PlatformServiceException;
  6 +import com.diligrp.boss.shared.util.RandomUtils;
  7 +
  8 +public class RandomConverter extends Converter<SequenceKey> {
  9 + private static final int DEFAULT_LENGTH = 1;
  10 +
  11 + private final int length;
  12 +
  13 + public RandomConverter(String length) {
  14 + if (length != null) {
  15 + try {
  16 + this.length = Integer.parseInt(length);
  17 + } catch (Exception ex) {
  18 + throw new PlatformServiceException(ErrorCode.ILLEGAL_ARGUMENT_ERROR, "Invalid length for %r token");
  19 + }
  20 + } else {
  21 + this.length = DEFAULT_LENGTH;
  22 + }
  23 + }
  24 +
  25 + @Override
  26 + public String convert(SequenceKey context) {
  27 + return RandomUtils.randomNumber(length);
  28 + }
  29 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/SequenceConverter.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/SequenceConverter.java
  1 +package com.diligrp.boss.shared.uid.pattern;
  2 +
  3 +import com.diligrp.boss.shared.ErrorCode;
  4 +import com.diligrp.boss.shared.domain.SequenceKey;
  5 +import com.diligrp.boss.shared.exception.PlatformServiceException;
  6 +
  7 +public class SequenceConverter extends Converter<SequenceKey> {
  8 + private static final int DEFAULT_LENGTH = 4;
  9 +
  10 + private final int minLength;
  11 +
  12 + public SequenceConverter(String minLength) {
  13 + if (minLength != null) {
  14 + try {
  15 + this.minLength = Integer.parseInt(minLength);
  16 + } catch (Exception ex) {
  17 + throw new PlatformServiceException(ErrorCode.ILLEGAL_ARGUMENT_ERROR, "Invalid minLength for %n token");
  18 + }
  19 + } else {
  20 + this.minLength = DEFAULT_LENGTH;
  21 + }
  22 + }
  23 +
  24 + @Override
  25 + public String convert(SequenceKey context) {
  26 + StringBuilder buffer = new StringBuilder();
  27 + buffer.append(context.getSequence());
  28 + int length = buffer.length();
  29 + if (length < minLength) {
  30 + for (int i = length; i < minLength; i++) {
  31 + buffer.insert(0, "0");
  32 + }
  33 + }
  34 + return buffer.toString();
  35 + }
  36 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/Token.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/uid/pattern/Token.java
  1 +package com.diligrp.boss.shared.uid.pattern;
  2 +
  3 +import com.diligrp.boss.shared.domain.SequenceKey;
  4 +
  5 +public abstract class Token {
  6 + protected final String token;
  7 +
  8 + protected String option;
  9 +
  10 + public Token(String token) {
  11 + this.token = token;
  12 + }
  13 +
  14 + public void setOption(String option) {
  15 + this.option = option;
  16 + }
  17 +
  18 + abstract Converter<SequenceKey> getConverter();
  19 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/util/AssertUtils.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/util/AssertUtils.java
  1 +package com.diligrp.boss.shared.util;
  2 +
  3 +import java.util.Collection;
  4 +import java.util.Map;
  5 +
  6 +/**
  7 + * 断言工具类
  8 + */
  9 +public class AssertUtils {
  10 + public static void notNull(Object object) {
  11 + notNull(object, "[Assertion failed] - this argument is required; it must not be null");
  12 + }
  13 +
  14 + public static void notNull(Object object, String message) {
  15 + if (object == null) {
  16 + throw new IllegalArgumentException(message);
  17 + }
  18 + }
  19 +
  20 + public static void notEmpty(String str) {
  21 + notEmpty(str, "[Assertion failed] - this argument is required; it must not be empty");
  22 + }
  23 +
  24 + public static void notEmpty(String str, String message) {
  25 + if (ObjectUtils.isEmpty(str)) {
  26 + throw new IllegalArgumentException(message);
  27 + }
  28 + }
  29 +
  30 + public static void isTrue(boolean expression, String message) {
  31 + if (!expression) {
  32 + throw new IllegalArgumentException(message);
  33 + }
  34 + }
  35 +
  36 + public static void isTrue(boolean expression) {
  37 + isTrue(expression, "[Assertion failed] - this expression must be true");
  38 + }
  39 +
  40 + public static void notEmpty(Collection<?> collection, String message) {
  41 + if (collection == null || collection.isEmpty()) {
  42 + throw new IllegalArgumentException(message);
  43 + }
  44 + }
  45 +
  46 + public static void notEmpty(Collection<?> collection) {
  47 + notEmpty(collection, "[Assertion failed] - this collection must not be empty");
  48 + }
  49 +
  50 + public static void notEmpty(Map<?, ?> map, String message) {
  51 + if (map == null || map.isEmpty()) {
  52 + throw new IllegalArgumentException(message);
  53 + }
  54 + }
  55 +
  56 + public static void notEmpty(Object[] array) {
  57 + notEmpty(array, "[Assertion failed] - this array must not be empty");
  58 + }
  59 +
  60 + public static void notEmpty(Object[] array, String message) {
  61 + if (array == null || array.length == 0) {
  62 + throw new IllegalArgumentException(message);
  63 + }
  64 + }
  65 +
  66 + public static void notEmpty(Map<?, ?> map) {
  67 + notEmpty(map, "[Assertion failed] - this map must not be empty");
  68 + }
  69 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/util/ClassUtils.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/util/ClassUtils.java
  1 +package com.diligrp.boss.shared.util;
  2 +
  3 +/**
  4 + * Classloader工具类
  5 + */
  6 +public class ClassUtils {
  7 + public static ClassLoader getDefaultClassLoader() {
  8 + ClassLoader cl = null;
  9 + try {
  10 + cl = Thread.currentThread().getContextClassLoader();
  11 + } catch (Throwable ex) {
  12 + // Cannot access thread context ClassLoader - falling back...
  13 + }
  14 +
  15 + if (cl == null) {
  16 + // No thread context class loader -> use class loader of this class.
  17 + cl = ClassUtils.class.getClassLoader();
  18 + if (cl == null) {
  19 + // getClassLoader() returning null indicates the bootstrap ClassLoader
  20 + try {
  21 + cl = ClassLoader.getSystemClassLoader();
  22 + } catch (Throwable ex) {
  23 + // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
  24 + }
  25 + }
  26 + }
  27 + return cl;
  28 + }
  29 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/util/CurrencyUtils.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/util/CurrencyUtils.java
  1 +package com.diligrp.boss.shared.util;
  2 +
  3 +import java.math.BigDecimal;
  4 +import java.math.BigInteger;
  5 +import java.math.RoundingMode;
  6 +import java.text.FieldPosition;
  7 +import java.text.NumberFormat;
  8 +import java.util.Locale;
  9 +
  10 +/**
  11 + * 金额格式转化工具类
  12 + */
  13 +public class CurrencyUtils {
  14 + private static final int DEFAULT_SCALE = 2;
  15 + private static final Locale CURRENT_LOCALE = Locale.CHINA;
  16 + private static final BigDecimal YUAN_CENT_UNIT = new BigDecimal(100);
  17 +
  18 + public static String toCurrency(Long cent) {
  19 + if (cent == null) {
  20 + return null;
  21 + }
  22 +
  23 + BigDecimal amount = new BigDecimal(cent);
  24 + BigDecimal yuan = amount.divide(YUAN_CENT_UNIT).setScale(DEFAULT_SCALE, RoundingMode.HALF_UP);
  25 +
  26 + NumberFormat numberFormat = NumberFormat.getCurrencyInstance(CURRENT_LOCALE);
  27 + StringBuffer currency = new StringBuffer();
  28 + numberFormat.format(yuan, currency, new FieldPosition(0));
  29 + if (cent < 0) {
  30 + correctSymbol(currency);
  31 + }
  32 + return currency.toString();
  33 + }
  34 +
  35 + public static String toNoSymbolCurrency(Long cent) {
  36 + if (cent == null) {
  37 + return null;
  38 + }
  39 +
  40 + BigDecimal amount = new BigDecimal(cent);
  41 + BigDecimal yuan = amount.divide(YUAN_CENT_UNIT).setScale(DEFAULT_SCALE, RoundingMode.HALF_UP);
  42 +
  43 + NumberFormat numberFormat = NumberFormat.getCurrencyInstance(CURRENT_LOCALE);
  44 + StringBuffer currency = new StringBuffer();
  45 + numberFormat.format(yuan, currency, new FieldPosition(0));
  46 + if (cent < 0) {
  47 + correctSymbol(currency);
  48 + }
  49 + return currency.substring(1);
  50 + }
  51 +
  52 + public static String cent2TenNoSymbol(Long cent) {
  53 +
  54 + BigDecimal yuan = point2ten(cent);
  55 + if (null == yuan) {
  56 + yuan = new BigDecimal(0L);
  57 + }
  58 + return yuan.toString();
  59 + }
  60 +
  61 + public static Long yuan2Cent(BigDecimal yuan) {
  62 + if (yuan == null) {
  63 + return null;
  64 + }
  65 +
  66 + BigDecimal amount = yuan.setScale(DEFAULT_SCALE, RoundingMode.HALF_UP);
  67 + BigDecimal cent = amount.multiply(YUAN_CENT_UNIT);
  68 + return cent.longValue();
  69 + }
  70 +
  71 + private static BigDecimal point2ten(Long point) {
  72 + if (null == point) {
  73 + point = 0L;
  74 + }
  75 + BigDecimal centBigDecimal = new BigDecimal(point);
  76 + BigInteger divisor = BigInteger.valueOf(1L);
  77 + for (int i = 0; i < DEFAULT_SCALE; i++) {
  78 + divisor = divisor.multiply(BigInteger.valueOf(10L));
  79 + }
  80 + return centBigDecimal.divide(new BigDecimal(divisor)).setScale(DEFAULT_SCALE);
  81 + }
  82 +
  83 + public static String convert2Percent(Long total, Long percentNumber) {
  84 + if (percentNumber == 0L || total == 0L) {
  85 + return "0.00%";
  86 + }
  87 + double percent = percentNumber.doubleValue() / total.doubleValue() * 100;
  88 + BigDecimal bigDecimal = new BigDecimal(percent);
  89 + return bigDecimal.setScale(2, RoundingMode.HALF_UP) + "%";
  90 + }
  91 +
  92 + /**
  93 + * $-100.00 => -$100.00
  94 + */
  95 + private static void correctSymbol(StringBuffer currency) {
  96 + char negativeSymbol = currency.charAt(0);
  97 + char currencySymbol = currency.charAt(1);
  98 + currency.setCharAt(0, currencySymbol);
  99 + currency.setCharAt(1, negativeSymbol);
  100 + }
  101 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/util/DateUtils.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/util/DateUtils.java
  1 +package com.diligrp.boss.shared.util;
  2 +
  3 +import com.diligrp.boss.shared.Constants;
  4 +
  5 +import java.text.SimpleDateFormat;
  6 +import java.time.LocalDate;
  7 +import java.time.LocalDateTime;
  8 +import java.time.ZoneOffset;
  9 +import java.time.format.DateTimeFormatter;
  10 +import java.util.Date;
  11 +
  12 +/**
  13 + * 日期格式转化工具类 - JDK1.8 TIME API
  14 + */
  15 +public class DateUtils {
  16 +
  17 + public static String formatDateTime(LocalDateTime when, String format) {
  18 + if (ObjectUtils.isNull(when)) {
  19 + return null;
  20 + }
  21 +
  22 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
  23 + return when.format(formatter);
  24 + }
  25 +
  26 + public static String formatDateTime(LocalDateTime when) {
  27 + return formatDateTime(when, Constants.DATE_TIME_FORMAT);
  28 + }
  29 +
  30 + public static String formatDate(LocalDate when, String format) {
  31 + if (ObjectUtils.isNull(when)) {
  32 + return null;
  33 + }
  34 +
  35 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
  36 + return when.format(formatter);
  37 + }
  38 +
  39 + public static String formatDate(LocalDate when) {
  40 + return formatDate(when, Constants.DATE_FORMAT);
  41 + }
  42 +
  43 + public static String formatNow(String format) {
  44 + return formatDateTime(LocalDateTime.now(), format);
  45 + }
  46 +
  47 + public static String formatNow() {
  48 + return formatNow(Constants.DATE_TIME_FORMAT);
  49 + }
  50 +
  51 + public static String format(Date date) {
  52 + return format(date, Constants.DATE_TIME_FORMAT);
  53 + }
  54 +
  55 + public static LocalDateTime addDays(long amount) {
  56 + LocalDateTime localDateTime = LocalDateTime.now();
  57 + localDateTime.plusDays(amount);
  58 + return localDateTime;
  59 + }
  60 +
  61 + public static String format(Date date, String format) {
  62 + if (ObjectUtils.isNull(date)) {
  63 + return null;
  64 + }
  65 +
  66 + SimpleDateFormat sdf = new SimpleDateFormat(format);
  67 + return sdf.format(date);
  68 +
  69 + }
  70 +
  71 + public static LocalDateTime parseDateTime(String datetimeStr, String format) {
  72 + if (ObjectUtils.isEmpty(datetimeStr)) {
  73 + return null;
  74 + }
  75 +
  76 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
  77 + return LocalDateTime.parse(datetimeStr, formatter);
  78 + }
  79 +
  80 + public static LocalDateTime parseDateTime(String datetimeStr) {
  81 + return parseDateTime(datetimeStr, Constants.DATE_TIME_FORMAT);
  82 + }
  83 +
  84 + public static LocalDate parseDate(String dateStr, String format) {
  85 + if (ObjectUtils.isEmpty(dateStr)) {
  86 + return null;
  87 + }
  88 +
  89 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
  90 + return LocalDate.parse(dateStr, formatter);
  91 + }
  92 +
  93 + public static LocalDate parseDate(String dateStr) {
  94 + return parseDate(dateStr, Constants.DATE_FORMAT);
  95 + }
  96 +
  97 + public static Date parse(String dateStr) {
  98 + return parse(dateStr, Constants.DATE_TIME_FORMAT);
  99 + }
  100 +
  101 + public static Date parse(String dateStr, String format) {
  102 + if (ObjectUtils.isEmpty(dateStr)) {
  103 + return null;
  104 + }
  105 +
  106 + try {
  107 + SimpleDateFormat sdf = new SimpleDateFormat(format);
  108 + return sdf.parse(dateStr);
  109 + } catch (Exception ex) {
  110 + throw new IllegalArgumentException("Invalid date format", ex);
  111 + }
  112 + }
  113 +
  114 + /**
  115 + * 获取时间戳
  116 + */
  117 + public static long parseMilliSecond(LocalDateTime localDateTime){
  118 + return parseMilliSecond(localDateTime,null);
  119 + }
  120 +
  121 + public static long parseMilliSecond(LocalDateTime localDateTime, String zoneNumStr){
  122 + //默认东八区
  123 + if (ObjectUtils.isEmpty(zoneNumStr)){
  124 + zoneNumStr = "+8";
  125 + }
  126 + return localDateTime.toInstant(ZoneOffset.of(zoneNumStr)).toEpochMilli();
  127 + }
  128 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/util/JsonUtils.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/util/JsonUtils.java
  1 +package com.diligrp.boss.shared.util;
  2 +
  3 +import com.diligrp.boss.shared.Constants;
  4 +import com.fasterxml.jackson.annotation.JsonInclude;
  5 +import com.fasterxml.jackson.core.JsonProcessingException;
  6 +import com.fasterxml.jackson.core.type.TypeReference;
  7 +import com.fasterxml.jackson.databind.DeserializationFeature;
  8 +import com.fasterxml.jackson.databind.ObjectMapper;
  9 +import com.fasterxml.jackson.databind.SerializationFeature;
  10 +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
  11 +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
  12 +import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
  13 +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
  14 +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
  15 +import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
  16 +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
  17 +
  18 +import java.text.SimpleDateFormat;
  19 +import java.time.LocalDate;
  20 +import java.time.LocalDateTime;
  21 +import java.time.LocalTime;
  22 +import java.time.ZoneOffset;
  23 +import java.time.format.DateTimeFormatter;
  24 +import java.util.TimeZone;
  25 +
  26 +public class JsonUtils {
  27 +
  28 + private static ObjectMapper objectMapper = initObjectMapper();
  29 +
  30 + private static ObjectMapper initObjectMapper(){
  31 + Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder = new Jackson2ObjectMapperBuilder();
  32 + initObjectMapperBuilder(jackson2ObjectMapperBuilder);
  33 + ObjectMapper objectMapper = jackson2ObjectMapperBuilder.createXmlMapper(false).build();
  34 + objectMapper.setSerializerFactory(objectMapper.getSerializerFactory());
  35 + return objectMapper;
  36 + }
  37 +
  38 + public static void initObjectMapperBuilder(Jackson2ObjectMapperBuilder builder) {
  39 + //序列化java.util.Date类型
  40 + builder.dateFormat(new SimpleDateFormat(Constants.DATE_TIME_FORMAT));
  41 + builder.timeZone(TimeZone.getTimeZone(ZoneOffset.of("+8")));
  42 + builder.serializationInclusion(JsonInclude.Include.NON_NULL);
  43 + builder.featuresToDisable(
  44 + DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, // Json串的属性无JavaBean字段对应时,避免抛出异常
  45 + DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, // JavaBean中primitive类型的字段无Json属性时,避免抛出异常
  46 + DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, // Json串数字类型属性,赋值JavaBean中Enum字段时,避免抛出异常
  47 + SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
  48 + SerializationFeature.FAIL_ON_EMPTY_BEANS
  49 + );
  50 + builder.featuresToEnable(
  51 + DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT,
  52 + DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY
  53 + );
  54 +
  55 + var dateTimeFormatter = DateTimeFormatter.ofPattern(Constants.DATE_TIME_FORMAT);
  56 + var dateFormatter = DateTimeFormatter.ofPattern(Constants.DATE_FORMAT);
  57 + var timeFormatter = DateTimeFormatter.ofPattern(Constants.TIME_FORMAT);
  58 + // 添加自定义序列化
  59 + builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));
  60 + builder.serializerByType(LocalDate.class, new LocalDateSerializer(dateFormatter));
  61 + builder.serializerByType(LocalTime.class, new LocalTimeSerializer(timeFormatter));
  62 + // 添加自定义反序列化
  63 + builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));
  64 + builder.deserializerByType(LocalDate.class, new LocalDateDeserializer(dateFormatter));
  65 + builder.deserializerByType(LocalTime.class, new LocalTimeDeserializer(timeFormatter));
  66 + }
  67 +
  68 + public static <T> T fromJsonString(String json, Class<T> type) {
  69 + try {
  70 + return objectMapper.readValue(json, type);
  71 + } catch (JsonProcessingException ex) {
  72 + throw new IllegalArgumentException("Deserialize json exception", ex);
  73 + }
  74 + }
  75 +
  76 + public static <T> T fromJsonString(String json, TypeReference<T> jsonTypeReference){
  77 + try {
  78 + return objectMapper.readValue(json, jsonTypeReference);
  79 + } catch (JsonProcessingException ex) {
  80 + throw new IllegalArgumentException("Deserialize json array exception", ex);
  81 + }
  82 + }
  83 +
  84 + public static <T> String toJsonString(T object) {
  85 + try {
  86 + return objectMapper.writeValueAsString(object);
  87 + } catch (JsonProcessingException ex) {
  88 + throw new IllegalArgumentException("Serialize json exception", ex);
  89 + }
  90 + }
  91 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/util/MathUtils.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/util/MathUtils.java
  1 +package com.diligrp.boss.shared.util;
  2 +
  3 +import java.math.BigDecimal;
  4 +import java.math.RoundingMode;
  5 +
  6 +/**
  7 + * 精确的浮点数运算
  8 + */
  9 +public class MathUtils {
  10 +
  11 + /**
  12 + * 默认除法运算精度
  13 + */
  14 + private static final int DEF_DIV_SCALE = 10;
  15 +
  16 + /**
  17 + * 提供精确的加法运算
  18 + */
  19 + public static double add(double v1, double v2) {
  20 + BigDecimal b1 = new BigDecimal(Double.toString(v1));
  21 + BigDecimal b2 = new BigDecimal(Double.toString(v2));
  22 + return b1.add(b2).doubleValue();
  23 + }
  24 +
  25 + /**
  26 + * 提供精确的减法运算。
  27 + */
  28 + public static double sub(double v1, double v2) {
  29 + BigDecimal b1 = new BigDecimal(Double.toString(v1));
  30 + BigDecimal b2 = new BigDecimal(Double.toString(v2));
  31 + return b1.subtract(b2).doubleValue();
  32 + }
  33 +
  34 + /**
  35 + * 提供精确的乘法运算。
  36 + */
  37 + public static double mul(double v1, double v2) {
  38 + BigDecimal b1 = new BigDecimal(Double.toString(v1));
  39 + BigDecimal b2 = new BigDecimal(Double.toString(v2));
  40 + return b1.multiply(b2).doubleValue();
  41 + }
  42 +
  43 + /**
  44 + * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到小数点以后10位,
  45 + * 以后的数字四舍五入。
  46 + */
  47 + public static double div(double v1, double v2) {
  48 + return div(v1, v2, DEF_DIV_SCALE);
  49 + }
  50 +
  51 + /**
  52 + * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指定精度,
  53 + * 以后的数字四舍五入。
  54 + */
  55 + public static double div(double v1, double v2, int scale) {
  56 + if (scale < 0) {
  57 + throw new IllegalArgumentException(
  58 + "The scale must be a positive integer or zero");
  59 + }
  60 + BigDecimal b1 = new BigDecimal(Double.toString(v1));
  61 + BigDecimal b2 = new BigDecimal(Double.toString(v2));
  62 + if (b1.compareTo(BigDecimal.ZERO) == 0) {
  63 + return BigDecimal.ZERO.doubleValue();
  64 + }
  65 + return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
  66 + }
  67 +
  68 + /**
  69 + * 提供精确的小数位四舍五入处理。
  70 + */
  71 + public static double round(double v, int scale) {
  72 + if (scale < 0) {
  73 + throw new IllegalArgumentException(
  74 + "The scale must be a positive integer or zero");
  75 + }
  76 + BigDecimal b = new BigDecimal(Double.toString(v));
  77 + BigDecimal one = new BigDecimal("1");
  78 + return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
  79 + }
  80 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/util/NumberUtils.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/util/NumberUtils.java
  1 +package com.diligrp.boss.shared.util;
  2 +
  3 +import org.slf4j.Logger;
  4 +import org.slf4j.LoggerFactory;
  5 +
  6 +/**
  7 + * 字符串-数值转化工具类
  8 + */
  9 +public class NumberUtils {
  10 + private static Logger LOG = LoggerFactory.getLogger(NumberUtils.class);
  11 +
  12 + public static int str2Int(String number, int defaultValue) {
  13 + if (ObjectUtils.isEmpty(number)) {
  14 + return defaultValue;
  15 + }
  16 +
  17 + try {
  18 + return Integer.parseInt(number);
  19 + } catch (NumberFormatException nfe) {
  20 + // Never ignore any exception
  21 + LOG.error("Invalid number format", nfe);
  22 + return defaultValue;
  23 + }
  24 + }
  25 +
  26 + public static long str2Long(String number, long defaultValue) {
  27 + if (ObjectUtils.isEmpty(number)) {
  28 + return defaultValue;
  29 + }
  30 +
  31 + try {
  32 + return Long.parseLong(number);
  33 + } catch (NumberFormatException nfe) {
  34 + // Never ignore any exception
  35 + LOG.error("Invalid number format", nfe);
  36 + return defaultValue;
  37 + }
  38 + }
  39 +
  40 + public static boolean isNumeric(String str) {
  41 + if (ObjectUtils.isEmpty(str)) {
  42 + return false;
  43 + } else {
  44 + int sz = str.length();
  45 +
  46 + for(int i = 0; i < sz; ++i) {
  47 + if (!Character.isDigit(str.charAt(i))) {
  48 + return false;
  49 + }
  50 + }
  51 +
  52 + return true;
  53 + }
  54 + }
  55 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/util/ObjectUtils.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/util/ObjectUtils.java
  1 +package com.diligrp.boss.shared.util;
  2 +
  3 +import java.util.ArrayList;
  4 +import java.util.List;
  5 +
  6 +/**
  7 + * 通用工具类
  8 + */
  9 +public class ObjectUtils {
  10 + public static boolean equals(String str1, String str2) {
  11 + if (str1 == str2) {
  12 + return true;
  13 + } else if (str1 != null && str2 != null) {
  14 + if (str1.length() != str2.length()) {
  15 + return false;
  16 + } else if (str1 instanceof String && str2 instanceof String) {
  17 + return str1.equals(str2);
  18 + } else {
  19 + int length = str1.length();
  20 +
  21 + for(int i = 0; i < length; ++i) {
  22 + if (str1.charAt(i) != str2.charAt(i)) {
  23 + return false;
  24 + }
  25 + }
  26 +
  27 + return true;
  28 + }
  29 + } else {
  30 + return false;
  31 + }
  32 + }
  33 +
  34 + public static boolean equals(Object object1, Object object2) {
  35 + if (object1 == object2) {
  36 + return true;
  37 + }
  38 + return object1 != null && object2 != null ? object1.equals(object2) : false;
  39 + }
  40 +
  41 + public static String[] split(String str, char separator) {
  42 + if (str == null) {
  43 + return null;
  44 + } else {
  45 + int len = str.length();
  46 + if (len == 0) {
  47 + return new String[0];
  48 + } else {
  49 + int i = 0;
  50 + int start = 0;
  51 + boolean match = false;
  52 + boolean lastMatch = false;
  53 + boolean preserveAllTokens = false;
  54 + List<String> list = new ArrayList<String>();
  55 +
  56 + while(true) {
  57 + while(i < len) {
  58 + if (str.charAt(i) == separator) {
  59 + if (match || preserveAllTokens) {
  60 + list.add(str.substring(start, i));
  61 + match = false;
  62 + lastMatch = true;
  63 + }
  64 +
  65 + ++i;
  66 + start = i;
  67 + } else {
  68 + lastMatch = false;
  69 + match = true;
  70 + ++i;
  71 + }
  72 + }
  73 +
  74 + if (match || preserveAllTokens && lastMatch) {
  75 + list.add(str.substring(start, i));
  76 + }
  77 +
  78 + return (String[])list.toArray(new String[list.size()]);
  79 + }
  80 + }
  81 + }
  82 + }
  83 +
  84 + public static boolean isEmpty(String str) {
  85 + return str == null || str.length() == 0;
  86 + }
  87 +
  88 + public static boolean isNotEmpty(String str) {
  89 + return !isEmpty(str);
  90 + }
  91 +
  92 + public static <T> boolean isEmpty(List<T> array) {
  93 + return array == null || array.isEmpty();
  94 + }
  95 +
  96 + public static <T> boolean isNotEmpty(List<T> array) {
  97 + return array != null && !array.isEmpty();
  98 + }
  99 +
  100 + public static String trimToEmpty(String str) {
  101 + return str == null ? "" : str.trim();
  102 + }
  103 +
  104 + public static boolean isNull(Object obj) {
  105 + return null == obj;
  106 + }
  107 +}
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/util/RandomUtils.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/util/RandomUtils.java
  1 +package com.diligrp.boss.shared.util;
  2 +
  3 +import java.util.Random;
  4 +import java.util.UUID;
  5 +import java.util.concurrent.ThreadLocalRandom;
  6 +
  7 +/**
  8 + * 随机数工具类
  9 + */
  10 +public final class RandomUtils {
  11 +
  12 + /**
  13 + * 生成随机字符串, a-z A-Z 0-9
  14 + */
  15 + public static String randomString(int length){
  16 + String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  17 + Random random = new Random();
  18 + StringBuilder sb = new StringBuilder();
  19 + for(int i = 0; i < length; i++){
  20 + int number = random.nextInt(62);
  21 + sb.append(str.charAt(number));
  22 + }
  23 + return sb.toString();
  24 + }
  25 +
  26 + /**
  27 + * 生成固定长度的随机数字字符串
  28 + */
  29 + public static String randomNumber(int length) {
  30 + AssertUtils.isTrue(length > 0, "invalid length");
  31 + StringBuilder builder = new StringBuilder();
  32 + for (int i = 0; i < length; i ++) {
  33 + int next = ThreadLocalRandom.current().nextInt(10);
  34 + builder.append((char) (48 + next));
  35 + }
  36 +
  37 + return builder.toString();
  38 + }
  39 +
  40 + /**
  41 + * 生成8位随机码(数字0~9+大写字母A~Z)
  42 + */
  43 + public static String randomCode() {
  44 + StringBuilder sb = new StringBuilder();
  45 + for (int i = 0; i < 8; i++) {
  46 + int a = Math.abs((new Random()).nextInt(32));
  47 + if (a <= 9) {
  48 + sb.append((char) (a + 48));
  49 + } else if (a < 33) {
  50 + if ((a + 55) == 79 || (a + 55) == 73) {
  51 + sb.append((char) (a + 63));
  52 + } else {
  53 + sb.append((char) (a + 55));
  54 + }
  55 + }
  56 + }
  57 + return sb.toString();
  58 + }
  59 +
  60 +
  61 + /**
  62 + * 功能描述:获取随机字符串
  63 + */
  64 + public static String randomString(int length, String original) {
  65 + if (length <= 0 || ObjectUtils.isEmpty(original)) {
  66 + return null;
  67 + }
  68 + StringBuilder sb = new StringBuilder();
  69 + int len = original.length();
  70 + for (int i = 0; i < length; i++) {
  71 + int round = (int) Math.round(Math.random() * (len - 1));
  72 + sb.append(original.charAt(round));
  73 + }
  74 + return sb.toString();
  75 + }
  76 +
  77 + /**
  78 + * UUID随机数,移除 "-"
  79 + */
  80 + public static String randomUUID() {
  81 + return randomUUID(true);
  82 + }
  83 +
  84 + public static String randomUUID(boolean upperCase) {
  85 + UUID uuid = UUID.randomUUID();
  86 + long mostSigBits = uuid.getMostSignificantBits();
  87 + long leastSigBits = uuid.getLeastSignificantBits();
  88 + return (digits(mostSigBits >> 32, 8, upperCase) +
  89 + digits(mostSigBits >> 16, 4, upperCase) +
  90 + digits(mostSigBits, 4, upperCase) +
  91 + digits(leastSigBits >> 48, 4, upperCase) +
  92 + digits(leastSigBits, 12, upperCase));
  93 + }
  94 +
  95 + public static String randomCaptcha() {
  96 + return String.valueOf((int) ((Math.random() * ((1 << 3) + 1) + 1) * 1000));
  97 + }
  98 +
  99 + /**
  100 + * 生成随机区间数字
  101 + */
  102 + public static int randomInt(int min, int max) {
  103 + return min + (int)(Math.random() * (max + 1 - min));
  104 + }
  105 +
  106 + private static String digits(long val, int digits, boolean upperCase) {
  107 + long hi = 1L << (digits * 4);
  108 + String hexDigits = Long.toHexString(hi | (val & (hi - 1))).substring(1);
  109 + return upperCase ? hexDigits.toUpperCase() : hexDigits;
  110 + }
  111 +}
0 112 \ No newline at end of file
... ...
boss-shared/src/main/java/com/diligrp/boss/shared/util/ReflectUtils.java 0 → 100644
  1 +++ a/boss-shared/src/main/java/com/diligrp/boss/shared/util/ReflectUtils.java
  1 +package com.diligrp.boss.shared.util;
  2 +
  3 +import java.lang.reflect.Field;
  4 +import java.lang.reflect.InvocationTargetException;
  5 +import java.lang.reflect.Method;
  6 +import java.util.Arrays;
  7 +
  8 +/**
  9 + * 利用反射进行操作的一个工具类
  10 + */
  11 +public class ReflectUtils {
  12 + /**
  13 + * 利用反射获取指定对象的指定属性
  14 + */
  15 + public static Object getFieldValue(Object target, String fieldName) {
  16 + Object result = null;
  17 + Field field = ReflectUtils.getField(target, fieldName);
  18 + if (field != null) {
  19 + field.setAccessible(true);
  20 + try {
  21 + result = field.get(target);
  22 + } catch (IllegalArgumentException | IllegalAccessException ex) {
  23 + throw new RuntimeException("Illegal access or argument exception", ex);
  24 + }
  25 + }
  26 + return result;
  27 + }
  28 +
  29 + /**
  30 + * 利用反射获取指定对象里面的指定属性
  31 + */
  32 + private static Field getField(Object obj, String fieldName) {
  33 + Field field = null;
  34 + for (Class<?> clazz = obj.getClass();
  35 + clazz != Object.class; clazz = clazz.getSuperclass()) {
  36 + try {
  37 + field = clazz.getDeclaredField(fieldName);
  38 + break;
  39 + } catch (NoSuchFieldException e) {
  40 + // 当前类没有此方法则向父类查找,都没有就返回NULL
  41 + }
  42 + }
  43 + return field;
  44 + }
  45 +
  46 + /**
  47 + * 利用反射设置指定对象的指定属性为指定的值
  48 + */
  49 + public static void setFieldValue(Object obj, String fieldName, String fieldValue) {
  50 + Field field = ReflectUtils.getField(obj, fieldName);
  51 + if (field != null) {
  52 + try {
  53 + field.setAccessible(true);
  54 + field.set(obj, fieldValue);
  55 + } catch (IllegalArgumentException | IllegalAccessException ex) {
  56 + throw new RuntimeException("Illegal access or argument exception", ex);
  57 + }
  58 + }
  59 + }
  60 +
  61 + /**
  62 + * 调用对象方法, 包含private/protected修饰的方法.
  63 + */
  64 + public static Object invokeMethod(final Object target, final String methodName, final Class<?>[] parameterTypes,
  65 + final Object[] parameters) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
  66 +
  67 + Method method = getDeclaredMethod(target, methodName, parameterTypes);
  68 + if (method == null) {
  69 + throw new IllegalArgumentException("Could not find method ["
  70 + + methodName + "] parameterType " + Arrays.toString(parameterTypes)
  71 + + " on target [" + target + "]");
  72 + }
  73 + method.setAccessible(true);
  74 + return method.invoke(target, parameters);
  75 + }
  76 +
  77 + /**
  78 + * 在target对象上查找方法,如果当前类定义未定义则向父类查找,都未查找到则返回Null.
  79 + */
  80 + protected static Method getDeclaredMethod(Object target, String methodName, Class<?>[] parameterTypes) {
  81 + AssertUtils.notNull(target, "target must be not null");
  82 + for (Class<?> superClass = target.getClass(); superClass != Object.class;
  83 + superClass = superClass.getSuperclass()) {
  84 + try {
  85 + return superClass.getDeclaredMethod(methodName, parameterTypes);
  86 + } catch (NoSuchMethodException sme) {
  87 + // 当前类未定义Method则向父类查找
  88 + }
  89 + }
  90 + return null;
  91 + }
  92 +}
0 93 \ No newline at end of file
... ...
boss-shared/src/main/resources/com/diligrp/boss/dao/mapper/SequenceKeyDao.xml 0 → 100644
  1 +++ a/boss-shared/src/main/resources/com/diligrp/boss/dao/mapper/SequenceKeyDao.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.boss.shared.dao.SequenceKeyDao">
  6 + <resultMap id="PersistentKeyMap" type="com.diligrp.boss.shared.domain.PersistentSequenceKey">
  7 + <id column="id" property="id"/>
  8 + <result column="key" property="key"/>
  9 + <result column="name" property="name"/>
  10 + <result column="value" property="value"/>
  11 + <result column="step" property="step"/>
  12 + <result column="pattern" property="pattern"/>
  13 + <result column="expired_on" property="expiredOn"/>
  14 + <result column="today" property="today"/>
  15 + <result column="version" property="version"/>
  16 + </resultMap>
  17 +
  18 + <insert id="insertSequenceKey" parameterType="com.diligrp.boss.shared.domain.PersistentSequenceKey">
  19 + INSERT INTO uid_sequence_key(`key`, name, value, step, pattern, expired_on, version)
  20 + VALUES (#{key}, #{name}, #{value}, #{step}, #{pattern}, #{expiredOn}, #{version})
  21 + </insert>
  22 +
  23 + <select id="findSequenceKey" parameterType="string" resultMap="PersistentKeyMap">
  24 + SELECT
  25 + id, `key`, name, value, step, pattern, expired_on, curdate() AS today, version
  26 + FROM
  27 + uid_sequence_key
  28 + WHERE
  29 + `key` = #{key}
  30 + </select>
  31 +
  32 + <select id="findSequenceKeyById" parameterType="long" resultMap="PersistentKeyMap">
  33 + SELECT
  34 + id, `key`, name, value, step, pattern, expired_on, curdate() AS today, version
  35 + FROM
  36 + uid_sequence_key
  37 + WHERE
  38 + id = #{id}
  39 + </select>
  40 +
  41 + <select id="lockSequenceKey" parameterType="long" resultMap="PersistentKeyMap">
  42 + SELECT
  43 + id, `key`, name, value, step, pattern, expired_on, curdate() AS today, version
  44 + FROM
  45 + uid_sequence_key
  46 + WHERE
  47 + id = #{id}
  48 + FOR UPDATE
  49 + </select>
  50 +
  51 + <update id="unlockSequenceKey" parameterType="com.diligrp.boss.shared.domain.PersistentSequenceKey">
  52 + UPDATE
  53 + uid_sequence_key
  54 + SET
  55 + value = #{value}, expired_on = #{expiredOn}, version = version + 1
  56 + WHERE
  57 + id = #{id}
  58 + </update>
  59 +</mapper>
0 60 \ No newline at end of file
... ...
boss-support/build.gradle 0 → 100644
  1 +++ a/boss-support/build.gradle
  1 +group = 'com.diligrp'
  2 +archivesBaseName = 'boss-auth'
  3 +
  4 +dependencies {
  5 + api project(':boss-admin')
  6 +}
0 7 \ No newline at end of file
... ...
boss-support/src/main/java/com/diligrp/boss/support/SupportConfiguration.java 0 → 100644
  1 +++ a/boss-support/src/main/java/com/diligrp/boss/support/SupportConfiguration.java
  1 +package com.diligrp.boss.support;
  2 +
  3 +import com.diligrp.boss.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.boss.support")
  10 +@MapperScan(basePackages = {"com.diligrp.boss.support.dao"}, markerInterface = MybatisMapperSupport.class)
  11 +public class SupportConfiguration {
  12 +}
... ...
build.gradle 0 → 100644
  1 +++ a/build.gradle
  1 +plugins {
  2 + id 'java'
  3 + id 'org.springframework.boot' version '3.1.2'
  4 + id 'io.spring.dependency-management' version '1.1.2'
  5 +}
  6 +
  7 +jar.enabled = false
  8 +bootJar.enabled = false
  9 +
  10 +allprojects {
  11 + repositories {
  12 + mavenLocal()
  13 + maven {
  14 + url 'https://maven.aliyun.com/nexus/content/groups/public/'
  15 + }
  16 + maven {
  17 + allowInsecureProtocol = true
  18 + // credentials
  19 + credentials {
  20 + username 'admin'
  21 + password 'for211314'
  22 + }
  23 + url = 'http://mvn2.diligrp.com/artifactory/libs-snapshot/'
  24 + }
  25 + mavenCentral()
  26 + }
  27 +}
  28 +
  29 +subprojects {
  30 + apply plugin: 'java'
  31 + apply plugin: 'java-library'
  32 + apply plugin: 'org.springframework.boot'
  33 + apply plugin: 'io.spring.dependency-management'
  34 +
  35 + version = '1.0.0'
  36 +
  37 + java {
  38 + sourceCompatibility = JavaVersion.VERSION_17
  39 + targetCompatibility = JavaVersion.VERSION_17
  40 + }
  41 +
  42 + ext {
  43 + set('springCloudVersion', "2022.0.4")
  44 + }
  45 +
  46 + jar.archiveClassifier = ''
  47 + bootJar.enabled = false
  48 +
  49 + dependencies {
  50 + implementation 'org.springframework.cloud:spring-cloud-starter'
  51 + implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'
  52 + implementation 'org.springframework.boot:spring-boot-starter-web'
  53 + implementation fileTree(dir: "$rootProject.projectDir/libs", includes: ['*jar'])
  54 + }
  55 +
  56 + dependencyManagement {
  57 + imports {
  58 + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
  59 + }
  60 + }
  61 +}
0 62 \ No newline at end of file
... ...
gradle/wrapper/gradle-wrapper.jar 0 → 100644
No preview for this file type
gradle/wrapper/gradle-wrapper.properties 0 → 100644
  1 +++ a/gradle/wrapper/gradle-wrapper.properties
  1 +distributionBase=GRADLE_USER_HOME
  2 +distributionPath=wrapper/dists
  3 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
  4 +networkTimeout=10000
  5 +validateDistributionUrl=true
  6 +zipStoreBase=GRADLE_USER_HOME
  7 +zipStorePath=wrapper/dists
... ...
gradlew 0 → 100644
  1 +++ a/gradlew
  1 +#!/bin/sh
  2 +
  3 +#
  4 +# Copyright © 2015-2021 the original authors.
  5 +#
  6 +# Licensed under the Apache License, Version 2.0 (the "License");
  7 +# you may not use this file except in compliance with the License.
  8 +# You may obtain a copy of the License at
  9 +#
  10 +# https://www.apache.org/licenses/LICENSE-2.0
  11 +#
  12 +# Unless required by applicable law or agreed to in writing, software
  13 +# distributed under the License is distributed on an "AS IS" BASIS,
  14 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15 +# See the License for the specific language governing permissions and
  16 +# limitations under the License.
  17 +#
  18 +
  19 +##############################################################################
  20 +#
  21 +# Gradle start up script for POSIX generated by Gradle.
  22 +#
  23 +# Important for running:
  24 +#
  25 +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
  26 +# noncompliant, but you have some other compliant shell such as ksh or
  27 +# bash, then to run this script, type that shell name before the whole
  28 +# command line, like:
  29 +#
  30 +# ksh Gradle
  31 +#
  32 +# Busybox and similar reduced shells will NOT work, because this script
  33 +# requires all of these POSIX shell features:
  34 +# * functions;
  35 +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
  36 +# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
  37 +# * compound commands having a testable exit status, especially «case»;
  38 +# * various built-in commands including «command», «set», and «ulimit».
  39 +#
  40 +# Important for patching:
  41 +#
  42 +# (2) This script targets any POSIX shell, so it avoids extensions provided
  43 +# by Bash, Ksh, etc; in particular arrays are avoided.
  44 +#
  45 +# The "traditional" practice of packing multiple parameters into a
  46 +# space-separated string is a well documented source of bugs and security
  47 +# problems, so this is (mostly) avoided, by progressively accumulating
  48 +# options in "$@", and eventually passing that to Java.
  49 +#
  50 +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
  51 +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
  52 +# see the in-line comments for details.
  53 +#
  54 +# There are tweaks for specific operating systems such as AIX, CygWin,
  55 +# Darwin, MinGW, and NonStop.
  56 +#
  57 +# (3) This script is generated from the Groovy template
  58 +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
  59 +# within the Gradle project.
  60 +#
  61 +# You can find Gradle at https://github.com/gradle/gradle/.
  62 +#
  63 +##############################################################################
  64 +
  65 +# Attempt to set APP_HOME
  66 +
  67 +# Resolve links: $0 may be a link
  68 +app_path=$0
  69 +
  70 +# Need this for daisy-chained symlinks.
  71 +while
  72 + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
  73 + [ -h "$app_path" ]
  74 +do
  75 + ls=$( ls -ld "$app_path" )
  76 + link=${ls#*' -> '}
  77 + case $link in #(
  78 + /*) app_path=$link ;; #(
  79 + *) app_path=$APP_HOME$link ;;
  80 + esac
  81 +done
  82 +
  83 +# This is normally unused
  84 +# shellcheck disable=SC2034
  85 +APP_BASE_NAME=${0##*/}
  86 +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
  87 +
  88 +# Use the maximum available, or set MAX_FD != -1 to use that value.
  89 +MAX_FD=maximum
  90 +
  91 +warn () {
  92 + echo "$*"
  93 +} >&2
  94 +
  95 +die () {
  96 + echo
  97 + echo "$*"
  98 + echo
  99 + exit 1
  100 +} >&2
  101 +
  102 +# OS specific support (must be 'true' or 'false').
  103 +cygwin=false
  104 +msys=false
  105 +darwin=false
  106 +nonstop=false
  107 +case "$( uname )" in #(
  108 + CYGWIN* ) cygwin=true ;; #(
  109 + Darwin* ) darwin=true ;; #(
  110 + MSYS* | MINGW* ) msys=true ;; #(
  111 + NONSTOP* ) nonstop=true ;;
  112 +esac
  113 +
  114 +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
  115 +
  116 +
  117 +# Determine the Java command to use to start the JVM.
  118 +if [ -n "$JAVA_HOME" ] ; then
  119 + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
  120 + # IBM's JDK on AIX uses strange locations for the executables
  121 + JAVACMD=$JAVA_HOME/jre/sh/java
  122 + else
  123 + JAVACMD=$JAVA_HOME/bin/java
  124 + fi
  125 + if [ ! -x "$JAVACMD" ] ; then
  126 + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
  127 +
  128 +Please set the JAVA_HOME variable in your environment to match the
  129 +location of your Java installation."
  130 + fi
  131 +else
  132 + JAVACMD=java
  133 + if ! command -v java >/dev/null 2>&1
  134 + then
  135 + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
  136 +
  137 +Please set the JAVA_HOME variable in your environment to match the
  138 +location of your Java installation."
  139 + fi
  140 +fi
  141 +
  142 +# Increase the maximum file descriptors if we can.
  143 +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
  144 + case $MAX_FD in #(
  145 + max*)
  146 + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
  147 + # shellcheck disable=SC3045
  148 + MAX_FD=$( ulimit -H -n ) ||
  149 + warn "Could not query maximum file descriptor limit"
  150 + esac
  151 + case $MAX_FD in #(
  152 + '' | soft) :;; #(
  153 + *)
  154 + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
  155 + # shellcheck disable=SC3045
  156 + ulimit -n "$MAX_FD" ||
  157 + warn "Could not set maximum file descriptor limit to $MAX_FD"
  158 + esac
  159 +fi
  160 +
  161 +# Collect all arguments for the java command, stacking in reverse order:
  162 +# * args from the command line
  163 +# * the main class name
  164 +# * -classpath
  165 +# * -D...appname settings
  166 +# * --module-path (only if needed)
  167 +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
  168 +
  169 +# For Cygwin or MSYS, switch paths to Windows format before running java
  170 +if "$cygwin" || "$msys" ; then
  171 + APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
  172 + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
  173 +
  174 + JAVACMD=$( cygpath --unix "$JAVACMD" )
  175 +
  176 + # Now convert the arguments - kludge to limit ourselves to /bin/sh
  177 + for arg do
  178 + if
  179 + case $arg in #(
  180 + -*) false ;; # don't mess with options #(
  181 + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
  182 + [ -e "$t" ] ;; #(
  183 + *) false ;;
  184 + esac
  185 + then
  186 + arg=$( cygpath --path --ignore --mixed "$arg" )
  187 + fi
  188 + # Roll the args list around exactly as many times as the number of
  189 + # args, so each arg winds up back in the position where it started, but
  190 + # possibly modified.
  191 + #
  192 + # NB: a `for` loop captures its iteration list before it begins, so
  193 + # changing the positional parameters here affects neither the number of
  194 + # iterations, nor the values presented in `arg`.
  195 + shift # remove old arg
  196 + set -- "$@" "$arg" # push replacement arg
  197 + done
  198 +fi
  199 +
  200 +
  201 +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
  202 +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
  203 +
  204 +# Collect all arguments for the java command;
  205 +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
  206 +# shell script including quotes and variable substitutions, so put them in
  207 +# double quotes to make sure that they get re-expanded; and
  208 +# * put everything else in single quotes, so that it's not re-expanded.
  209 +
  210 +set -- \
  211 + "-Dorg.gradle.appname=$APP_BASE_NAME" \
  212 + -classpath "$CLASSPATH" \
  213 + org.gradle.wrapper.GradleWrapperMain \
  214 + "$@"
  215 +
  216 +# Stop when "xargs" is not available.
  217 +if ! command -v xargs >/dev/null 2>&1
  218 +then
  219 + die "xargs is not available"
  220 +fi
  221 +
  222 +# Use "xargs" to parse quoted args.
  223 +#
  224 +# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
  225 +#
  226 +# In Bash we could simply go:
  227 +#
  228 +# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
  229 +# set -- "${ARGS[@]}" "$@"
  230 +#
  231 +# but POSIX shell has neither arrays nor command substitution, so instead we
  232 +# post-process each arg (as a line of input to sed) to backslash-escape any
  233 +# character that might be a shell metacharacter, then use eval to reverse
  234 +# that process (while maintaining the separation between arguments), and wrap
  235 +# the whole thing up as a single "set" statement.
  236 +#
  237 +# This will of course break if any of these variables contains a newline or
  238 +# an unmatched quote.
  239 +#
  240 +
  241 +eval "set -- $(
  242 + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
  243 + xargs -n1 |
  244 + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
  245 + tr '\n' ' '
  246 + )" '"$@"'
  247 +
  248 +exec "$JAVACMD" "$@"
... ...
gradlew.bat 0 → 100644
  1 +++ a/gradlew.bat
  1 +@rem
  2 +@rem Copyright 2015 the original author or authors.
  3 +@rem
  4 +@rem Licensed under the Apache License, Version 2.0 (the "License");
  5 +@rem you may not use this file except in compliance with the License.
  6 +@rem You may obtain a copy of the License at
  7 +@rem
  8 +@rem https://www.apache.org/licenses/LICENSE-2.0
  9 +@rem
  10 +@rem Unless required by applicable law or agreed to in writing, software
  11 +@rem distributed under the License is distributed on an "AS IS" BASIS,
  12 +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +@rem See the License for the specific language governing permissions and
  14 +@rem limitations under the License.
  15 +@rem
  16 +
  17 +@if "%DEBUG%"=="" @echo off
  18 +@rem ##########################################################################
  19 +@rem
  20 +@rem Gradle startup script for Windows
  21 +@rem
  22 +@rem ##########################################################################
  23 +
  24 +@rem Set local scope for the variables with windows NT shell
  25 +if "%OS%"=="Windows_NT" setlocal
  26 +
  27 +set DIRNAME=%~dp0
  28 +if "%DIRNAME%"=="" set DIRNAME=.
  29 +@rem This is normally unused
  30 +set APP_BASE_NAME=%~n0
  31 +set APP_HOME=%DIRNAME%
  32 +
  33 +@rem Resolve any "." and ".." in APP_HOME to make it shorter.
  34 +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
  35 +
  36 +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
  37 +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
  38 +
  39 +@rem Find java.exe
  40 +if defined JAVA_HOME goto findJavaFromJavaHome
  41 +
  42 +set JAVA_EXE=java.exe
  43 +%JAVA_EXE% -version >NUL 2>&1
  44 +if %ERRORLEVEL% equ 0 goto execute
  45 +
  46 +echo.
  47 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
  48 +echo.
  49 +echo Please set the JAVA_HOME variable in your environment to match the
  50 +echo location of your Java installation.
  51 +
  52 +goto fail
  53 +
  54 +:findJavaFromJavaHome
  55 +set JAVA_HOME=%JAVA_HOME:"=%
  56 +set JAVA_EXE=%JAVA_HOME%/bin/java.exe
  57 +
  58 +if exist "%JAVA_EXE%" goto execute
  59 +
  60 +echo.
  61 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
  62 +echo.
  63 +echo Please set the JAVA_HOME variable in your environment to match the
  64 +echo location of your Java installation.
  65 +
  66 +goto fail
  67 +
  68 +:execute
  69 +@rem Setup the command line
  70 +
  71 +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
  72 +
  73 +
  74 +@rem Execute Gradle
  75 +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
  76 +
  77 +:end
  78 +@rem End local scope for the variables with windows NT shell
  79 +if %ERRORLEVEL% equ 0 goto mainEnd
  80 +
  81 +:fail
  82 +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
  83 +rem the _cmd.exe /c_ return code!
  84 +set EXIT_CODE=%ERRORLEVEL%
  85 +if %EXIT_CODE% equ 0 set EXIT_CODE=1
  86 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
  87 +exit /b %EXIT_CODE%
  88 +
  89 +:mainEnd
  90 +if "%OS%"=="Windows_NT" endlocal
  91 +
  92 +:omega
... ...
scripts/boss-data.sql 0 → 100644
  1 +++ a/scripts/boss-data.sql
  1 +USE dili_boss;
  2 +INSERT INTO uid_sequence_key(`key`, name, value, step, pattern, expired_on, version) VALUES ('TEST_KEY', '编号生成测试', 1, 1, 'TEST%d{yyyyMMdd}%n{5}%r{1}', CURDATE() - INTERVAL 1 DAY, 1);
... ...
scripts/dili-boss.sql 0 → 100644
  1 +++ a/scripts/dili-boss.sql
  1 +USE dili_boss;
  2 +
  3 +-- --------------------------------------------------------------------
  4 +-- 系统ID生成器数据模型
  5 +-- --------------------------------------------------------------------
  6 +DROP TABLE IF EXISTS `uid_sequence_key`;
  7 +CREATE TABLE `uid_sequence_key` (
  8 + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  9 + `KEY` VARCHAR(40) NOT NULL COMMENT 'KEY标识',
  10 + `name` VARCHAR(80) NOT NULL COMMENT 'KEY名称',
  11 + `value` BIGINT NOT NULl COMMENT '起始值',
  12 + `step` TINYINT UNSIGNED NOT NULL COMMENT '步长',
  13 + `pattern` VARCHAR(60) COMMENT 'ID格式', -- ORDER-%d{yyyyMMdd}-%n{5}-%r{1}
  14 + `expired_on` DATE COMMENT '有效日期',
  15 + `version` BIGINT NOT NULL COMMENT '数据版本',
  16 + PRIMARY KEY (`id`),
  17 + UNIQUE KEY `uk_sequence_key_key` (`KEY`) USING BTREE
  18 +) ENGINE=InnoDB;
0 19 \ No newline at end of file
... ...
settings.gradle 0 → 100644
  1 +++ a/settings.gradle
  1 +rootProject.name = 'dili-boss'
  2 +include 'boss-shared'
  3 +include 'boss-auth'
  4 +include 'boss-admin'
  5 +include 'boss-support'
  6 +include 'boss-report'
  7 +include 'boss-boot'
... ...