1、结构调整

2、抽象工厂优化
This commit is contained in:
2026-03-31 19:35:21 +08:00
parent 87757b352c
commit ebdbdbeb41
667 changed files with 1240 additions and 50173 deletions

3
.gitignore vendored
View File

@@ -73,3 +73,6 @@ functions/mock
screenshot screenshot
.firebase .firebase
sessionStore sessionStore
# local docs
/docs/

View File

@@ -1,721 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.njcn</groupId>
<artifactId>msgpush-dependencies</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>基础 bom 文件,管理整个项目的依赖版本</description>
<properties>
<revision>2025.12-SNAPSHOT</revision>
<flatten-maven-plugin.version>1.7.2</flatten-maven-plugin.version>
<!-- 统一依赖管理 -->
<spring.boot.version>3.5.9</spring.boot.version>
<spring.cloud.version>2025.0.0</spring.cloud.version>
<spring.cloud.alibaba.version>2023.0.3.3</spring.cloud.alibaba.version>
<!-- Web 相关 -->
<springdoc.version>2.8.14</springdoc.version>
<knife4j.version>4.5.0</knife4j.version>
<!-- DB 相关 -->
<druid.version>1.2.27</druid.version>
<mybatis.version>3.5.19</mybatis.version>
<mybatis-plus.version>3.5.15</mybatis-plus.version>
<mybatis-plus-join.version>1.5.5</mybatis-plus-join.version>
<dynamic-datasource.version>4.5.0</dynamic-datasource.version>
<easy-trans.version>3.0.6</easy-trans.version>
<redisson.version>3.52.0</redisson.version>
<dm8.jdbc.version>8.1.3.140</dm8.jdbc.version>
<kingbase.jdbc.version>8.6.0</kingbase.jdbc.version>
<opengauss.jdbc.version>5.1.0</opengauss.jdbc.version>
<taos.version>3.7.9</taos.version>
<!-- 消息队列 -->
<rocketmq-spring.version>2.3.5</rocketmq-spring.version>
<!-- RPC 相关 -->
<!-- Config 配置中心相关 -->
<!-- Job 定时任务相关 -->
<xxl-job.version>2.4.0</xxl-job.version>
<!-- 服务保障相关 -->
<lock4j.version>2.2.7</lock4j.version>
<!-- 监控相关 -->
<skywalking.version>9.5.0</skywalking.version>
<spring-boot-admin.version>3.5.6</spring-boot-admin.version>
<opentracing.version>0.33.0</opentracing.version>
<!-- Test 测试相关 -->
<podam.version>8.0.2.RELEASE</podam.version>
<jedis-mock.version>1.1.12</jedis-mock.version>
<mockito-inline.version>5.2.0</mockito-inline.version>
<!-- Bpm 工作流相关 -->
<flowable.version>7.2.0</flowable.version>
<!-- 工具类相关 -->
<anji-plus-captcha.version>1.4.0</anji-plus-captcha.version>
<jsoup.version>1.21.2</jsoup.version>
<lombok.version>1.18.42</lombok.version>
<mapstruct.version>1.6.3</mapstruct.version>
<hutool-5.version>5.8.42</hutool-5.version>
<hutool-6.version>6.0.0-M22</hutool-6.version>
<fastexcel.version>1.3.0</fastexcel.version>
<velocity.version>2.4.1</velocity.version>
<fastjson.version>1.2.83</fastjson.version>
<guava.version>33.5.0-jre</guava.version>
<transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
<commons-net.version>3.12.0</commons-net.version>
<commons-lang3.version>3.20.0</commons-lang3.version>
<jsch.version>2.27.7</jsch.version>
<tika-core.version>3.2.3</tika-core.version>
<ip2region.version>2.7.0</ip2region.version>
<bizlog-sdk.version>3.0.6</bizlog-sdk.version>
<reflections.version>0.10.2</reflections.version>
<netty.version>4.2.9.Final</netty.version>
<mqtt.version>1.2.5</mqtt.version>
<vertx.version>4.5.22</vertx.version>
<!-- 三方云服务相关 -->
<awssdk.version>2.40.15</awssdk.version>
<justauth.version>1.16.7</justauth.version>
<justauth-starter.version>1.4.0</justauth-starter.version>
<jimureport.version>2.1.3</jimureport.version>
<jimubi.version>2.3.0</jimubi.version>
<weixin-java.version>4.7.9-20251224.161447</weixin-java.version>
<alipay-sdk-java.version>4.40.607.ALL</alipay-sdk-java.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- 统一依赖管理 -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-bom</artifactId>
<version>${netty.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 业务组件 -->
<dependency>
<groupId>io.github.mouzt</groupId>
<artifactId>bizlog-sdk</artifactId>
<version>${bizlog-sdk.version}</version>
<exclusions>
<exclusion> <!-- 排除掉springboot依赖使用项目的 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-biz-ip</artifactId>
<version>${revision}</version>
</dependency>
<!-- Spring 核心 -->
<dependency>
<!-- 用于生成自定义的 Spring @ConfigurationProperties 配置类的说明文件 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-env</artifactId>
<version>${revision}</version>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-web</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-security</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-websocket</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
<exclusions>
<exclusion>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId> <!-- 接口文档 UIknife4j【网关专属】 -->
<artifactId>knife4j-gateway-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-mybatis</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<!-- 注意:必须声明,避免 flowable 和 mybatis-plus 引入的 mybatis 版本不一致!!! -->
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId> <!-- 代码生成器,使用它解析表结构 -->
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId> <!-- 多数据源 -->
<version>${dynamic-datasource.version}</version>
</dependency>
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->
<version>${mybatis-plus-join.version}</version>
</dependency>
<dependency>
<groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 -->
<artifactId>easy-trans-spring-boot-starter</artifactId>
<version>${easy-trans.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fhs-opensource</groupId>
<artifactId>easy-trans-mybatis-plus-extend</artifactId>
<version>${easy-trans.version}</version>
</dependency>
<dependency>
<groupId>com.fhs-opensource</groupId>
<artifactId>easy-trans-anno</artifactId>
<version>${easy-trans.version}</version>
</dependency>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-redis</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>${redisson.version}</version>
</dependency>
<dependency>
<groupId>com.dameng</groupId>
<artifactId>DmJdbcDriver18</artifactId>
<version>${dm8.jdbc.version}</version>
</dependency>
<dependency>
<groupId>org.opengauss</groupId>
<artifactId>opengauss-jdbc</artifactId>
<version>${opengauss.jdbc.version}</version>
</dependency>
<dependency>
<groupId>cn.com.kingbase</groupId>
<artifactId>kingbase8</artifactId>
<version>${kingbase.jdbc.version}</version>
</dependency>
<dependency>
<groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId>
<version>${taos.version}</version>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-rpc</artifactId>
<version>${revision}</version>
</dependency>
<!-- Registry 注册中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<exclusions>
<!-- 目的:解决 Nacos 启动的 NAMING_LOG_FILE 告警 -->
<exclusion>
<artifactId>logback-adapter</artifactId>
<groupId>com.alibaba.nacos</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- Config 配置中心相关 -->
<!-- Job 定时任务相关 -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>${xxl-job.version}</version>
</dependency>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-job</artifactId>
<version>${revision}</version>
</dependency>
<!-- 消息队列相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-mq</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>${rocketmq-spring.version}</version>
</dependency>
<!-- 服务保障相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-protection</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
<version>${lock4j.version}</version>
<exclusions>
<exclusion>
<artifactId>redisson-spring-boot-starter</artifactId>
<groupId>org.redisson</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>${skywalking.version}</version>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>${skywalking.version}</version>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-opentracing</artifactId>
<version>${skywalking.version}</version>
<!-- <exclusions>-->
<!-- <exclusion>-->
<!-- <artifactId>opentracing-api</artifactId>-->
<!-- <groupId>io.opentracing</groupId>-->
<!-- </exclusion>-->
<!-- <exclusion>-->
<!-- <artifactId>opentracing-util</artifactId>-->
<!-- <groupId>io.opentracing</groupId>-->
<!-- </exclusion>-->
<!-- </exclusions>-->
</dependency>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-api</artifactId>
<version>${opentracing.version}</version>
</dependency>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-util</artifactId>
<version>${opentracing.version}</version>
</dependency>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-noop</artifactId>
<version>${opentracing.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 -->
<version>${spring-boot-admin.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 -->
<version>${spring-boot-admin.version}</version>
</dependency>
<!-- Test 测试相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-test</artifactId>
<version>${revision}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>${mockito-inline.version}</version> <!-- 支持 Mockito 的 final 类与 static 方法的 mock -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring.boot.version}</version>
<exclusions>
<exclusion>
<artifactId>asm</artifactId>
<groupId>org.ow2.asm</groupId>
</exclusion>
<exclusion>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.fppt</groupId> <!-- 单元测试,我们采用内嵌的 Redis 数据库 -->
<artifactId>jedis-mock</artifactId>
<version>${jedis-mock.version}</version>
</dependency>
<dependency>
<groupId>uk.co.jemos.podam</groupId> <!-- 单元测试,随机生成 POJO 类 -->
<artifactId>podam</artifactId>
<version>${podam.version}</version>
</dependency>
<!-- 工作流相关 -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-process</artifactId>
<version>${flowable.version}</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-actuator</artifactId>
<version>${flowable.version}</version>
</dependency>
<!-- 工作流相关结束 -->
<!-- 工具类相关 -->
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-common</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.njcn</groupId>
<artifactId>msgpush-spring-boot-starter-excel</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool-5.version}</version>
</dependency>
<dependency>
<groupId>org.dromara.hutool</groupId>
<artifactId>hutool-extra</artifactId>
<version>${hutool-6.version}</version>
</dependency>
<dependency>
<groupId>cn.idev.excel</groupId>
<artifactId>fastexcel</artifactId>
<version>${fastexcel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId> <!-- 文件类型的识别 -->
<version>${tika-core.version}</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId> <!-- 解决 ThreadLocal 父子线程的传值问题 -->
<version>${transmittable-thread-local.version}</version>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId> <!-- 解决 ftp 连接 -->
<version>${commons-net.version}</version>
</dependency>
<dependency>
<groupId>com.github.mwiede</groupId>
<artifactId>jsch</artifactId> <!-- 解决 sftp 连接 -->
<version>${jsch.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version> <!-- 解决 CVE-2025-48924 漏洞 -->
</dependency>
<dependency>
<groupId>com.anji-plus</groupId>
<artifactId>captcha-spring-boot-starter</artifactId> <!-- 验证码,一般用于登录使用 -->
<version>${anji-plus-captcha.version}</version>
</dependency>
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>${ip2region.version}</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>${reflections.version}</version>
</dependency>
<!-- 三方云服务相关 -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>${awssdk.version}</version>
</dependency>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>${alipay-sdk-java.version}</version>
<exclusions>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>${weixin-java.version}</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-mp-spring-boot-starter</artifactId>
<version>${weixin-java.version}</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
<version>${weixin-java.version}</version>
</dependency>
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId> <!-- 社交登陆(例如说,个人微信、企业微信等等) -->
<version>${justauth.version}</version>
</dependency>
<dependency>
<groupId>com.xkcoding.justauth</groupId>
<artifactId>justauth-spring-boot-starter</artifactId>
<version>${justauth-starter.version}</version>
</dependency>
<!-- 积木报表-->
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimureport-spring-boot3-starter-fastjson2</artifactId>
<version>${jimureport.version}</version>
</dependency>
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimubi-spring-boot3-starter</artifactId>
<version>${jimubi.version}</version>
<exclusions>
<exclusion>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
</exclusion>
<exclusion>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Vert.x -->
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-mqtt</artifactId>
<version>${vertx.version}</version>
</dependency>
<!-- MQTT -->
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>${mqtt.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!-- 统一 revision 版本 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>${flatten-maven-plugin.version}</version>
<configuration>
<flattenMode>bom</flattenMode>
<updatePomFile>true</updatePomFile>
</configuration>
<executions>
<execution>
<goals>
<goal>flatten</goal>
</goals>
<id>flatten</id>
<phase>process-resources</phase>
</execution>
<execution>
<goals>
<goal>clean</goal>
</goals>
<id>flatten.clean</id>
<phase>clean</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -2,36 +2,45 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.njcn</groupId> <groupId>com.njcn</groupId>
<artifactId>msgpush-framework</artifactId> <artifactId>msgpush-framework</artifactId>
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>msgpush-common</artifactId> <artifactId>msgpush-common</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>${project.artifactId}</name> <name>${project.artifactId}</name>
<description>定义基础 pojo 类、枚举、工具类等等</description> <description>定义基础 pojo 类、枚举、工具类等等</description>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies> <dependencies>
<!-- Spring 核心 --> <!-- Spring 核心 -->
<!-- Spring 核心工具类,提供资源加载、类型转换等基础功能 -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId> <artifactId>spring-core</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 --> <scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency> </dependency>
<!-- Spring 表达式语言(SpEL),用于动态表达式解析 -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId> <artifactId>spring-expression</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 --> <scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency> </dependency>
<!-- Spring AOP 支持,提供面向切面编程能力 -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId> <artifactId>spring-aop</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 --> <scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency> </dependency>
<!-- AspectJ 织入器,实现 AOP 切面逻辑 -->
<dependency> <dependency>
<groupId>org.aspectj</groupId> <groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId> <artifactId>aspectjweaver</artifactId>
@@ -46,25 +55,28 @@
</dependency> </dependency>
<!-- Web 相关 --> <!-- Web 相关 -->
<!-- Spring Web 工具类,提供 HTTP 请求处理、文件上传等功能 -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId> <artifactId>spring-web</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 --> <scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency> </dependency>
<!-- Jakarta Servlet API,定义 HTTP 请求响应接口规范 -->
<dependency> <dependency>
<groupId>jakarta.servlet</groupId> <groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId> <artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 --> <scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency> </dependency>
<!-- Swagger 注解,用于 API 文档生成(@Schema、@Operation 等) -->
<dependency> <dependency>
<groupId>org.springdoc</groupId> <groupId>io.swagger.core.v3</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <artifactId>swagger-annotations</artifactId>
<scope>provided</scope> <!-- 设置为 provided主要是 PageParam 使用到 -->
</dependency> </dependency>
<!-- RPC 远程调用相关 --> <!-- RPC 远程调用相关 -->
<!-- OpenFeign 核心,用于声明式 HTTP 客户端(@FeignClient 注解) -->
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-core</artifactId> <artifactId>spring-cloud-openfeign-core</artifactId>
@@ -72,6 +84,7 @@
</dependency> </dependency>
<!-- 监控相关 --> <!-- 监控相关 -->
<!-- SkyWalking 链路追踪工具包,用于分布式追踪(@Trace 注解) -->
<dependency> <dependency>
<groupId>org.apache.skywalking</groupId> <groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId> <artifactId>apm-toolkit-trace</artifactId>
@@ -79,78 +92,92 @@
<!-- 工具类相关 --> <!-- 工具类相关 -->
<dependency> <dependency>
<!-- Lombok,自动生成 getter/setter/构造器等样板代码 -->
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<!-- MapStruct 核心,用于对象映射转换(DTO/VO/DO 互转) -->
<groupId>org.mapstruct</groupId> <groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> <artifactId>mapstruct</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<!-- MapStruct JDK8+ 支持,提供 Java 8 时间类型等映射 -->
<groupId>org.mapstruct</groupId> <groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher --> <artifactId>mapstruct-jdk8</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<!-- MapStruct 注解处理器,编译期生成映射实现代码 -->
<groupId>org.mapstruct</groupId> <groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId> <artifactId>mapstruct-processor</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<!-- Google Guava,提供集合、缓存、并发等增强工具类 -->
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 --> <scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency> </dependency>
<dependency> <dependency>
<!-- Jackson 数据绑定,用于 JSON 序列化/反序列化 -->
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 --> <scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency> </dependency>
<dependency> <dependency>
<!-- Jackson 核心,提供 JSON 解析底层能力 -->
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId> <artifactId>jackson-core</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 --> <scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency> </dependency>
<dependency> <dependency>
<!-- Jackson JSR310 支持,用于 LocalDateTime 等 Java 8 时间类型序列化 -->
<groupId>com.fasterxml.jackson.datatype</groupId> <groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId> <artifactId>jackson-datatype-jsr310</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 --> <scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency> </dependency>
<dependency> <dependency>
<!-- SLF4J 日志门面,提供统一的日志接口 -->
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
<scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 --> <scope>provided</scope> <!-- 设置为 provided只有工具类需要使用到 -->
</dependency> </dependency>
<dependency> <dependency>
<!-- Jakarta Validation API,用于参数校验(@NotNull、@Valid 等注解) -->
<groupId>jakarta.validation</groupId> <groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId> <artifactId>jakarta.validation-api</artifactId>
<scope>provided</scope> <!-- 设置为 provided主要是 PageParam 使用到 --> <scope>provided</scope> <!-- 设置为 provided主要是 PageParam 使用到 -->
</dependency> </dependency>
<dependency> <dependency>
<!-- Hutool 工具包,提供日期、加密、HTTP、Excel 等常用工具类 -->
<groupId>cn.hutool</groupId> <groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId> <artifactId>hutool-all</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<!-- 阿里 TTL,用于线程池场景下 ThreadLocal 值的传递 -->
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId> <artifactId>transmittable-thread-local</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fhs-opensource</groupId> <!-- VO 数据翻译 --> <!-- Easy-Trans 注解包,用于 VO 数据字典翻译(@Trans 注解) -->
<groupId>com.fhs-opensource</groupId>
<artifactId>easy-trans-anno</artifactId> <!-- 默认引入的原因,方便 xxx-module-api 包使用 --> <artifactId>easy-trans-anno</artifactId> <!-- 默认引入的原因,方便 xxx-module-api 包使用 -->
</dependency> </dependency>
<!-- Test 测试相关 --> <!-- Test 测试相关 -->
<dependency> <dependency>
<!-- Spring Boot 测试启动器,集成 JUnit、Mockito 等测试框架 -->
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -11,8 +11,6 @@ import java.util.List;
* 为什么要赋值粘贴到 msgpush-common 包下? * 为什么要赋值粘贴到 msgpush-common 包下?
* 因为 AutoTransable 属于 easy-trans-service 下,无法方便的在 msgpush-module-xxx-api 模块下使用 * 因为 AutoTransable 属于 easy-trans-service 下,无法方便的在 msgpush-module-xxx-api 模块下使用
* *
* @author hongawen
* @since 2020-05-19 10:26:15
*/ */
public interface AutoTransable<V extends VO> { public interface AutoTransable<V extends VO> {

View File

@@ -1,4 +0,0 @@
/**
* 针对 infra 模块的 api 包
*/
package com.njcn.msgpush.framework.common.biz.infra;

View File

@@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import java.util.List; import java.util.List;
@FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false) // TODO 芋艿fallbackFactory = @FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false)
@Tag(name = "RPC 服务 - 字典数据") @Tag(name = "RPC 服务 - 字典数据")
public interface DictDataCommonApi { public interface DictDataCommonApi {

View File

@@ -10,7 +10,7 @@ public class DictDataRespDTO {
@Schema(description = "字典标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能") @Schema(description = "字典标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
private String label; private String label;
@Schema(description = "字典值", requiredMode = Schema.RequiredMode.REQUIRED, example = "iocoder") @Schema(description = "字典值", requiredMode = Schema.RequiredMode.REQUIRED, example = "njcn")
private String value; private String value;
@Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex") @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex")

View File

@@ -11,7 +11,7 @@ import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false) // TODO 芋艿fallbackFactory = @FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false)
@Tag(name = "RPC 服务 - 操作日志") @Tag(name = "RPC 服务 - 操作日志")
public interface OperateLogCommonApi { public interface OperateLogCommonApi {

View File

@@ -1,20 +1,19 @@
package com.njcn.msgpush.framework.common.biz.system.oauth2; package com.njcn.msgpush.framework.common.biz.system.oauth2;
import com.njcn.msgpush.framework.common.enums.RpcConstants;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO; import com.njcn.msgpush.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
import com.njcn.msgpush.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCreateReqDTO; import com.njcn.msgpush.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
import com.njcn.msgpush.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenRespDTO; import com.njcn.msgpush.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenRespDTO;
import io.swagger.v3.oas.annotations.tags.Tag; import com.njcn.msgpush.framework.common.enums.RpcConstants;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid; @FeignClient(name = RpcConstants.SYSTEM_NAME)
@FeignClient(name = RpcConstants.SYSTEM_NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - OAuth2.0 令牌") @Tag(name = "RPC 服务 - OAuth2.0 令牌")
public interface OAuth2TokenCommonApi { public interface OAuth2TokenCommonApi {

View File

@@ -8,23 +8,23 @@ import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@Schema(description = "RPC 服务 - OAuth2 访问令牌的校验 Response DTO") @Schema(description = "RPC service - OAuth2 access token check response")
@Data @Data
public class OAuth2AccessTokenCheckRespDTO implements Serializable { public class OAuth2AccessTokenCheckRespDTO implements Serializable {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") @Schema(description = "User id", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Long userId; private Long userId;
@Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @Schema(description = "User type", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer userType; private Integer userType;
@Schema(description = "用户信息", example = "{\"nickname\": \"灿能\"}") @Schema(description = "User info", example = "{\"nickname\": \"msgpush\"}")
private Map<String, String> userInfo; private Map<String, String> userInfo;
@Schema(description = "授权范围的数组", example = "user_info") @Schema(description = "Scopes", example = "user_info")
private List<String> scopes; private List<String> scopes;
@Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "Expire time", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime expiresTime; private LocalDateTime expiresTime;
} }

View File

@@ -3,9 +3,9 @@ package com.njcn.msgpush.framework.common.biz.system.oauth2.dto;
import com.njcn.msgpush.framework.common.enums.UserTypeEnum; import com.njcn.msgpush.framework.common.enums.UserTypeEnum;
import com.njcn.msgpush.framework.common.validation.InEnum; import com.njcn.msgpush.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
import jakarta.validation.constraints.NotNull;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;

View File

@@ -1,6 +1,5 @@
package com.njcn.msgpush.framework.common.biz.system.permission; package com.njcn.msgpush.framework.common.biz.system.permission;
import com.njcn.msgpush.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO;
import com.njcn.msgpush.framework.common.enums.RpcConstants; import com.njcn.msgpush.framework.common.enums.RpcConstants;
import com.njcn.msgpush.framework.common.pojo.CommonResult; import com.njcn.msgpush.framework.common.pojo.CommonResult;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@@ -11,7 +10,7 @@ import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false) // TODO 芋艿fallbackFactory = @FeignClient(name = RpcConstants.SYSTEM_NAME, primary = false)
@Tag(name = "RPC 服务 - 权限") @Tag(name = "RPC 服务 - 权限")
public interface PermissionCommonApi { public interface PermissionCommonApi {
@@ -35,9 +34,4 @@ public interface PermissionCommonApi {
CommonResult<Boolean> hasAnyRoles(@RequestParam("userId") Long userId, CommonResult<Boolean> hasAnyRoles(@RequestParam("userId") Long userId,
@RequestParam("roles") String... roles); @RequestParam("roles") String... roles);
@GetMapping(PREFIX + "/get-dept-data-permission")
@Operation(summary = "获得登陆用户的部门数据权限")
@Parameter(name = "userId", description = "用户编号", example = "2", required = true)
CommonResult<DeptDataPermissionRespDTO> getDeptDataPermission(@RequestParam("userId") Long userId);
} }

View File

@@ -1,28 +0,0 @@
package com.njcn.msgpush.framework.common.biz.system.permission.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.HashSet;
import java.util.Set;
@Schema(description = "RPC 服务 - 部门的数据权限 Response DTO")
@Data
public class DeptDataPermissionRespDTO {
@Schema(description = "是否可查看全部数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean all;
@Schema(description = "是否可查看自己的数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean self;
@Schema(description = "可查看的部门编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 3]")
private Set<Long> deptIds;
public DeptDataPermissionRespDTO() {
this.all = false;
this.self = false;
this.deptIds = new HashSet<>();
}
}

View File

@@ -3,7 +3,7 @@ package com.njcn.msgpush.framework.common.core;
/** /**
* 可生成 T 数组的接口 * 可生成 T 数组的接口
* *
* @author HUIHUI * @author hongawen
*/ */
public interface ArrayValuable<T> { public interface ArrayValuable<T> {

View File

@@ -3,11 +3,6 @@ package com.njcn.msgpush.framework.common.enums;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
/**
* 文档地址
*
* @author hongawen
*/
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum DocumentEnum { public enum DocumentEnum {

View File

@@ -19,22 +19,12 @@ public interface RpcConstants {
* *
* 注意,需要保证和 spring.application.name 保持一致 * 注意,需要保证和 spring.application.name 保持一致
*/ */
String SYSTEM_NAME = "system-server"; String SYSTEM_NAME = "msgpush-system-server";
/** /**
* system 服务的前缀 * system 服务的前缀
*/ */
String SYSTEM_PREFIX = RPC_API_PREFIX + "/system"; String SYSTEM_PREFIX = RPC_API_PREFIX + "/system";
/**
* infra 服务名
*
* 注意,需要保证和 spring.application.name 保持一致
*/
String INFRA_NAME = "infra-server";
/**
* infra 服务的前缀
*/
String INFRA_PREFIX = RPC_API_PREFIX + "/infra";
} }

View File

@@ -9,13 +9,17 @@ import java.util.Arrays;
/** /**
* 全局用户类型枚举 * 全局用户类型枚举
*
* 用于区分不同访问端的用户类型:
* - MEMBER: 对应 /app-api/** 路径,通常用于移动端或 C 端用户
* - ADMIN: 对应 /admin-api/** 路径,通常用于 Web 管理端或 B 端用户
*/ */
@AllArgsConstructor @AllArgsConstructor
@Getter @Getter
public enum UserTypeEnum implements ArrayValuable<Integer> { public enum UserTypeEnum implements ArrayValuable<Integer> {
MEMBER(1, "会员"), // 面向 c 端,普通用户 MEMBER(1, "App端用户"), // 对应 /app-api移动端或 C 端
ADMIN(2, "管理员"); // 面向 b 端,管理后台 ADMIN(2, "Web端用户"); // 对应 /admin-apiWeb 管理端或 B 端
public static final Integer[] ARRAYS = Arrays.stream(values()).map(UserTypeEnum::getValue).toArray(Integer[]::new); public static final Integer[] ARRAYS = Arrays.stream(values()).map(UserTypeEnum::getValue).toArray(Integer[]::new);

View File

@@ -29,12 +29,11 @@ public interface GlobalErrorCodeConstants {
// ========== 服务端错误段 ========== // ========== 服务端错误段 ==========
ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常"); ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常");
ErrorCode NOT_IMPLEMENTED = new ErrorCode(501, "功能未实现/未开启"); ErrorCode TABLE_NOT_EXISTS = new ErrorCode(501, "表不存在");
ErrorCode ERROR_CONFIGURATION = new ErrorCode(502, "错误的配置项"); ErrorCode ERROR_CONFIGURATION = new ErrorCode(502, "错误的配置项");
// ========== 自定义错误段 ========== // ========== 自定义错误段 ==========
ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求 ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求
ErrorCode DEMO_DENY = new ErrorCode(901, "演示模式,禁止写操作");
ErrorCode UNKNOWN = new ErrorCode(999, "未知错误"); ErrorCode UNKNOWN = new ErrorCode(999, "未知错误");

View File

@@ -1,9 +1,9 @@
package com.njcn.msgpush.framework.common.exception.util; package com.njcn.msgpush.framework.common.exception.util;
import com.google.common.annotations.VisibleForTesting;
import com.njcn.msgpush.framework.common.exception.ErrorCode; import com.njcn.msgpush.framework.common.exception.ErrorCode;
import com.njcn.msgpush.framework.common.exception.ServiceException; import com.njcn.msgpush.framework.common.exception.ServiceException;
import com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants; import com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
/** /**

View File

@@ -1,6 +0,0 @@
/**
* 基础的通用类,和框架无关
*
* 例如说CommonResult 为通用返回
*/
package com.njcn.msgpush.framework.common;

View File

@@ -1,11 +1,11 @@
package com.njcn.msgpush.framework.common.pojo; package com.njcn.msgpush.framework.common.pojo;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.njcn.msgpush.framework.common.exception.ErrorCode; import com.njcn.msgpush.framework.common.exception.ErrorCode;
import com.njcn.msgpush.framework.common.exception.ServiceException; import com.njcn.msgpush.framework.common.exception.ServiceException;
import com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants; import com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.njcn.msgpush.framework.common.exception.util.ServiceExceptionUtil; import com.njcn.msgpush.framework.common.exception.util.ServiceExceptionUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data; import lombok.Data;
import java.io.Serializable; import java.io.Serializable;

View File

@@ -1,11 +1,11 @@
package com.njcn.msgpush.framework.common.pojo; package com.njcn.msgpush.framework.common.pojo;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotNull;
import java.io.Serializable; import java.io.Serializable;
@Schema(description="分页参数") @Schema(description="分页参数")

View File

@@ -40,7 +40,7 @@ public class CacheUtils {
// 只阻塞当前数据加载线程,其他线程返回旧值 // 只阻塞当前数据加载线程,其他线程返回旧值
.refreshAfterWrite(duration) .refreshAfterWrite(duration)
// 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程 // 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
.build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool())); // TODO 芋艿:可能要思考下,未来要不要做成可配置 .build(CacheLoader.asyncReloading(loader, Executors.newCachedThreadPool())); // TODO 可能要思考下,未来要不要做成可配置
} }
/** /**

View File

@@ -3,8 +3,8 @@ package com.njcn.msgpush.framework.common.util.collection;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import java.util.*; import java.util.*;
import java.util.function.*; import java.util.function.*;

View File

@@ -3,10 +3,11 @@ package com.njcn.msgpush.framework.common.util.collection;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ObjUtil;
import com.njcn.msgpush.framework.common.core.KeyValue;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.njcn.msgpush.framework.common.core.KeyValue;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -65,4 +66,47 @@ public class MapUtils {
return map; return map;
} }
/**
* 从 Map 中获取 BigDecimal 值
*
* @param map Map 数据源
* @param key 键名
* @return BigDecimal 值,解析失败或值为 null 时返回 null
*/
public static BigDecimal getBigDecimal(Map<String, ?> map, String key) {
return getBigDecimal(map, key, null);
}
/**
* 从 Map 中获取 BigDecimal 值
*
* @param map Map 数据源
* @param key 键名
* @param defaultValue 默认值
* @return BigDecimal 值,解析失败或值为 null 时返回默认值
*/
public static BigDecimal getBigDecimal(Map<String, ?> map, String key, BigDecimal defaultValue) {
if (map == null) {
return defaultValue;
}
Object value = map.get(key);
if (value == null) {
return defaultValue;
}
if (value instanceof BigDecimal) {
return (BigDecimal) value;
}
if (value instanceof Number) {
return BigDecimal.valueOf(((Number) value).doubleValue());
}
if (value instanceof String) {
try {
return new BigDecimal((String) value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
return defaultValue;
}
} }

View File

@@ -17,7 +17,8 @@ import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static cn.hutool.core.date.DatePattern.*; import static cn.hutool.core.date.DatePattern.UTC_MS_WITH_XXX_OFFSET_PATTERN;
import static cn.hutool.core.date.DatePattern.createFormatter;
/** /**
* 时间工具类,用于 {@link LocalDateTime} * 时间工具类,用于 {@link LocalDateTime}

View File

@@ -7,7 +7,7 @@ import cn.hutool.core.util.StrUtil;
import java.io.InputStream; import java.io.InputStream;
/** /**
* IO 工具类,用于 {@link cn.hutool.core.io.IoUtil} 缺失的方法 * IO 工具类,用于 {@link IoUtil} 缺失的方法
* *
* @author hongawen * @author hongawen
*/ */

View File

@@ -3,8 +3,6 @@ package com.njcn.msgpush.framework.common.util.json;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.njcn.msgpush.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer;
import com.njcn.msgpush.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -13,6 +11,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.njcn.msgpush.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer;
import com.njcn.msgpush.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;
import lombok.Getter; import lombok.Getter;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -229,4 +229,53 @@ public class JsonUtils {
return JSONUtil.isTypeJSONObject(str); return JSONUtil.isTypeJSONObject(str);
} }
/**
* 将 Object 转换为目标类型
* <p>
* 避免先转 jsonString 再 parseObject 的性能损耗
*
* @param obj 源对象(可以是 Map、POJO 等)
* @param clazz 目标类型
* @return 转换后的对象
*/
public static <T> T convertObject(Object obj, Class<T> clazz) {
if (obj == null) {
return null;
}
if (clazz.isInstance(obj)) {
return clazz.cast(obj);
}
return objectMapper.convertValue(obj, clazz);
}
/**
* 将 Object 转换为目标类型(支持泛型)
*
* @param obj 源对象
* @param typeReference 目标类型引用
* @return 转换后的对象
*/
public static <T> T convertObject(Object obj, TypeReference<T> typeReference) {
if (obj == null) {
return null;
}
return objectMapper.convertValue(obj, typeReference);
}
/**
* 将 Object 转换为 List 类型
* <p>
* 避免先转 jsonString 再 parseArray 的性能损耗
*
* @param obj 源对象(可以是 List、数组等
* @param clazz 目标元素类型
* @return 转换后的 List
*/
public static <T> List<T> convertList(Object obj, Class<T> clazz) {
if (obj == null) {
return new ArrayList<>();
}
return objectMapper.convertValue(obj, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
}
} }

View File

@@ -8,7 +8,7 @@ import java.math.BigDecimal;
import java.util.List; import java.util.List;
/** /**
* 数字的工具类,补全 {@link cn.hutool.core.util.NumberUtil} 的功能 * 数字的工具类,补全 {@link NumberUtil} 的功能
* *
* @author hongawen * @author hongawen
*/ */

View File

@@ -10,7 +10,7 @@ import java.util.function.Consumer;
/** /**
* Bean 工具类 * Bean 工具类
* *
* 1. 默认使用 {@link cn.hutool.core.bean.BeanUtil} 作为实现类,虽然不同 bean 工具的性能有差别,但是对绝大多数同学的项目,不用在意这点性能 * 1. 默认使用 {@link BeanUtil} 作为实现类,虽然不同 bean 工具的性能有差别,但是对绝大多数同学的项目,不用在意这点性能
* 2. 针对复杂的对象转换,可以搜参考 AuthConvert 实现,通过 mapstruct + default 配合实现 * 2. 针对复杂的对象转换,可以搜参考 AuthConvert 实现,通过 mapstruct + default 配合实现
* *
* @author hongawen * @author hongawen

View File

@@ -13,7 +13,7 @@ import org.springframework.util.Assert;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
/** /**
* {@link com.njcn.msgpush.framework.common.pojo.PageParam} 工具类 * {@link PageParam} 工具类
* *
* @author hongawen * @author hongawen
*/ */

View File

@@ -2,12 +2,12 @@ package com.njcn.msgpush.framework.common.util.validation;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import org.springframework.util.StringUtils;
import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException; import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Validation; import jakarta.validation.Validation;
import jakarta.validation.Validator; import jakarta.validation.Validator;
import org.springframework.util.StringUtils;
import java.util.Set; import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;

View File

@@ -2,6 +2,7 @@ package com.njcn.msgpush.framework.common.validation;
import jakarta.validation.Constraint; import jakarta.validation.Constraint;
import jakarta.validation.Payload; import jakarta.validation.Payload;
import java.lang.annotation.*; import java.lang.annotation.*;
@Target({ @Target({

View File

@@ -2,7 +2,6 @@ package com.njcn.msgpush.framework.common.validation;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.njcn.msgpush.framework.common.util.validation.ValidationUtils; import com.njcn.msgpush.framework.common.util.validation.ValidationUtils;
import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext; import jakarta.validation.ConstraintValidatorContext;

View File

@@ -2,6 +2,7 @@ package com.njcn.msgpush.framework.common.validation;
import jakarta.validation.Constraint; import jakarta.validation.Constraint;
import jakarta.validation.Payload; import jakarta.validation.Payload;
import java.lang.annotation.*; import java.lang.annotation.*;
@Target({ @Target({

View File

@@ -2,7 +2,6 @@ package com.njcn.msgpush.framework.common.validation;
import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.PhoneUtil; import cn.hutool.core.util.PhoneUtil;
import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext; import jakarta.validation.ConstraintValidatorContext;

View File

@@ -1,64 +0,0 @@
package com.njcn.msgpush.framework.common.util.collection;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* {@link CollectionUtils} 的单元测试
*/
public class CollectionUtilsTest {
@Data
@AllArgsConstructor
private static class Dog {
private Integer id;
private String name;
private String code;
}
@Test
public void testDiffList() {
// 准备参数
Collection<Dog> oldList = Arrays.asList(
new Dog(1, "花花", "hh"),
new Dog(2, "旺财", "wc")
);
Collection<Dog> newList = Arrays.asList(
new Dog(null, "花花2", "hh"),
new Dog(null, "小白", "xb")
);
BiFunction<Dog, Dog, Boolean> sameFunc = (oldObj, newObj) -> {
boolean same = oldObj.getCode().equals(newObj.getCode());
// 如果相等的情况下,需要设置下 id后续好更新
if (same) {
newObj.setId(oldObj.getId());
}
return same;
};
// 调用
List<List<Dog>> result = CollectionUtils.diffList(oldList, newList, sameFunc);
// 断言
assertEquals(result.size(), 3);
// 断言 create
assertEquals(result.get(0).size(), 1);
assertEquals(result.get(0).get(0), new Dog(null, "小白", "xb"));
// 断言 update
assertEquals(result.get(1).size(), 1);
assertEquals(result.get(1).get(0), new Dog(1, "花花2", "hh"));
// 断言 delete
assertEquals(result.get(2).size(), 1);
assertEquals(result.get(2).get(0), new Dog(2, "旺财", "wc"));
}
}

View File

@@ -2,21 +2,20 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.njcn</groupId> <groupId>com.njcn</groupId>
<artifactId>msgpush-framework</artifactId> <artifactId>msgpush-framework</artifactId>
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>msgpush-spring-boot-starter-biz-ip</artifactId> <artifactId>msgpush-spring-boot-starter-biz-ip</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>${project.artifactId}</name> <name>${project.artifactId}</name>
<description>IP 拓展,支持如下功能: <description>IP 拓展,支持如下功能:
1. IP 功能:查询 IP 对应的城市信息 1. IP 功能:查询 IP 对应的城市信息
基于 https://gitee.com/lionsoul/ip2region 实现 基于 https://gitee.com/lionsoul/ip2region 实现
2. 城市功能:查询城市编码对应的城市信息 2. 城市功能:查询城市编码对应的城市信息
基于 https://github.com/modood/Administrative-divisions-of-China 实现 基于 https://github.com/modood/Administrative-divisions-of-China 实现
</description> </description>
@@ -50,5 +49,4 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project>
</project>

View File

@@ -13,7 +13,7 @@ import java.io.IOException;
* *
* IP 数据源来自 ip2region.xdb 精简版,基于 <a href="https://gitee.com/zhijiantianya/ip2region"/> 项目 * IP 数据源来自 ip2region.xdb 精简版,基于 <a href="https://gitee.com/zhijiantianya/ip2region"/> 项目
* *
* @author wanglhup * @author hongawen
*/ */
@Slf4j @Slf4j
public class IPUtils { public class IPUtils {

View File

@@ -1,11 +0,0 @@
/**
* IP 拓展,支持如下功能:
*
* 1. IP 功能:查询 IP 对应的城市信息
* 基于 https://gitee.com/lionsoul/ip2region 实现
* 2. 城市功能:查询城市编码对应的城市信息
* 基于 https://github.com/modood/Administrative-divisions-of-China 实现
*
* @author hongawen
*/
package com.njcn.msgpush.framework.ip;

View File

@@ -1,36 +0,0 @@
package com.njcn.msgpush.framework.ip.core.utils;
import com.njcn.msgpush.framework.ip.core.Area;
import com.njcn.msgpush.framework.ip.core.enums.AreaTypeEnum;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* {@link AreaUtils} 的单元测试
*
* @author hongawen
*/
public class AreaUtilsTest {
@Test
public void testGetArea() {
// 调用:北京
Area area = AreaUtils.getArea(110100);
// 断言
assertEquals(area.getId(), 110100);
assertEquals(area.getName(), "北京市");
assertEquals(area.getType(), AreaTypeEnum.CITY.getType());
assertEquals(area.getParent().getId(), 110000);
assertEquals(area.getChildren().size(), 16);
}
@Test
public void testFormat() {
assertEquals(AreaUtils.format(110105), "北京市 北京市 朝阳区");
assertEquals(AreaUtils.format(1), "中国");
assertEquals(AreaUtils.format(2), "蒙古");
}
}

View File

@@ -1,47 +0,0 @@
package com.njcn.msgpush.framework.ip.core.utils;
import com.njcn.msgpush.framework.ip.core.Area;
import org.junit.jupiter.api.Test;
import org.lionsoul.ip2region.xdb.Searcher;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* {@link IPUtils} 的单元测试
*
* @author wanglhup
*/
public class IPUtilsTest {
@Test
public void testGetAreaId_string() {
// 120.202.4.0|120.202.4.255|420600
Integer areaId = IPUtils.getAreaId("120.202.4.50");
assertEquals(420600, areaId);
}
@Test
public void testGetAreaId_long() throws Exception {
// 120.203.123.0|120.203.133.255|360900
long ip = Searcher.checkIP("120.203.123.250");
Integer areaId = IPUtils.getAreaId(ip);
assertEquals(360900, areaId);
}
@Test
public void testGetArea_string() {
// 120.202.4.0|120.202.4.255|420600
Area area = IPUtils.getArea("120.202.4.50");
assertEquals("襄阳市", area.getName());
}
@Test
public void testGetArea_long() throws Exception {
// 120.203.123.0|120.203.133.255|360900
long ip = Searcher.checkIP("120.203.123.252");
Area area = IPUtils.getArea(ip);
assertEquals("宜春市", area.getName());
}
}

View File

@@ -2,27 +2,20 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.njcn</groupId> <groupId>com.njcn</groupId>
<artifactId>msgpush-framework</artifactId> <artifactId>msgpush-framework</artifactId>
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>msgpush-spring-boot-starter-env</artifactId> <artifactId>msgpush-spring-boot-starter-env</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>${project.artifactId}</name> <name>${project.artifactId}</name>
<description> <description>
开发环境拓展,实现类似阿里的特性环境的能力 开发环境拓展,实现类似阿里的特性环境的能力
1. https://segmentfault.com/a/1190000018022987
</description> </description>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.njcn</groupId> <groupId>com.njcn</groupId>
@@ -62,5 +55,4 @@
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
</project>
</project>

View File

@@ -21,7 +21,6 @@ public class EnvEnvironmentPostProcessor implements EnvironmentPostProcessor {
private static final Set<String> TARGET_TAG_KEYS = SetUtils.asSet( private static final Set<String> TARGET_TAG_KEYS = SetUtils.asSet(
"spring.cloud.nacos.discovery.metadata.tag" // Nacos 注册中心 "spring.cloud.nacos.discovery.metadata.tag" // Nacos 注册中心
// MQ TODO
); );
@Override @Override
@@ -29,7 +28,6 @@ public class EnvEnvironmentPostProcessor implements EnvironmentPostProcessor {
// 0. 设置 ${HOST_NAME} 兜底的环境变量 // 0. 设置 ${HOST_NAME} 兜底的环境变量
String hostNameKey = StrUtil.subBetween(HOST_NAME_VALUE, "{", "}"); String hostNameKey = StrUtil.subBetween(HOST_NAME_VALUE, "{", "}");
if (!environment.containsProperty(hostNameKey)) { if (!environment.containsProperty(hostNameKey)) {
System.out.println(EnvUtils.getHostName());
environment.getSystemProperties().put(hostNameKey, EnvUtils.getHostName()); environment.getSystemProperties().put(hostNameKey, EnvUtils.getHostName());
} }

View File

@@ -76,9 +76,9 @@ public class EnvLoadBalancerClient implements ReactorServiceInstanceLoadBalancer
chooseInstances = instances; chooseInstances = instances;
} }
// TODO 芋艿:https://juejin.cn/post/7056770721858469896 相同网段 // TODO https://juejin.cn/post/7056770721858469896 相同网段
// 随机 + 权重获取实例列表 TODO 芋艿:目前直接使用 Nacos 提供的方法,如果替换注册中心,需要重新失败该方法 // 随机 + 权重获取实例列表 TODO 目前直接使用 Nacos 提供的方法,如果替换注册中心,需要重新失败该方法
return new DefaultResponse(NacosBalancer.getHostByRandomWeight3(chooseInstances)); return new DefaultResponse(NacosBalancer.getHostByRandomWeight3(chooseInstances));
} }

View File

@@ -1 +0,0 @@
package com.njcn.msgpush.framework.env.core;

View File

@@ -1,7 +0,0 @@
/**
* 开发环境拓展,实现类似阿里的特性环境的能力
* 1. https://segmentfault.com/a/1190000018022987
*
* @author hongawen
*/
package com.njcn.msgpush.framework.env;

View File

@@ -2,12 +2,13 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.njcn</groupId> <groupId>com.njcn</groupId>
<artifactId>msgpush-framework</artifactId> <artifactId>msgpush-framework</artifactId>
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>msgpush-spring-boot-starter-excel</artifactId> <artifactId>msgpush-spring-boot-starter-excel</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@@ -15,6 +16,7 @@
<description>Excel 拓展</description> <description>Excel 拓展</description>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.njcn</groupId> <groupId>com.njcn</groupId>
@@ -71,5 +73,4 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project>
</project>

View File

@@ -7,7 +7,7 @@ import java.lang.annotation.*;
* *
* 其中 {@link #dictType()} 和 {@link #functionName()} 二选一 * 其中 {@link #dictType()} 和 {@link #functionName()} 二选一
* *
* @author HUIHUI * @author hongawen
*/ */
@Target({ElementType.FIELD}) @Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)

View File

@@ -13,7 +13,7 @@ import lombok.extern.slf4j.Slf4j;
/** /**
* Excel 数据地区转换器 * Excel 数据地区转换器
* *
* @author HUIHUI * @author hongawen
*/ */
@Slf4j @Slf4j
public class AreaConvert implements Converter<Object> { public class AreaConvert implements Converter<Object> {

View File

@@ -7,7 +7,7 @@ import java.util.List;
* *
* 为什么不直接解析字典还搞个接口?考虑到有的下拉数据不是从字典中获取的所有需要做一个兼容 * 为什么不直接解析字典还搞个接口?考虑到有的下拉数据不是从字典中获取的所有需要做一个兼容
* @author HUIHUI * @author hongawen
*/ */
public interface ExcelColumnSelectFunction { public interface ExcelColumnSelectFunction {

View File

@@ -34,7 +34,7 @@ import static com.njcn.msgpush.framework.common.util.collection.CollectionUtils.
/** /**
* 基于固定 sheet 实现下拉框 * 基于固定 sheet 实现下拉框
* *
* @author HUIHUI * @author hongawen
*/ */
@Slf4j @Slf4j
public class SelectSheetWriteHandler implements SheetWriteHandler { public class SelectSheetWriteHandler implements SheetWriteHandler {

View File

@@ -1,61 +0,0 @@
package com.njcn.msgpush.framework.dict.core.util;
import com.njcn.msgpush.framework.common.biz.system.dict.DictDataCommonApi;
import com.njcn.msgpush.framework.dict.core.DictFrameworkUtils;
import com.njcn.msgpush.framework.test.core.ut.BaseMockitoUnitTest;
import com.njcn.msgpush.framework.common.biz.system.dict.dto.DictDataRespDTO;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import java.util.List;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
import static com.njcn.msgpush.framework.test.core.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
/**
* {@link DictFrameworkUtils} 的单元测试
*/
public class DictFrameworkUtilsTest extends BaseMockitoUnitTest {
@Mock
private DictDataCommonApi dictDataApi;
@BeforeEach
public void setUp() {
DictFrameworkUtils.init(dictDataApi);
DictFrameworkUtils.clearCache();
}
@Test
public void testParseDictDataLabel() {
// mock 数据
List<DictDataRespDTO> dictDatas = List.of(
randomPojo(DictDataRespDTO.class, o -> o.setDictType("animal").setValue("cat").setLabel("")),
randomPojo(DictDataRespDTO.class, o -> o.setDictType("animal").setValue("dog").setLabel(""))
);
// mock 方法
when(dictDataApi.getDictDataList(eq("animal"))).thenReturn(success(dictDatas));
// 断言返回值
assertEquals("", DictFrameworkUtils.parseDictDataLabel("animal", "dog"));
}
@Test
public void testParseDictDataValue() {
// mock 数据
List<DictDataRespDTO> dictDatas = List.of(
randomPojo(DictDataRespDTO.class, o -> o.setDictType("animal").setValue("cat").setLabel("")),
randomPojo(DictDataRespDTO.class, o -> o.setDictType("animal").setValue("dog").setLabel(""))
);
// mock 方法
when(dictDataApi.getDictDataList(eq("animal"))).thenReturn(success(dictDatas));
// 断言返回值
assertEquals("dog", DictFrameworkUtils.parseDictDataValue("animal", ""));
}
}

View File

@@ -2,12 +2,13 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.njcn</groupId> <groupId>com.njcn</groupId>
<artifactId>msgpush-framework</artifactId> <artifactId>msgpush-framework</artifactId>
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>msgpush-spring-boot-starter-mq</artifactId> <artifactId>msgpush-spring-boot-starter-mq</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@@ -40,4 +41,4 @@
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -1,4 +0,0 @@
/**
* 消息队列,支持 Redis、RocketMQ、RabbitMQ、Kafka 四种
*/
package com.njcn.msgpush.framework.mq;

View File

@@ -69,7 +69,7 @@ public abstract class AbstractRedisStreamMessageListener<T extends AbstractRedis
this.onMessage(messageObj); this.onMessage(messageObj);
// ack 消息消费完成 // ack 消息消费完成
redisMQTemplate.getRedisTemplate().opsForStream().acknowledge(group, message); redisMQTemplate.getRedisTemplate().opsForStream().acknowledge(group, message);
// TODO 芋艿:需要额外考虑以下几个点: // TODO 需要额外考虑以下几个点:
// 1. 处理异常的情况 // 1. 处理异常的情况
// 2. 发送日志;以及事务的结合 // 2. 发送日志;以及事务的结合
// 3. 消费日志;以及通用的幂等性 // 3. 消费日志;以及通用的幂等性

View File

@@ -2,12 +2,13 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.njcn</groupId> <groupId>com.njcn</groupId>
<artifactId>msgpush-framework</artifactId> <artifactId>msgpush-framework</artifactId>
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>msgpush-spring-boot-starter-mybatis</artifactId> <artifactId>msgpush-spring-boot-starter-mybatis</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@@ -102,4 +103,4 @@
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -1,7 +1,7 @@
package com.njcn.msgpush.framework.datasource.config; package com.njcn.msgpush.framework.datasource.config;
import com.njcn.msgpush.framework.datasource.core.filter.DruidAdRemoveFilter;
import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties; import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties;
import com.njcn.msgpush.framework.datasource.core.filter.DruidAdRemoveFilter;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;

View File

@@ -1,12 +1,12 @@
package com.njcn.msgpush.framework.datasource.core.filter; package com.njcn.msgpush.framework.datasource.core.filter;
import com.alibaba.druid.util.Utils; import com.alibaba.druid.util.Utils;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain; import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException; import java.io.IOException;
/** /**

View File

@@ -1,10 +1,10 @@
package com.njcn.msgpush.framework.mybatis.config; package com.njcn.msgpush.framework.mybatis.config;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.njcn.msgpush.framework.common.util.collection.SetUtils;
import com.njcn.msgpush.framework.mybatis.core.util.JdbcUtils;
import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.njcn.msgpush.framework.common.util.collection.SetUtils;
import com.njcn.msgpush.framework.mybatis.core.util.JdbcUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.boot.env.EnvironmentPostProcessor;
@@ -41,7 +41,7 @@ public class IdTypeEnvironmentPostProcessor implements EnvironmentPostProcessor
} }
// 设置 Quartz JobStore 对应的 Driver // 设置 Quartz JobStore 对应的 Driver
// TODO 芋艿:暂时没有找到特别合适的地方,先放在这里 // TODO 暂时没有找到特别合适的地方,先放在这里
setJobStoreDriverIfPresent(environment, dbType); setJobStoreDriverIfPresent(environment, dbType);
// 如果非 NONE则不进行处理 // 如果非 NONE则不进行处理

View File

@@ -2,11 +2,8 @@ package com.njcn.msgpush.framework.mybatis.config;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.njcn.msgpush.framework.common.util.json.JsonUtils;
import com.njcn.msgpush.framework.mybatis.core.handler.DefaultDBFieldHandler;
import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator; import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
@@ -16,6 +13,8 @@ import com.baomidou.mybatisplus.extension.parser.cache.JdkSerialCaffeineJsqlPars
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.njcn.msgpush.framework.common.util.json.JsonUtils;
import com.njcn.msgpush.framework.mybatis.core.handler.DefaultDBFieldHandler;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;

View File

@@ -32,6 +32,10 @@ public class DefaultDBFieldHandler implements MetaObjectHandler {
if (Objects.isNull(baseDO.getUpdateTime())) { if (Objects.isNull(baseDO.getUpdateTime())) {
baseDO.setUpdateTime(current); baseDO.setUpdateTime(current);
} }
// deleted 为空时,统一按未删除处理,避免依赖数据库默认值
if (Objects.isNull(baseDO.getDeleted())) {
baseDO.setDeleted(Boolean.FALSE);
}
Long userId = SecurityFrameworkUtils.getLoginUserId(); Long userId = SecurityFrameworkUtils.getLoginUserId();
// 当前登录用户不为空,创建人为空,则当前登录用户为创建人 // 当前登录用户不为空,创建人为空,则当前登录用户为创建人

View File

@@ -1,12 +1,6 @@
package com.njcn.msgpush.framework.mybatis.core.mapper; package com.njcn.msgpush.framework.mybatis.core.mapper;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.pojo.SortablePageParam;
import com.njcn.msgpush.framework.common.pojo.SortingField;
import com.njcn.msgpush.framework.mybatis.core.util.JdbcUtils;
import com.njcn.msgpush.framework.mybatis.core.util.MyBatisUtils;
import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -18,6 +12,12 @@ import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.github.yulichang.base.MPJBaseMapper; import com.github.yulichang.base.MPJBaseMapper;
import com.github.yulichang.interfaces.MPJBaseJoin; import com.github.yulichang.interfaces.MPJBaseJoin;
import com.github.yulichang.wrapper.MPJLambdaWrapper; import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.pojo.SortablePageParam;
import com.njcn.msgpush.framework.common.pojo.SortingField;
import com.njcn.msgpush.framework.mybatis.core.util.JdbcUtils;
import com.njcn.msgpush.framework.mybatis.core.util.MyBatisUtils;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.Collection; import java.util.Collection;

View File

@@ -2,9 +2,9 @@ package com.njcn.msgpush.framework.mybatis.core.query;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.njcn.msgpush.framework.common.util.collection.ArrayUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.njcn.msgpush.framework.common.util.collection.ArrayUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.util.Collection; import java.util.Collection;

View File

@@ -2,9 +2,9 @@ package com.njcn.msgpush.framework.mybatis.core.query;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.njcn.msgpush.framework.common.util.collection.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.github.yulichang.wrapper.MPJLambdaWrapper; import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.njcn.msgpush.framework.common.util.collection.ArrayUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.util.Collection; import java.util.Collection;
@@ -15,6 +15,7 @@ import java.util.function.Consumer;
* <p> * <p>
* 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。 * 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。
* 2. SFunction<S, ?> column + <S> 泛型:支持任意类字段(主表、子表、三表),推荐写法, 让编译器自动推断 S 类型 * 2. SFunction<S, ?> column + <S> 泛型:支持任意类字段(主表、子表、三表),推荐写法, 让编译器自动推断 S 类型
*
* @param <T> 数据类型 * @param <T> 数据类型
*/ */
public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> { public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> {
@@ -122,6 +123,12 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> {
return this; return this;
} }
@Override
public <X> MPJLambdaWrapperX<T> orderByAsc(SFunction<X, ?> column) {
super.orderByAsc(true, column);
return this;
}
@Override @Override
public MPJLambdaWrapperX<T> last(String lastSql) { public MPJLambdaWrapperX<T> last(String lastSql) {
super.last(lastSql); super.last(lastSql);

View File

@@ -1,10 +1,10 @@
package com.njcn.msgpush.framework.mybatis.core.query; package com.njcn.msgpush.framework.mybatis.core.query;
import com.njcn.msgpush.framework.mybatis.core.util.JdbcUtils;
import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils; import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.njcn.msgpush.framework.mybatis.core.util.JdbcUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.util.Collection; import java.util.Collection;
@@ -142,7 +142,7 @@ public class QueryWrapperX<T> extends QueryWrapper<T> {
/** /**
* 设置只返回最后一条 * 设置只返回最后一条
* *
* TODO 芋艿:不是完美解,需要在思考下。如果使用多数据源,并且数据源是多种类型时,可能会存在问题:实现之返回一条的语法不同 * TODO 不是完美解,需要在思考下。如果使用多数据源,并且数据源是多种类型时,可能会存在问题:实现之返回一条的语法不同
* *
* @return this * @return this
*/ */

View File

@@ -1,10 +1,10 @@
package com.njcn.msgpush.framework.mybatis.core.util; package com.njcn.msgpush.framework.mybatis.core.util;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.mybatisplus.annotation.DbType;
import com.njcn.msgpush.framework.common.util.object.ObjectUtils; import com.njcn.msgpush.framework.common.util.object.ObjectUtils;
import com.njcn.msgpush.framework.common.util.spring.SpringUtils; import com.njcn.msgpush.framework.common.util.spring.SpringUtils;
import com.njcn.msgpush.framework.mybatis.core.enums.DbTypeEnum; import com.njcn.msgpush.framework.mybatis.core.enums.DbTypeEnum;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.mybatisplus.annotation.DbType;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import javax.sql.DataSource; import javax.sql.DataSource;

View File

@@ -4,9 +4,6 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.func.Func1; import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.lang.func.LambdaUtil; import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import com.njcn.msgpush.framework.common.pojo.SortingField;
import com.njcn.msgpush.framework.mybatis.core.enums.DbTypeEnum;
import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -16,6 +13,9 @@ import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import com.njcn.msgpush.framework.common.pojo.SortingField;
import com.njcn.msgpush.framework.mybatis.core.enums.DbTypeEnum;
import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.schema.Table;

View File

@@ -1,7 +1,7 @@
package com.njcn.msgpush.framework.translate.config; package com.njcn.msgpush.framework.translate.config;
import com.njcn.msgpush.framework.translate.core.TranslateUtils;
import com.fhs.trans.service.impl.TransService; import com.fhs.trans.service.impl.TransService;
import com.njcn.msgpush.framework.translate.core.TranslateUtils;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;

View File

@@ -2,12 +2,13 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.njcn</groupId> <groupId>com.njcn</groupId>
<artifactId>msgpush-framework</artifactId> <artifactId>msgpush-framework</artifactId>
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>msgpush-spring-boot-starter-protection</artifactId> <artifactId>msgpush-spring-boot-starter-protection</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@@ -43,5 +44,4 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project>
</project>

View File

@@ -1,13 +1,13 @@
package com.njcn.msgpush.framework.idempotent.config; package com.njcn.msgpush.framework.idempotent.config;
import com.njcn.msgpush.framework.idempotent.core.aop.IdempotentAspect; import com.njcn.msgpush.framework.idempotent.core.aop.IdempotentAspect;
import com.njcn.msgpush.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
import com.njcn.msgpush.framework.idempotent.core.keyresolver.impl.DefaultIdempotentKeyResolver; import com.njcn.msgpush.framework.idempotent.core.keyresolver.impl.DefaultIdempotentKeyResolver;
import com.njcn.msgpush.framework.idempotent.core.keyresolver.impl.ExpressionIdempotentKeyResolver; import com.njcn.msgpush.framework.idempotent.core.keyresolver.impl.ExpressionIdempotentKeyResolver;
import com.njcn.msgpush.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
import com.njcn.msgpush.framework.idempotent.core.keyresolver.impl.UserIdempotentKeyResolver; import com.njcn.msgpush.framework.idempotent.core.keyresolver.impl.UserIdempotentKeyResolver;
import com.njcn.msgpush.framework.idempotent.core.redis.IdempotentRedisDAO; import com.njcn.msgpush.framework.idempotent.core.redis.IdempotentRedisDAO;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import com.njcn.msgpush.framework.redis.config.MsgpushRedisAutoConfiguration; import com.njcn.msgpush.framework.redis.config.MsgpushRedisAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;

View File

@@ -1,7 +1,7 @@
package com.njcn.msgpush.framework.idempotent.core.annotation; package com.njcn.msgpush.framework.idempotent.core.annotation;
import com.njcn.msgpush.framework.idempotent.core.keyresolver.impl.DefaultIdempotentKeyResolver;
import com.njcn.msgpush.framework.idempotent.core.keyresolver.IdempotentKeyResolver; import com.njcn.msgpush.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
import com.njcn.msgpush.framework.idempotent.core.keyresolver.impl.DefaultIdempotentKeyResolver;
import com.njcn.msgpush.framework.idempotent.core.keyresolver.impl.ExpressionIdempotentKeyResolver; import com.njcn.msgpush.framework.idempotent.core.keyresolver.impl.ExpressionIdempotentKeyResolver;
import com.njcn.msgpush.framework.idempotent.core.keyresolver.impl.UserIdempotentKeyResolver; import com.njcn.msgpush.framework.idempotent.core.keyresolver.impl.UserIdempotentKeyResolver;

View File

@@ -1,7 +1,7 @@
package com.njcn.msgpush.framework.lock4j.config; package com.njcn.msgpush.framework.lock4j.config;
import com.njcn.msgpush.framework.lock4j.core.DefaultLockFailureStrategy;
import com.baomidou.lock.spring.boot.autoconfigure.LockAutoConfiguration; import com.baomidou.lock.spring.boot.autoconfigure.LockAutoConfiguration;
import com.njcn.msgpush.framework.lock4j.core.DefaultLockFailureStrategy;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;

View File

@@ -1,8 +1,8 @@
package com.njcn.msgpush.framework.lock4j.core; package com.njcn.msgpush.framework.lock4j.core;
import com.baomidou.lock.LockFailureStrategy;
import com.njcn.msgpush.framework.common.exception.ServiceException; import com.njcn.msgpush.framework.common.exception.ServiceException;
import com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants; import com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.baomidou.lock.LockFailureStrategy;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method; import java.lang.reflect.Method;

View File

@@ -1,7 +1,10 @@
package com.njcn.msgpush.framework.ratelimiter.core.redis; package com.njcn.msgpush.framework.ratelimiter.core.redis;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.redisson.api.*; import org.redisson.api.RRateLimiter;
import org.redisson.api.RateLimiterConfig;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import java.time.Duration; import java.time.Duration;
import java.util.Objects; import java.util.Objects;

View File

@@ -1,74 +0,0 @@
package com.njcn.msgpush.framework.signature.core;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.njcn.msgpush.framework.signature.core.annotation.ApiSignature;
import com.njcn.msgpush.framework.signature.core.aop.ApiSignatureAspect;
import com.njcn.msgpush.framework.signature.core.redis.ApiSignatureRedisDAO;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.concurrent.TimeUnit;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
/**
* {@link ApiSignatureTest} 的单元测试
*/
@ExtendWith(MockitoExtension.class)
public class ApiSignatureTest {
@InjectMocks
private ApiSignatureAspect apiSignatureAspect;
@Mock
private ApiSignatureRedisDAO signatureRedisDAO;
@Test
public void testSignatureGet() throws IOException {
// 搞一个签名
Long timestamp = System.currentTimeMillis();
String nonce = IdUtil.randomUUID();
String appId = "xxxxxx";
String appSecret = "yyyyyy";
String signString = "k1=v1&v1=k1testappId=xxxxxx&nonce=" + nonce + "&timestamp=" + timestamp + "yyyyyy";
String sign = DigestUtil.sha256Hex(signString);
// 准备参数
ApiSignature apiSignature = mock(ApiSignature.class);
when(apiSignature.appId()).thenReturn("appId");
when(apiSignature.timestamp()).thenReturn("timestamp");
when(apiSignature.nonce()).thenReturn("nonce");
when(apiSignature.sign()).thenReturn("sign");
when(apiSignature.timeout()).thenReturn(60);
when(apiSignature.timeUnit()).thenReturn(TimeUnit.SECONDS);
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getHeader(eq("appId"))).thenReturn(appId);
when(request.getHeader(eq("timestamp"))).thenReturn(String.valueOf(timestamp));
when(request.getHeader(eq("nonce"))).thenReturn(nonce);
when(request.getHeader(eq("sign"))).thenReturn(sign);
when(request.getParameterMap()).thenReturn(MapUtil.<String, String[]>builder()
.put("v1", new String[]{"k1"}).put("k1", new String[]{"v1"}).build());
when(request.getContentType()).thenReturn("application/json");
when(request.getReader()).thenReturn(new BufferedReader(new StringReader("test")));
// mock 方法
when(signatureRedisDAO.getAppSecret(eq(appId))).thenReturn(appSecret);
when(signatureRedisDAO.setNonce(eq(appId), eq(nonce), eq(120), eq(TimeUnit.SECONDS))).thenReturn(true);
// 调用
boolean result = apiSignatureAspect.verifySignature(apiSignature, request);
// 断言结果
assertTrue(result);
}
}

View File

@@ -2,12 +2,12 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.njcn</groupId> <groupId>com.njcn</groupId>
<artifactId>msgpush-framework</artifactId> <artifactId>msgpush-framework</artifactId>
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>msgpush-spring-boot-starter-redis</artifactId> <artifactId>msgpush-spring-boot-starter-redis</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@@ -37,5 +37,4 @@
<artifactId>jackson-datatype-jsr310</artifactId> <artifactId>jackson-datatype-jsr310</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
</project>
</project>

View File

@@ -75,7 +75,7 @@ public class MsgpushCacheAutoConfiguration {
RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory()); RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory, RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory,
BatchStrategies.scan(msgpushCacheProperties.getRedisScanBatchSize())); BatchStrategies.scan(msgpushCacheProperties.getRedisScanBatchSize()));
// 创建 RedisCacheManager 对象 // 创建 TenantRedisCacheManager 对象
return new TimeoutRedisCacheManager(cacheWriter, redisCacheConfiguration); return new TimeoutRedisCacheManager(cacheWriter, redisCacheConfiguration);
} }

View File

@@ -1,4 +0,0 @@
/**
* 采用 Spring Data Redis 操作 Redis底层使用 Redisson 作为客户端
*/
package com.njcn.msgpush.framework.redis;

View File

@@ -2,12 +2,13 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent> <parent>
<artifactId>msgpush-framework</artifactId>
<groupId>com.njcn</groupId> <groupId>com.njcn</groupId>
<artifactId>msgpush-framework</artifactId>
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>msgpush-spring-boot-starter-rpc</artifactId> <artifactId>msgpush-spring-boot-starter-rpc</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@@ -16,6 +17,10 @@
OpenFeign提供 RESTful API 的调用 OpenFeign提供 RESTful API 的调用
</description> </description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies> <dependencies>
<dependency> <dependency>
@@ -43,4 +48,5 @@
<artifactId>jakarta.validation-api</artifactId> <artifactId>jakarta.validation-api</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
</project>
</project>

View File

@@ -1,6 +0,0 @@
/**
* OpenFeign提供 RESTful API 的调用
*
* @author hongawen
*/
package com.njcn.msgpush.framework.rpc;

View File

@@ -2,12 +2,13 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.njcn</groupId> <groupId>com.njcn</groupId>
<artifactId>msgpush-framework</artifactId> <artifactId>msgpush-framework</artifactId>
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>msgpush-spring-boot-starter-security</artifactId> <artifactId>msgpush-spring-boot-starter-security</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@@ -68,5 +69,4 @@
</dependency> </dependency>
</dependencies> </dependencies>
</project>
</project>

View File

@@ -1,8 +1,8 @@
package com.njcn.msgpush.framework.operatelog.config; package com.njcn.msgpush.framework.operatelog.config;
import com.njcn.msgpush.framework.operatelog.core.service.LogRecordServiceImpl;
import com.mzt.logapi.service.ILogRecordService; import com.mzt.logapi.service.ILogRecordService;
import com.mzt.logapi.starter.annotation.EnableLogRecord; import com.mzt.logapi.starter.annotation.EnableLogRecord;
import com.njcn.msgpush.framework.operatelog.core.service.LogRecordServiceImpl;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@@ -11,9 +11,9 @@ import org.springframework.context.annotation.Primary;
/** /**
* 操作日志配置类 * 操作日志配置类
* *
* @author HUIHUI * @author hongawen
*/ */
@EnableLogRecord(tenant = "admin") // 貌似用不上 tenant 这玩意给个空好啦 @EnableLogRecord(tenant = "") // 貌似用不上 tenant 这玩意给个空好啦
@AutoConfiguration @AutoConfiguration
@Slf4j @Slf4j
public class MsgpushOperateLogConfiguration { public class MsgpushOperateLogConfiguration {

View File

@@ -1,17 +1,16 @@
package com.njcn.msgpush.framework.operatelog.core.service; package com.njcn.msgpush.framework.operatelog.core.service;
import com.mzt.logapi.beans.LogRecord;
import com.mzt.logapi.service.ILogRecordService;
import com.njcn.msgpush.framework.common.biz.system.logger.OperateLogCommonApi; import com.njcn.msgpush.framework.common.biz.system.logger.OperateLogCommonApi;
import com.njcn.msgpush.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO;
import com.njcn.msgpush.framework.common.util.monitor.TracerUtils; import com.njcn.msgpush.framework.common.util.monitor.TracerUtils;
import com.njcn.msgpush.framework.common.util.servlet.ServletUtils; import com.njcn.msgpush.framework.common.util.servlet.ServletUtils;
import com.njcn.msgpush.framework.security.core.LoginUser; import com.njcn.msgpush.framework.security.core.LoginUser;
import com.njcn.msgpush.framework.security.core.util.SecurityFrameworkUtils; import com.njcn.msgpush.framework.security.core.util.SecurityFrameworkUtils;
import com.njcn.msgpush.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO;
import com.mzt.logapi.beans.LogRecord;
import com.mzt.logapi.service.ILogRecordService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import java.util.List; import java.util.List;
@@ -20,7 +19,7 @@ import java.util.List;
* *
* 基于 {@link OperateLogCommonApi} 实现,记录操作日志 * 基于 {@link OperateLogCommonApi} 实现,记录操作日志
* *
* @author HUIHUI * @author hongawen
*/ */
@Slf4j @Slf4j
public class LogRecordServiceImpl implements ILogRecordService { public class LogRecordServiceImpl implements ILogRecordService {

View File

@@ -2,6 +2,6 @@
* 基于 mzt-log 框架 * 基于 mzt-log 框架
* 实现操作日志功能 * 实现操作日志功能
* *
* @author HUIHUI * @author hongawen
*/ */
package com.njcn.msgpush.framework.operatelog; package com.njcn.msgpush.framework.operatelog;

View File

@@ -6,7 +6,6 @@ import org.springframework.core.Ordered;
import org.springframework.security.config.Customizer; import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
import org.springframework.stereotype.Component;
/** /**
* 自定义的 URL 的安全配置 * 自定义的 URL 的安全配置

View File

@@ -1,5 +1,6 @@
package com.njcn.msgpush.framework.security.config; package com.njcn.msgpush.framework.security.config;
import com.njcn.msgpush.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
import com.njcn.msgpush.framework.common.biz.system.permission.PermissionCommonApi; import com.njcn.msgpush.framework.common.biz.system.permission.PermissionCommonApi;
import com.njcn.msgpush.framework.security.core.context.TransmittableThreadLocalSecurityContextHolderStrategy; import com.njcn.msgpush.framework.security.core.context.TransmittableThreadLocalSecurityContextHolderStrategy;
import com.njcn.msgpush.framework.security.core.filter.TokenAuthenticationFilter; import com.njcn.msgpush.framework.security.core.filter.TokenAuthenticationFilter;
@@ -8,7 +9,6 @@ import com.njcn.msgpush.framework.security.core.handler.AuthenticationEntryPoint
import com.njcn.msgpush.framework.security.core.service.SecurityFrameworkService; import com.njcn.msgpush.framework.security.core.service.SecurityFrameworkService;
import com.njcn.msgpush.framework.security.core.service.SecurityFrameworkServiceImpl; import com.njcn.msgpush.framework.security.core.service.SecurityFrameworkServiceImpl;
import com.njcn.msgpush.framework.web.core.handler.GlobalExceptionHandler; import com.njcn.msgpush.framework.web.core.handler.GlobalExceptionHandler;
import com.njcn.msgpush.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean; import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -38,7 +38,7 @@ public class MsgpushSecurityAutoConfiguration {
private SecurityProperties securityProperties; private SecurityProperties securityProperties;
/** /**
* 身份认证失败处理类 Bean * 认证失败处理类 Bean
*/ */
@Bean @Bean
public AuthenticationEntryPoint authenticationEntryPoint() { public AuthenticationEntryPoint authenticationEntryPoint() {

View File

@@ -1,8 +1,8 @@
package com.njcn.msgpush.framework.security.config; package com.njcn.msgpush.framework.security.config;
import com.njcn.msgpush.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
import com.njcn.msgpush.framework.common.biz.system.permission.PermissionCommonApi; import com.njcn.msgpush.framework.common.biz.system.permission.PermissionCommonApi;
import com.njcn.msgpush.framework.security.core.rpc.LoginUserRequestInterceptor; import com.njcn.msgpush.framework.security.core.rpc.LoginUserRequestInterceptor;
import com.njcn.msgpush.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;

View File

@@ -1,10 +1,10 @@
package com.njcn.msgpush.framework.security.config; package com.njcn.msgpush.framework.security.config;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import com.njcn.msgpush.framework.security.core.filter.TokenAuthenticationFilter;
import com.njcn.msgpush.framework.web.config.WebProperties;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.njcn.msgpush.framework.security.core.filter.TokenAuthenticationFilter;
import com.njcn.msgpush.framework.web.config.WebProperties;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.PermitAll;
import jakarta.servlet.DispatcherType; import jakarta.servlet.DispatcherType;
@@ -114,6 +114,8 @@ public class MsgpushWebSecurityConfigurerAdapter {
.cors(Customizer.withDefaults()) .cors(Customizer.withDefaults())
// CSRF 禁用,因为不使用 Session // CSRF 禁用,因为不使用 Session
.csrf(AbstractHttpConfigurer::disable) .csrf(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
// 基于 token 机制,所以不需要 Session // 基于 token 机制,所以不需要 Session
.sessionManagement(c -> c.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .sessionManagement(c -> c.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.headers(c -> c.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) .headers(c -> c.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))

View File

@@ -1,11 +1,11 @@
package com.njcn.msgpush.framework.security.config; package com.njcn.msgpush.framework.security.config;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;

View File

@@ -1,7 +1,6 @@
package com.njcn.msgpush.framework.security.core; package com.njcn.msgpush.framework.security.core;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import com.njcn.msgpush.framework.common.enums.UserTypeEnum;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data; import lombok.Data;
@@ -10,47 +9,18 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/**
* 登录用户信息
*
* @author hongawen
*/
@Data @Data
public class LoginUser { public class LoginUser {
public static final String INFO_KEY_NICKNAME = "nickname"; public static final String INFO_KEY_NICKNAME = "nickname";
public static final String INFO_KEY_DEPT_ID = "deptId"; public static final String INFO_KEY_DEPT_ID = "deptId";
/**
* 用户编号
*/
private Long id; private Long id;
/**
* 用户类型
*
* 关联 {@link UserTypeEnum}
*/
private Integer userType; private Integer userType;
/**
* 额外的用户信息
*/
private Map<String, String> info; private Map<String, String> info;
/**
* 授权范围
*/
private List<String> scopes; private List<String> scopes;
/**
* 过期时间
*/
private LocalDateTime expiresTime; private LocalDateTime expiresTime;
// ========== 上下文 ==========
/**
* 上下文字段,不进行持久化
*
* 1. 用于基于 LoginUser 维度的临时缓存
*/
@JsonIgnore @JsonIgnore
private Map<String, Object> context; private Map<String, Object> context;

View File

@@ -15,7 +15,7 @@ import org.springframework.util.Assert;
public class TransmittableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy { public class TransmittableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
/** /**
* 使用 TransmittableThreadLocal 实现线程之间上下文的传递。 * 使用 TransmittableThreadLocal 作为上下文
*/ */
private static final ThreadLocal<SecurityContext> CONTEXT_HOLDER = new TransmittableThreadLocal<>(); private static final ThreadLocal<SecurityContext> CONTEXT_HOLDER = new TransmittableThreadLocal<>();

View File

@@ -2,7 +2,10 @@ package com.njcn.msgpush.framework.security.core.filter;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.njcn.msgpush.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
import com.njcn.msgpush.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
import com.njcn.msgpush.framework.common.exception.ServiceException; import com.njcn.msgpush.framework.common.exception.ServiceException;
import com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.njcn.msgpush.framework.common.pojo.CommonResult; import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.util.json.JsonUtils; import com.njcn.msgpush.framework.common.util.json.JsonUtils;
import com.njcn.msgpush.framework.common.util.servlet.ServletUtils; import com.njcn.msgpush.framework.common.util.servlet.ServletUtils;
@@ -11,8 +14,6 @@ import com.njcn.msgpush.framework.security.core.LoginUser;
import com.njcn.msgpush.framework.security.core.util.SecurityFrameworkUtils; import com.njcn.msgpush.framework.security.core.util.SecurityFrameworkUtils;
import com.njcn.msgpush.framework.web.core.handler.GlobalExceptionHandler; import com.njcn.msgpush.framework.web.core.handler.GlobalExceptionHandler;
import com.njcn.msgpush.framework.web.core.util.WebFrameworkUtils; import com.njcn.msgpush.framework.web.core.util.WebFrameworkUtils;
import com.njcn.msgpush.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
import com.njcn.msgpush.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
import jakarta.servlet.FilterChain; import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
@@ -26,42 +27,29 @@ import java.io.IOException;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
/**
* Token 过滤器,验证 token 的有效性
* 验证通过后,获得 {@link LoginUser} 信息,并加入到 Spring Security 上下文
*
* @author hongawen
*/
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j @Slf4j
public class TokenAuthenticationFilter extends OncePerRequestFilter { public class TokenAuthenticationFilter extends OncePerRequestFilter {
private final SecurityProperties securityProperties; private final SecurityProperties securityProperties;
private final GlobalExceptionHandler globalExceptionHandler; private final GlobalExceptionHandler globalExceptionHandler;
private final OAuth2TokenCommonApi oauth2TokenApi; private final OAuth2TokenCommonApi oauth2TokenApi;
@Override @Override
@SuppressWarnings("NullableProblems") @SuppressWarnings("NullableProblems")
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException { throws ServletException, IOException {
// 情况一,基于 header[login-user] 获得用户,例如说来自 Gateway 或者其它服务透传
LoginUser loginUser = buildLoginUserByHeader(request); LoginUser loginUser = buildLoginUserByHeader(request);
// 情况二,基于 Token 获得用户
// 注意,这里主要满足直接使用 Nginx 直接转发到 Spring Cloud 服务的场景。
if (loginUser == null) { if (loginUser == null) {
String token = SecurityFrameworkUtils.obtainAuthorization(request, String token = SecurityFrameworkUtils.obtainAuthorization(request,
securityProperties.getTokenHeader(), securityProperties.getTokenParameter()); securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
if (StrUtil.isNotEmpty(token)) { if (StrUtil.isNotEmpty(token)) {
Integer userType = WebFrameworkUtils.getLoginUserType(request); Integer userType = WebFrameworkUtils.getLoginUserType(request);
try { try {
// 1.1 基于 token 构建登录用户
loginUser = buildLoginUserByToken(token, userType); loginUser = buildLoginUserByToken(token, userType);
// 1.2 模拟 Login 功能,方便日常开发调试
if (loginUser == null) { if (loginUser == null) {
loginUser = mockLoginUser(request, token, userType); loginUser = mockLoginUser(token, userType);
} }
} catch (Throwable ex) { } catch (Throwable ex) {
CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex); CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex);
@@ -71,57 +59,41 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
} }
} }
// 设置当前用户
if (loginUser != null) { if (loginUser != null) {
SecurityFrameworkUtils.setLoginUser(loginUser, request); SecurityFrameworkUtils.setLoginUser(loginUser, request);
} }
// 继续过滤链
chain.doFilter(request, response); chain.doFilter(request, response);
} }
private LoginUser buildLoginUserByToken(String token, Integer userType) { private LoginUser buildLoginUserByToken(String token, Integer userType) {
try { try {
// 校验访问令牌
OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token).getCheckedData(); OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token).getCheckedData();
if (accessToken == null) { if (accessToken == null) {
return null; return null;
} }
// 用户类型不匹配,无权限 if (userType != null && ObjectUtil.notEqual(accessToken.getUserType(), userType)) {
// 注意:只有 /admin-api/* 和 /app-api/* 有 userType才需要比对用户类型
// 类似 WebSocket 的 /ws/* 连接地址,是不需要比对用户类型的
if (userType != null
&& ObjectUtil.notEqual(accessToken.getUserType(), userType)) {
throw new AccessDeniedException("错误的用户类型"); throw new AccessDeniedException("错误的用户类型");
} }
// 构建登录用户 return new LoginUser().setId(accessToken.getUserId())
return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType()) .setUserType(accessToken.getUserType())
.setInfo(accessToken.getUserInfo()) // 额外的用户信息 .setInfo(accessToken.getUserInfo())
.setScopes(accessToken.getScopes())
.setExpiresTime(accessToken.getExpiresTime()); .setExpiresTime(accessToken.getExpiresTime());
} catch (ServiceException serviceException) { } catch (ServiceException serviceException) {
// 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可 if (ObjectUtil.equal(serviceException.getCode(), GlobalErrorCodeConstants.UNAUTHORIZED.getCode())) {
return null; return null;
}
throw serviceException;
} }
} }
/** private LoginUser mockLoginUser(String token, Integer userType) {
* 模拟登录用户,方便日常开发调试
*
* 注意,在线上环境下,一定要关闭该功能!!!
*
* @param request 请求
* @param token 模拟的 token格式为 {@link SecurityProperties#getMockSecret()} + 用户编号
* @param userType 用户类型
* @return 模拟的 LoginUser
*/
private LoginUser mockLoginUser(HttpServletRequest request, String token, Integer userType) {
if (!securityProperties.getMockEnable()) { if (!securityProperties.getMockEnable()) {
return null; return null;
} }
// 必须以 mockSecret 开头
if (!token.startsWith(securityProperties.getMockSecret())) { if (!token.startsWith(securityProperties.getMockSecret())) {
return null; return null;
} }
// 构建模拟用户
Long userId = Long.valueOf(token.substring(securityProperties.getMockSecret().length())); Long userId = Long.valueOf(token.substring(securityProperties.getMockSecret().length()));
return new LoginUser().setId(userId).setUserType(userType); return new LoginUser().setId(userId).setUserType(userType);
} }
@@ -132,20 +104,15 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
return null; return null;
} }
try { try {
loginUserStr = URLDecoder.decode(loginUserStr, StandardCharsets.UTF_8); // 解码,解决中文乱码问题 loginUserStr = URLDecoder.decode(loginUserStr, StandardCharsets.UTF_8);
LoginUser loginUser = JsonUtils.parseObject(loginUserStr, LoginUser.class); LoginUser loginUser = JsonUtils.parseObject(loginUserStr, LoginUser.class);
// 用户类型不匹配,无权限
// 注意:只有 /admin-api/* 和 /app-api/* 有 userType才需要比对用户类型
// 类似 WebSocket 的 /ws/* 连接地址,是不需要比对用户类型的
Integer userType = WebFrameworkUtils.getLoginUserType(request); Integer userType = WebFrameworkUtils.getLoginUserType(request);
if (userType != null if (userType != null && loginUser != null && ObjectUtil.notEqual(loginUser.getUserType(), userType)) {
&& loginUser != null
&& ObjectUtil.notEqual(loginUser.getUserType(), userType)) {
throw new AccessDeniedException("错误的用户类型"); throw new AccessDeniedException("错误的用户类型");
} }
return loginUser; return loginUser;
} catch (Exception ex) { } catch (Exception ex) {
log.error("[buildLoginUserByHeader][解析 LoginUser({}) 发生异常]", loginUserStr, ex); ; log.error("[buildLoginUserByHeader][parse LoginUser({}) error]", loginUserStr, ex);
throw ex; throw ex;
} }
} }

View File

@@ -2,17 +2,17 @@ package com.njcn.msgpush.framework.security.core.handler;
import com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants; import com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.njcn.msgpush.framework.common.pojo.CommonResult; import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.security.core.util.SecurityFrameworkUtils;
import com.njcn.msgpush.framework.common.util.servlet.ServletUtils; import com.njcn.msgpush.framework.common.util.servlet.ServletUtils;
import com.njcn.msgpush.framework.security.core.util.SecurityFrameworkUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.ExceptionTranslationFilter; import org.springframework.security.web.access.ExceptionTranslationFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import static com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants.FORBIDDEN; import static com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants.FORBIDDEN;

View File

@@ -3,15 +3,14 @@ package com.njcn.msgpush.framework.security.core.handler;
import com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants; import com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.njcn.msgpush.framework.common.pojo.CommonResult; import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.util.servlet.ServletUtils; import com.njcn.msgpush.framework.common.util.servlet.ServletUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.ExceptionTranslationFilter; import org.springframework.security.web.access.ExceptionTranslationFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import static com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED; import static com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED;
/** /**
@@ -19,7 +18,7 @@ import static com.njcn.msgpush.framework.common.exception.enums.GlobalErrorCodeC
* *
* 补充Spring Security 通过 {@link ExceptionTranslationFilter#sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException)} 方法,调用当前类 * 补充Spring Security 通过 {@link ExceptionTranslationFilter#sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException)} 方法,调用当前类
* *
* @author ruoyi * @author hongawen
*/ */
@Slf4j @Slf4j
@SuppressWarnings("JavadocReference") // 忽略文档引用报错 @SuppressWarnings("JavadocReference") // 忽略文档引用报错

View File

@@ -1,12 +1,12 @@
package com.njcn.msgpush.framework.security.core.service; package com.njcn.msgpush.framework.security.core.service;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.njcn.msgpush.framework.common.biz.system.permission.PermissionCommonApi; import com.njcn.msgpush.framework.common.biz.system.permission.PermissionCommonApi;
import com.njcn.msgpush.framework.common.core.KeyValue; import com.njcn.msgpush.framework.common.core.KeyValue;
import com.njcn.msgpush.framework.security.core.LoginUser; import com.njcn.msgpush.framework.security.core.LoginUser;
import com.njcn.msgpush.framework.security.core.util.SecurityFrameworkUtils; import com.njcn.msgpush.framework.security.core.util.SecurityFrameworkUtils;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.SneakyThrows; import lombok.SneakyThrows;
@@ -17,42 +17,27 @@ import java.util.List;
import static com.njcn.msgpush.framework.common.util.cache.CacheUtils.buildCache; import static com.njcn.msgpush.framework.common.util.cache.CacheUtils.buildCache;
import static com.njcn.msgpush.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static com.njcn.msgpush.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
/**
* 默认的 {@link SecurityFrameworkService} 实现类
*
* @author hongawen
*/
@AllArgsConstructor @AllArgsConstructor
public class SecurityFrameworkServiceImpl implements SecurityFrameworkService { public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
private final PermissionCommonApi permissionApi; private final PermissionCommonApi permissionApi;
/**
* 针对 {@link #hasAnyRoles(String...)} 的缓存
*/
private final LoadingCache<KeyValue<Long, List<String>>, Boolean> hasAnyRolesCache = buildCache( private final LoadingCache<KeyValue<Long, List<String>>, Boolean> hasAnyRolesCache = buildCache(
Duration.ofMinutes(1L), // 过期时间 1 分钟 Duration.ofMinutes(1L),
new CacheLoader<KeyValue<Long, List<String>>, Boolean>() { new CacheLoader<KeyValue<Long, List<String>>, Boolean>() {
@Override @Override
public Boolean load(KeyValue<Long, List<String>> key) { public Boolean load(KeyValue<Long, List<String>> key) {
return permissionApi.hasAnyRoles(key.getKey(), key.getValue().toArray(new String[0])).getCheckedData(); return permissionApi.hasAnyRoles(key.getKey(), key.getValue().toArray(new String[0])).getCheckedData();
} }
}); });
/**
* 针对 {@link #hasAnyPermissions(String...)} 的缓存
*/
private final LoadingCache<KeyValue<Long, List<String>>, Boolean> hasAnyPermissionsCache = buildCache( private final LoadingCache<KeyValue<Long, List<String>>, Boolean> hasAnyPermissionsCache = buildCache(
Duration.ofMinutes(1L), // 过期时间 1 分钟 Duration.ofMinutes(1L),
new CacheLoader<KeyValue<Long, List<String>>, Boolean>() { new CacheLoader<KeyValue<Long, List<String>>, Boolean>() {
@Override @Override
public Boolean load(KeyValue<Long, List<String>> key) { public Boolean load(KeyValue<Long, List<String>> key) {
return permissionApi.hasAnyPermissions(key.getKey(), key.getValue().toArray(new String[0])).getCheckedData(); return permissionApi.hasAnyPermissions(key.getKey(), key.getValue().toArray(new String[0])).getCheckedData();
} }
}); });
@Override @Override
@@ -63,7 +48,6 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
@Override @Override
@SneakyThrows @SneakyThrows
public boolean hasAnyPermissions(String... permissions) { public boolean hasAnyPermissions(String... permissions) {
// 权限校验
Long userId = getLoginUserId(); Long userId = getLoginUserId();
if (userId == null) { if (userId == null) {
return false; return false;
@@ -79,7 +63,6 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
@Override @Override
@SneakyThrows @SneakyThrows
public boolean hasAnyRoles(String... roles) { public boolean hasAnyRoles(String... roles) {
// 权限校验
Long userId = getLoginUserId(); Long userId = getLoginUserId();
if (userId == null) { if (userId == null) {
return false; return false;
@@ -94,7 +77,6 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
@Override @Override
public boolean hasAnyScopes(String... scope) { public boolean hasAnyScopes(String... scope) {
// 权限校验
LoginUser user = SecurityFrameworkUtils.getLoginUser(); LoginUser user = SecurityFrameworkUtils.getLoginUser();
if (user == null) { if (user == null) {
return false; return false;

View File

@@ -1,10 +1,10 @@
package com.njcn.msgpush.framework.security.core.util; package com.njcn.msgpush.framework.security.core.util;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.njcn.msgpush.framework.security.core.LoginUser; import com.njcn.msgpush.framework.security.core.LoginUser;
import com.njcn.msgpush.framework.web.core.util.WebFrameworkUtils; import com.njcn.msgpush.framework.web.core.util.WebFrameworkUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@@ -13,36 +13,16 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Collections; import java.util.Collections;
/**
* 安全服务工具类
*
* @author hongawen
*/
public class SecurityFrameworkUtils { public class SecurityFrameworkUtils {
/**
* HEADER 认证头 value 的前缀
*/
public static final String AUTHORIZATION_BEARER = "Bearer"; public static final String AUTHORIZATION_BEARER = "Bearer";
public static final String LOGIN_USER_HEADER = "login-user"; public static final String LOGIN_USER_HEADER = "login-user";
private SecurityFrameworkUtils() {} private SecurityFrameworkUtils() {}
/** public static String obtainAuthorization(HttpServletRequest request, String headerName, String parameterName) {
* 从请求中,获得认证 Token
*
* @param request 请求
* @param headerName 认证 Token 对应的 Header 名字
* @param parameterName 认证 Token 对应的 Parameter 名字
* @return 认证 Token
*/
public static String obtainAuthorization(HttpServletRequest request,
String headerName, String parameterName) {
// 1. 获得 Token。优先级Header > Parameter
String token = request.getHeader(headerName); String token = request.getHeader(headerName);
if (StrUtil.isEmpty(token)) { if (StrUtil.isEmpty(token)) {
token = request.getParameter(parameterName); token = request.getParameter(parameterName);
@@ -50,16 +30,10 @@ public class SecurityFrameworkUtils {
if (!StringUtils.hasText(token)) { if (!StringUtils.hasText(token)) {
return null; return null;
} }
// 2. 去除 Token 中带的 Bearer
int index = token.indexOf(AUTHORIZATION_BEARER + " "); int index = token.indexOf(AUTHORIZATION_BEARER + " ");
return index >= 0 ? token.substring(index + 7).trim() : token; return index >= 0 ? token.substring(index + 7).trim() : token;
} }
/**
* 获得当前认证信息
*
* @return 认证信息
*/
public static Authentication getAuthentication() { public static Authentication getAuthentication() {
SecurityContext context = SecurityContextHolder.getContext(); SecurityContext context = SecurityContextHolder.getContext();
if (context == null) { if (context == null) {
@@ -68,11 +42,6 @@ public class SecurityFrameworkUtils {
return context.getAuthentication(); return context.getAuthentication();
} }
/**
* 获取当前用户
*
* @return 当前用户
*/
@Nullable @Nullable
public static LoginUser getLoginUser() { public static LoginUser getLoginUser() {
Authentication authentication = getAuthentication(); Authentication authentication = getAuthentication();
@@ -82,52 +51,28 @@ public class SecurityFrameworkUtils {
return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null; return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
} }
/**
* 获得当前用户的编号,从上下文中
*
* @return 用户编号
*/
@Nullable @Nullable
public static Long getLoginUserId() { public static Long getLoginUserId() {
LoginUser loginUser = getLoginUser(); LoginUser loginUser = getLoginUser();
return loginUser != null ? loginUser.getId() : null; return loginUser != null ? loginUser.getId() : null;
} }
/**
* 获得当前用户的昵称,从上下文中
*
* @return 昵称
*/
@Nullable @Nullable
public static String getLoginUserNickname() { public static String getLoginUserNickname() {
LoginUser loginUser = getLoginUser(); LoginUser loginUser = getLoginUser();
return loginUser != null ? MapUtil.getStr(loginUser.getInfo(), LoginUser.INFO_KEY_NICKNAME) : null; return loginUser != null ? MapUtil.getStr(loginUser.getInfo(), LoginUser.INFO_KEY_NICKNAME) : null;
} }
/**
* 获得当前用户的部门编号,从上下文中
*
* @return 部门编号
*/
@Nullable @Nullable
public static Long getLoginUserDeptId() { public static Long getLoginUserDeptId() {
LoginUser loginUser = getLoginUser(); LoginUser loginUser = getLoginUser();
return loginUser != null ? MapUtil.getLong(loginUser.getInfo(), LoginUser.INFO_KEY_DEPT_ID) : null; return loginUser != null ? MapUtil.getLong(loginUser.getInfo(), LoginUser.INFO_KEY_DEPT_ID) : null;
} }
/**
* 设置当前用户
*
* @param loginUser 登录用户
* @param request 请求
*/
public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) { public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) {
// 创建 Authentication并设置到上下文
Authentication authentication = buildAuthentication(loginUser, request); Authentication authentication = buildAuthentication(loginUser, request);
SecurityContextHolder.getContext().setAuthentication(authentication); SecurityContextHolder.getContext().setAuthentication(authentication);
// 额外设置到 request 中,用于 ApiAccessLogFilter 可以获取到用户编号;
// 原因是Spring Security 的 Filter 在 ApiAccessLogFilter 后面,在它记录访问日志时,线上上下文已经没有用户编号等信息
if (request != null) { if (request != null) {
WebFrameworkUtils.setLoginUserId(request, loginUser.getId()); WebFrameworkUtils.setLoginUserId(request, loginUser.getId());
WebFrameworkUtils.setLoginUserType(request, loginUser.getUserType()); WebFrameworkUtils.setLoginUserType(request, loginUser.getUserType());
@@ -135,12 +80,10 @@ public class SecurityFrameworkUtils {
} }
private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) { private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) {
// 创建 UsernamePasswordAuthenticationToken 对象 UsernamePasswordAuthenticationToken authenticationToken =
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( new UsernamePasswordAuthenticationToken(loginUser, null, Collections.emptyList());
loginUser, null, Collections.emptyList());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
return authenticationToken; return authenticationToken;
} }
} }

View File

@@ -2,4 +2,4 @@ com.njcn.msgpush.framework.security.config.MsgpushSecurityRpcAutoConfiguration
com.njcn.msgpush.framework.security.config.MsgpushSecurityAutoConfiguration com.njcn.msgpush.framework.security.config.MsgpushSecurityAutoConfiguration
com.njcn.msgpush.framework.security.config.MsgpushWebSecurityConfigurerAdapter com.njcn.msgpush.framework.security.config.MsgpushWebSecurityConfigurerAdapter
com.njcn.msgpush.framework.operatelog.config.MsgpushOperateLogConfiguration com.njcn.msgpush.framework.operatelog.config.MsgpushOperateLogConfiguration
com.njcn.msgpush.framework.operatelog.config.MsgpushOperateLogRpcAutoConfiguration com.njcn.msgpush.framework.operatelog.config.MsgpushOperateLogRpcAutoConfiguration

View File

@@ -2,12 +2,13 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>com.njcn</groupId> <groupId>com.njcn</groupId>
<artifactId>msgpush-framework</artifactId> <artifactId>msgpush-framework</artifactId>
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>msgpush-spring-boot-starter-test</artifactId> <artifactId>msgpush-spring-boot-starter-test</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@@ -57,4 +58,4 @@
<artifactId>podam</artifactId> <artifactId>podam</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -1,13 +1,13 @@
package com.njcn.msgpush.framework.test.core.ut; package com.njcn.msgpush.framework.test.core.ut;
import cn.hutool.extra.spring.SpringUtil; import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import com.njcn.msgpush.framework.datasource.config.MsgpushDataSourceAutoConfiguration; import com.njcn.msgpush.framework.datasource.config.MsgpushDataSourceAutoConfiguration;
import com.njcn.msgpush.framework.mybatis.config.MsgpushMybatisAutoConfiguration; import com.njcn.msgpush.framework.mybatis.config.MsgpushMybatisAutoConfiguration;
import com.njcn.msgpush.framework.redis.config.MsgpushRedisAutoConfiguration; import com.njcn.msgpush.framework.redis.config.MsgpushRedisAutoConfiguration;
import com.njcn.msgpush.framework.test.config.RedisTestConfiguration; import com.njcn.msgpush.framework.test.config.RedisTestConfiguration;
import com.njcn.msgpush.framework.test.config.SqlInitializationTestConfiguration; import com.njcn.msgpush.framework.test.config.SqlInitializationTestConfiguration;
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import org.redisson.spring.starter.RedissonAutoConfiguration; import org.redisson.spring.starter.RedissonAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

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