Compare commits
5 Commits
6d05b3b95d
...
616d55d1a7
Author | SHA1 | Date | |
---|---|---|---|
616d55d1a7 | |||
e17a63b96d | |||
ac6e89af79 | |||
9a8890996f | |||
c9fd364428 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -43,4 +43,6 @@ build/
|
||||
*.cache
|
||||
*.diff
|
||||
*.patch
|
||||
*.tmp
|
||||
*.tmp
|
||||
|
||||
!docker/zayac-admin/Dockerfile
|
@ -1,62 +1,15 @@
|
||||
version: '3'
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.0.33
|
||||
restart: always
|
||||
container_name: mysql-master
|
||||
ports:
|
||||
- '3306:3306'
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
MYSQL_ROOT_PASSWORD: TMrmNY839KtZfpHb
|
||||
# 初始化数据库(后续的初始化 SQL 会在这个库执行)
|
||||
MYSQL_DATABASE: zayac_admin
|
||||
#MYSQL_USER: 你的数据库用户名
|
||||
#MYSQL_PASSWORD: 你的数据库密码
|
||||
volumes:
|
||||
- /docker/mysql/conf/:/etc/mysql/conf.d/
|
||||
- /docker/mysql/data/:/var/lib/mysql/
|
||||
command:
|
||||
--default-authentication-plugin=mysql_native_password
|
||||
--character-set-server=utf8mb4
|
||||
--collation-server=utf8mb4_general_ci
|
||||
--explicit_defaults_for_timestamp=true
|
||||
--lower_case_table_names=1
|
||||
redis:
|
||||
image: redis:7.2.3
|
||||
restart: always
|
||||
container_name: redis
|
||||
ports:
|
||||
- '6379:6379'
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- /docker/redis/conf/redis.conf:/usr/local/redis/config/redis.conf
|
||||
- /docker/redis/data/:/data/
|
||||
- /docker/redis/logs/:/logs/
|
||||
command: 'redis-server /usr/local/redis/config/redis.conf --appendonly yes --requirepass 你的 Redis 密码'
|
||||
continew-admin-server:
|
||||
build: ./zayac-admin
|
||||
restart: always
|
||||
container_name: continew-admin-server
|
||||
container_name: zayac-admin-server
|
||||
ports:
|
||||
- '18000:18000'
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
DB_HOST: 172.17.0.1
|
||||
DB_PORT: 3306
|
||||
DB_USER: 你的数据库用户名
|
||||
DB_PWD: 你的数据库密码
|
||||
DB_NAME: continew_admin
|
||||
REDIS_HOST: 172.17.0.1
|
||||
REDIS_PORT: 6379
|
||||
REDIS_PWD: 你的 Redis 密码
|
||||
REDIS_DB: 0
|
||||
volumes:
|
||||
- /docker/continew-admin/config/:/app/config/
|
||||
- /docker/continew-admin/data/file/:/app/data/file/
|
||||
- /docker/continew-admin/logs/:/app/logs/
|
||||
- /docker/continew-admin/lib/:/app/lib/
|
||||
depends_on:
|
||||
- redis
|
||||
- mysql
|
||||
- /docker/zayac-admin/config/:/app/config/
|
||||
- /docker/zayac-admin/data/file/:/app/data/file/
|
||||
- /docker/zayac-admin/logs/:/app/logs/
|
||||
- /docker/zayac-admin/lib/:/app/lib/
|
||||
|
@ -1,14 +1,22 @@
|
||||
# 使用 OpenJDK 17 作为基础镜像
|
||||
FROM openjdk:17
|
||||
|
||||
MAINTAINER Charles7c charles7c@126.com
|
||||
# 维护者信息
|
||||
MAINTAINER zayac stupidzayac@gmail.com
|
||||
|
||||
ARG JAR_FILE=./bin/*.jar
|
||||
COPY ${JAR_FILE} /app/bin/app.jar
|
||||
# 复制 JAR 文件和配置文件到容器中
|
||||
COPY ./bin/zayac-admin.jar /app/bin/app.jar
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app/bin
|
||||
|
||||
# 暴露应用运行的端口
|
||||
EXPOSE 18000
|
||||
|
||||
# 运行应用
|
||||
ENTRYPOINT ["java", \
|
||||
"-jar", \
|
||||
"-XX:+UseZGC", \
|
||||
"-Djava.security.egd=file:/dev/./urandom", \
|
||||
"-Dspring.profiles.active=prod", \
|
||||
"app.jar"]
|
||||
"/app/bin/app.jar"]
|
||||
|
108
docker/zayac-admin/config/application-generator.yml
Normal file
108
docker/zayac-admin/config/application-generator.yml
Normal file
@ -0,0 +1,108 @@
|
||||
--- ### 代码生成器配置
|
||||
generator:
|
||||
# 排除数据表
|
||||
excludeTables:
|
||||
- DATABASECHANGELOG
|
||||
- DATABASECHANGELOGLOCK
|
||||
- gen_config
|
||||
- gen_field_config
|
||||
## 类型映射
|
||||
typeMappings:
|
||||
MYSQL:
|
||||
Integer:
|
||||
- int
|
||||
- tinyint
|
||||
- smallint
|
||||
- mediumint
|
||||
- integer
|
||||
String:
|
||||
- varchar
|
||||
- char
|
||||
- text
|
||||
- mediumtext
|
||||
- longtext
|
||||
- tinytext
|
||||
- json
|
||||
LocalDate:
|
||||
- date
|
||||
LocalDateTime:
|
||||
- datetime
|
||||
- timestamp
|
||||
Long:
|
||||
- bigint
|
||||
Float:
|
||||
- float
|
||||
Double:
|
||||
- double
|
||||
BigDecimal:
|
||||
- decimal
|
||||
Boolean:
|
||||
- bit
|
||||
## 模板配置
|
||||
templateConfigs:
|
||||
DO:
|
||||
# 模板路径
|
||||
templatePath: backend/Entity.ftl
|
||||
# 包名称
|
||||
packageName: model.entity
|
||||
# 排除字段
|
||||
excludeFields:
|
||||
- id
|
||||
- createUser
|
||||
- createTime
|
||||
- updateUser
|
||||
- updateTime
|
||||
Query:
|
||||
templatePath: backend/Query.ftl
|
||||
packageName: model.query
|
||||
Req:
|
||||
templatePath: backend/Req.ftl
|
||||
packageName: model.req
|
||||
Resp:
|
||||
templatePath: backend/Resp.ftl
|
||||
packageName: model.resp
|
||||
excludeFields:
|
||||
- id
|
||||
- createUser
|
||||
- createTime
|
||||
DetailResp:
|
||||
templatePath: backend/DetailResp.ftl
|
||||
packageName: model.resp
|
||||
excludeFields:
|
||||
- id
|
||||
- createUser
|
||||
- createTime
|
||||
- updateUser
|
||||
- updateTime
|
||||
Mapper:
|
||||
templatePath: backend/Mapper.ftl
|
||||
packageName: mapper
|
||||
Service:
|
||||
templatePath: backend/Service.ftl
|
||||
packageName: service
|
||||
ServiceImpl:
|
||||
templatePath: backend/ServiceImpl.ftl
|
||||
packageName: service.impl
|
||||
Controller:
|
||||
templatePath: backend/Controller.ftl
|
||||
packageName: controller
|
||||
api:
|
||||
templatePath: frontend/api.ftl
|
||||
packageName: src/apis
|
||||
extension: .ts
|
||||
backend: false
|
||||
index:
|
||||
templatePath: frontend/index.ftl
|
||||
packageName: src/views
|
||||
extension: .vue
|
||||
backend: false
|
||||
AddModal:
|
||||
templatePath: frontend/AddModal.ftl
|
||||
packageName: src/views
|
||||
extension: .vue
|
||||
backend: false
|
||||
DetailDrawer:
|
||||
templatePath: frontend/DetailDrawer.ftl
|
||||
packageName: src/views
|
||||
extension: .vue
|
||||
backend: false
|
284
docker/zayac-admin/config/application-prod.yml
Normal file
284
docker/zayac-admin/config/application-prod.yml
Normal file
@ -0,0 +1,284 @@
|
||||
--- ### 项目配置
|
||||
project:
|
||||
# URL(跨域配置默认放行此 URL,第三方登录回调默认使用此 URL 为前缀,请注意更改为你实际的前端 URL)
|
||||
url: http://localhost:5173
|
||||
# 是否为生产环境
|
||||
production: true
|
||||
|
||||
--- ### 服务器配置
|
||||
server:
|
||||
# HTTP 端口(默认 8080)
|
||||
port: 8000
|
||||
|
||||
--- ### 数据源配置
|
||||
spring.datasource:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
## 动态数据源配置(可配多主多从:m1、s1...;纯粹多库:mysql、oracle...;混合配置:m1、s1、oracle...)
|
||||
dynamic:
|
||||
# 是否启用 P6Spy(SQL 性能分析组件,该插件有性能损耗,不建议生产环境使用)
|
||||
p6spy: false
|
||||
# 设置默认的数据源或者数据源组(默认:master)
|
||||
primary: master
|
||||
# 严格匹配数据源(true:未匹配到指定数据源时抛异常;false:使用默认数据源;默认 false)
|
||||
strict: false
|
||||
datasource:
|
||||
# 主库配置(可配多个,构成多主)
|
||||
master:
|
||||
url: jdbc:mysql://${DB_HOST:45.89.233.228}:${DB_PORT:3306}/${DB_NAME:zayac_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false
|
||||
username: ${DB_USER:zayac_admin}
|
||||
password: ${DB_PWD:2hMtBRzZrDAkRynX}
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
type: ${spring.datasource.type}
|
||||
# 从库配置(可配多个,构成多从)
|
||||
slave_1:
|
||||
url: jdbc:mysql://${DB_HOST:38.6.218.29}:${DB_PORT:3306}/${DB_NAME:zayac_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false
|
||||
username: zayac_admin
|
||||
password: PxkF52eGTz48izZG
|
||||
lazy: true
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
type: ${spring.datasource.type}
|
||||
# # PostgreSQL 库配置
|
||||
# postgresql:
|
||||
# url: jdbc:postgresql://${DB_HOST:127.0.0.1}:${DB_PORT:5432}/${DB_NAME:continew_admin}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&rewriteBatchedStatements=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false
|
||||
# username: ${DB_USER:root}
|
||||
# password: ${DB_PWD:123456}
|
||||
# driver-class-name: org.postgresql.Driver
|
||||
# type: ${spring.datasource.type}
|
||||
# Hikari 连接池配置(完整配置请参阅:https://github.com/brettwooldridge/HikariCP)
|
||||
hikari:
|
||||
# 最大连接数量(默认 10,根据实际环境调整)
|
||||
# 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒
|
||||
max-pool-size: 20
|
||||
# 获取连接超时时间(默认 30000 毫秒,30 秒)
|
||||
connection-timeout: 30000
|
||||
# 空闲连接最大存活时间(默认 600000 毫秒,10 分钟)
|
||||
idle-timeout: 600000
|
||||
# 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime(默认 0,禁用)
|
||||
keepaliveTime: 30000
|
||||
# 连接最大生存时间(默认 1800000 毫秒,30 分钟)
|
||||
max-lifetime: 1800000
|
||||
## Liquibase 配置
|
||||
spring.liquibase:
|
||||
# 是否启用
|
||||
enabled: false
|
||||
# 配置文件路径
|
||||
change-log: classpath:/db/changelog/db.changelog-master.yaml
|
||||
|
||||
--- ### 缓存配置
|
||||
spring.data:
|
||||
## Redis 配置(单机模式)
|
||||
redis:
|
||||
# 地址
|
||||
host: ${REDIS_HOST:38.6.218.29}
|
||||
# 端口(默认 6379)
|
||||
port: ${REDIS_PORT:6379}
|
||||
# 密码(未设置密码时可为空或注释掉)
|
||||
password: ${REDIS_PWD:jhkdjhkjdhsIUTYURTU_mWHmDY}
|
||||
# 数据库索引
|
||||
database: ${REDIS_DB:0}
|
||||
# 连接超时时间
|
||||
timeout: 10s
|
||||
# 是否开启 SSL
|
||||
ssl:
|
||||
enabled: false
|
||||
## Redisson 配置
|
||||
redisson:
|
||||
enabled: true
|
||||
mode: SINGLE
|
||||
## JetCache 配置
|
||||
jetcache:
|
||||
# 统计间隔(默认 0,表示不统计)
|
||||
statIntervalMinutes: 0
|
||||
## 本地/进程级/一级缓存配置
|
||||
local:
|
||||
default:
|
||||
# 缓存类型
|
||||
type: caffeine
|
||||
# key 转换器的全局配置
|
||||
keyConvertor: jackson
|
||||
# 以毫秒为单位指定超时时间的全局配置
|
||||
expireAfterWriteInMillis: 7200000
|
||||
# 每个缓存实例的最大元素的全局配置,仅 local 类型的缓存需要指定
|
||||
limit: 1000
|
||||
## 远程/分布式/二级缓存配置
|
||||
remote:
|
||||
default:
|
||||
# 缓存类型
|
||||
type: redisson
|
||||
# key 转换器的全局配置(用于将复杂的 KEY 类型转换为缓存实现可以接受的类型)
|
||||
keyConvertor: jackson
|
||||
# 以毫秒为单位指定超时时间的全局配置
|
||||
expireAfterWriteInMillis: 7200000
|
||||
# 2.7+ 支持两级缓存更新以后失效其他 JVM 中的 local cache,但多个服务共用 Redis 同一个 channel 可能会造成广播风暴,需要在这里指定channel。
|
||||
# 你可以决定多个不同的服务是否共用同一个 channel,如果没有指定则不开启。
|
||||
broadcastChannel: ${spring.application.name}
|
||||
# 序列化器的全局配置,仅 remote 类型的缓存需要指定
|
||||
valueEncoder: java
|
||||
valueDecoder: java
|
||||
|
||||
--- ### 验证码配置
|
||||
continew-starter.captcha:
|
||||
## 行为验证码
|
||||
behavior:
|
||||
enabled: true
|
||||
cache-type: REDIS
|
||||
water-mark: ${project.app-name}
|
||||
## 图形验证码
|
||||
graphic:
|
||||
# 类型
|
||||
type: SPEC
|
||||
# 内容长度
|
||||
length: 4
|
||||
# 过期时间
|
||||
expirationInMinutes: 2
|
||||
## 其他验证码配置
|
||||
captcha:
|
||||
## 邮箱验证码配置
|
||||
mail:
|
||||
# 内容长度
|
||||
length: 6
|
||||
# 过期时间
|
||||
expirationInMinutes: 5
|
||||
# 限制时间
|
||||
limitInSeconds: 60
|
||||
# 模板路径
|
||||
templatePath: mail/captcha.ftl
|
||||
## 短信验证码配置
|
||||
sms:
|
||||
# 内容长度
|
||||
length: 4
|
||||
# 过期时间
|
||||
expirationInMinutes: 5
|
||||
# 模板 ID
|
||||
templateId: 1
|
||||
|
||||
--- ### 日志配置
|
||||
continew-starter.log:
|
||||
# 是否打印日志,开启后可打印访问日志(类似于 Nginx access log)
|
||||
is-print: false
|
||||
## 项目日志配置(配置重叠部分,优先级高于 logback-spring.xml 中的配置)
|
||||
logging:
|
||||
level:
|
||||
top.continew: INFO
|
||||
file:
|
||||
path: ../logs
|
||||
|
||||
--- ### 跨域配置
|
||||
continew-starter.web.cors:
|
||||
enabled: true
|
||||
# 配置允许跨域的域名
|
||||
allowed-origins:
|
||||
- ${project.url}
|
||||
# 配置允许跨域的请求方式
|
||||
allowed-methods: '*'
|
||||
# 配置允许跨域的请求头
|
||||
allowed-headers: '*'
|
||||
# 配置允许跨域的响应头
|
||||
exposed-headers: '*'
|
||||
|
||||
--- ### 接口文档配置
|
||||
springdoc:
|
||||
swagger-ui:
|
||||
enabled: false
|
||||
## 接口文档增强配置
|
||||
knife4j:
|
||||
# 开启生产环境屏蔽
|
||||
production: true
|
||||
|
||||
--- ### 短信配置
|
||||
sms:
|
||||
# 从 YAML 读取配置
|
||||
config-type: YAML
|
||||
is-print: false
|
||||
blends:
|
||||
cloopen:
|
||||
# 短信厂商
|
||||
supplier: cloopen
|
||||
base-url: https://app.cloopen.com:8883/2013-12-26
|
||||
access-key-id: 你的Access Key
|
||||
access-key-secret: 你的Access Key Secret
|
||||
sdk-app-id: 你的应用ID
|
||||
|
||||
--- ### 邮件配置
|
||||
spring.mail:
|
||||
# 根据需要更换
|
||||
host: smtp.126.com
|
||||
port: 465
|
||||
username: 你的邮箱
|
||||
password: 你的邮箱授权码
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
auth: true
|
||||
socketFactory:
|
||||
class: javax.net.ssl.SSLSocketFactory
|
||||
port: 465
|
||||
|
||||
--- ### Just Auth 配置
|
||||
justauth:
|
||||
enabled: true
|
||||
type:
|
||||
GITEE:
|
||||
client-id: 5d271b7f638941812aaf8bfc2e2f08f06d6235ef934e0e39537e2364eb8452c4
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${project.url}/social/callback?source=gitee
|
||||
GITHUB:
|
||||
client-id: 38080dad08cfbdfacca9
|
||||
client-secret: 1f7d08**********5b7**********29e
|
||||
redirect-uri: ${project.url}/social/callback?source=github
|
||||
cache:
|
||||
type: REDIS
|
||||
|
||||
--- ### Sa-Token 扩展配置
|
||||
sa-token.extension:
|
||||
# 安全配置:排除(放行)路径配置
|
||||
security.excludes:
|
||||
- /error
|
||||
# 静态资源
|
||||
- /*.html
|
||||
- /*/*.html
|
||||
- /*/*.css
|
||||
- /*/*.js
|
||||
- /webSocket/**
|
||||
# 本地存储资源
|
||||
- /file/**
|
||||
|
||||
--- ### 字段加/解密配置
|
||||
continew-starter.security:
|
||||
crypto:
|
||||
enabled: true
|
||||
# 对称加密算法密钥
|
||||
password: abcdefghijklmnop
|
||||
# 非对称加密算法密钥(在线生成 RSA 密钥对:http://web.chacuo.net/netrsakeypair)
|
||||
public-key: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAM51dgYtMyF+tTQt80sfFOpSV27a7t9uaUVeFrdGiVxscuizE7H8SMntYqfn9lp8a5GH5P1/GGehVjUD2gF/4kcCAwEAAQ==
|
||||
private-key: MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAznV2Bi0zIX61NC3zSx8U6lJXbtru325pRV4Wt0aJXGxy6LMTsfxIye1ip+f2WnxrkYfk/X8YZ6FWNQPaAX/iRwIDAQABAkEAk/VcAusrpIqA5Ac2P5Tj0VX3cOuXmyouaVcXonr7f+6y2YTjLQuAnkcfKKocQI/juIRQBFQIqqW/m1nmz1wGeQIhAO8XaA/KxzOIgU0l/4lm0A2Wne6RokJ9HLs1YpOzIUmVAiEA3Q9DQrpAlIuiT1yWAGSxA9RxcjUM/1kdVLTkv0avXWsCIE0X8woEjK7lOSwzMG6RpEx9YHdopjViOj1zPVH61KTxAiBmv/dlhqkJ4rV46fIXELZur0pj6WC3N7a4brR8a+CLLQIhAMQyerWl2cPNVtE/8tkziHKbwW3ZUiBXU24wFxedT9iV
|
||||
|
||||
--- ### 密码编码器配置
|
||||
continew-starter.security:
|
||||
password:
|
||||
enabled: true
|
||||
# BCryptPasswordEncoder
|
||||
encoding-id: bcrypt
|
||||
|
||||
--- ### 文件上传配置
|
||||
spring.servlet:
|
||||
multipart:
|
||||
enabled: true
|
||||
# 单文件上传大小限制
|
||||
max-file-size: 10MB
|
||||
# 单次总上传文件大小限制
|
||||
max-request-size: 20MB
|
||||
## 头像支持格式配置
|
||||
avatar:
|
||||
support-suffix: jpg,jpeg,png,gif
|
||||
|
||||
webclient:
|
||||
max-concurrent-requests: 60
|
||||
|
||||
spring:
|
||||
rabbitmq:
|
||||
host: 45.89.233.228
|
||||
port: 5672
|
||||
username: bot
|
||||
password: xiaomi@123
|
||||
|
239
docker/zayac-admin/config/application.yml
Normal file
239
docker/zayac-admin/config/application.yml
Normal file
@ -0,0 +1,239 @@
|
||||
--- ### 项目配置
|
||||
project:
|
||||
# 名称
|
||||
name: Zayac Admin
|
||||
# 应用名称
|
||||
app-name: zayac-admin
|
||||
# 版本
|
||||
version: 3.1.0-SNAPSHOT
|
||||
# 描述
|
||||
description: 持续迭代优化的前后端分离中后台管理系统框架,开箱即用,持续提供舒适的开发体验。
|
||||
# 基本包
|
||||
base-package: com.zayac.admin
|
||||
## 作者信息配置
|
||||
contact:
|
||||
name: zayac
|
||||
email: stupidzayac@gmail.com
|
||||
url: https://blog.charles7c.top/about/me
|
||||
## 许可协议信息配置
|
||||
license:
|
||||
name: Apache-2.0
|
||||
url: https://github.com/Charles7c/continew-admin/blob/dev/LICENSE
|
||||
|
||||
--- ### 日志配置
|
||||
continew-starter.log:
|
||||
# 包含信息
|
||||
includes:
|
||||
- DESCRIPTION
|
||||
- MODULE
|
||||
- REQUEST_HEADERS
|
||||
- REQUEST_BODY
|
||||
- IP_ADDRESS
|
||||
- BROWSER
|
||||
- OS
|
||||
- RESPONSE_HEADERS
|
||||
- RESPONSE_BODY
|
||||
## 项目日志配置
|
||||
logging:
|
||||
config: classpath:logback-spring.xml
|
||||
|
||||
--- ### 链路跟踪配置
|
||||
continew-starter.web:
|
||||
trace:
|
||||
enabled: true
|
||||
header-name: traceId
|
||||
## TLog 配置
|
||||
tlog:
|
||||
enable-invoke-time-print: false
|
||||
pattern: '[$spanId][$traceId]'
|
||||
mdc-enable: false
|
||||
|
||||
--- ### 线程池配置
|
||||
continew-starter.thread-pool:
|
||||
enabled: true
|
||||
# 队列容量
|
||||
queue-capacity: 128
|
||||
# 活跃时间(单位:秒)
|
||||
keep-alive-seconds: 300
|
||||
|
||||
--- ### 接口文档配置
|
||||
springdoc:
|
||||
# 设置对象型参数的展示形式(设为 true 表示将对象型参数平展开,即对象内的属性直接作为参数展示而不是嵌套在对象内,默认 false)
|
||||
# 如果不添加该全局配置,可以在需要如此处理的对象参数类上使用 @ParameterObject
|
||||
default-flat-param-object: true
|
||||
# 分组配置
|
||||
group-configs:
|
||||
- group: all
|
||||
paths-to-match: /**
|
||||
paths-to-exclude:
|
||||
- /error
|
||||
- group: auth
|
||||
display-name: 系统认证
|
||||
packages-to-scan: ${project.base-package}.webapi.auth
|
||||
- group: common
|
||||
display-name: 通用接口
|
||||
packages-to-scan: ${project.base-package}.webapi.common
|
||||
- group: system
|
||||
display-name: 系统管理
|
||||
packages-to-scan: ${project.base-package}.webapi.system
|
||||
- group: monitor
|
||||
display-name: 系统监控
|
||||
packages-to-scan: ${project.base-package}.webapi.monitor
|
||||
## 组件配置
|
||||
components:
|
||||
# 鉴权配置
|
||||
security-schemes:
|
||||
Authorization:
|
||||
type: HTTP
|
||||
in: HEADER
|
||||
name: ${sa-token.token-name}
|
||||
scheme: ${sa-token.token-prefix}
|
||||
## 接口文档增强配置
|
||||
knife4j:
|
||||
enable: true
|
||||
setting:
|
||||
# 是否显示默认的 footer(默认 true,显示)
|
||||
enable-footer: false
|
||||
# 是否自定义 footer(默认 false,非自定义)
|
||||
enable-footer-custom: true
|
||||
# 自定义 footer 内容,支持 Markdown 语法
|
||||
footer-custom-content: 'Copyright © 2022-present [${project.contact.name}](${project.contact.url}) ⋅ [${project.name}](${project.url}) v${project.version}'
|
||||
|
||||
--- ### Sa-Token 配置
|
||||
sa-token:
|
||||
# token 名称(同时也是 cookie 名称)
|
||||
token-name: Authorization
|
||||
# token 有效期(单位:秒,默认 30 天,-1 代表永不过期)
|
||||
timeout: 86400
|
||||
# token 最低活跃频率(单位:秒,默认 -1,代表不限制,永不冻结。如果 token 超过此时间没有访问系统就会被冻结)
|
||||
active-timeout: 1800
|
||||
# 是否打开自动续签(如果此值为 true,框架会在每次直接或间接调用 getLoginId() 时进行一次过期检查与续签操作)
|
||||
auto-renew: true
|
||||
# 是否允许同一账号多地同时登录(为 true 时允许一起登录,为 false 时新登录挤掉旧登录)
|
||||
is-concurrent: true
|
||||
# 在多人登录同一账号时,是否共用一个 token(为 true 时所有登录共用一个 token,为 false 时每次登录新建一个 token)
|
||||
is-share: false
|
||||
# 是否输出操作日志
|
||||
is-log: false
|
||||
# JWT 秘钥
|
||||
jwt-secret-key: asdasdasifhueuiwyurfewbfjsdafjk
|
||||
## 扩展配置
|
||||
extension:
|
||||
enabled: true
|
||||
enableJwt: true
|
||||
# 持久层配置
|
||||
dao.type: REDIS
|
||||
|
||||
--- ### MyBatis Plus 配置
|
||||
mybatis-plus:
|
||||
# Mapper XML 文件目录配置
|
||||
mapper-locations: classpath*:/mapper/**/*Mapper.xml
|
||||
# 类型别名扫描包配置
|
||||
type-aliases-package: ${project.base-package}.**.model
|
||||
configuration:
|
||||
# MyBatis 自动映射策略
|
||||
# NONE:不启用 PARTIAL:只对非嵌套 resultMap 自动映射 FULL:对所有 resultMap 自动映射
|
||||
auto-mapping-behavior: PARTIAL
|
||||
global-config:
|
||||
banner: true
|
||||
db-config:
|
||||
# 主键类型(默认 assign_id,表示自行赋值)
|
||||
# auto 代表使用数据库自增策略(需要在表中设置好自增约束)
|
||||
id-type: ASSIGN_ID
|
||||
# 逻辑删除字段
|
||||
logic-delete-field: isDeleted
|
||||
# 逻辑删除全局值(默认 1,表示已删除)
|
||||
logic-delete-value: 1
|
||||
# 逻辑未删除全局值(默认 0,表示未删除)
|
||||
logic-not-delete-value: 0
|
||||
## 扩展配置
|
||||
extension:
|
||||
enabled: true
|
||||
# Mapper 接口扫描包配置
|
||||
mapper-package: ${project.base-package}.**.mapper
|
||||
# ID 生成器配置
|
||||
id-generator:
|
||||
type: COSID
|
||||
# 数据权限配置
|
||||
data-permission:
|
||||
enabled: true
|
||||
# 分页插件配置
|
||||
pagination:
|
||||
enabled: true
|
||||
db-type: MYSQL
|
||||
|
||||
--- ### CosId 配置
|
||||
cosid:
|
||||
namespace: ${spring.application.name}
|
||||
machine:
|
||||
enabled: true
|
||||
# 机器号分配器
|
||||
distributor:
|
||||
type: REDIS
|
||||
guarder:
|
||||
# 开启机器号守护
|
||||
enabled: true
|
||||
snowflake:
|
||||
enabled: true
|
||||
zone-id: Asia/Shanghai
|
||||
epoch: 1577203200000
|
||||
share:
|
||||
# 开启时钟回拨同步
|
||||
clock-sync: true
|
||||
friendly: true
|
||||
provider:
|
||||
safe-js:
|
||||
machine-bit: 3
|
||||
sequence-bit: 9
|
||||
|
||||
--- ### 服务器配置
|
||||
server:
|
||||
servlet:
|
||||
# 应用访问路径
|
||||
context-path: /
|
||||
## Undertow 服务器配置
|
||||
undertow:
|
||||
# HTTP POST 请求内容的大小上限(默认 -1,不限制)
|
||||
max-http-post-size: -1
|
||||
# 以下的配置会影响 buffer,这些 buffer 会用于服务器连接的 IO 操作,有点类似 Netty 的池化内存管理
|
||||
# 每块 buffer的空间大小(越小的空间被利用越充分,不要设置太大,以免影响其他应用,合适即可)
|
||||
buffer-size: 512
|
||||
# 是否分配的直接内存(NIO 直接分配的堆外内存)
|
||||
direct-buffers: true
|
||||
threads:
|
||||
# 设置 IO 线程数,它主要执行非阻塞的任务,它们会负责多个连接(默认每个 CPU 核心一个线程)
|
||||
io: 8
|
||||
# 阻塞任务线程池,当执行类似 Servlet 请求阻塞操作,Undertow 会从这个线程池中取得线程(它的值设置取决于系统的负载)
|
||||
worker: 256
|
||||
|
||||
--- ### Spring 配置
|
||||
spring:
|
||||
application:
|
||||
name: ${project.app-name}
|
||||
## 环境配置
|
||||
profiles:
|
||||
# 启用的环境
|
||||
active: dev
|
||||
include:
|
||||
- generator
|
||||
main:
|
||||
# 允许定义重名的 bean 对象覆盖原有的 bean
|
||||
allow-bean-definition-overriding: true
|
||||
# 允许循环依赖
|
||||
allow-circular-references: true
|
||||
|
||||
--- ### 健康检查配置
|
||||
management.health:
|
||||
mail:
|
||||
# 关闭邮箱健康检查(邮箱配置错误或邮箱服务器不可用时,健康检查会报错)
|
||||
enabled: false
|
||||
|
||||
webclient:
|
||||
max-concurrent-requests: 60
|
||||
|
||||
spring:
|
||||
rabbitmq:
|
||||
host: 45.89.233.228
|
||||
port: 5672
|
||||
username: bot
|
||||
password: xiaomi@123
|
@ -1,12 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.mapper;
|
||||
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import com.zayac.admin.agent.model.entity.StatsDO;
|
||||
|
||||
/**
|
||||
* 代理每日数据 Mapper
|
||||
*
|
||||
* @author zayac
|
||||
* @since 2024/06/04 17:10
|
||||
*/
|
||||
* 代理每日数据 Mapper
|
||||
*
|
||||
* @author zayac
|
||||
* @since 2024/06/04 17:10
|
||||
*/
|
||||
public interface DailyStatsMapper extends BaseMapper<StatsDO> {}
|
@ -1,12 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.mapper;
|
||||
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import com.zayac.admin.agent.model.entity.FinanceDO;
|
||||
|
||||
/**
|
||||
* 代理线财务报 Mapper
|
||||
*
|
||||
* @author zayac
|
||||
* @since 2024/06/04 17:14
|
||||
*/
|
||||
* 代理线财务报 Mapper
|
||||
*
|
||||
* @author zayac
|
||||
* @since 2024/06/04 17:14
|
||||
*/
|
||||
public interface FinanceMapper extends BaseMapper<FinanceDO> {}
|
@ -1,12 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.mapper;
|
||||
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
import com.zayac.admin.agent.model.entity.FinanceSumDO;
|
||||
|
||||
/**
|
||||
* 财务报汇总 Mapper
|
||||
*
|
||||
* @author zayac
|
||||
* @since 2024/06/04 17:10
|
||||
*/
|
||||
* 财务报汇总 Mapper
|
||||
*
|
||||
* @author zayac
|
||||
* @since 2024/06/04 17:10
|
||||
*/
|
||||
public interface FinanceSumMapper extends BaseMapper<FinanceSumDO> {}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.model.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.model.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.model.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
@ -1,9 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.model.query;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
@ -1,17 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.model.query;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import top.continew.starter.data.core.annotation.Query;
|
||||
import top.continew.starter.data.core.enums.QueryType;
|
||||
|
||||
/**
|
||||
* 财务报汇总查询条件
|
||||
*
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.model.query;
|
||||
|
||||
import java.io.Serial;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.model.req;
|
||||
|
||||
import java.io.Serial;
|
||||
@ -5,14 +21,10 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import top.continew.starter.extension.crud.model.req.BaseReq;
|
||||
|
||||
/**
|
||||
|
@ -1,16 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.model.req;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.time.LocalDateTime;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import top.continew.starter.extension.crud.model.req.BaseReq;
|
||||
|
||||
/**
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.model.req;
|
||||
|
||||
import java.io.Serial;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.model.resp;
|
||||
|
||||
import java.io.Serial;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.model.resp;
|
||||
|
||||
import java.io.Serial;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.model.resp;
|
||||
|
||||
import java.io.Serial;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.model.resp;
|
||||
|
||||
import java.io.Serial;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.model.resp;
|
||||
|
||||
import java.io.Serial;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.model.resp;
|
||||
|
||||
import java.io.Serial;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.service;
|
||||
|
||||
import com.zayac.admin.agent.model.entity.FinanceDO;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.service;
|
||||
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.service;
|
||||
|
||||
import com.zayac.admin.agent.model.entity.StatsDO;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.service.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
@ -1,6 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -25,7 +40,7 @@ import java.util.List;
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class FinanceServiceImpl extends BaseServiceImpl<FinanceMapper, FinanceDO, FinanceResp, FinanceDetailResp, FinanceQuery, FinanceReq> implements FinanceService {
|
||||
public void addAll(List<FinanceDO> financeReqList){
|
||||
public void addAll(List<FinanceDO> financeReqList) {
|
||||
baseMapper.insertBatch(financeReqList);
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.agent.service.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.req;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.req.team;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.resp.team;
|
||||
|
||||
import com.zayac.admin.resp.Pagination;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.resp.team;
|
||||
|
||||
import com.zayac.admin.resp.Venue;
|
||||
@ -7,6 +23,7 @@ import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.resp.team;
|
||||
|
||||
import com.zayac.admin.resp.Pagination;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.resp.team;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
@ -16,20 +16,17 @@
|
||||
|
||||
package com.zayac.admin.schedule;
|
||||
|
||||
import com.zayac.admin.common.enums.DisEnableStatusEnum;
|
||||
import com.zayac.admin.req.team.TeamInfoReq;
|
||||
import com.zayac.admin.resp.team.Team;
|
||||
import com.zayac.admin.resp.team.TeamAccount;
|
||||
import com.zayac.admin.service.*;
|
||||
import com.zayac.admin.system.model.entity.RoleDO;
|
||||
import com.zayac.admin.system.model.entity.UserDO;
|
||||
import com.zayac.admin.system.model.resp.AccountResp;
|
||||
import com.zayac.admin.system.service.AccountService;
|
||||
import com.zayac.admin.system.service.RoleService;
|
||||
import com.zayac.admin.system.service.UserRoleService;
|
||||
import com.zayac.admin.system.service.UserService;
|
||||
import com.zayac.admin.system.model.resp.DeptUsersResp;
|
||||
import com.zayac.admin.system.model.resp.UserWithRolesAndAccountsResp;
|
||||
import com.zayac.admin.system.service.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@ -37,6 +34,7 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@ -51,13 +49,11 @@ import java.util.stream.Collectors;
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Profile("prod")
|
||||
public class CheckRegAndDep {
|
||||
|
||||
private final UserService userService;
|
||||
private final AccountService accountService;
|
||||
private final TeamService teamService;
|
||||
private final RoleService roleService;
|
||||
private final UserRoleService userRoleService;
|
||||
private final DeptService deptService;
|
||||
private final RegistrationService registrationService;
|
||||
private final DepositService depositService;
|
||||
private final Executor asyncTaskExecutor;
|
||||
@ -66,37 +62,56 @@ public class CheckRegAndDep {
|
||||
private static final long FIXED_DELAY = 60000L;
|
||||
|
||||
@Scheduled(fixedDelay = FIXED_DELAY)
|
||||
public void CheckRegistrationAndNewDeposit() {
|
||||
public void checkRegistrationAndNewDeposit() {
|
||||
LocalDate nowDate = LocalDate.now();
|
||||
LocalDateTime nowDateTime = LocalDateTime.now();
|
||||
RoleDO minister = roleService.getByCode(MINISTER_ROLE_CODE);
|
||||
List<Long> userIds = userRoleService.listUserIdByRoleId(minister.getId());
|
||||
userIds.forEach(userId -> {
|
||||
processUser(userService.getById(userId), nowDate, nowDateTime).join();
|
||||
//查询用户角色为部长的部门所有用户
|
||||
List<DeptUsersResp> deptWithUsersAndAccounts = deptService.getDeptWithUsersAndAccounts(MINISTER_ROLE_CODE);
|
||||
deptWithUsersAndAccounts.forEach(dept -> {
|
||||
//根据用户角色对部门用户进行分组
|
||||
Map<String, List<UserWithRolesAndAccountsResp>> usersByRole = dept.getUsers()
|
||||
.stream()
|
||||
.flatMap(user -> user.getRoles()
|
||||
.stream()
|
||||
.map(role -> new AbstractMap.SimpleEntry<>(role.getCode(), user)))
|
||||
.collect(Collectors.groupingByConcurrent(Map.Entry::getKey, Collectors
|
||||
.mapping(Map.Entry::getValue, Collectors.toList())));
|
||||
// 获取所有账号的username与用户的映射
|
||||
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap = dept.getUsers()
|
||||
.stream()
|
||||
.flatMap(user -> user.getAccounts()
|
||||
.stream()
|
||||
.map(account -> new AbstractMap.SimpleEntry<>(account.getUsername(), user)))
|
||||
.collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
var ministerUser = usersByRole.get(MINISTER_ROLE_CODE).get(0);
|
||||
processUser(ministerUser, accountUsernameToUserMap, nowDate, nowDateTime).join();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> processUser(UserDO minister, LocalDate nowDate, LocalDateTime nowDateTime) {
|
||||
List<AccountResp> accounts = accountService.getAccountsByUserId(minister.getId(), DisEnableStatusEnum.ENABLE)
|
||||
.stream()
|
||||
.filter(AccountResp::getIsTeam)
|
||||
.toList();
|
||||
|
||||
private CompletableFuture<Void> processUser(UserWithRolesAndAccountsResp minister,
|
||||
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap,
|
||||
LocalDate nowDate,
|
||||
LocalDateTime nowDateTime) {
|
||||
//根据总线用户的账号查询数据
|
||||
List<AccountResp> accounts = minister.getAccounts();
|
||||
List<CompletableFuture<Void>> futures = accounts.stream()
|
||||
.map(account -> processTeamAccount(minister, account, nowDate, nowDateTime))
|
||||
.toList();
|
||||
.map(account -> processTeamAccount(minister, accountUsernameToUserMap, account, nowDate, nowDateTime))
|
||||
.toList();
|
||||
|
||||
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> processTeamAccount(UserDO minister,
|
||||
private CompletableFuture<Void> processTeamAccount(UserWithRolesAndAccountsResp minister,
|
||||
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap,
|
||||
AccountResp account,
|
||||
LocalDate nowDate,
|
||||
LocalDateTime nowDateTime) {
|
||||
TeamInfoReq teamInfoReq = TeamInfoReq.builder()
|
||||
.startDate(nowDateTime.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN))
|
||||
.endDate(nowDateTime.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX))
|
||||
.build();
|
||||
.startDate(nowDateTime.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN))
|
||||
.endDate(nowDateTime.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX))
|
||||
.build();
|
||||
CompletableFuture<Team> teamFuture = teamService.getLatestTeamInfoAsync(account, teamInfoReq);
|
||||
Team prevTeamInfo = teamService.getPreviousTeamInfo(account);
|
||||
|
||||
@ -104,22 +119,21 @@ public class CheckRegAndDep {
|
||||
log.info("Previous Team Info: {}", prevTeamInfo);
|
||||
log.info("Current Team Info: {}", currentTeamInfo);
|
||||
Map<String, TeamAccount> teamAccountMap = currentTeamInfo.getList()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(TeamAccount::getAgentName, Function.identity()));
|
||||
.stream()
|
||||
.collect(Collectors.toMap(TeamAccount::getAgentName, Function.identity()));
|
||||
|
||||
CompletableFuture<Void> registrationProcess = registrationService
|
||||
.processRegistration(minister, account, currentTeamInfo, prevTeamInfo, nowDate, teamAccountMap, asyncTaskExecutor)
|
||||
.thenRunAsync(() -> log.info("Registration process completed"), asyncTaskExecutor);
|
||||
.processRegistration(minister, account, accountUsernameToUserMap, teamAccountMap, currentTeamInfo, prevTeamInfo, nowDate, asyncTaskExecutor)
|
||||
.thenRunAsync(() -> log.info("Registration process completed"), asyncTaskExecutor);
|
||||
|
||||
CompletableFuture<Void> depositProcess = depositService
|
||||
.processDeposits(minister, account, currentTeamInfo, prevTeamInfo, nowDate, nowDateTime, asyncTaskExecutor)
|
||||
.thenRunAsync(() -> log.info("Deposit process completed"), asyncTaskExecutor);
|
||||
.processDeposits(minister, accountUsernameToUserMap, account, currentTeamInfo, prevTeamInfo, nowDate, nowDateTime, asyncTaskExecutor)
|
||||
.thenRunAsync(() -> log.info("Deposit process completed"), asyncTaskExecutor);
|
||||
|
||||
return CompletableFuture.allOf(registrationProcess, depositProcess)
|
||||
.thenRunAsync(() -> {
|
||||
teamService.updateTeamInfo(account, currentTeamInfo);
|
||||
log.info("Team info updated");
|
||||
}, asyncTaskExecutor);
|
||||
return CompletableFuture.allOf(registrationProcess, depositProcess).thenRunAsync(() -> {
|
||||
teamService.updateTeamInfo(account, currentTeamInfo);
|
||||
log.info("Team info updated");
|
||||
}, asyncTaskExecutor);
|
||||
}, asyncTaskExecutor);
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
package com.zayac.admin.schedule;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.zayac.admin.agent.model.entity.FinanceDO;
|
||||
import com.zayac.admin.agent.model.entity.StatsDO;
|
||||
@ -34,13 +35,14 @@ import com.zayac.admin.req.team.TeamMemberReq;
|
||||
import com.zayac.admin.resp.*;
|
||||
import com.zayac.admin.resp.team.*;
|
||||
import com.zayac.admin.service.*;
|
||||
import com.zayac.admin.system.model.entity.RoleDO;
|
||||
import com.zayac.admin.system.model.entity.UserDO;
|
||||
import com.zayac.admin.system.model.resp.AccountResp;
|
||||
import com.zayac.admin.system.model.resp.DeptUsersResp;
|
||||
import com.zayac.admin.system.model.resp.UserWithRolesAndAccountsResp;
|
||||
import com.zayac.admin.system.service.*;
|
||||
import com.zayac.admin.utils.TableFormatter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -60,12 +62,10 @@ import java.util.stream.Collectors;
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@Profile("prod")
|
||||
public class DailyReport {
|
||||
private final UserService userService;
|
||||
private final TeamService teamService;
|
||||
private final RoleService roleService;
|
||||
private final UserRoleService userRoleService;
|
||||
private final AccountService accountService;
|
||||
private final DeptService deptService;
|
||||
private final TelegramMessageService telegramMessageService;
|
||||
private final AgentDataVisualListService agentDataVisualListService;
|
||||
private final StatsService statsService;
|
||||
@ -76,20 +76,93 @@ public class DailyReport {
|
||||
private final Executor asyncTaskExecutor;
|
||||
|
||||
private static final String MINISTER_ROLE_CODE = "minister";
|
||||
private static final String SEO_TEAM_LEADER_ROLE_CODE = "seo_team_leader";
|
||||
private static final String ASSISTANT_ROLE_CODE = "assistant";
|
||||
private static final String SEO_ROLE_CODE = "seo";
|
||||
|
||||
@Scheduled(cron = "0 40 11,14,17,21 * * ?")
|
||||
public void teamAccountDailyReport() {
|
||||
LocalDateTime nowDateTime = LocalDateTime.now();
|
||||
LocalDate nowDate = LocalDate.now();
|
||||
sendDailyReport(nowDate, nowDate.atStartOfDay(), nowDateTime);
|
||||
//查询部门下的所有用户
|
||||
List<DeptUsersResp> deptWithUsersAndAccounts = deptService.getDeptWithUsersAndAccounts(MINISTER_ROLE_CODE);
|
||||
deptWithUsersAndAccounts.forEach(dept -> {
|
||||
//根据用户角色对部门用户进行分组
|
||||
Map<String, List<UserWithRolesAndAccountsResp>> usersByRole = dept.getUsers()
|
||||
.stream()
|
||||
.flatMap(user -> user.getRoles()
|
||||
.stream()
|
||||
.map(role -> new AbstractMap.SimpleEntry<>(role.getCode(), user)))
|
||||
.collect(Collectors.groupingByConcurrent(Map.Entry::getKey, Collectors
|
||||
.mapping(Map.Entry::getValue, Collectors.toList())));
|
||||
|
||||
var ministerUser = usersByRole.get(MINISTER_ROLE_CODE).get(0);
|
||||
//获取账号不为空的用户
|
||||
var deptUsers = dept.getUsers().stream().filter(user -> CollUtil.isNotEmpty(user.getAccounts())).toList();
|
||||
var assistants = usersByRole.get(ASSISTANT_ROLE_CODE);
|
||||
sendDailyReport(nowDate, nowDate.atStartOfDay(), nowDateTime, ministerUser, assistants, deptUsers);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 15 0 * * ?")
|
||||
public void dailySummarize() {
|
||||
LocalDate yesterday = LocalDate.now().minusDays(1);
|
||||
sendDailyReport(yesterday, yesterday.atStartOfDay(), LocalDateTime.of(yesterday, LocalTime.MAX));
|
||||
getPayFailedMember(yesterday);
|
||||
saveData(yesterday);
|
||||
//查询部门下的所有用户
|
||||
List<DeptUsersResp> deptWithUsersAndAccounts = deptService.getDeptWithUsersAndAccounts(MINISTER_ROLE_CODE);
|
||||
|
||||
deptWithUsersAndAccounts.forEach(dept -> {
|
||||
//根据用户角色对部门用户进行分组
|
||||
Map<String, List<UserWithRolesAndAccountsResp>> usersByRole = dept.getUsers()
|
||||
.stream()
|
||||
.flatMap(user -> user.getRoles()
|
||||
.stream()
|
||||
.map(role -> new AbstractMap.SimpleEntry<>(role.getCode(), user)))
|
||||
.collect(Collectors.groupingByConcurrent(Map.Entry::getKey, Collectors
|
||||
.mapping(Map.Entry::getValue, Collectors.toList())));
|
||||
// 获取所有账号的username与用户的映射
|
||||
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap = dept.getUsers()
|
||||
.stream()
|
||||
.flatMap(user -> user.getAccounts()
|
||||
.stream()
|
||||
.map(account -> new AbstractMap.SimpleEntry<>(account.getUsername(), user)))
|
||||
.collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
var ministerUser = usersByRole.get(MINISTER_ROLE_CODE).get(0);
|
||||
//获取账号不为空的用户
|
||||
var deptUsers = dept.getUsers().stream().filter(user -> CollUtil.isNotEmpty(user.getAccounts())).toList();
|
||||
var assistants = usersByRole.get(ASSISTANT_ROLE_CODE);
|
||||
|
||||
sendDailyReport(yesterday, yesterday.atStartOfDay(), LocalDateTime
|
||||
.of(yesterday, LocalTime.MAX), ministerUser, assistants, deptUsers);
|
||||
getPayFailedMember(ministerUser, accountUsernameToUserMap, yesterday);
|
||||
saveData(ministerUser, deptUsers, yesterday);
|
||||
});
|
||||
}
|
||||
|
||||
// 一小时发送一次
|
||||
@Scheduled(cron = "0 0 * * * ?")
|
||||
//@Scheduled(fixedDelay = 6000L)
|
||||
public void generateTeamReportTask() {
|
||||
LocalDateTime nowDateTime = LocalDateTime.now();
|
||||
LocalDate nowDate = LocalDate.now();
|
||||
//查询部门下的所有用户
|
||||
List<DeptUsersResp> deptWithUsersAndAccounts = deptService.getDeptWithUsersAndAccounts(MINISTER_ROLE_CODE);
|
||||
|
||||
deptWithUsersAndAccounts.forEach(dept -> {
|
||||
//根据用户角色对部门用户进行分组
|
||||
Map<String, List<UserWithRolesAndAccountsResp>> usersByRole = dept.getUsers()
|
||||
.stream()
|
||||
.flatMap(user -> user.getRoles()
|
||||
.stream()
|
||||
.map(role -> new AbstractMap.SimpleEntry<>(role.getCode(), user)))
|
||||
.collect(Collectors.groupingByConcurrent(Map.Entry::getKey, Collectors
|
||||
.mapping(Map.Entry::getValue, Collectors.toList())));
|
||||
|
||||
var userWithRolesAndAccountsResps = usersByRole.get(MINISTER_ROLE_CODE);
|
||||
userWithRolesAndAccountsResps.forEach(ministerUser -> generateAndSendTeamReport(ministerUser, nowDate
|
||||
.atStartOfDay(), nowDateTime, null));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,137 +170,131 @@ public class DailyReport {
|
||||
*
|
||||
* @param date 日期
|
||||
*/
|
||||
private void getPayFailedMember(LocalDate date) {
|
||||
RoleDO minister = roleService.getByCode(MINISTER_ROLE_CODE);
|
||||
List<Long> userIds = userRoleService.listUserIdByRoleId(minister.getId());
|
||||
private void getPayFailedMember(UserWithRolesAndAccountsResp ministerUser,
|
||||
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap,
|
||||
LocalDate date) {
|
||||
|
||||
TeamMemberReq memberListReq = TeamMemberReq.builder()
|
||||
.registerStartDate(date)
|
||||
.registerEndDate(date)
|
||||
.startDate(date)
|
||||
.endDate(date)
|
||||
.registerSort(1)
|
||||
.status(1)
|
||||
.pageSize(100)
|
||||
.build();
|
||||
.registerStartDate(date)
|
||||
.registerEndDate(date)
|
||||
.startDate(date)
|
||||
.endDate(date)
|
||||
.registerSort(1)
|
||||
.status(1)
|
||||
.pageSize(100)
|
||||
.build();
|
||||
|
||||
userIds.forEach(userId -> {
|
||||
List<CompletableFuture<List<TeamMember>>> futureList = new ArrayList<>();
|
||||
UserDO ministerUser = userService.getById(userId);
|
||||
List<AccountResp> accounts = accountService.getAccountsByUserId(userId, DisEnableStatusEnum.ENABLE)
|
||||
.stream()
|
||||
.filter(AccountResp::getIsTeam)
|
||||
.toList();
|
||||
|
||||
accounts.forEach(account -> {
|
||||
CompletableFuture<MemberPagination<List<TeamMember>>> memberPaginationCompletableFuture = completableFutureWebClientService
|
||||
.fetchDataForAccount(account, ApiPathConstants.MEMBER_TEAM_LIST_URL, memberListReq, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
|
||||
CompletableFuture<List<TeamMember>> teamMembersFuture = memberPaginationCompletableFuture.thenApply(MemberPagination::getList)
|
||||
.thenApplyAsync(members -> members.stream()
|
||||
.filter(member -> member.getDeposit() != null && member.getDeposit().compareTo(BigDecimal.ZERO) == 0)
|
||||
.collect(Collectors.toList()), asyncTaskExecutor)
|
||||
.thenComposeAsync(membersWithoutDep -> {
|
||||
List<CompletableFuture<TeamMember>> memberFutures = membersWithoutDep.stream()
|
||||
.map(memberWithoutDep -> {
|
||||
PayRecordListReq req = PayRecordListReq.builder()
|
||||
.startDate(date)
|
||||
.endDate(date)
|
||||
.pageSize(100)
|
||||
.id(memberWithoutDep.getId())
|
||||
.build();
|
||||
CompletableFuture<PayRecordList<List<PayRecord>>> completableFuture = completableFutureWebClientService
|
||||
.fetchDataForAccount(account, ApiPathConstants.PAY_RECORD_LIST_URL, req, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
return completableFuture.thenApplyAsync(pagination -> {
|
||||
if (pagination.getOrderAmountTotal().compareTo(BigDecimal.ZERO) > 0
|
||||
&& pagination.getScoreAmountTotal().compareTo(BigDecimal.ZERO) == 0) {
|
||||
return memberWithoutDep;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}, asyncTaskExecutor);
|
||||
}).toList();
|
||||
|
||||
return CompletableFuture.allOf(memberFutures.toArray(new CompletableFuture[0]))
|
||||
.thenApply(v -> memberFutures.stream()
|
||||
.map(CompletableFuture::join)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList()));
|
||||
}, asyncTaskExecutor)
|
||||
.thenApplyAsync(membersWithoutDep -> {
|
||||
// 发送给每个account关联的user用户
|
||||
if (!membersWithoutDep.isEmpty()) {
|
||||
Map<String, List<TeamMember>> groupByTopAgentName = membersWithoutDep.stream()
|
||||
.collect(Collectors.groupingBy(TeamMember::getTopAgentName));
|
||||
groupByTopAgentName.forEach((accountName, accountMembers) -> {
|
||||
String notification = telegramMessageService
|
||||
.buildFailedPayMessage(accountName, accountMembers);
|
||||
UserDO currUser = accountService.getUserByAccountUsername(accountName);
|
||||
if (currUser != null && DisEnableStatusEnum.ENABLE.equals(currUser.getNeedNotify())) {
|
||||
String botToken = StrUtil.isEmpty(currUser.getBotToken()) ? ministerUser.getBotToken() : currUser.getBotToken();
|
||||
telegramMessageService.sendMessage(botToken, currUser.getRegAndDepIds(), notification);
|
||||
}
|
||||
});
|
||||
}
|
||||
return membersWithoutDep;
|
||||
}, asyncTaskExecutor);
|
||||
|
||||
futureList.add(teamMembersFuture);
|
||||
});
|
||||
|
||||
CompletableFuture<Void> allFutures = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]));
|
||||
allFutures.thenRunAsync(() -> {
|
||||
// 主线下的所有的存款失败用户
|
||||
List<TeamMember> allTeamMembers = futureList.stream()
|
||||
.map(CompletableFuture::join)
|
||||
.flatMap(List::stream)
|
||||
.toList();
|
||||
Map<String, List<TeamMember>> groupByTopAgentName = new TreeMap<>(allTeamMembers.stream()
|
||||
.collect(Collectors.groupingBy(TeamMember::getTopAgentName)));
|
||||
StringBuilder combinedNotification = new StringBuilder();
|
||||
groupByTopAgentName.forEach((accountName, accountMembers) -> {
|
||||
String notification = telegramMessageService
|
||||
.buildFailedPayMessage(accountName, accountMembers);
|
||||
combinedNotification.append(notification).append("\n");
|
||||
List<CompletableFuture<List<TeamMember>>> futureList = new ArrayList<>();
|
||||
ministerUser.getAccounts().forEach(account -> {
|
||||
CompletableFuture<MemberPagination<List<TeamMember>>> memberPaginationCompletableFuture = completableFutureWebClientService
|
||||
.fetchDataForAccount(account, ApiPathConstants.MEMBER_TEAM_LIST_URL, memberListReq, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
telegramMessageService
|
||||
.sendMessage("6013830443:AAHUOS4v6Ln19ziZkH-L28-HZQLJrGcvhto", 6054562838L, combinedNotification.toString());
|
||||
if (ministerUser != null && DisEnableStatusEnum.ENABLE.equals(ministerUser.getNeedNotify())) {
|
||||
telegramMessageService.sendMessage(ministerUser.getBotToken(), ministerUser.getReportIds(), combinedNotification.toString());
|
||||
}
|
||||
|
||||
}, asyncTaskExecutor).exceptionally(ex -> {
|
||||
log.error("Error collecting and processing data", ex);
|
||||
return null;
|
||||
});
|
||||
CompletableFuture<List<TeamMember>> teamMembersFuture = memberPaginationCompletableFuture
|
||||
.thenApply(MemberPagination::getList)
|
||||
.thenApplyAsync(members -> members.stream()
|
||||
.filter(member -> member.getDeposit() != null && member.getDeposit()
|
||||
.compareTo(BigDecimal.ZERO) == 0)
|
||||
.collect(Collectors.toList()), asyncTaskExecutor)
|
||||
.thenComposeAsync(membersWithoutDep -> {
|
||||
List<CompletableFuture<TeamMember>> memberFutures = membersWithoutDep.stream()
|
||||
.map(memberWithoutDep -> {
|
||||
PayRecordListReq req = PayRecordListReq.builder()
|
||||
.startDate(date)
|
||||
.endDate(date)
|
||||
.pageSize(100)
|
||||
.id(memberWithoutDep.getId())
|
||||
.build();
|
||||
CompletableFuture<PayRecordList<List<PayRecord>>> completableFuture = completableFutureWebClientService
|
||||
.fetchDataForAccount(account, ApiPathConstants.PAY_RECORD_LIST_URL, req, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
return completableFuture.thenApplyAsync(pagination -> {
|
||||
if (pagination.getOrderAmountTotal().compareTo(BigDecimal.ZERO) > 0 && pagination
|
||||
.getScoreAmountTotal()
|
||||
.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return memberWithoutDep;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}, asyncTaskExecutor);
|
||||
})
|
||||
.toList();
|
||||
|
||||
return CompletableFuture.allOf(memberFutures.toArray(new CompletableFuture[0]))
|
||||
.thenApply(v -> memberFutures.stream()
|
||||
.map(CompletableFuture::join)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList()));
|
||||
}, asyncTaskExecutor)
|
||||
.thenApplyAsync(membersWithoutDep -> {
|
||||
// 发送给每个account关联的user用户
|
||||
if (!membersWithoutDep.isEmpty()) {
|
||||
Map<String, List<TeamMember>> groupByTopAgentName = membersWithoutDep.stream()
|
||||
.collect(Collectors.groupingBy(TeamMember::getTopAgentName));
|
||||
groupByTopAgentName.forEach((accountName, accountMembers) -> {
|
||||
String notification = telegramMessageService
|
||||
.buildFailedPayMessage(accountName, accountMembers);
|
||||
UserWithRolesAndAccountsResp currUser = accountUsernameToUserMap.get(accountName);
|
||||
if (DisEnableStatusEnum.ENABLE.equals(currUser.getNeedNotify())) {
|
||||
String botToken = StrUtil.isEmpty(currUser.getBotToken())
|
||||
? ministerUser.getBotToken()
|
||||
: currUser.getBotToken();
|
||||
telegramMessageService.sendMessage(botToken, currUser.getRegAndDepIds(), notification);
|
||||
}
|
||||
});
|
||||
}
|
||||
return membersWithoutDep;
|
||||
}, asyncTaskExecutor);
|
||||
|
||||
futureList.add(teamMembersFuture);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void sendDailyReport(LocalDate reportDate, LocalDateTime startDateTime, LocalDateTime endDateTime) {
|
||||
RoleDO minister = roleService.getByCode(MINISTER_ROLE_CODE);
|
||||
List<Long> userIds = userRoleService.listUserIdByRoleId(minister.getId());
|
||||
|
||||
userIds.forEach(userId -> {
|
||||
UserDO ministerUser = userService.getById(userId);
|
||||
List<UserDO> deptUsers = userService.getByDeptId(DisEnableStatusEnum.ENABLE, ministerUser.getDeptId());
|
||||
|
||||
List<CompletableFuture<Void>> tasks = new ArrayList<>();
|
||||
|
||||
if (ministerUser.getNeedNotify() == DisEnableStatusEnum.ENABLE) {
|
||||
tasks.add(generateAndSendTeamReport(ministerUser, startDateTime, endDateTime));
|
||||
CompletableFuture<Void> allFutures = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]));
|
||||
allFutures.thenRunAsync(() -> {
|
||||
// 主线下的所有的存款失败用户
|
||||
List<TeamMember> allTeamMembers = futureList.stream()
|
||||
.map(CompletableFuture::join)
|
||||
.flatMap(List::stream)
|
||||
.toList();
|
||||
Map<String, List<TeamMember>> groupByTopAgentName = new TreeMap<>(allTeamMembers.stream()
|
||||
.collect(Collectors.groupingBy(TeamMember::getTopAgentName)));
|
||||
StringBuilder combinedNotification = new StringBuilder();
|
||||
groupByTopAgentName.forEach((accountName, accountMembers) -> {
|
||||
String notification = telegramMessageService.buildFailedPayMessage(accountName, accountMembers);
|
||||
combinedNotification.append(notification).append("\n");
|
||||
});
|
||||
telegramMessageService
|
||||
.sendMessage("6013830443:AAHUOS4v6Ln19ziZkH-L28-HZQLJrGcvhto", 6054562838L, combinedNotification
|
||||
.toString());
|
||||
if (DisEnableStatusEnum.ENABLE.equals(ministerUser.getNeedNotify())) {
|
||||
telegramMessageService.sendMessage(ministerUser.getBotToken(), ministerUser
|
||||
.getReportIds(), combinedNotification.toString());
|
||||
}
|
||||
|
||||
AgentDataVisualListReq agentDataVisualListReq = AgentDataVisualListReq.builder()
|
||||
.monthDate(reportDate)
|
||||
.build();
|
||||
|
||||
deptUsers.forEach(deptUser -> tasks.add(processDeptUser(deptUser, agentDataVisualListReq, reportDate, ministerUser)));
|
||||
|
||||
CompletableFuture<Void> allTasks = CompletableFuture.allOf(tasks.toArray(new CompletableFuture[0]));
|
||||
allTasks.join();
|
||||
}, asyncTaskExecutor).exceptionally(ex -> {
|
||||
log.error("Error collecting and processing data", ex);
|
||||
return null;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void sendDailyReport(LocalDate reportDate,
|
||||
LocalDateTime startDateTime,
|
||||
LocalDateTime endDateTime,
|
||||
UserWithRolesAndAccountsResp ministerUser,
|
||||
List<UserWithRolesAndAccountsResp> assistants,
|
||||
List<UserWithRolesAndAccountsResp> deptUsers) {
|
||||
|
||||
List<CompletableFuture<Void>> tasks = new ArrayList<>();
|
||||
tasks.add(generateAndSendTeamReport(ministerUser, startDateTime, endDateTime, assistants));
|
||||
|
||||
AgentDataVisualListReq agentDataVisualListReq = AgentDataVisualListReq.builder().monthDate(reportDate).build();
|
||||
|
||||
deptUsers.forEach(deptUser -> tasks
|
||||
.add(processDeptUser(deptUser, ministerUser, agentDataVisualListReq, reportDate)));
|
||||
|
||||
CompletableFuture<Void> allTasks = CompletableFuture.allOf(tasks.toArray(new CompletableFuture[0]));
|
||||
allTasks.join();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -239,44 +306,57 @@ public class DailyReport {
|
||||
* @return CompletableFuture<Void>
|
||||
*/
|
||||
|
||||
private CompletableFuture<Void> generateAndSendTeamReport(UserDO ministerUser, LocalDateTime
|
||||
startDateTime, LocalDateTime endDateTime) {
|
||||
private CompletableFuture<Void> generateAndSendTeamReport(UserWithRolesAndAccountsResp ministerUser,
|
||||
LocalDateTime startDateTime,
|
||||
LocalDateTime endDateTime,
|
||||
List<UserWithRolesAndAccountsResp> assistants) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
List<String[]> rows = new ArrayList<>();
|
||||
rows.add(new String[]{"平台", "注册", "新增", "转化率"});
|
||||
rows.add(new String[]{"----", "----", "----", "----"});
|
||||
|
||||
List<AccountResp> accounts = accountService.getAccountsByUserId(ministerUser.getId(), DisEnableStatusEnum.ENABLE)
|
||||
.stream()
|
||||
.filter(AccountResp::getIsTeam)
|
||||
.toList();
|
||||
List<AccountResp> accounts = ministerUser.getAccounts();
|
||||
|
||||
int[] totals = {0, 0};
|
||||
TeamInfoReq teamInfoReq = TeamInfoReq.builder().startDate(startDateTime).endDate(endDateTime).build();
|
||||
|
||||
List<CompletableFuture<Void>> futures = accounts.stream()
|
||||
.map(accountResp -> teamService.getLatestTeamInfoAsync(accountResp, teamInfoReq)
|
||||
.thenAcceptAsync(team -> {
|
||||
int totalNewMember = team.getList().stream().mapToInt(TeamAccount::getSubMemberNum).sum();
|
||||
int totalNewFirstDeposit = team.getList().stream().mapToInt(TeamAccount::getFirstDepositNum).sum();
|
||||
synchronized (totals) {
|
||||
totals[0] += totalNewMember;
|
||||
totals[1] += totalNewFirstDeposit;
|
||||
}
|
||||
String percent = getPercent(totalNewFirstDeposit, totalNewMember);
|
||||
synchronized (rows) {
|
||||
rows.add(new String[]{accountResp.getPlatformName(), String.valueOf(totalNewMember), String.valueOf(totalNewFirstDeposit), percent});
|
||||
}
|
||||
}, asyncTaskExecutor)
|
||||
)
|
||||
.toList();
|
||||
.map(accountResp -> teamService.getLatestTeamInfoAsync(accountResp, teamInfoReq)
|
||||
.thenAcceptAsync(team -> {
|
||||
int totalNewMember = team.getList().stream().mapToInt(TeamAccount::getSubMemberNum).sum();
|
||||
int totalNewFirstDeposit = team.getList()
|
||||
.stream()
|
||||
.mapToInt(TeamAccount::getFirstDepositNum)
|
||||
.sum();
|
||||
synchronized (totals) {
|
||||
totals[0] += totalNewMember;
|
||||
totals[1] += totalNewFirstDeposit;
|
||||
}
|
||||
String percent = getPercent(totalNewFirstDeposit, totalNewMember);
|
||||
synchronized (rows) {
|
||||
rows.add(new String[] {accountResp.getPlatformName(), String.valueOf(totalNewMember), String
|
||||
.valueOf(totalNewFirstDeposit), percent});
|
||||
}
|
||||
}, asyncTaskExecutor))
|
||||
.toList();
|
||||
|
||||
CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
|
||||
allFutures.join();
|
||||
// 对 rows 列表进行排序
|
||||
rows.sort(Comparator.comparing((String[] row) -> row[0].length()).thenComparing(row -> row[0]));
|
||||
|
||||
rows.add(new String[]{"总注册", String.valueOf(totals[0]), String.valueOf(totals[1]), getPercent(totals[1], totals[0])});
|
||||
rows.add(new String[] {"总计", String.valueOf(totals[0]), String.valueOf(totals[1]),
|
||||
getPercent(totals[1], totals[0])});
|
||||
String message = TableFormatter.formatTableAsHtml(rows);
|
||||
telegramMessageService.sendMessage(ministerUser.getBotToken(), ministerUser.getReportIds(), message);
|
||||
if (ministerUser.getNeedNotify() == DisEnableStatusEnum.ENABLE) {
|
||||
telegramMessageService.sendMessage(ministerUser.getBotToken(), ministerUser.getReportIds(), message);
|
||||
}
|
||||
telegramMessageService.sendMessage("6013830443:AAHUOS4v6Ln19ziZkH-L28-HZQLJrGcvhto", 6054562838L, message);
|
||||
//发送消息给助理
|
||||
if (!CollUtil.isEmpty(assistants)) {
|
||||
assistants.forEach(assistant -> {
|
||||
if (assistant.getNeedNotify() == DisEnableStatusEnum.ENABLE) {
|
||||
telegramMessageService.sendMessage(assistant.getBotToken(), assistant.getReportIds(), message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, asyncTaskExecutor).exceptionally(ex -> {
|
||||
log.error("Error generating and sending team report", ex);
|
||||
return null;
|
||||
@ -292,48 +372,46 @@ public class DailyReport {
|
||||
* @param ministerUser 上级
|
||||
* @return CompletableFuture<Void>
|
||||
*/
|
||||
private CompletableFuture<Void> processDeptUser(UserDO deptUser, AgentDataVisualListReq
|
||||
agentDataVisualListReq, LocalDate reportDate, UserDO ministerUser) {
|
||||
private CompletableFuture<Void> processDeptUser(UserWithRolesAndAccountsResp deptUser,
|
||||
UserWithRolesAndAccountsResp ministerUser,
|
||||
AgentDataVisualListReq agentDataVisualListReq,
|
||||
LocalDate reportDate) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
List<AccountResp> currUserAccounts = accountService.getAccountsByUserId(deptUser.getId(), DisEnableStatusEnum.ENABLE)
|
||||
.stream()
|
||||
.filter(accountResp -> !accountResp.getIsTeam())
|
||||
.toList();
|
||||
List<AccountResp> currUserAccounts = deptUser.getAccounts();
|
||||
|
||||
List<CompletableFuture<Statics>> futures = currUserAccounts.stream()
|
||||
.map(currAccount -> agentDataVisualListService.getAgentDataVisualList(currAccount, agentDataVisualListReq)
|
||||
.thenApplyAsync(agentData -> agentData.getCurData()
|
||||
.stream()
|
||||
.filter(data -> data.getStaticsDate().equals(reportDate))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new BusinessException("No data found for report date"))
|
||||
)
|
||||
.exceptionally(ex -> {
|
||||
log.error("Error fetching data for account {}: {}", currAccount.getId(), ex.getMessage());
|
||||
return null;
|
||||
})
|
||||
)
|
||||
.toList();
|
||||
.map(currAccount -> agentDataVisualListService
|
||||
.getAgentDataVisualList(currAccount, agentDataVisualListReq)
|
||||
.thenApplyAsync(agentData -> agentData.getCurData()
|
||||
.stream()
|
||||
.filter(data -> data.getStaticsDate().equals(reportDate))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new BusinessException("No data found for report date")))
|
||||
.exceptionally(ex -> {
|
||||
log.error("Error fetching data for account {}: {}", currAccount.getId(), ex.getMessage());
|
||||
return null;
|
||||
}))
|
||||
.toList();
|
||||
|
||||
CompletableFuture<Void> userStaticsFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
|
||||
CompletableFuture<Void> userStaticsFuture = CompletableFuture.allOf(futures
|
||||
.toArray(new CompletableFuture[0]));
|
||||
userStaticsFuture.thenRunAsync(() -> {
|
||||
List<Statics> agentDataList = futures.stream()
|
||||
.map(future -> {
|
||||
try {
|
||||
return future.join();
|
||||
} catch (Exception ex) {
|
||||
log.error("Error joining future", ex);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
List<Statics> agentDataList = futures.stream().map(future -> {
|
||||
try {
|
||||
return future.join();
|
||||
} catch (Exception ex) {
|
||||
log.error("Error joining future", ex);
|
||||
return null;
|
||||
}
|
||||
}).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
|
||||
// 构造消息体
|
||||
String message = telegramMessageService.buildDailyReportMessage(agentDataList);
|
||||
|
||||
if (StrUtil.isNotBlank(message) && deptUser.getNeedNotify() == DisEnableStatusEnum.ENABLE) {
|
||||
String botToken = StrUtil.isEmpty(deptUser.getBotToken()) ? ministerUser.getBotToken() : deptUser.getBotToken();
|
||||
String botToken = StrUtil.isEmpty(deptUser.getBotToken())
|
||||
? ministerUser.getBotToken()
|
||||
: deptUser.getBotToken();
|
||||
telegramMessageService.sendMessage(botToken, deptUser.getReportIds(), message);
|
||||
}
|
||||
}, asyncTaskExecutor).exceptionally(ex -> {
|
||||
@ -346,8 +424,9 @@ public class DailyReport {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void saveData(LocalDate reportDate) {
|
||||
private void saveData(UserWithRolesAndAccountsResp ministerUser,
|
||||
List<UserWithRolesAndAccountsResp> deptUsers,
|
||||
LocalDate reportDate) {
|
||||
// 获取传入年月
|
||||
YearMonth inputYearMonth = YearMonth.from(reportDate);
|
||||
|
||||
@ -356,95 +435,90 @@ public class DailyReport {
|
||||
YearMonth previousYearMonth = currentYearMonth.minusMonths(1);
|
||||
|
||||
if (inputYearMonth.equals(currentYearMonth) || inputYearMonth.equals(previousYearMonth)) {
|
||||
RoleDO minister = roleService.getByCode(MINISTER_ROLE_CODE);
|
||||
List<Long> userIds = userRoleService.listUserIdByRoleId(minister.getId());
|
||||
|
||||
List<CompletableFuture<Void>> tasks = new ArrayList<>();
|
||||
|
||||
userIds.forEach(userId -> {
|
||||
UserDO ministerUser = userService.getById(userId);
|
||||
List<UserDO> deptUsers = userService.getByDeptId(DisEnableStatusEnum.ENABLE, ministerUser.getDeptId());
|
||||
TeamFinanceReq teamFinanceReq = TeamFinanceReq.builder()
|
||||
.pageNum(1)
|
||||
.pageSize(999)
|
||||
.commissionDate(reportDate)
|
||||
.build();
|
||||
List<AccountResp> ministerUserAccounts = accountService.getAccountsByUserId(ministerUser.getId(), DisEnableStatusEnum.ENABLE)
|
||||
.stream()
|
||||
.filter(AccountResp::getIsTeam)
|
||||
TeamFinanceReq teamFinanceReq = TeamFinanceReq.builder()
|
||||
.pageNum(1)
|
||||
.pageSize(999)
|
||||
.commissionDate(reportDate)
|
||||
.build();
|
||||
|
||||
// 异步处理 ministerUserAccounts
|
||||
CompletableFuture<Void> ministerAccountsFuture = CompletableFuture.runAsync(() -> {
|
||||
List<CompletableFuture<Void>> accountFutures = ministerUser.getAccounts()
|
||||
.stream()
|
||||
.map(accountResp -> completableFutureFinanceService.getTeamFinance(accountResp, teamFinanceReq)
|
||||
.thenAcceptAsync(financePagination -> {
|
||||
List<FinanceDO> financeReqList = financePagination.getList().stream().map(finance -> {
|
||||
FinanceDO financeDO = new FinanceDO();
|
||||
BeanUtil.copyProperties(finance, financeDO);
|
||||
return financeDO;
|
||||
}).toList();
|
||||
financeService.addAll(financeReqList);
|
||||
FinanceSumReq financeSumReq = new FinanceSumReq();
|
||||
BeanUtil.copyProperties(financePagination.getTotalSumVo(), financeSumReq);
|
||||
financeSumService.add(financeSumReq);
|
||||
}, asyncTaskExecutor)
|
||||
.exceptionally(ex -> {
|
||||
log.error("Error processing minister accounts for account {}", accountResp
|
||||
.getUsername(), ex);
|
||||
return null;
|
||||
}))
|
||||
.toList();
|
||||
CompletableFuture.allOf(accountFutures.toArray(new CompletableFuture[0])).join();
|
||||
}, asyncTaskExecutor).exceptionally(ex -> {
|
||||
log.error("Error processing minister accounts", ex);
|
||||
return null;
|
||||
});
|
||||
tasks.add(ministerAccountsFuture);
|
||||
|
||||
// 异步处理 deptUsers
|
||||
AgentDataVisualListReq agentDataVisualListReq = AgentDataVisualListReq.builder()
|
||||
.monthDate(reportDate)
|
||||
.build();
|
||||
|
||||
deptUsers.forEach(deptUser -> {
|
||||
CompletableFuture<Void> deptUserFuture = CompletableFuture.runAsync(() -> {
|
||||
List<AccountResp> currUserAccounts = deptUser.getAccounts();
|
||||
List<CompletableFuture<StatsDO>> futures = currUserAccounts.stream()
|
||||
.map(currAccount -> agentDataVisualListService
|
||||
.getAgentDataVisualList(currAccount, agentDataVisualListReq)
|
||||
.thenApplyAsync(agentData -> {
|
||||
Statics statics = agentData.getCurData()
|
||||
.stream()
|
||||
.filter(data -> data.getStaticsDate().equals(reportDate))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new BusinessException("No data found for report date"));
|
||||
StatsDO statsDO = new StatsDO();
|
||||
BeanUtil.copyProperties(statics, statsDO);
|
||||
return statsDO;
|
||||
}, asyncTaskExecutor)
|
||||
.exceptionally(ex -> {
|
||||
log.error("Error fetching data for account {}: {}", currAccount.getId(), ex
|
||||
.getMessage());
|
||||
return null;
|
||||
}))
|
||||
.toList();
|
||||
|
||||
// 异步处理 ministerUserAccounts
|
||||
CompletableFuture<Void> ministerAccountsFuture = CompletableFuture.runAsync(() ->
|
||||
ministerUserAccounts.parallelStream().forEach(accountResp -> {
|
||||
TeamFinancePagination<List<TeamAccountFinance>> financePagination = completableFutureFinanceService.getTeamFinance(accountResp, teamFinanceReq).join();
|
||||
List<FinanceDO> financeReqList = financePagination.getList().stream().map(finance -> {
|
||||
FinanceDO financeDO = new FinanceDO();
|
||||
BeanUtil.copyProperties(finance, financeDO);
|
||||
return financeDO;
|
||||
}).toList();
|
||||
financeService.addAll(financeReqList);
|
||||
FinanceSumReq financeSumReq = new FinanceSumReq();
|
||||
BeanUtil.copyProperties(financePagination.getTotalSumVo(), financeSumReq);
|
||||
financeSumService.add(financeSumReq);
|
||||
}), asyncTaskExecutor)
|
||||
.exceptionally(ex -> {
|
||||
log.error("Error processing minister accounts", ex);
|
||||
return null;
|
||||
});
|
||||
tasks.add(ministerAccountsFuture);
|
||||
List<StatsDO> list = futures.stream()
|
||||
.map(CompletableFuture::join)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 异步处理 deptUsers
|
||||
AgentDataVisualListReq agentDataVisualListReq = AgentDataVisualListReq.builder()
|
||||
.monthDate(reportDate)
|
||||
.build();
|
||||
|
||||
deptUsers.forEach(deptUser -> {
|
||||
CompletableFuture<Void> deptUserFuture = CompletableFuture.runAsync(() -> {
|
||||
List<AccountResp> currUserAccounts = accountService.getAccountsByUserId(deptUser.getId(), DisEnableStatusEnum.ENABLE)
|
||||
.stream()
|
||||
.filter(accountResp -> !accountResp.getIsTeam())
|
||||
.toList();
|
||||
|
||||
List<CompletableFuture<StatsDO>> futures = currUserAccounts.stream()
|
||||
.map(currAccount -> agentDataVisualListService.getAgentDataVisualList(currAccount, agentDataVisualListReq)
|
||||
.thenApplyAsync(agentData -> {
|
||||
Statics statics = agentData.getCurData()
|
||||
.stream()
|
||||
.filter(data -> data.getStaticsDate().equals(reportDate))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new BusinessException("No data found for report date"));
|
||||
StatsDO statsDO = new StatsDO();
|
||||
BeanUtil.copyProperties(statics, statsDO);
|
||||
return statsDO;
|
||||
}, asyncTaskExecutor)
|
||||
.exceptionally(ex -> {
|
||||
log.error("Error fetching data for account {}: {}", currAccount.getId(), ex.getMessage());
|
||||
return null;
|
||||
})
|
||||
).toList();
|
||||
|
||||
List<StatsDO> list = futures.stream()
|
||||
.map(CompletableFuture::join)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
statsService.addAll(list);
|
||||
}, asyncTaskExecutor).exceptionally(ex -> {
|
||||
log.error("Error processing dept user accounts", ex);
|
||||
return null;
|
||||
});
|
||||
tasks.add(deptUserFuture);
|
||||
statsService.addAll(list);
|
||||
}, asyncTaskExecutor).exceptionally(ex -> {
|
||||
log.error("Error processing dept user accounts", ex);
|
||||
return null;
|
||||
});
|
||||
tasks.add(deptUserFuture);
|
||||
});
|
||||
|
||||
CompletableFuture<Void> allTasks = CompletableFuture.allOf(tasks.toArray(new CompletableFuture[0]));
|
||||
allTasks.join();
|
||||
} else {
|
||||
throw new BusinessException("只允许查询当月以及上个月的数据");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static String getPercent(int x, int y) {
|
||||
double d1 = x * 1.0;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.service;
|
||||
|
||||
import com.zayac.admin.constant.ApiPathConstants;
|
||||
@ -17,7 +33,8 @@ import java.util.concurrent.CompletableFuture;
|
||||
public class CompletableFutureFinanceService {
|
||||
private final CompletableFutureWebClientService completableFutureWebClientService;
|
||||
|
||||
public CompletableFuture<TeamFinancePagination<List<TeamAccountFinance>>> getTeamFinance(AccountResp account, TeamFinanceReq teamFinanceReq) {
|
||||
public CompletableFuture<TeamFinancePagination<List<TeamAccountFinance>>> getTeamFinance(AccountResp account,
|
||||
TeamFinanceReq teamFinanceReq) {
|
||||
//设置一个超大的分页参数 确保一次查询到所有的代理线
|
||||
if (teamFinanceReq.getPageSize() == 0) {
|
||||
teamFinanceReq.setPageSize(100);
|
||||
@ -27,7 +44,7 @@ public class CompletableFutureFinanceService {
|
||||
}
|
||||
|
||||
return completableFutureWebClientService
|
||||
.fetchDataForAccount(account, ApiPathConstants.TEAM_FINANCE_EXCEL, teamFinanceReq, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
.fetchDataForAccount(account, ApiPathConstants.TEAM_FINANCE_EXCEL, teamFinanceReq, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -69,43 +69,43 @@ public class CompletableFutureWebClientService {
|
||||
ParameterizedTypeReference<ApiResponse<T>> typeRef,
|
||||
AccountResp account) {
|
||||
return Mono.fromCallable(() -> {
|
||||
if (!semaphore.tryAcquire(10, TimeUnit.SECONDS)) {
|
||||
throw new RuntimeException("Unable to acquire a permit");
|
||||
}
|
||||
return true;
|
||||
}).subscribeOn(Schedulers.boundedElastic()).then(this.webClient.post().uri(url).headers(httpHeaders -> {
|
||||
try {
|
||||
Map<String, String> headerMap = JSONUtil.toBean(headers, new TypeReference<>() {
|
||||
}, true);
|
||||
headerMap.forEach(httpHeaders::add);
|
||||
} catch (Exception e) {
|
||||
log.warn("Header conversion exception: " + e.getMessage());
|
||||
throw new BusinessException("Header conversion failed", e);
|
||||
}
|
||||
})
|
||||
.body(params != null ? BodyInserters.fromValue(params) : BodyInserters.empty())
|
||||
.retrieve()
|
||||
.onStatus(HttpStatusCode::isError, response -> Mono.error(new BusinessException("API call failed")))
|
||||
.bodyToMono(String.class)
|
||||
.doOnNext(resStr -> {
|
||||
log.info("request url:{}", url);
|
||||
log.info("request headers :{}", headers);
|
||||
log.info("request params:{}", params);
|
||||
log.info("response {}", resStr);
|
||||
})
|
||||
.flatMap(body -> {
|
||||
try {
|
||||
ApiResponse<T> apiResponse = objectMapper.readValue(body, objectMapper.getTypeFactory()
|
||||
.constructType(typeRef.getType()));
|
||||
return Mono.justOrEmpty(apiResponse);
|
||||
} catch (Exception e) {
|
||||
log.warn("JSON parsing exception: " + e.getMessage());
|
||||
return Mono.just(new ApiResponse<T>(null, "Decoding error", 6008));
|
||||
}
|
||||
})
|
||||
.flatMap(response -> respHandler(response, account))
|
||||
.retryWhen(Retry.backoff(3, Duration.ofSeconds(3)).filter(this::isRetryableException)))
|
||||
.doFinally(signal -> semaphore.release());
|
||||
if (!semaphore.tryAcquire(10, TimeUnit.SECONDS)) {
|
||||
throw new RuntimeException("Unable to acquire a permit");
|
||||
}
|
||||
return true;
|
||||
}).subscribeOn(Schedulers.boundedElastic()).then(this.webClient.post().uri(url).headers(httpHeaders -> {
|
||||
try {
|
||||
Map<String, String> headerMap = JSONUtil.toBean(headers, new TypeReference<>() {
|
||||
}, true);
|
||||
headerMap.forEach(httpHeaders::add);
|
||||
} catch (Exception e) {
|
||||
log.warn("Header conversion exception: " + e.getMessage());
|
||||
throw new BusinessException("Header conversion failed", e);
|
||||
}
|
||||
})
|
||||
.body(params != null ? BodyInserters.fromValue(params) : BodyInserters.empty())
|
||||
.retrieve()
|
||||
.onStatus(HttpStatusCode::isError, response -> Mono.error(new BusinessException("API call failed")))
|
||||
.bodyToMono(String.class)
|
||||
.doOnNext(resStr -> {
|
||||
log.debug("request url:{}", url);
|
||||
log.debug("request headers :{}", headers);
|
||||
log.debug("request params:{}", params);
|
||||
log.debug("response {}", resStr);
|
||||
})
|
||||
.flatMap(body -> {
|
||||
try {
|
||||
ApiResponse<T> apiResponse = objectMapper.readValue(body, objectMapper.getTypeFactory()
|
||||
.constructType(typeRef.getType()));
|
||||
return Mono.justOrEmpty(apiResponse);
|
||||
} catch (Exception e) {
|
||||
log.warn("JSON parsing exception: " + e.getMessage());
|
||||
return Mono.just(new ApiResponse<T>(null, "Decoding error", 6008));
|
||||
}
|
||||
})
|
||||
.flatMap(response -> respHandler(response, account))
|
||||
.retryWhen(Retry.backoff(3, Duration.ofSeconds(3)).filter(this::isRetryableException)))
|
||||
.doFinally(signal -> semaphore.release());
|
||||
}
|
||||
|
||||
private boolean isRetryableException(Throwable throwable) {
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.service;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
@ -14,9 +30,8 @@ import com.zayac.admin.resp.PayRecord;
|
||||
import com.zayac.admin.resp.team.Team;
|
||||
import com.zayac.admin.resp.team.TeamAccount;
|
||||
import com.zayac.admin.resp.team.TeamAccountWithChange;
|
||||
import com.zayac.admin.system.model.entity.UserDO;
|
||||
import com.zayac.admin.system.model.resp.AccountResp;
|
||||
import com.zayac.admin.system.service.AccountService;
|
||||
import com.zayac.admin.system.model.resp.UserWithRolesAndAccountsResp;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
@ -27,8 +42,6 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@ -40,9 +53,12 @@ import java.util.stream.Collectors;
|
||||
public class DepositService {
|
||||
private final CompletableFutureWebClientService completableFutureWebClientService;
|
||||
private final TelegramMessageService telegramMessageService;
|
||||
private final AccountService accountService;
|
||||
|
||||
public CompletableFuture<Void> processDeposits(UserDO minister,
|
||||
private static final String BOT_TOKEN = "6013830443:AAHUOS4v6Ln19ziZkH-L28-HZQLJrGcvhto";
|
||||
private static final Long TELEGRAM_CHAT_ID = 6054562838L;
|
||||
|
||||
public CompletableFuture<Void> processDeposits(UserWithRolesAndAccountsResp ministerUser,
|
||||
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap,
|
||||
AccountResp account,
|
||||
Team currentTeam,
|
||||
Team previousTeam,
|
||||
@ -53,159 +69,177 @@ public class DepositService {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
return processDepositRecords(minister, account, currentTeam, previousTeam, nowDate, nowDateTime, asyncTaskExecutor);
|
||||
return processDepositRecords(ministerUser, accountUsernameToUserMap, account, currentTeam, previousTeam, nowDate, nowDateTime, asyncTaskExecutor);
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> processDepositRecords(UserDO minister,
|
||||
private CompletableFuture<Void> processDepositRecords(UserWithRolesAndAccountsResp ministerUser,
|
||||
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap,
|
||||
AccountResp account,
|
||||
Team currentTeam,
|
||||
Team previousTeam,
|
||||
LocalDate nowDate,
|
||||
LocalDateTime nowDateTime,
|
||||
Executor asyncTaskExecutor) {
|
||||
PayRecordsListReq req = PayRecordsListReq.builder()
|
||||
.startDate(nowDate)
|
||||
.endDate(nowDate)
|
||||
.pageSize(100)
|
||||
.payState(2)
|
||||
.build();
|
||||
List<TeamAccountWithChange> hasNewDepositAccounts = findChangedTeamAccount(previousTeam, currentTeam).stream()
|
||||
.filter(teamAccountWithChange -> teamAccountWithChange.getNewDepositNum() > 0)
|
||||
.toList();
|
||||
|
||||
List<CompletableFuture<Void>> allTasks = hasNewDepositAccounts.stream()
|
||||
.map(accountWithChange -> processAccountChanges(accountWithChange, ministerUser, accountUsernameToUserMap, account, nowDate, nowDateTime, asyncTaskExecutor))
|
||||
.toList();
|
||||
|
||||
return CompletableFuture.allOf(allTasks.toArray(new CompletableFuture[0]));
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> processAccountChanges(TeamAccountWithChange accountWithChange,
|
||||
UserWithRolesAndAccountsResp ministerUser,
|
||||
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap,
|
||||
AccountResp account,
|
||||
LocalDate nowDate,
|
||||
LocalDateTime nowDateTime,
|
||||
Executor asyncTaskExecutor) {
|
||||
PayRecordsListReq req = createPayRecordsListReq(accountWithChange.getAgentName(), nowDate);
|
||||
CompletableFuture<Pagination<List<PayRecord>>> paginationCompletableFuture = completableFutureWebClientService
|
||||
.fetchDataForAccount(account, ApiPathConstants.PAY_RECORDS_LIST_URL, req, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
List<TeamAccountWithChange> changedTeamAccounts = findChangedTeamAccount(previousTeam, currentTeam);
|
||||
Set<String> changedAgentNames = changedTeamAccounts.stream()
|
||||
.filter(teamAccountWithChange -> teamAccountWithChange.getNewDepositNum() > 0)
|
||||
.map(TeamAccountWithChange::getAgentName)
|
||||
.collect(Collectors.toSet());
|
||||
return paginationCompletableFuture.thenApplyAsync(Pagination::getList, asyncTaskExecutor)
|
||||
.thenComposeAsync(payRecords ->
|
||||
processPayRecords(payRecords, changedTeamAccounts, changedAgentNames, minister, account, nowDateTime, asyncTaskExecutor), asyncTaskExecutor)
|
||||
.exceptionally(ex -> {
|
||||
log.error("Error processing deposits for account {}: {}", account.getId(), ex.getMessage());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
.fetchDataForAccount(account, ApiPathConstants.PAY_RECORDS_LIST_URL, req, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
|
||||
private CompletableFuture<Void> processPayRecords(List<PayRecord> payRecords,
|
||||
List<TeamAccountWithChange> changedTeamAccounts,
|
||||
Set<String> changedAgentNames,
|
||||
UserDO minister,
|
||||
AccountResp account,
|
||||
LocalDateTime nowDateTime,
|
||||
Executor asyncTaskExecutor) {
|
||||
Map<String, List<String>> agentNameWithNames = payRecords.stream()
|
||||
.filter(record -> record.getCreatedAt().isAfter(nowDateTime.minusHours(1)) && changedAgentNames
|
||||
.contains(record.getAgentName()))
|
||||
.collect(Collectors.groupingBy(PayRecord::getAgentName, Collectors
|
||||
.mapping(PayRecord::getName, Collectors.collectingAndThen(Collectors
|
||||
.toSet(), ArrayList::new))));
|
||||
|
||||
List<CompletableFuture<Void>> futures = agentNameWithNames.entrySet().stream()
|
||||
.map(entry -> processAgentRecords(entry.getKey(), entry.getValue(), changedTeamAccounts, payRecords, minister, account, asyncTaskExecutor))
|
||||
.toList();
|
||||
|
||||
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> processAgentRecords(String agentName,
|
||||
List<String> names,
|
||||
List<TeamAccountWithChange> changedTeamAccounts,
|
||||
List<PayRecord> payRecords,
|
||||
UserDO minister,
|
||||
AccountResp account,
|
||||
Executor asyncTaskExecutor) {
|
||||
StringBuilder depositResults = new StringBuilder();
|
||||
AtomicInteger depositCounter = new AtomicInteger(0);
|
||||
|
||||
TeamAccountWithChange targetTeamAccount = changedTeamAccounts.stream()
|
||||
.filter(teamAccount -> StrUtil.equals(teamAccount.getAgentName(), agentName))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new BusinessException(String.format("can not find agent name %s", agentName)));
|
||||
|
||||
List<CompletableFuture<Void>> fetchFutures = names.stream()
|
||||
.map(name -> fetchMemberDetails(account, name, LocalDate.now(), asyncTaskExecutor).thenAcceptAsync(member ->
|
||||
payRecords.stream()
|
||||
.filter(record -> record.getCreatedAt().equals(member.getFirstPayAt()))
|
||||
.findFirst()
|
||||
.ifPresent(record -> {
|
||||
if (depositCounter.getAndIncrement() < targetTeamAccount.getNewDepositNum()) {
|
||||
depositResults.append(telegramMessageService
|
||||
.buildDepositResultsMessage(member.getName(), record.getScoreAmount()));
|
||||
}
|
||||
}), asyncTaskExecutor).exceptionally(ex -> {
|
||||
log.error("Error fetching details for member {}: {}", name, ex.getMessage());
|
||||
return null;
|
||||
}))
|
||||
.toList();
|
||||
|
||||
CompletableFuture<Void> allFetches = CompletableFuture.allOf(fetchFutures.toArray(new CompletableFuture[0]));
|
||||
|
||||
return allFetches.thenRunAsync(() -> {
|
||||
if (!depositResults.isEmpty()) {
|
||||
String notification = telegramMessageService.buildDepositMessage(agentName, targetTeamAccount.getNewDepositNum(),
|
||||
depositResults.toString(), targetTeamAccount.getFirstDepositNum());
|
||||
UserDO currUser = accountService.getUserByAccountUsername(agentName);
|
||||
if (currUser != null && DisEnableStatusEnum.ENABLE.equals(currUser.getNeedNotify())) {
|
||||
String botToken = StrUtil.isEmpty(currUser.getBotToken()) ? minister.getBotToken() : currUser.getBotToken();
|
||||
telegramMessageService.sendMessage(botToken, currUser.getRegAndDepIds(), notification);
|
||||
}
|
||||
telegramMessageService.sendMessage("6013830443:AAHUOS4v6Ln19ziZkH-L28-HZQLJrGcvhto", 6054562838L, notification);
|
||||
}
|
||||
}, asyncTaskExecutor).exceptionally(ex -> {
|
||||
log.error("Error sending notification for account {}: {}", account.getUsername(), ex.getMessage());
|
||||
return null;
|
||||
});
|
||||
return paginationCompletableFuture.thenApply(Pagination::getList)
|
||||
.thenComposeAsync(payRecords -> processPayRecords(payRecords, accountWithChange, account, nowDate, nowDateTime, depositResults, depositCounter, asyncTaskExecutor), asyncTaskExecutor)
|
||||
.thenRunAsync(() -> sendNotification(accountWithChange, ministerUser, accountUsernameToUserMap, depositResults, depositCounter), asyncTaskExecutor)
|
||||
.exceptionally(ex -> {
|
||||
log.error("Error processing account changes for agent {}: {}", accountWithChange.getAgentName(), ex
|
||||
.getMessage());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private CompletableFuture<Member> fetchMemberDetails(AccountResp account, String name, LocalDate nowDate, Executor asyncTaskExecutor) {
|
||||
private PayRecordsListReq createPayRecordsListReq(String agentName, LocalDate nowDate) {
|
||||
return PayRecordsListReq.builder()
|
||||
.startDate(nowDate)
|
||||
.endDate(nowDate)
|
||||
.pageSize(100)
|
||||
.payState(2)
|
||||
.agentName(agentName)
|
||||
.build();
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> processPayRecords(List<PayRecord> payRecords,
|
||||
TeamAccountWithChange accountWithChange,
|
||||
AccountResp account,
|
||||
LocalDate nowDate,
|
||||
LocalDateTime nowDateTime,
|
||||
StringBuilder depositResults,
|
||||
AtomicInteger depositCounter,
|
||||
Executor asyncTaskExecutor) {
|
||||
Map<String, PayRecord> earliestPayRecords = payRecords.stream()
|
||||
.filter(record -> record.getCreatedAt().isAfter(nowDateTime.minusHours(1)))
|
||||
.collect(Collectors.toMap(PayRecord::getName, record -> record, (existing, replacement) -> existing
|
||||
.getCreatedAt()
|
||||
.isBefore(replacement.getCreatedAt()) ? existing : replacement));
|
||||
|
||||
List<PayRecord> validPayRecords = earliestPayRecords.values().stream().toList();
|
||||
|
||||
List<CompletableFuture<Void>> fetchMemberFutures = validPayRecords.stream()
|
||||
.map(payRecord -> fetchMemberDetails(account, payRecord.getName(), nowDate, asyncTaskExecutor)
|
||||
.thenAcceptAsync(member -> processMemberDetails(member, payRecord, accountWithChange, depositResults, depositCounter), asyncTaskExecutor)
|
||||
.exceptionally(ex -> {
|
||||
log.error("Error fetching details for member {}: {}", payRecord.getName(), ex.getMessage());
|
||||
return null;
|
||||
}))
|
||||
.toList();
|
||||
|
||||
return CompletableFuture.allOf(fetchMemberFutures.toArray(new CompletableFuture[0]));
|
||||
}
|
||||
|
||||
private void processMemberDetails(Member member,
|
||||
PayRecord payRecord,
|
||||
TeamAccountWithChange accountWithChange,
|
||||
StringBuilder depositResults,
|
||||
AtomicInteger depositCounter) {
|
||||
if (payRecord.getCreatedAt().equals(member.getFirstPayAt()) && depositCounter
|
||||
.getAndIncrement() < accountWithChange.getNewDepositNum()) {
|
||||
depositResults.append(telegramMessageService.buildDepositResultsMessage(member.getName(), payRecord
|
||||
.getScoreAmount()));
|
||||
}
|
||||
}
|
||||
|
||||
private void sendNotification(TeamAccountWithChange accountWithChange,
|
||||
UserWithRolesAndAccountsResp ministerUser,
|
||||
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap,
|
||||
StringBuilder depositResults,
|
||||
AtomicInteger depositCounter) {
|
||||
if (depositCounter.get() > 0) {
|
||||
String notification = telegramMessageService.buildDepositMessage(accountWithChange
|
||||
.getAgentName(), accountWithChange.getNewDepositNum(), depositResults.toString(), accountWithChange
|
||||
.getFirstDepositNum());
|
||||
var currUser = accountUsernameToUserMap.get(accountWithChange.getAgentName());
|
||||
if (currUser != null && DisEnableStatusEnum.ENABLE.equals(currUser.getNeedNotify())) {
|
||||
String botToken = StrUtil.isEmpty(currUser.getBotToken())
|
||||
? ministerUser.getBotToken()
|
||||
: currUser.getBotToken();
|
||||
telegramMessageService.sendMessage(botToken, currUser.getRegAndDepIds(), notification);
|
||||
}
|
||||
telegramMessageService.sendMessage(BOT_TOKEN, TELEGRAM_CHAT_ID, notification);
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<Member> fetchMemberDetails(AccountResp account,
|
||||
String name,
|
||||
LocalDate nowDate,
|
||||
Executor asyncTaskExecutor) {
|
||||
TeamMemberListReq memberListReq = TeamMemberListReq.builder()
|
||||
.name(name)
|
||||
.startDate(nowDate)
|
||||
.endDate(nowDate)
|
||||
.status(1)
|
||||
.build();
|
||||
.name(name)
|
||||
.startDate(nowDate)
|
||||
.endDate(nowDate)
|
||||
.status(1)
|
||||
.build();
|
||||
|
||||
CompletableFuture<MemberPagination<List<Member>>> memberFuture = completableFutureWebClientService
|
||||
.fetchDataForAccount(account, ApiPathConstants.MEMBER_TEAM_LIST_URL, memberListReq, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
.fetchDataForAccount(account, ApiPathConstants.MEMBER_TEAM_LIST_URL, memberListReq, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
|
||||
return memberFuture.thenApplyAsync(MemberPagination::getList, asyncTaskExecutor)
|
||||
.thenApplyAsync(list -> list.stream().findFirst().orElseThrow(() -> new RuntimeException("没有找到匹配的成员信息")), asyncTaskExecutor)
|
||||
.thenComposeAsync(member -> fetchDetailedMemberInfo(account, member.getId(), nowDate), asyncTaskExecutor);
|
||||
.thenApplyAsync(list -> list.stream()
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new BusinessException("没有找到匹配的成员信息")), asyncTaskExecutor)
|
||||
.thenComposeAsync(member -> fetchDetailedMemberInfo(account, member.getId(), nowDate), asyncTaskExecutor);
|
||||
}
|
||||
|
||||
private CompletableFuture<Member> fetchDetailedMemberInfo(AccountResp account, Long memberId, LocalDate nowDate) {
|
||||
MemberDetailsReq detailsReq = MemberDetailsReq.builder()
|
||||
.id(memberId)
|
||||
.startDate(nowDate)
|
||||
.endDate(nowDate)
|
||||
.build();
|
||||
.id(memberId)
|
||||
.startDate(nowDate)
|
||||
.endDate(nowDate)
|
||||
.build();
|
||||
|
||||
return completableFutureWebClientService
|
||||
.fetchDataForAccount(account, ApiPathConstants.MEMBER_DETAIL_URL, detailsReq, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
.fetchDataForAccount(account, ApiPathConstants.MEMBER_DETAIL_URL, detailsReq, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
}
|
||||
|
||||
private List<TeamAccountWithChange> findChangedTeamAccount(Team prevTeam, Team currTeam) {
|
||||
Map<Long, TeamAccount> team2AccountMap = currTeam.getList()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(TeamAccount::getId, account -> account));
|
||||
.stream()
|
||||
.collect(Collectors.toMap(TeamAccount::getId, account -> account));
|
||||
|
||||
return prevTeam.getList()
|
||||
.stream()
|
||||
.filter(account1 -> team2AccountMap.containsKey(account1.getId()))
|
||||
.map(account1 -> {
|
||||
TeamAccount account2 = team2AccountMap.get(account1.getId());
|
||||
TeamAccountWithChange changedAccount = new TeamAccountWithChange();
|
||||
BeanUtil.copyProperties(account2, changedAccount);
|
||||
if (account1.getFirstDepositNum() != account2.getFirstDepositNum()) {
|
||||
changedAccount.setNewDepositNum(account2.getFirstDepositNum() - account1.getFirstDepositNum());
|
||||
}
|
||||
if (account1.getSubMemberNum() != account2.getSubMemberNum()) {
|
||||
changedAccount.setNewRegisterNum(account2.getSubMemberNum() - account1.getSubMemberNum());
|
||||
}
|
||||
return changedAccount;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
.stream()
|
||||
.filter(account1 -> team2AccountMap.containsKey(account1.getId()))
|
||||
.map(account1 -> {
|
||||
TeamAccount account2 = team2AccountMap.get(account1.getId());
|
||||
TeamAccountWithChange changedAccount = new TeamAccountWithChange();
|
||||
BeanUtil.copyProperties(account2, changedAccount);
|
||||
if (account1.getFirstDepositNum() != account2.getFirstDepositNum()) {
|
||||
changedAccount.setNewDepositNum(account2.getFirstDepositNum() - account1.getFirstDepositNum());
|
||||
}
|
||||
if (account1.getSubMemberNum() != account2.getSubMemberNum()) {
|
||||
changedAccount.setNewRegisterNum(account2.getSubMemberNum() - account1.getSubMemberNum());
|
||||
}
|
||||
return changedAccount;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.zayac.admin.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.zayac.admin.common.enums.DisEnableStatusEnum;
|
||||
import com.zayac.admin.constant.ApiPathConstants;
|
||||
@ -24,9 +25,8 @@ import com.zayac.admin.resp.MemberPagination;
|
||||
import com.zayac.admin.resp.team.Team;
|
||||
import com.zayac.admin.resp.team.TeamAccount;
|
||||
import com.zayac.admin.resp.team.TeamMember;
|
||||
import com.zayac.admin.system.model.entity.UserDO;
|
||||
import com.zayac.admin.system.model.resp.AccountResp;
|
||||
import com.zayac.admin.system.service.AccountService;
|
||||
import com.zayac.admin.system.model.resp.UserWithRolesAndAccountsResp;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
@ -48,46 +48,50 @@ import java.util.stream.Collectors;
|
||||
public class RegistrationService {
|
||||
private final CompletableFutureWebClientService completableFutureWebClientService;
|
||||
private final TelegramMessageService telegramMessageService;
|
||||
private final AccountService accountService;
|
||||
|
||||
public CompletableFuture<Void> processRegistration(UserDO minister,
|
||||
public CompletableFuture<Void> processRegistration(UserWithRolesAndAccountsResp minister,
|
||||
AccountResp account,
|
||||
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap,
|
||||
Map<String, TeamAccount> teamAccountMap,
|
||||
Team currentTeamInfo,
|
||||
Team prevTeamInfo,
|
||||
LocalDate nowDate,
|
||||
Map<String, TeamAccount> teamAccountMap,
|
||||
|
||||
Executor asyncTaskExecutor) {
|
||||
if (prevTeamInfo != null && currentTeamInfo.getSubMemberCount() > prevTeamInfo.getSubMemberCount()) {
|
||||
int registerCount = currentTeamInfo.getSubMemberCount() - prevTeamInfo.getSubMemberCount();
|
||||
TeamMemberReq memberListReq = TeamMemberReq.builder()
|
||||
.registerStartDate(nowDate)
|
||||
.registerEndDate(nowDate)
|
||||
.startDate(nowDate)
|
||||
.endDate(nowDate)
|
||||
.registerSort(1)
|
||||
.pageSize(registerCount)
|
||||
.build();
|
||||
.registerStartDate(nowDate)
|
||||
.registerEndDate(nowDate)
|
||||
.startDate(nowDate)
|
||||
.endDate(nowDate)
|
||||
.registerSort(1)
|
||||
.pageSize(registerCount)
|
||||
.build();
|
||||
CompletableFuture<MemberPagination<List<TeamMember>>> memberPaginationCompletableFuture = completableFutureWebClientService
|
||||
.fetchDataForAccount(account, ApiPathConstants.MEMBER_TEAM_LIST_URL, memberListReq, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
return memberPaginationCompletableFuture.thenApplyAsync(MemberPagination::getList, asyncTaskExecutor).thenAcceptAsync(members -> {
|
||||
log.info("Successfully get [{}] new registered members", members.size());
|
||||
if (!members.isEmpty()) {
|
||||
Map<String, List<TeamMember>> groupByTopAgentName = members.stream()
|
||||
.fetchDataForAccount(account, ApiPathConstants.MEMBER_TEAM_LIST_URL, memberListReq, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
return memberPaginationCompletableFuture.thenApplyAsync(MemberPagination::getList, asyncTaskExecutor)
|
||||
.thenAcceptAsync(members -> {
|
||||
log.info("Successfully get [{}] new registered members", members.size());
|
||||
if (CollUtil.isNotEmpty(members)) {
|
||||
Map<String, List<TeamMember>> groupByTopAgentName = members.stream()
|
||||
.collect(Collectors.groupingBy(TeamMember::getTopAgentName));
|
||||
groupByTopAgentName.forEach((accountName, accountMembers) -> {
|
||||
String notification = telegramMessageService
|
||||
groupByTopAgentName.forEach((accountName, accountMembers) -> {
|
||||
String notification = telegramMessageService
|
||||
.buildRegistrationMessage(accountName, accountMembers, teamAccountMap.get(accountName));
|
||||
UserDO currUser = accountService.getUserByAccountUsername(accountName);
|
||||
if (currUser != null && DisEnableStatusEnum.ENABLE.equals(currUser.getNeedNotify())) {
|
||||
String botToken = StrUtil.isEmpty(currUser.getBotToken()) ? minister.getBotToken() : currUser.getBotToken();
|
||||
telegramMessageService.sendMessage(botToken, currUser.getRegAndDepIds(), notification);
|
||||
}
|
||||
telegramMessageService
|
||||
var currUser = accountUsernameToUserMap.get(accountName);
|
||||
if (currUser != null && DisEnableStatusEnum.ENABLE.equals(currUser.getNeedNotify())) {
|
||||
String botToken = StrUtil.isEmpty(currUser.getBotToken())
|
||||
? minister.getBotToken()
|
||||
: currUser.getBotToken();
|
||||
telegramMessageService.sendMessage(botToken, currUser.getRegAndDepIds(), notification);
|
||||
}
|
||||
telegramMessageService
|
||||
.sendMessage("6013830443:AAHUOS4v6Ln19ziZkH-L28-HZQLJrGcvhto", 6054562838L, notification);
|
||||
});
|
||||
}
|
||||
}, asyncTaskExecutor);
|
||||
});
|
||||
}
|
||||
}, asyncTaskExecutor);
|
||||
}
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
@ -59,6 +59,4 @@ public class TeamService {
|
||||
// Method intentionally left empty, used only for cache update
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TelegramMessageService {
|
||||
@ -53,13 +52,12 @@ public class TelegramMessageService {
|
||||
}
|
||||
|
||||
public void sendMessage(String botToken, List<Long> targetIds, String message) {
|
||||
targetIds.parallelStream()
|
||||
.forEach(targetId -> this.sendMessage(botToken, targetId, message));
|
||||
targetIds.parallelStream().forEach(targetId -> this.sendMessage(botToken, targetId, message));
|
||||
}
|
||||
|
||||
public void sendMessage(String botToken, String targetIds, String message) {
|
||||
convertStringToList(targetIds).parallelStream()
|
||||
.forEach(targetId -> this.sendMessage(botToken, targetId, message));
|
||||
.forEach(targetId -> this.sendMessage(botToken, targetId, message));
|
||||
}
|
||||
|
||||
public static List<Long> convertStringToList(String str) {
|
||||
@ -71,7 +69,7 @@ public class TelegramMessageService {
|
||||
private static String escapeMarkdown(String text) {
|
||||
|
||||
List<Character> escapeChars = Arrays
|
||||
.asList('_', '[', ']', '(', ')', '~', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!');
|
||||
.asList('_', '[', ']', '(', ')', '~', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!');
|
||||
for (Character charItem : escapeChars) {
|
||||
text = text.replace(charItem.toString(), "\\" + charItem);
|
||||
}
|
||||
@ -88,7 +86,7 @@ public class TelegramMessageService {
|
||||
TeamAccount teamAccount) {
|
||||
String memberNames = accountMembers.stream().map(TeamMember::getName).collect(Collectors.joining(", "));
|
||||
return String.format("👏 %s 注册: %d 用户: `%s` 总数:*%d*", accountName, accountMembers
|
||||
.size(), memberNames, teamAccount.getSubMemberNum());
|
||||
.size(), memberNames, teamAccount.getSubMemberNum());
|
||||
}
|
||||
|
||||
public String buildDepositMessage(String agentName, int newDepositNum, String depositResults, int firstDepositNum) {
|
||||
@ -99,23 +97,16 @@ public class TelegramMessageService {
|
||||
return String.format("用户: `%s`, 首存金额: *%s*\n", name, scoreAmount);
|
||||
}
|
||||
|
||||
public String buildFailedPayMessage(String accountName,
|
||||
List<TeamMember> accountMembers) {
|
||||
public String buildFailedPayMessage(String accountName, List<TeamMember> accountMembers) {
|
||||
String memberNames = accountMembers.stream().map(TeamMember::getName).collect(Collectors.joining("\n"));
|
||||
return String.format("%s\n%s\n", accountName, memberNames);
|
||||
}
|
||||
|
||||
|
||||
public String buildDailyReportMessage(List<Statics> statics) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
statics.forEach(stat -> {
|
||||
String formattedStat = String.format(
|
||||
"%s\n注册: %s\n新增: %d\n日活: %d\n\n",
|
||||
stat.getAgentName(),
|
||||
stat.getIsNew(),
|
||||
stat.getFirstCount(),
|
||||
stat.getCountBets()
|
||||
);
|
||||
String formattedStat = String.format("%s\n注册: %s\n新增: %d\n日活: %d\n\n", stat.getAgentName(), stat
|
||||
.getIsNew(), stat.getFirstCount(), stat.getCountBets());
|
||||
message.append(formattedStat);
|
||||
});
|
||||
return message.toString();
|
||||
|
@ -54,19 +54,19 @@ public class WebClientService {
|
||||
try {
|
||||
semaphore.acquire(); // 尝试获取许可
|
||||
return this.webClient.post().uri(url).headers(httpHeaders -> {
|
||||
Map<String, String> headerMap = JSONUtil.toBean(headers, new TypeReference<>() {
|
||||
}, true);
|
||||
headerMap.forEach(httpHeaders::add);
|
||||
})
|
||||
.body(params != null ? BodyInserters.fromValue(params) : BodyInserters.empty())
|
||||
.retrieve()
|
||||
.onStatus(status -> status.is4xxClientError() || status.is5xxServerError(), response -> response
|
||||
.bodyToMono(String.class)
|
||||
.map(body -> new BusinessException("Error response: " + body)))
|
||||
.bodyToMono(typeRef)
|
||||
.flatMap(this::respHandler)
|
||||
.retryWhen(Retry.backoff(3, Duration.ofSeconds(3)))
|
||||
.block(); // 遇到网络问题,每三秒重试一次
|
||||
Map<String, String> headerMap = JSONUtil.toBean(headers, new TypeReference<>() {
|
||||
}, true);
|
||||
headerMap.forEach(httpHeaders::add);
|
||||
})
|
||||
.body(params != null ? BodyInserters.fromValue(params) : BodyInserters.empty())
|
||||
.retrieve()
|
||||
.onStatus(status -> status.is4xxClientError() || status.is5xxServerError(), response -> response
|
||||
.bodyToMono(String.class)
|
||||
.map(body -> new BusinessException("Error response: " + body)))
|
||||
.bodyToMono(typeRef)
|
||||
.flatMap(this::respHandler)
|
||||
.retryWhen(Retry.backoff(3, Duration.ofSeconds(3)))
|
||||
.block(); // 遇到网络问题,每三秒重试一次
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new BusinessException("Failed to acquire semaphore", e);
|
||||
@ -82,24 +82,24 @@ public class WebClientService {
|
||||
System.out.println(Thread.currentThread().getName());
|
||||
System.out.println(Thread.currentThread().getId());
|
||||
return Mono.fromCallable(() -> {
|
||||
semaphore.acquire(); // 尝试获取许可
|
||||
return true;
|
||||
})
|
||||
.subscribeOn(Schedulers.boundedElastic())
|
||||
.flatMap(acquired -> this.webClient.post().uri(url).headers(httpHeaders -> {
|
||||
Map<String, String> headerMap = JSONUtil.toBean(headers, new TypeReference<>() {
|
||||
}, true);
|
||||
headerMap.forEach(httpHeaders::add);
|
||||
})
|
||||
.body(params != null ? BodyInserters.fromValue(params) : BodyInserters.empty())
|
||||
.retrieve()
|
||||
.onStatus(status -> status.is4xxClientError() || status.is5xxServerError(), response -> response
|
||||
.bodyToMono(String.class)
|
||||
.map(body -> new BusinessException("Error response: " + body)))
|
||||
.bodyToMono(typeRef)
|
||||
.flatMap(this::monoRespHandler)
|
||||
.retryWhen(Retry.backoff(3, Duration.ofSeconds(3)))
|
||||
.doFinally(signal -> semaphore.release()));
|
||||
semaphore.acquire(); // 尝试获取许可
|
||||
return true;
|
||||
})
|
||||
.subscribeOn(Schedulers.boundedElastic())
|
||||
.flatMap(acquired -> this.webClient.post().uri(url).headers(httpHeaders -> {
|
||||
Map<String, String> headerMap = JSONUtil.toBean(headers, new TypeReference<>() {
|
||||
}, true);
|
||||
headerMap.forEach(httpHeaders::add);
|
||||
})
|
||||
.body(params != null ? BodyInserters.fromValue(params) : BodyInserters.empty())
|
||||
.retrieve()
|
||||
.onStatus(status -> status.is4xxClientError() || status.is5xxServerError(), response -> response
|
||||
.bodyToMono(String.class)
|
||||
.map(body -> new BusinessException("Error response: " + body)))
|
||||
.bodyToMono(typeRef)
|
||||
.flatMap(this::monoRespHandler)
|
||||
.retryWhen(Retry.backoff(3, Duration.ofSeconds(3)))
|
||||
.doFinally(signal -> semaphore.release()));
|
||||
}
|
||||
|
||||
private <T> Mono<T> monoRespHandler(ApiResponse<T> apiResponse) {
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.utils;
|
||||
|
||||
import java.util.List;
|
||||
@ -28,6 +44,8 @@ public class TableFormatter {
|
||||
public static String formatTableAsHtml(List<String[]> rows) {
|
||||
int[] colWidths = calculateColumnWidths(rows);
|
||||
StringBuilder table = new StringBuilder("<pre>\n");
|
||||
rows.add(0, new String[] {"平台", "注册", "新增", "转化率"});
|
||||
rows.add(1, new String[] {"----", "----", "----", "----"});
|
||||
for (String[] row : rows) {
|
||||
table.append(formatRow(row, colWidths)).append("\n");
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package com.zayac.admin.common.config.mybatis;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import com.zayac.admin.common.util.helper.LoginHelper;
|
||||
|
@ -71,6 +71,11 @@ public class CacheConstants {
|
||||
*/
|
||||
public static final String ENABLED_ACCOUNTS_KEY_PREFIX = "ENABLED_ACCOUNTS" + DELIMITER;
|
||||
|
||||
/**
|
||||
* 根据用户角色查询所有用户角色账号缓存前缀
|
||||
*/
|
||||
public static final String DEPT_USERS_ROLES_ACCOUNTS_KEY_PREFIX = "DEPT_USERS_ROLES_ACCOUNTS" + DELIMITER;
|
||||
|
||||
private CacheConstants() {
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,11 @@ public class RegexConstants {
|
||||
*/
|
||||
public static final String USERNAME = "^[a-zA-Z][a-zA-Z0-9_]{3,64}$";
|
||||
|
||||
/**
|
||||
* 密码正则严格版(长度为 8 到 32 位,包含至少1个大写字母、1个小写字母、1个数字,1个特殊字符)
|
||||
*/
|
||||
public static final String PASSWORD_STRICT = "^\\S*(?=\\S{8,32})(?=\\S*\\d)(?=\\S*[A-Z])(?=\\S*[a-z])(?=\\S*[!@#$%^&*? ])\\S*$";
|
||||
|
||||
/**
|
||||
* 密码正则(长度为 6 到 32 位,可以包含字母、数字、下划线,特殊字符,同时包含字母和数字)
|
||||
*/
|
||||
|
@ -38,6 +38,9 @@ public class GeneratePreviewResp implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "生成的文件路径", example = "continew-admin\\continew-admin\\continew-admin-generator\\src\\main\\java\\top\\continew\\admin\\generator\\service")
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
|
@ -63,6 +63,13 @@ import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 代码生成业务实现
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/4/12 23:58
|
||||
*/
|
||||
|
||||
/**
|
||||
* 代码生成业务实现
|
||||
*
|
||||
@ -248,10 +255,38 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
genConfigMap.put("fieldConfigs", fieldConfigList);
|
||||
generatePreview.setContent(TemplateUtils.render(templateConfig.getTemplatePath(), genConfigMap));
|
||||
}
|
||||
setPreviewPath(generatePreview, genConfig, templateConfig);
|
||||
}
|
||||
return generatePreviewList;
|
||||
}
|
||||
|
||||
private void setPreviewPath(GeneratePreviewResp generatePreview,
|
||||
GenConfigDO genConfig,
|
||||
GeneratorProperties.TemplateConfig templateConfig) {
|
||||
// 获取前后端基础路径
|
||||
String backendBasicPackagePath = this.buildBackendBasicPackagePath(genConfig);
|
||||
String frontendBasicPackagePath = String.join(File.separator, projectProperties.getAppName(), projectProperties
|
||||
.getAppName() + "-ui");
|
||||
String packageName = genConfig.getPackageName();
|
||||
String moduleName = StrUtil.subSuf(packageName, StrUtil
|
||||
.lastIndexOfIgnoreCase(packageName, StringConstants.DOT) + 1);
|
||||
String packagePath;
|
||||
if (generatePreview.isBackend()) {
|
||||
// 例如:continew-admin/continew-system/src/main/java/top/continew/admin/system/service/impl
|
||||
packagePath = String.join(File.separator, backendBasicPackagePath, templateConfig.getPackageName()
|
||||
.replace(StringConstants.DOT, File.separator));
|
||||
} else {
|
||||
// 例如:continew-admin/continew-admin-ui/src/views/system
|
||||
packagePath = String.join(File.separator, frontendBasicPackagePath, templateConfig.getPackageName()
|
||||
.replace(StringConstants.SLASH, File.separator), moduleName);
|
||||
// 例如:continew-admin/continew-admin-ui/src/views/system/user
|
||||
packagePath = ".vue".equals(templateConfig.getExtension())
|
||||
? packagePath + File.separator + StrUtil.lowerFirst(genConfig.getClassNamePrefix())
|
||||
: packagePath;
|
||||
}
|
||||
generatePreview.setPath(packagePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(List<String> tableNames, HttpServletRequest request, HttpServletResponse response) {
|
||||
try {
|
||||
@ -282,37 +317,11 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
* @param genConfig 生成配置
|
||||
*/
|
||||
private void generateCode(List<GeneratePreviewResp> generatePreviewList, GenConfigDO genConfig) {
|
||||
// 获取前后端基础路径
|
||||
String backendBasicPackagePath = this.buildBackendBasicPackagePath(genConfig);
|
||||
String frontendBasicPackagePath = SystemUtil.getUserInfo().getTempDir() + String
|
||||
.join(File.separator, projectProperties.getAppName(), projectProperties.getAppName() + "-ui");
|
||||
String packageName = genConfig.getPackageName();
|
||||
String moduleName = StrUtil.subSuf(packageName, StrUtil
|
||||
.lastIndexOfIgnoreCase(packageName, StringConstants.DOT) + 1);
|
||||
// 生成代码
|
||||
Map<String, GeneratorProperties.TemplateConfig> templateConfigMap = generatorProperties.getTemplateConfigs();
|
||||
for (GeneratePreviewResp generatePreview : generatePreviewList) {
|
||||
// 获取对应模板配置
|
||||
GeneratorProperties.TemplateConfig templateConfig = templateConfigMap.getOrDefault(StrUtil
|
||||
.subBefore(generatePreview.getFileName(), StringConstants.DOT, true)
|
||||
.replace(genConfig.getClassNamePrefix(), StringConstants.EMPTY), templateConfigMap.get("api"));
|
||||
String packagePath;
|
||||
if (generatePreview.isBackend()) {
|
||||
// 例如:continew-admin/continew-system/src/main/java/top/continew/admin/system/service/impl
|
||||
packagePath = String.join(File.separator, backendBasicPackagePath, templateConfig.getPackageName()
|
||||
.replace(StringConstants.DOT, File.separator));
|
||||
} else {
|
||||
// 例如:continew-admin/continew-admin-ui/src/views/system
|
||||
packagePath = String.join(File.separator, frontendBasicPackagePath, templateConfig.getPackageName()
|
||||
.replace(StringConstants.SLASH, File.separator), moduleName);
|
||||
// 例如:continew-admin/continew-admin-ui/src/views/system/user
|
||||
packagePath = ".vue".equals(templateConfig.getExtension())
|
||||
? packagePath + File.separator + StrUtil.lowerFirst(genConfig.getClassNamePrefix())
|
||||
: packagePath;
|
||||
}
|
||||
// 后端:continew-admin/continew-system/src/main/java/top/continew/admin/system/service/impl/XxxServiceImpl.java
|
||||
// 前端:continew-admin/continew-admin-ui/src/views/system/user/index.vue
|
||||
File file = new File(packagePath, generatePreview.getFileName());
|
||||
File file = new File(SystemUtil.getUserInfo().getTempDir() + generatePreview.getPath(), generatePreview
|
||||
.getFileName());
|
||||
// 如果已经存在,且不允许覆盖,则跳过
|
||||
if (!file.exists() || Boolean.TRUE.equals(genConfig.getIsOverride())) {
|
||||
FileUtil.writeUtf8String(generatePreview.getContent(), file);
|
||||
@ -328,9 +337,8 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
*/
|
||||
private String buildBackendBasicPackagePath(GenConfigDO genConfig) {
|
||||
// 例如:continew-admin/continew-system/src/main/java/top/continew/admin/system
|
||||
return SystemUtil.getUserInfo().getTempDir() + String.join(File.separator, projectProperties
|
||||
.getAppName(), projectProperties.getAppName(), genConfig.getModuleName(), "src", "main", "java", genConfig
|
||||
.getPackageName()
|
||||
return String.join(File.separator, projectProperties.getAppName(), projectProperties.getAppName(), genConfig
|
||||
.getModuleName(), "src", "main", "java", genConfig.getPackageName()
|
||||
.replace(StringConstants.DOT, File.separator));
|
||||
}
|
||||
|
||||
@ -387,4 +395,4 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
String subPackageName = templateConfig.getPackageName();
|
||||
genConfigMap.put("subPackageName", subPackageName);
|
||||
}
|
||||
}
|
||||
}
|
@ -98,6 +98,12 @@ public class UserInfoResp implements Serializable {
|
||||
@Schema(description = "最后一次修改密码时间", example = "2023-08-08 08:08:08", type = "string")
|
||||
private LocalDateTime pwdResetTime;
|
||||
|
||||
/**
|
||||
* 密码是否已过期
|
||||
*/
|
||||
@Schema(description = "密码是否已过期", example = "true")
|
||||
private Boolean pwdExpired;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
|
@ -20,12 +20,11 @@ import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import cn.hutool.core.lang.tree.TreeNodeConfig;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.*;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.zayac.admin.auth.model.resp.RouteResp;
|
||||
import com.zayac.admin.common.constant.CacheConstants;
|
||||
import com.zayac.admin.system.enums.OptionCodeEnum;
|
||||
import com.zayac.admin.system.service.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
@ -49,11 +48,13 @@ import com.zayac.admin.system.model.entity.UserSocialDO;
|
||||
import com.zayac.admin.system.model.req.MessageReq;
|
||||
import com.zayac.admin.system.model.resp.MenuResp;
|
||||
import com.zayac.admin.system.service.*;
|
||||
import top.continew.starter.cache.redisson.util.RedisUtils;
|
||||
import top.continew.starter.core.autoconfigure.project.ProjectProperties;
|
||||
import top.continew.starter.core.util.validate.CheckUtils;
|
||||
import top.continew.starter.extension.crud.annotation.TreeField;
|
||||
import top.continew.starter.extension.crud.util.TreeUtils;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
@ -77,16 +78,41 @@ public class LoginServiceImpl implements LoginService {
|
||||
private final UserSocialService userSocialService;
|
||||
private final MessageService messageService;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final OptionService optionService;
|
||||
|
||||
@Override
|
||||
public String accountLogin(String username, String password) {
|
||||
UserDO user = userService.getByUsername(username);
|
||||
CheckUtils.throwIfNull(user, "用户名或密码不正确");
|
||||
CheckUtils.throwIf(!passwordEncoder.matches(password, user.getPassword()), "用户名或密码不正确");
|
||||
boolean isError = ObjectUtil.isNull(user) || !passwordEncoder.matches(password, user.getPassword());
|
||||
isPasswordLocked(username, isError);
|
||||
CheckUtils.throwIf(isError, "用户名或密码错误");
|
||||
this.checkUserStatus(user);
|
||||
return this.login(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测用户是否被密码锁定
|
||||
*
|
||||
* @param username 用户名
|
||||
*/
|
||||
private void isPasswordLocked(String username, boolean isError) {
|
||||
// 不锁定账户
|
||||
int maxErrorCount = optionService.getValueByCode2Int(OptionCodeEnum.PASSWORD_ERROR_COUNT);
|
||||
if (maxErrorCount <= 0) {
|
||||
return;
|
||||
}
|
||||
String key = CacheConstants.USER_KEY_PREFIX + "PASSWORD-ERROR:" + username;
|
||||
Long currentErrorCount = RedisUtils.get(key);
|
||||
currentErrorCount = currentErrorCount == null ? 0 : currentErrorCount;
|
||||
int lockMinutes = optionService.getValueByCode2Int(OptionCodeEnum.PASSWORD_LOCK_MINUTES);
|
||||
if (isError) {
|
||||
// 密码错误自增次数,并重置时间
|
||||
currentErrorCount = currentErrorCount + 1;
|
||||
RedisUtils.set(key, currentErrorCount, Duration.ofMinutes(lockMinutes));
|
||||
}
|
||||
CheckUtils.throwIf(currentErrorCount >= maxErrorCount, "密码错误已达 {} 次,账户锁定 {} 分钟", maxErrorCount, lockMinutes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String phoneLogin(String phone) {
|
||||
UserDO user = userService.getByPhone(phone);
|
||||
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.system.enums;/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 参数枚举
|
||||
*
|
||||
* @author Kils
|
||||
* @since 2024/05/09 11:25
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum OptionCodeEnum {
|
||||
|
||||
/**
|
||||
* 密码是否允许包含正反序帐户名
|
||||
*/
|
||||
PASSWORD_CONTAIN_NAME("password_contain_name", "密码不允许包含正反序帐户名"),
|
||||
/**
|
||||
* 密码错误锁定帐户次数
|
||||
*/
|
||||
PASSWORD_ERROR_COUNT("password_error_count", "密码错误锁定帐户次数"),
|
||||
/**
|
||||
* 密码有效期
|
||||
*/
|
||||
PASSWORD_EXPIRATION_DAYS("password_expiration_days", "密码有效期"),
|
||||
/**
|
||||
* 密码是否允许包含正反序帐户名
|
||||
*/
|
||||
PASSWORD_LOCK_MINUTES("password_lock_minutes", "密码错误锁定帐户的时间"),
|
||||
/**
|
||||
* 密码最小长度
|
||||
*/
|
||||
PASSWORD_MIN_LENGTH("password_min_length", "密码最小长度"),
|
||||
/**
|
||||
* 密码是否必须包含特殊字符
|
||||
*/
|
||||
PASSWORD_SPECIAL_CHAR("password_special_char", "密码是否必须包含特殊字符"),
|
||||
/**
|
||||
* 修改密码最短间隔
|
||||
*/
|
||||
PASSWORD_UPDATE_INTERVAL("password_update_interval", "修改密码最短间隔");
|
||||
|
||||
private final String value;
|
||||
private final String description;
|
||||
}
|
@ -17,12 +17,17 @@
|
||||
package com.zayac.admin.system.mapper;
|
||||
|
||||
import com.zayac.admin.system.model.entity.AccountDO;
|
||||
import com.zayac.admin.system.model.resp.AccountResp;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 账号 Mapper
|
||||
*
|
||||
* @author zayac
|
||||
* @since 2024/05/10 20:44
|
||||
*/
|
||||
public interface AccountMapper extends BaseMapper<AccountDO> {}
|
||||
public interface AccountMapper extends BaseMapper<AccountDO> {
|
||||
List<AccountResp> selectAccountsByUserIds(List<Long> userIds);
|
||||
}
|
@ -17,8 +17,12 @@
|
||||
package com.zayac.admin.system.mapper;
|
||||
|
||||
import com.zayac.admin.system.model.entity.DeptDO;
|
||||
import com.zayac.admin.system.model.resp.DeptUsersResp;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 部门 Mapper
|
||||
*
|
||||
@ -26,4 +30,11 @@ import top.continew.starter.data.mybatis.plus.base.BaseMapper;
|
||||
* @since 2023/1/22 17:56
|
||||
*/
|
||||
public interface DeptMapper extends BaseMapper<DeptDO> {
|
||||
/**
|
||||
* 查询指定指定角色下的所有所属部门用户
|
||||
*
|
||||
* @param roleCode 角色列表
|
||||
* @return 部门用户列表
|
||||
*/
|
||||
List<DeptUsersResp> selectDeptUsersByRoleCode(@Param("roleCode") String roleCode);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.zayac.admin.system.mapper;
|
||||
|
||||
import com.zayac.admin.system.model.entity.RoleDO;
|
||||
import com.zayac.admin.system.model.entity.RoleDeptDO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
@ -39,4 +40,6 @@ public interface RoleDeptMapper extends BaseMapper<RoleDeptDO> {
|
||||
*/
|
||||
@Select("SELECT dept_id FROM sys_role_dept WHERE role_id = #{roleId}")
|
||||
List<Long> selectDeptIdByRoleId(@Param("roleId") Long roleId);
|
||||
|
||||
List<RoleDO> selectRolesByDeptIds(List<Long> deptIds);
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ import com.zayac.admin.system.model.entity.UserDO;
|
||||
import top.continew.starter.data.mybatis.plus.datapermission.DataPermission;
|
||||
import top.continew.starter.security.crypto.annotation.FieldEncrypt;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户 Mapper
|
||||
*
|
||||
@ -96,4 +98,13 @@ public interface UserMapper extends DataPermissionMapper<UserDO> {
|
||||
* @return 用户数量
|
||||
*/
|
||||
Long selectCountByPhone(@FieldEncrypt @Param("phone") String phone, @Param("id") Long id);
|
||||
|
||||
/**
|
||||
* 根据部门id查询直系用户
|
||||
*
|
||||
* @param deptIds 部门id
|
||||
* @return 直系用户列表
|
||||
*/
|
||||
List<UserDO> selectUsersByDeptIds(@Param("deptIds") List<Long> deptIds);
|
||||
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.zayac.admin.system.mapper;
|
||||
|
||||
import com.zayac.admin.system.model.entity.RoleDO;
|
||||
import com.zayac.admin.system.model.entity.UserRoleDO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
@ -48,4 +49,6 @@ public interface UserRoleMapper extends BaseMapper<UserRoleDO> {
|
||||
*/
|
||||
@Select("SELECT user_id FROM sys_user_role WHERE role_id = #{roleId}")
|
||||
List<Long> selectUserIdByRoleId(@Param("roleId") Long roleId);
|
||||
|
||||
List<RoleDO> selectRolesByUserIds(List<Long> userIds);
|
||||
}
|
||||
|
@ -78,9 +78,4 @@ public class AccountDO extends BaseDO {
|
||||
* 是否为团队账号
|
||||
*/
|
||||
private Boolean isTeam;
|
||||
|
||||
// /**
|
||||
// * 消息通知对象IDs
|
||||
// */
|
||||
// private String receiverIds;
|
||||
}
|
@ -26,7 +26,6 @@ import top.continew.starter.security.crypto.annotation.FieldEncrypt;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户实体
|
||||
|
@ -45,7 +45,7 @@ public class UserEmailUpdateRequest implements Serializable {
|
||||
@Schema(description = "新邮箱", example = "123456789@qq.com")
|
||||
@NotBlank(message = "新邮箱不能为空")
|
||||
@Pattern(regexp = RegexPool.EMAIL, message = "邮箱格式错误")
|
||||
private String newEmail;
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
@ -60,5 +60,5 @@ public class UserEmailUpdateRequest implements Serializable {
|
||||
*/
|
||||
@Schema(description = "当前密码(加密)", example = "SYRLSszQGcMv4kP2Yolou9zf28B9GDakR9u91khxmR7V++i5A384kwnNZxqgvT6bjT4zqpIDuMFLWSt92hQJJA==")
|
||||
@NotBlank(message = "当前密码不能为空")
|
||||
private String currentPassword;
|
||||
private String oldPassword;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ public class UserPhoneUpdateReq implements Serializable {
|
||||
@Schema(description = "新手机号", example = "13811111111")
|
||||
@NotBlank(message = "新手机号不能为空")
|
||||
@Pattern(regexp = RegexPool.MOBILE, message = "手机号格式错误")
|
||||
private String newPhone;
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
@ -60,5 +60,5 @@ public class UserPhoneUpdateReq implements Serializable {
|
||||
*/
|
||||
@Schema(description = "当前密码(加密)", example = "SYRLSszQGcMv4kP2Yolou9zf28B9GDakR9u91khxmR7V++i5A384kwnNZxqgvT6bjT4zqpIDuMFLWSt92hQJJA==")
|
||||
@NotBlank(message = "当前密码不能为空")
|
||||
private String currentPassword;
|
||||
private String oldPassword;
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.system.model.resp;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class DeptUsersResp implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long deptId;
|
||||
private String deptName;
|
||||
private List<UserWithRolesAndAccountsResp> users;
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zayac.admin.system.model.resp;
|
||||
|
||||
import com.zayac.admin.common.enums.DisEnableStatusEnum;
|
||||
import com.zayac.admin.system.model.entity.RoleDO;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class UserWithRolesAndAccountsResp {
|
||||
private Long userId;
|
||||
private String username;
|
||||
private String nickname;
|
||||
private DisEnableStatusEnum needNotify;
|
||||
private String botToken;
|
||||
private String telegramIds;
|
||||
private String regAndDepIds;
|
||||
private String reportIds;
|
||||
private Long deptId;
|
||||
private String deptName;
|
||||
private List<RoleDO> roles;
|
||||
private List<AccountResp> accounts;
|
||||
}
|
@ -16,8 +16,6 @@
|
||||
|
||||
package com.zayac.admin.system.service;
|
||||
|
||||
import com.zayac.admin.common.enums.DisEnableStatusEnum;
|
||||
import com.zayac.admin.system.model.entity.UserDO;
|
||||
import com.zayac.admin.system.model.query.AccountQuery;
|
||||
import com.zayac.admin.system.model.req.AccountReq;
|
||||
import com.zayac.admin.system.model.resp.AccountDetailResp;
|
||||
@ -33,9 +31,8 @@ import java.util.List;
|
||||
* @since 2024/05/10 20:44
|
||||
*/
|
||||
public interface AccountService extends BaseService<AccountResp, AccountDetailResp, AccountQuery, AccountReq> {
|
||||
List<AccountResp> getAccountsByUserId(Long id, DisEnableStatusEnum status);
|
||||
|
||||
UserDO getUserByAccountUsername(String username);
|
||||
|
||||
void updateHeaders(String headers, Long id);
|
||||
|
||||
List<AccountResp> getAccountsByUserIds(List<Long> userIds);
|
||||
}
|
@ -20,6 +20,7 @@ import com.zayac.admin.system.model.entity.DeptDO;
|
||||
import com.zayac.admin.system.model.query.DeptQuery;
|
||||
import com.zayac.admin.system.model.req.DeptReq;
|
||||
import com.zayac.admin.system.model.resp.DeptResp;
|
||||
import com.zayac.admin.system.model.resp.DeptUsersResp;
|
||||
import top.continew.starter.data.mybatis.plus.service.IService;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
|
||||
@ -41,5 +42,5 @@ public interface DeptService extends BaseService<DeptResp, DeptResp, DeptQuery,
|
||||
*/
|
||||
List<DeptDO> listChildren(Long id);
|
||||
|
||||
List<DeptDO> getByName(String name);
|
||||
List<DeptUsersResp> getDeptWithUsersAndAccounts(String roleCode);
|
||||
}
|
||||
|
@ -17,7 +17,9 @@
|
||||
package com.zayac.admin.system.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.zayac.admin.system.enums.OptionCodeEnum;
|
||||
import com.zayac.admin.system.model.query.OptionQuery;
|
||||
import com.zayac.admin.system.model.req.OptionReq;
|
||||
import com.zayac.admin.system.model.req.OptionResetValueReq;
|
||||
@ -52,4 +54,21 @@ public interface OptionService {
|
||||
* @param req 重置信息
|
||||
*/
|
||||
void resetValue(OptionResetValueReq req);
|
||||
|
||||
/**
|
||||
* 根据code获取int参数值
|
||||
*
|
||||
* @param code code
|
||||
* @return 参数值
|
||||
*/
|
||||
int getValueByCode2Int(OptionCodeEnum code);
|
||||
|
||||
/**
|
||||
* 根据code获取参数值
|
||||
*
|
||||
* @param code code
|
||||
* @param mapper 类型转换 ex:value -> Integer.parseInt(value)
|
||||
* @return 参数值
|
||||
*/
|
||||
<T> T getValueByCode(OptionCodeEnum code, Function<String, T> mapper);
|
||||
}
|
@ -16,6 +16,8 @@
|
||||
|
||||
package com.zayac.admin.system.service;
|
||||
|
||||
import com.zayac.admin.system.model.entity.RoleDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -56,4 +58,6 @@ public interface RoleDeptService {
|
||||
* @return 部门 ID 列表
|
||||
*/
|
||||
List<Long> listDeptIdByRoleId(Long roleId);
|
||||
|
||||
List<RoleDO> getRolesByDeptIds(List<Long> deptIds);
|
||||
}
|
@ -16,6 +16,9 @@
|
||||
|
||||
package com.zayac.admin.system.service;
|
||||
|
||||
import com.zayac.admin.system.model.entity.RoleDO;
|
||||
import com.zayac.admin.system.model.entity.UserDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -58,5 +61,7 @@ public interface UserRoleService {
|
||||
*/
|
||||
boolean isRoleIdExists(List<Long> roleIds);
|
||||
|
||||
List<Long> listUserIdByRoleId(Long roleId);
|
||||
List<UserDO> getsUserByRoleCode(String roleCode);
|
||||
|
||||
List<RoleDO> getRolesByUserIds(List<Long> collect);
|
||||
}
|
@ -16,7 +16,6 @@
|
||||
|
||||
package com.zayac.admin.system.service;
|
||||
|
||||
import com.zayac.admin.common.enums.DisEnableStatusEnum;
|
||||
import com.zayac.admin.common.model.resp.LabelValueResp;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.zayac.admin.system.model.entity.UserDO;
|
||||
@ -30,6 +29,7 @@ import com.zayac.admin.system.model.resp.UserResp;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
import top.continew.starter.data.mybatis.plus.service.IService;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -77,20 +77,20 @@ public interface UserService extends BaseService<UserResp, UserDetailResp, UserQ
|
||||
/**
|
||||
* 修改手机号
|
||||
*
|
||||
* @param newPhone 新手机号
|
||||
* @param currentPassword 当前密码
|
||||
* @param id ID
|
||||
* @param newPhone 新手机号
|
||||
* @param oldPassword 当前密码
|
||||
* @param id ID
|
||||
*/
|
||||
void updatePhone(String newPhone, String currentPassword, Long id);
|
||||
void updatePhone(String newPhone, String oldPassword, Long id);
|
||||
|
||||
/**
|
||||
* 修改邮箱
|
||||
*
|
||||
* @param newEmail 新邮箱
|
||||
* @param currentPassword 当前密码
|
||||
* @param id ID
|
||||
* @param newEmail 新邮箱
|
||||
* @param oldPassword 当前密码
|
||||
* @param id ID
|
||||
*/
|
||||
void updateEmail(String newEmail, String currentPassword, Long id);
|
||||
void updateEmail(String newEmail, String oldPassword, Long id);
|
||||
|
||||
/**
|
||||
* 重置密码
|
||||
@ -140,6 +140,14 @@ public interface UserService extends BaseService<UserResp, UserDetailResp, UserQ
|
||||
*/
|
||||
Long countByDeptIds(List<Long> deptIds);
|
||||
|
||||
/**
|
||||
* 密码是否已过期
|
||||
*
|
||||
* @param pwdResetTime 上次重置密码时间
|
||||
* @return 是否过期
|
||||
*/
|
||||
Boolean isPasswordExpired(LocalDateTime pwdResetTime);
|
||||
|
||||
/**
|
||||
* 构建字典
|
||||
*
|
||||
@ -148,16 +156,4 @@ public interface UserService extends BaseService<UserResp, UserDetailResp, UserQ
|
||||
*/
|
||||
List<LabelValueResp<Long>> buildDict(List<UserResp> list);
|
||||
|
||||
/**
|
||||
* 查询所有的可用用户
|
||||
*
|
||||
* @return 用户列表
|
||||
*/
|
||||
List<UserDO> getAllEnabledUsers();
|
||||
|
||||
/**
|
||||
* 查询所有部门用户
|
||||
*/
|
||||
List<UserDO> getByDeptId(DisEnableStatusEnum status, Long deptId);
|
||||
|
||||
}
|
||||
|
@ -16,19 +16,13 @@
|
||||
|
||||
package com.zayac.admin.system.service.impl;
|
||||
|
||||
import com.alicp.jetcache.anno.CacheType;
|
||||
import com.alicp.jetcache.anno.Cached;
|
||||
import com.zayac.admin.common.constant.CacheConstants;
|
||||
import com.zayac.admin.common.enums.DisEnableStatusEnum;
|
||||
import com.zayac.admin.system.mapper.AccountMapper;
|
||||
import com.zayac.admin.system.model.entity.AccountDO;
|
||||
import com.zayac.admin.system.model.entity.UserDO;
|
||||
import com.zayac.admin.system.model.query.AccountQuery;
|
||||
import com.zayac.admin.system.model.req.AccountReq;
|
||||
import com.zayac.admin.system.model.resp.AccountDetailResp;
|
||||
import com.zayac.admin.system.model.resp.AccountResp;
|
||||
import com.zayac.admin.system.service.AccountService;
|
||||
import com.zayac.admin.system.service.UserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -46,30 +40,14 @@ import java.util.List;
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AccountServiceImpl extends BaseServiceImpl<AccountMapper, AccountDO, AccountResp, AccountDetailResp, AccountQuery, AccountReq> implements AccountService {
|
||||
private final UserService userService;
|
||||
|
||||
@Override
|
||||
@Cached(cacheType = CacheType.LOCAL, key = "#userId", name = CacheConstants.ENABLED_ACCOUNTS_KEY_PREFIX, localExpire = 300, expire = 300)
|
||||
public List<AccountResp> getAccountsByUserId(Long id, DisEnableStatusEnum status) {
|
||||
AccountQuery accountQuery = new AccountQuery();
|
||||
accountQuery.setStatus(status.getValue());
|
||||
accountQuery.setUserId(id);
|
||||
return list(accountQuery, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cached(cacheType = CacheType.LOCAL, key = "#username", name = CacheConstants.ENABLED_ACCOUNTS_KEY_PREFIX, localExpire = 300, expire = 300)
|
||||
public UserDO getUserByAccountUsername(String username) {
|
||||
return baseMapper.lambdaQuery()
|
||||
.eq(AccountDO::getUsername, username)
|
||||
.eq(AccountDO::getStatus, DisEnableStatusEnum.ENABLE)
|
||||
.oneOpt()
|
||||
.map(account -> userService.getById(account.getUserId()))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHeaders(String headers, Long id) {
|
||||
baseMapper.lambdaUpdate().set(AccountDO::getHeaders, headers).eq(AccountDO::getId, id).update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccountResp> getAccountsByUserIds(List<Long> userIds) {
|
||||
return baseMapper.selectAccountsByUserIds(userIds);
|
||||
}
|
||||
}
|
@ -19,10 +19,12 @@ package com.zayac.admin.system.service.impl;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.alicp.jetcache.anno.CacheType;
|
||||
import com.alicp.jetcache.anno.Cached;
|
||||
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
|
||||
import com.zayac.admin.system.service.DeptService;
|
||||
import com.zayac.admin.system.service.RoleDeptService;
|
||||
import com.zayac.admin.system.service.UserService;
|
||||
import com.zayac.admin.common.constant.CacheConstants;
|
||||
import com.zayac.admin.system.model.resp.DeptUsersResp;
|
||||
import com.zayac.admin.system.service.*;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -37,9 +39,7 @@ import top.continew.starter.data.core.enums.DatabaseType;
|
||||
import top.continew.starter.data.core.util.MetaUtils;
|
||||
import top.continew.starter.extension.crud.service.impl.BaseServiceImpl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 部门业务实现
|
||||
@ -54,6 +54,7 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptRes
|
||||
@Resource
|
||||
private UserService userService;
|
||||
private final RoleDeptService roleDeptService;
|
||||
private final DeptMapper baseMapper;
|
||||
|
||||
@Override
|
||||
public List<DeptDO> listChildren(Long id) {
|
||||
@ -62,11 +63,6 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptRes
|
||||
return baseMapper.lambdaQuery().apply(databaseType.findInSet(id, "ancestors")).list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeptDO> getByName(String name) {
|
||||
return baseMapper.lambdaQuery().eq(DeptDO::getName, name).list();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeAdd(DeptReq req) {
|
||||
String name = req.getName();
|
||||
@ -202,4 +198,10 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptRes
|
||||
}
|
||||
baseMapper.updateBatchById(list);
|
||||
}
|
||||
|
||||
@Cached(key = "#roleCode", cacheType = CacheType.LOCAL, name = CacheConstants.DEPT_USERS_ROLES_ACCOUNTS_KEY_PREFIX, localExpire = 360)
|
||||
public List<DeptUsersResp> getDeptWithUsersAndAccounts(String roleCode) {
|
||||
return baseMapper.selectDeptUsersByRoleCode(roleCode);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,11 @@
|
||||
package com.zayac.admin.system.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.zayac.admin.system.enums.OptionCodeEnum;
|
||||
import com.zayac.admin.system.service.OptionService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -29,9 +34,11 @@ import com.zayac.admin.system.model.req.OptionResetValueReq;
|
||||
import com.zayac.admin.system.model.resp.OptionResp;
|
||||
import top.continew.starter.cache.redisson.util.RedisUtils;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.core.util.validate.CheckUtils;
|
||||
import top.continew.starter.data.mybatis.plus.query.QueryWrapperHelper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 参数业务实现
|
||||
@ -61,4 +68,27 @@ public class OptionServiceImpl implements OptionService {
|
||||
RedisUtils.deleteByPattern(CacheConstants.OPTION_KEY_PREFIX + StringConstants.ASTERISK);
|
||||
baseMapper.lambdaUpdate().set(OptionDO::getValue, null).in(OptionDO::getCode, req.getCode()).update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getValueByCode2Int(OptionCodeEnum code) {
|
||||
return this.getValueByCode(code, Integer::parseInt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getValueByCode(OptionCodeEnum code, Function<String, T> mapper) {
|
||||
String value = RedisUtils.get(CacheConstants.OPTION_KEY_PREFIX + code.getValue());
|
||||
if (StrUtil.isNotBlank(value)) {
|
||||
return mapper.apply(value);
|
||||
}
|
||||
LambdaQueryWrapper<OptionDO> queryWrapper = Wrappers.<OptionDO>lambdaQuery()
|
||||
.eq(OptionDO::getCode, code.getValue())
|
||||
.select(OptionDO::getValue, OptionDO::getDefaultValue);
|
||||
OptionDO optionDO = baseMapper.selectOne(queryWrapper);
|
||||
CheckUtils.throwIf(ObjUtil.isEmpty(optionDO), "配置 [{}] 不存在", code);
|
||||
value = StrUtil.nullToDefault(optionDO.getValue(), optionDO.getDefaultValue());
|
||||
CheckUtils.throwIf(StrUtil.isBlank(value), "配置 [{}] 不存在", code);
|
||||
RedisUtils.set(CacheConstants.OPTION_KEY_PREFIX + code.getValue(), value);
|
||||
return mapper.apply(value);
|
||||
}
|
||||
|
||||
}
|
@ -19,6 +19,7 @@ package com.zayac.admin.system.service.impl;
|
||||
import cn.crane4j.annotation.ContainerMethod;
|
||||
import cn.crane4j.annotation.MappingType;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.zayac.admin.system.model.entity.RoleDO;
|
||||
import com.zayac.admin.system.service.RoleDeptService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -74,6 +75,11 @@ public class RoleDeptServiceImpl implements RoleDeptService {
|
||||
baseMapper.lambdaUpdate().in(RoleDeptDO::getDeptId, deptIds).remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoleDO> getRolesByDeptIds(List<Long> deptIds) {
|
||||
return baseMapper.selectRolesByDeptIds(deptIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ContainerMethod(namespace = ContainerConstants.ROLE_DEPT_ID_LIST, type = MappingType.ORDER_OF_KEYS)
|
||||
public List<Long> listDeptIdByRoleId(Long roleId) {
|
||||
|
@ -19,6 +19,8 @@ package com.zayac.admin.system.service.impl;
|
||||
import cn.crane4j.annotation.ContainerMethod;
|
||||
import cn.crane4j.annotation.MappingType;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.zayac.admin.system.model.entity.RoleDO;
|
||||
import com.zayac.admin.system.model.entity.UserDO;
|
||||
import com.zayac.admin.system.service.UserRoleService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -74,14 +76,18 @@ public class UserRoleServiceImpl implements UserRoleService {
|
||||
return baseMapper.selectRoleIdByUserId(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ContainerMethod(namespace = ContainerConstants.ROLE_USER_ID_LIST, type = MappingType.ORDER_OF_KEYS)
|
||||
public List<Long> listUserIdByRoleId(Long roleId) {
|
||||
return baseMapper.selectUserIdByRoleId(roleId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRoleIdExists(List<Long> roleIds) {
|
||||
return baseMapper.lambdaQuery().in(UserRoleDO::getRoleId, roleIds).exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserDO> getsUserByRoleCode(String roleCode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoleDO> getRolesByUserIds(List<Long> userIds) {
|
||||
return baseMapper.selectRolesByUserIds(userIds);
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,10 @@ package com.zayac.admin.system.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alicp.jetcache.anno.CacheInvalidate;
|
||||
import com.alicp.jetcache.anno.CacheType;
|
||||
@ -27,6 +29,7 @@ import com.alicp.jetcache.anno.CacheUpdate;
|
||||
import com.alicp.jetcache.anno.Cached;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.zayac.admin.common.constant.RegexConstants;
|
||||
import com.zayac.admin.common.model.resp.LabelValueResp;
|
||||
import com.zayac.admin.system.mapper.UserMapper;
|
||||
import com.zayac.admin.system.service.*;
|
||||
@ -52,9 +55,9 @@ import com.zayac.admin.system.model.req.UserReq;
|
||||
import com.zayac.admin.system.model.req.UserRoleUpdateReq;
|
||||
import com.zayac.admin.system.model.resp.UserDetailResp;
|
||||
import com.zayac.admin.system.model.resp.UserResp;
|
||||
import com.zayac.admin.system.service.*;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.core.util.validate.CheckUtils;
|
||||
import top.continew.starter.core.util.validate.ValidationUtils;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
import top.continew.starter.extension.crud.service.CommonUserService;
|
||||
@ -64,6 +67,8 @@ import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.zayac.admin.system.enums.OptionCodeEnum.*;
|
||||
|
||||
/**
|
||||
* 用户业务实现
|
||||
*
|
||||
@ -80,6 +85,7 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
private final FileService fileService;
|
||||
private final FileStorageService fileStorageService;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final OptionService optionService;
|
||||
@Resource
|
||||
private DeptService deptService;
|
||||
@Value("${avatar.support-suffix}")
|
||||
@ -192,16 +198,74 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
if (StrUtil.isNotBlank(password)) {
|
||||
CheckUtils.throwIf(!passwordEncoder.matches(oldPassword, password), "当前密码错误");
|
||||
}
|
||||
// 校验密码合法性
|
||||
checkPassword(newPassword, user);
|
||||
// 更新密码和密码重置时间
|
||||
user.setPassword(newPassword);
|
||||
user.setPwdResetTime(LocalDateTime.now());
|
||||
baseMapper.updateById(user);
|
||||
onlineUserService.cleanByUserId(user.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测修改密码合法性
|
||||
*
|
||||
* @param password 密码
|
||||
* @param user 用户
|
||||
*/
|
||||
private void checkPassword(String password, UserDO user) {
|
||||
// 密码最小长度
|
||||
int passwordMinLength = optionService.getValueByCode2Int(PASSWORD_MIN_LENGTH);
|
||||
ValidationUtils.throwIf(StrUtil.length(password) < passwordMinLength, PASSWORD_MIN_LENGTH
|
||||
.getDescription() + "为 {}", passwordMinLength);
|
||||
// 密码是否允许包含正反序用户名
|
||||
int passwordContainName = optionService.getValueByCode2Int(PASSWORD_CONTAIN_NAME);
|
||||
if (passwordContainName == 1) {
|
||||
String username = user.getUsername();
|
||||
ValidationUtils.throwIf(StrUtil.containsIgnoreCase(password, username) || StrUtil
|
||||
.containsIgnoreCase(password, StrUtil.reverse(username)), PASSWORD_CONTAIN_NAME.getDescription());
|
||||
}
|
||||
// 密码是否必须包含特殊字符
|
||||
int passwordSpecialChar = optionService.getValueByCode2Int(PASSWORD_SPECIAL_CHAR);
|
||||
String match = RegexConstants.PASSWORD;
|
||||
String desc = "密码长度为 6 到 32 位,可以包含字母、数字、下划线,特殊字符,同时包含字母和数字";
|
||||
if (passwordSpecialChar == 1) {
|
||||
match = RegexConstants.PASSWORD_STRICT;
|
||||
desc = "密码长度为 8 到 32 位,包含至少1个大写字母、1个小写字母、1个数字,1个特殊字符";
|
||||
}
|
||||
ValidationUtils.throwIf(!ReUtil.isMatch(match, password), desc);
|
||||
// 密码修改间隔
|
||||
if (ObjectUtil.isNull(user.getPwdResetTime())) {
|
||||
return;
|
||||
}
|
||||
int passwordUpdateInterval = optionService.getValueByCode2Int(PASSWORD_UPDATE_INTERVAL);
|
||||
if (passwordUpdateInterval <= 0) {
|
||||
return;
|
||||
}
|
||||
LocalDateTime lastResetTime = user.getPwdResetTime();
|
||||
LocalDateTime limitUpdateTime = lastResetTime.plusMinutes(passwordUpdateInterval);
|
||||
ValidationUtils.throwIf(LocalDateTime.now().isBefore(limitUpdateTime), "上次修改于:{},下次可修改时间:{}", LocalDateTimeUtil
|
||||
.formatNormal(lastResetTime), LocalDateTimeUtil.formatNormal(limitUpdateTime));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePhone(String newPhone, String currentPassword, Long id) {
|
||||
public Boolean isPasswordExpired(LocalDateTime pwdResetTime) {
|
||||
// 永久有效
|
||||
int passwordExpirationDays = optionService.getValueByCode2Int(PASSWORD_EXPIRATION_DAYS);
|
||||
if (passwordExpirationDays <= 0) {
|
||||
return false;
|
||||
}
|
||||
// 初始密码也提示修改
|
||||
if (pwdResetTime == null) {
|
||||
return true;
|
||||
}
|
||||
return pwdResetTime.plusDays(passwordExpirationDays).isBefore(LocalDateTime.now());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePhone(String newPhone, String oldPassword, Long id) {
|
||||
UserDO user = super.getById(id);
|
||||
CheckUtils.throwIf(!passwordEncoder.matches(currentPassword, user.getPassword()), "当前密码错误");
|
||||
CheckUtils.throwIf(!passwordEncoder.matches(oldPassword, user.getPassword()), "当前密码错误");
|
||||
CheckUtils.throwIf(this.isPhoneExists(newPhone, id), "手机号已绑定其他账号,请更换其他手机号");
|
||||
CheckUtils.throwIfEqual(newPhone, user.getPhone(), "新手机号不能与当前手机号相同");
|
||||
// 更新手机号
|
||||
@ -209,9 +273,9 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateEmail(String newEmail, String currentPassword, Long id) {
|
||||
public void updateEmail(String newEmail, String oldPassword, Long id) {
|
||||
UserDO user = super.getById(id);
|
||||
CheckUtils.throwIf(!passwordEncoder.matches(currentPassword, user.getPassword()), "当前密码错误");
|
||||
CheckUtils.throwIf(!passwordEncoder.matches(oldPassword, user.getPassword()), "当前密码错误");
|
||||
CheckUtils.throwIf(this.isEmailExists(newEmail, id), "邮箱已绑定其他账号,请更换其他邮箱");
|
||||
CheckUtils.throwIfEqual(newEmail, user.getEmail(), "新邮箱不能与当前邮箱相同");
|
||||
// 更新邮箱
|
||||
@ -254,19 +318,6 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
return baseMapper.lambdaQuery().in(UserDO::getDeptId, deptIds).count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LabelValueResp<Long>> buildDict(List<UserResp> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
return list.stream().map(r -> new LabelValueResp<>(r.getUsername(), r.getId())).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserDO> getAllEnabledUsers() {
|
||||
return baseMapper.lambdaQuery().eq(UserDO::getStatus, DisEnableStatusEnum.ENABLE.getValue()).list();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cached(key = "#id", cacheType = CacheType.BOTH, name = CacheConstants.USER_KEY_PREFIX, syncLocal = true)
|
||||
public String getNicknameById(Long id) {
|
||||
@ -303,20 +354,6 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
userRoleService.add(req.getRoleIds(), userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserDO> getByDeptId(DisEnableStatusEnum status, Long deptId) {
|
||||
QueryWrapper<UserDO> wrapper = new QueryWrapper<UserDO>().eq(null != status, "status", status.getValue())
|
||||
.and(q -> {
|
||||
List<Long> deptIdList = deptService.listChildren(deptId)
|
||||
.stream()
|
||||
.map(DeptDO::getId)
|
||||
.collect(Collectors.toList());
|
||||
deptIdList.add(deptId);
|
||||
q.in("dept_id", deptIdList);
|
||||
});
|
||||
return baseMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 QueryWrapper
|
||||
*
|
||||
@ -380,4 +417,12 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
Long count = baseMapper.selectCountByPhone(phone, id);
|
||||
return null != count && count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LabelValueResp<Long>> buildDict(List<UserResp> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
return list.stream().map(r -> new LabelValueResp<>(r.getUsername(), r.getId())).toList();
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
|
||||
|
||||
<mapper namespace="com.zayac.admin.system.mapper.AccountMapper">
|
||||
<select id="selectAccountsByUserIds" resultType="com.zayac.admin.system.model.resp.AccountResp">
|
||||
SELECT
|
||||
id, username, nickname, status, platform_id, is_team, user_id
|
||||
FROM sys_account
|
||||
WHERE user_id IN
|
||||
<foreach item="userIds" collection="list" open="(" separator="," close=")">
|
||||
#{userIds}
|
||||
</foreach>;
|
||||
</select>
|
||||
</mapper>
|
@ -1,4 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
|
||||
<mapper namespace="com.zayac.admin.system.mapper.DeptMapper">
|
||||
</mapper>
|
||||
<select id="selectDeptUsersByRoleCode" resultMap="DeptUserResultMap">
|
||||
WITH RECURSIVE dept_hierarchy AS (SELECT d.id,
|
||||
d.parent_id,
|
||||
d.name,
|
||||
d.id AS top_level_dept_id,
|
||||
d.name AS top_level_dept_name
|
||||
FROM sys_dept d
|
||||
JOIN sys_user u ON u.dept_id = d.id
|
||||
JOIN sys_user_role ur ON ur.user_id = u.id
|
||||
JOIN sys_role r ON ur.role_id = r.id
|
||||
WHERE d.status = 1
|
||||
AND r.code = #{roleCode}
|
||||
UNION ALL
|
||||
SELECT d.id, d.parent_id, d.name, dh.top_level_dept_id, dh.top_level_dept_name
|
||||
FROM sys_dept d
|
||||
INNER JOIN dept_hierarchy dh ON d.parent_id = dh.id)
|
||||
SELECT u.id AS user_id,
|
||||
u.username,
|
||||
u.nickname,
|
||||
u.need_notify,
|
||||
u.bot_token,
|
||||
u.telegram_ids,
|
||||
u.reg_and_dep_ids,
|
||||
u.report_ids,
|
||||
r.id AS role_id,
|
||||
r.code AS role_code,
|
||||
r.name AS role_name,
|
||||
a.id AS account_id,
|
||||
a.nickname AS account_nickname,
|
||||
a.username AS account_username,
|
||||
a.status AS account_status,
|
||||
a.headers AS account_headers,
|
||||
a.platform_id AS account_platform_id,
|
||||
p.name AS platform_name,
|
||||
p.url AS platform_url,
|
||||
d.id AS dept_id,
|
||||
d.name AS dept_name,
|
||||
dh.top_level_dept_id,
|
||||
dh.top_level_dept_name
|
||||
FROM sys_user u
|
||||
LEFT JOIN sys_user_role ur ON u.id = ur.user_id
|
||||
LEFT JOIN sys_role r ON ur.role_id = r.id
|
||||
LEFT JOIN sys_account a ON u.id = a.user_id AND a.status = 1
|
||||
LEFT JOIN sys_platform p ON a.platform_id = p.id
|
||||
LEFT JOIN sys_dept d ON u.dept_id = d.id
|
||||
LEFT JOIN dept_hierarchy dh ON d.id = dh.id
|
||||
WHERE u.status = 1
|
||||
AND d.id IN (SELECT id FROM dept_hierarchy)
|
||||
ORDER BY dh.top_level_dept_id, u.id, r.id, a.id;
|
||||
</select>
|
||||
|
||||
<resultMap id="DeptUserResultMap" type="com.zayac.admin.system.model.resp.DeptUsersResp">
|
||||
<id column="top_level_dept_id" property="deptId"/>
|
||||
<result column="top_level_dept_name" property="deptName"/>
|
||||
<collection property="users" ofType="com.zayac.admin.system.model.resp.UserWithRolesAndAccountsResp"
|
||||
javaType="java.util.List">
|
||||
<id column="user_id" property="userId"/>
|
||||
<result column="username" property="username"/>
|
||||
<result column="nickname" property="nickname"/>
|
||||
<result column="need_notify" property="needNotify"/>
|
||||
<result column="bot_token" property="botToken"/>
|
||||
<result column="telegram_ids" property="telegramIds"/>
|
||||
<result column="reg_and_dep_ids" property="regAndDepIds"/>
|
||||
<result column="report_ids" property="reportIds"/>
|
||||
<result column="dept_id" property="deptId"/>
|
||||
<result column="dept_name" property="deptName"/>
|
||||
<collection property="roles" ofType="com.zayac.admin.system.model.entity.RoleDO">
|
||||
<id column="role_id" property="id"/>
|
||||
<id column="role_code" property="code"/>
|
||||
<result column="role_name" property="name"/>
|
||||
</collection>
|
||||
<collection property="accounts" ofType="com.zayac.admin.system.model.resp.AccountResp">
|
||||
<id column="account_id" property="id"/>
|
||||
<result column="account_nickname" property="nickname"/>
|
||||
<result column="account_username" property="username"/>
|
||||
<result column="account_status" property="status"/>
|
||||
<result column="account_headers" property="headers"/>
|
||||
<result column="account_platform_id" property="platformId"/>
|
||||
<result column="platform_name" property="platformName"/>
|
||||
<result column="platform_url" property="platformUrl"/>
|
||||
</collection>
|
||||
</collection>
|
||||
</resultMap>
|
||||
</mapper>
|
||||
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
|
||||
|
||||
<mapper namespace="com.zayac.admin.system.mapper.RoleDeptMapper">
|
||||
<select id="selectRolesByDeptIds" resultType="com.zayac.admin.system.model.entity.RoleDO">
|
||||
SELECT
|
||||
rd.dept_id AS deptId,
|
||||
r.id,
|
||||
r.name,
|
||||
r.code
|
||||
FROM sys_role_dept rd
|
||||
JOIN sys_role r ON rd.role_id = r.id
|
||||
WHERE rd.dept_id IN
|
||||
<foreach item="deptId" collection="deptIds" open="(" separator="," close=")">
|
||||
#{deptId}
|
||||
</foreach>;
|
||||
</select>
|
||||
</mapper>
|
@ -1,4 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
<mapper namespace="com.zayac.admin.system.mapper.RoleMapper">
|
||||
<select id="selectRolesByUserIds" resultType="com.zayac.admin.system.model.entity.RoleDO">
|
||||
SELECT
|
||||
r.id, r.name, r.code, ur.user_id AS userId
|
||||
FROM sys_role r
|
||||
JOIN sys_user_role ur ON r.id = ur.role_id
|
||||
WHERE ur.user_id IN
|
||||
<foreach item="userId" collection="userIds" open="(" separator="," close=")">
|
||||
#{userId}
|
||||
</foreach>;
|
||||
</select>
|
||||
</mapper>
|
@ -5,17 +5,17 @@
|
||||
<select id="selectUserPage" resultType="com.zayac.admin.system.model.entity.UserDO">
|
||||
SELECT t1.*
|
||||
FROM sys_user AS t1
|
||||
LEFT JOIN sys_dept AS t2 ON t2.id = t1.dept_id
|
||||
${ew.customSqlSegment}
|
||||
LEFT JOIN sys_dept AS t2 ON t2.id = t1.dept_id
|
||||
${ew.customSqlSegment}
|
||||
</select>
|
||||
|
||||
<select id="selectCountByEmail" resultType="java.lang.Long">
|
||||
SELECT count(*)
|
||||
FROM sys_user
|
||||
WHERE email = #{email}
|
||||
<if test="id != null">
|
||||
<if test="id != null">
|
||||
AND id != #{id}
|
||||
</if>
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="selectCountByPhone" resultType="java.lang.Long">
|
||||
|
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
|
||||
|
||||
<mapper namespace="com.zayac.admin.system.mapper.UserRoleMapper">
|
||||
<select id="selectRolesByUserIds" resultType="com.zayac.admin.system.model.entity.RoleDO">
|
||||
SELECT
|
||||
r.id, r.name, r.code, ur.user_id AS userId
|
||||
FROM sys_role r
|
||||
JOIN sys_user_role ur ON r.id = ur.role_id
|
||||
WHERE ur.user_id IN
|
||||
<foreach item="userId" collection="userIds" open="(" separator="," close=")">
|
||||
#{userId}
|
||||
</foreach>;
|
||||
</select>
|
||||
|
||||
</mapper>
|
@ -15,7 +15,7 @@
|
||||
<properties>
|
||||
<!-- ### 打包配置相关 ### -->
|
||||
<!-- 启动类 -->
|
||||
<main-class>zayac.admin.ContiNewAdminApplication</main-class>
|
||||
<main-class>com.zayac.admin.ZayacAdminApplication</main-class>
|
||||
<!-- 程序 jar 输出目录 -->
|
||||
<bin-path>bin</bin-path>
|
||||
<!-- 配置文件输出目录 -->
|
||||
|
@ -127,6 +127,7 @@ public class AuthController {
|
||||
UserInfoResp userInfoResp = BeanUtil.copyProperties(userDetailResp, UserInfoResp.class);
|
||||
userInfoResp.setPermissions(loginUser.getPermissions());
|
||||
userInfoResp.setRoles(loginUser.getRoleCodes());
|
||||
userInfoResp.setPwdExpired(userService.isPasswordExpired(userDetailResp.getPwdResetTime()));
|
||||
return R.ok(userInfoResp);
|
||||
}
|
||||
|
||||
|
@ -104,30 +104,30 @@ public class UserCenterController {
|
||||
@Operation(summary = "修改手机号", description = "修改手机号")
|
||||
@PatchMapping("/phone")
|
||||
public R<Void> updatePhone(@Validated @RequestBody UserPhoneUpdateReq updateReq) {
|
||||
String rawCurrentPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq
|
||||
.getCurrentPassword()));
|
||||
ValidationUtils.throwIfBlank(rawCurrentPassword, DECRYPT_FAILED);
|
||||
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + updateReq.getNewPhone();
|
||||
String rawOldPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq
|
||||
.getOldPassword()));
|
||||
ValidationUtils.throwIfBlank(rawOldPassword, DECRYPT_FAILED);
|
||||
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + updateReq.getPhone();
|
||||
String captcha = RedisUtils.get(captchaKey);
|
||||
ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED);
|
||||
ValidationUtils.throwIfNotEqualIgnoreCase(updateReq.getCaptcha(), captcha, "验证码错误");
|
||||
RedisUtils.delete(captchaKey);
|
||||
userService.updatePhone(updateReq.getNewPhone(), rawCurrentPassword, LoginHelper.getUserId());
|
||||
userService.updatePhone(updateReq.getPhone(), rawOldPassword, LoginHelper.getUserId());
|
||||
return R.ok("修改成功");
|
||||
}
|
||||
|
||||
@Operation(summary = "修改邮箱", description = "修改用户邮箱")
|
||||
@PatchMapping("/email")
|
||||
public R<Void> updateEmail(@Validated @RequestBody UserEmailUpdateRequest updateReq) {
|
||||
String rawCurrentPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq
|
||||
.getCurrentPassword()));
|
||||
ValidationUtils.throwIfBlank(rawCurrentPassword, DECRYPT_FAILED);
|
||||
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + updateReq.getNewEmail();
|
||||
String rawOldPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq
|
||||
.getOldPassword()));
|
||||
ValidationUtils.throwIfBlank(rawOldPassword, DECRYPT_FAILED);
|
||||
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + updateReq.getEmail();
|
||||
String captcha = RedisUtils.get(captchaKey);
|
||||
ValidationUtils.throwIfBlank(captcha, CAPTCHA_EXPIRED);
|
||||
ValidationUtils.throwIfNotEqualIgnoreCase(updateReq.getCaptcha(), captcha, "验证码错误");
|
||||
RedisUtils.delete(captchaKey);
|
||||
userService.updateEmail(updateReq.getNewEmail(), rawCurrentPassword, LoginHelper.getUserId());
|
||||
userService.updateEmail(updateReq.getEmail(), rawOldPassword, LoginHelper.getUserId());
|
||||
return R.ok("修改成功");
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
--- ### 项目配置
|
||||
project:
|
||||
# URL
|
||||
# URL(跨域配置默认放行此 URL,第三方登录回调默认使用此 URL 为前缀,请注意更改为你实际的前端 URL)
|
||||
url: http://localhost:5173
|
||||
|
||||
--- ### 服务器配置
|
||||
@ -58,7 +58,7 @@ spring.datasource:
|
||||
## Liquibase 配置
|
||||
spring.liquibase:
|
||||
# 是否启用
|
||||
enabled: true
|
||||
enabled: false
|
||||
# 配置文件路径
|
||||
change-log: classpath:/db/changelog/db.changelog-master.yaml
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
--- ### 项目配置
|
||||
project:
|
||||
# URL(跨域配置默认放行此 URL,请注意更改为你实际的前端 URL)
|
||||
# URL(跨域配置默认放行此 URL,第三方登录回调默认使用此 URL 为前缀,请注意更改为你实际的前端 URL)
|
||||
url: http://localhost:5173
|
||||
# 是否为生产环境
|
||||
production: true
|
||||
|
@ -114,7 +114,14 @@ VALUES
|
||||
'Copyright © 2022-present <a href="https://blog.charles7c.top/about/me" target="_blank" rel="noopener">Charles7c</a> <span>⋅</span> <a href="https://github.com/Charles7c/continew-admin" target="_blank" rel="noopener">ContiNew Admin</a> <span>⋅</span> <a href="https://beian.miit.gov.cn" target="_blank" rel="noopener">津ICP备2022005864号-2</a>',
|
||||
'用于显示登录页面的底部版权信息。', NULL, NULL),
|
||||
('系统LOGO(16*16)', 'site_favicon', NULL, '/favicon.ico', '用于显示浏览器地址栏的系统LOGO。', NULL, NULL),
|
||||
('系统LOGO(33*33)', 'site_logo', NULL, '/logo.svg', '用于显示登录页面的系统LOGO。', NULL, NULL);
|
||||
('系统LOGO(33*33)', 'site_logo', NULL, '/logo.svg', '用于显示登录页面的系统LOGO。', NULL, NULL),
|
||||
('密码是否允许包含正反序帐户名', 'password_contain_name', NULL, '0', '', NULL, NULL),
|
||||
('密码错误锁定帐户次数', 'password_error_count', NULL, '5', '0表示不限制。', NULL, NULL),
|
||||
('密码有效期', 'password_expiration_days', NULL, '0', '取值范围为0-999,0表示永久有效。', NULL, NULL),
|
||||
('密码错误锁定帐户的时间', 'password_lock_minutes', NULL, '5', '0表示不解锁。', NULL, NULL),
|
||||
('密码最小长度', 'password_min_length', NULL, '8', '取值范围为8-32。', NULL, NULL),
|
||||
('密码是否必须包含特殊字符', 'password_special_char', NULL, '0', '', NULL, NULL),
|
||||
('修改密码最短间隔', 'password_update_interval', NULL, '5', '取值范围为0-9999,0表示不限制。', NULL, NULL);
|
||||
|
||||
-- 初始化默认字典
|
||||
INSERT INTO `sys_dict`
|
||||
|
@ -114,7 +114,14 @@ VALUES
|
||||
'Copyright © 2022-present <a href="https://blog.charles7c.top/about/me" target="_blank" rel="noopener">Charles7c</a> <span>⋅</span> <a href="https://github.com/Charles7c/continew-admin" target="_blank" rel="noopener">ContiNew Admin</a> <span>⋅</span> <a href="https://beian.miit.gov.cn" target="_blank" rel="noopener">津ICP备2022005864号-2</a>',
|
||||
'用于显示登录页面的底部版权信息。', NULL, NULL),
|
||||
('系统LOGO(16*16)', 'site_favicon', NULL, '/favicon.ico', '用于显示浏览器地址栏的系统LOGO。', NULL, NULL),
|
||||
('系统LOGO(33*33)', 'site_logo', NULL, '/logo.svg', '用于显示登录页面的系统LOGO。', NULL, NULL);
|
||||
('系统LOGO(33*33)', 'site_logo', NULL, '/logo.svg', '用于显示登录页面的系统LOGO。', NULL, NULL),
|
||||
('密码是否允许包含正反序帐户名', 'password_contain_name', NULL, '0', '', NULL, NULL),
|
||||
('密码错误锁定帐户次数', 'password_error_count', NULL, '5', '0表示不限制。', NULL, NULL),
|
||||
('密码有效期', 'password_expiration_days', NULL, '0', '取值范围为0-999,0表示永久有效。', NULL, NULL),
|
||||
('密码错误锁定帐户的时间', 'password_lock_minutes', NULL, '5', '0表示不解锁。', NULL, NULL),
|
||||
('密码最小长度', 'password_min_length', NULL, '8', '取值范围为8-32。', NULL, NULL),
|
||||
('密码是否必须包含特殊字符', 'password_special_char', NULL, '0', '', NULL, NULL),
|
||||
('修改密码最短间隔', 'password_update_interval', NULL, '5', '取值范围为0-9999,0表示不限制。', NULL, NULL);
|
||||
|
||||
-- 初始化默认字典
|
||||
INSERT INTO "sys_dict"
|
||||
@ -125,8 +132,8 @@ VALUES
|
||||
INSERT INTO "sys_dict_item"
|
||||
("id", "label", "value", "color", "sort", "description", "status", "dict_id", "create_user", "create_time", "update_user", "update_time")
|
||||
VALUES
|
||||
(547889649658363951, '通知', '1', 'blue', 1, NULL, 547889614262632491, 1, 1, NOW(), NULL, NULL),
|
||||
(547890124537462835, '活动', '2', 'orangered', 2, NULL, 547889614262632491, 1, 1, NOW(), NULL, NULL);
|
||||
(547889649658363951, '通知', '1', 'blue', 1, NULL, 1, 547889614262632491, 1, NOW(), NULL, NULL),
|
||||
(547890124537462835, '活动', '2', 'orangered', 2, NULL, 1, 547889614262632491, 1, NOW(), NULL, NULL);
|
||||
|
||||
-- 初始化默认用户和角色关联数据
|
||||
INSERT INTO "sys_user_role"
|
||||
|
@ -16,13 +16,31 @@
|
||||
|
||||
package com.zayac.admin;
|
||||
|
||||
import com.zayac.admin.schedule.CheckRegAndDep;
|
||||
import com.zayac.admin.system.service.DeptService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class ContiNewAdminApplicationTests {
|
||||
class ZayacAdminApplicationTests {
|
||||
@Autowired
|
||||
private CheckRegAndDep checkRegAndDep;
|
||||
@Autowired
|
||||
private DeptService deptService;
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSchedule() {
|
||||
checkRegAndDep.checkRegistrationAndNewDeposit();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testQuery() {
|
||||
var users = deptService.getDeptWithUsersAndAccounts("minister");
|
||||
System.out.println(users);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user