diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 53a83f70..e28392dd 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -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 \ No newline at end of file + - /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/ diff --git a/docker/zayac-admin/Dockerfile b/docker/zayac-admin/Dockerfile index 26476841..7a147155 100644 --- a/docker/zayac-admin/Dockerfile +++ b/docker/zayac-admin/Dockerfile @@ -1,14 +1,24 @@ +# 使用 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 +COPY ./config /app/config/ +COPY ./lib /app/lib/ + +# 设置工作目录 WORKDIR /app/bin +# 暴露应用运行的端口 +EXPOSE 18000 + +# 运行应用 ENTRYPOINT ["java", \ "-jar", \ "-XX:+UseZGC", \ "-Djava.security.egd=file:/dev/./urandom", \ "-Dspring.profiles.active=prod", \ - "app.jar"] \ No newline at end of file + "/app/bin/app.jar"] diff --git a/docker/zayac-admin/config/application-generator.yml b/docker/zayac-admin/config/application-generator.yml new file mode 100644 index 00000000..c686c7c9 --- /dev/null +++ b/docker/zayac-admin/config/application-generator.yml @@ -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 \ No newline at end of file diff --git a/docker/zayac-admin/config/application-prod.yml b/docker/zayac-admin/config/application-prod.yml new file mode 100644 index 00000000..ab4b109e --- /dev/null +++ b/docker/zayac-admin/config/application-prod.yml @@ -0,0 +1,284 @@ +--- ### 项目配置 +project: + # URL(跨域配置默认放行此 URL,请注意更改为你实际的前端 URL) + url: http://localhost:5173 + # 是否为生产环境 + production: true + +--- ### 服务器配置 +server: + # HTTP 端口(默认 8080) + port: 18000 + +--- ### 数据源配置 +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: true + # 配置文件路径 + 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 + diff --git a/docker/zayac-admin/config/application.yml b/docker/zayac-admin/config/application.yml new file mode 100644 index 00000000..ffc3f3f2 --- /dev/null +++ b/docker/zayac-admin/config/application.yml @@ -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 diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/mapper/DailyStatsMapper.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/mapper/DailyStatsMapper.java index bdb16b1e..c5993934 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/mapper/DailyStatsMapper.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/mapper/DailyStatsMapper.java @@ -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 {} \ No newline at end of file diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/mapper/FinanceMapper.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/mapper/FinanceMapper.java index aecbc7fa..7bfccdf3 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/mapper/FinanceMapper.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/mapper/FinanceMapper.java @@ -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 {} \ No newline at end of file diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/mapper/FinanceSumMapper.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/mapper/FinanceSumMapper.java index 6da2a1a6..c1a6f2d8 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/mapper/FinanceSumMapper.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/mapper/FinanceSumMapper.java @@ -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 {} \ No newline at end of file diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/entity/FinanceDO.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/entity/FinanceDO.java index fc78e511..03d0f3d4 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/entity/FinanceDO.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/entity/FinanceDO.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/entity/FinanceSumDO.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/entity/FinanceSumDO.java index 31e4ddee..42e1a700 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/entity/FinanceSumDO.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/entity/FinanceSumDO.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/entity/StatsDO.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/entity/StatsDO.java index cb1534b2..f1ed1e42 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/entity/StatsDO.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/entity/StatsDO.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/query/FinanceQuery.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/query/FinanceQuery.java index 4ed94f95..ed8125ba 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/query/FinanceQuery.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/query/FinanceQuery.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/query/FinanceSumQuery.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/query/FinanceSumQuery.java index 99c3b722..81a652f5 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/query/FinanceSumQuery.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/query/FinanceSumQuery.java @@ -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; - /** * 财务报汇总查询条件 * diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/query/StatsQuery.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/query/StatsQuery.java index 1c4acc9d..61b2fb22 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/query/StatsQuery.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/query/StatsQuery.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/req/FinanceReq.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/req/FinanceReq.java index 8e9e2ecc..c50d5538 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/req/FinanceReq.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/req/FinanceReq.java @@ -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,11 @@ 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; /** diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/req/FinanceSumReq.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/req/FinanceSumReq.java index 57e4beda..e070224b 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/req/FinanceSumReq.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/req/FinanceSumReq.java @@ -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; /** diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/req/StatsReq.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/req/StatsReq.java index c8a03b37..2a61af5f 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/req/StatsReq.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/req/StatsReq.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/FinanceDetailResp.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/FinanceDetailResp.java index d9a6c7c2..1b774d5d 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/FinanceDetailResp.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/FinanceDetailResp.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/FinanceResp.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/FinanceResp.java index b6ecc92d..bf5887cc 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/FinanceResp.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/FinanceResp.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/FinanceSumDetailResp.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/FinanceSumDetailResp.java index 9fa4764c..aeedd008 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/FinanceSumDetailResp.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/FinanceSumDetailResp.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/FinanceSumResp.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/FinanceSumResp.java index 59139cd2..da68b6a0 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/FinanceSumResp.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/FinanceSumResp.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/StatsDetailResp.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/StatsDetailResp.java index fe337454..1b00d8a0 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/StatsDetailResp.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/StatsDetailResp.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/StatsResp.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/StatsResp.java index 52fa9c79..0b469962 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/StatsResp.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/model/resp/StatsResp.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/FinanceService.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/FinanceService.java index 0417d422..19fe4cb0 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/FinanceService.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/FinanceService.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/FinanceSumService.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/FinanceSumService.java index 92e58de0..e45f90d8 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/FinanceSumService.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/FinanceSumService.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/StatsService.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/StatsService.java index 612d93a8..d2d98a88 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/StatsService.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/StatsService.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/impl/DailyStatsServiceImpl.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/impl/DailyStatsServiceImpl.java index 59f1ed23..20b5c1a2 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/impl/DailyStatsServiceImpl.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/impl/DailyStatsServiceImpl.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/impl/FinanceServiceImpl.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/impl/FinanceServiceImpl.java index aa0cad5f..6aae4e27 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/impl/FinanceServiceImpl.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/impl/FinanceServiceImpl.java @@ -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 implements FinanceService { - public void addAll(List financeReqList){ + public void addAll(List financeReqList) { baseMapper.insertBatch(financeReqList); } diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/impl/FinanceSumServiceImpl.java b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/impl/FinanceSumServiceImpl.java index 01ece6a8..6fd9eae5 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/impl/FinanceSumServiceImpl.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/agent/service/impl/FinanceSumServiceImpl.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/config/ThreadPoolConfig.java b/zayac-admin-agent/src/main/java/com/zayac/admin/config/ThreadPoolConfig.java index f4505a28..36416cfc 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/config/ThreadPoolConfig.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/config/ThreadPoolConfig.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/req/PayRecordListReq.java b/zayac-admin-agent/src/main/java/com/zayac/admin/req/PayRecordListReq.java index 8786ee97..503030ae 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/req/PayRecordListReq.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/req/PayRecordListReq.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/req/team/TeamFinanceReq.java b/zayac-admin-agent/src/main/java/com/zayac/admin/req/team/TeamFinanceReq.java index 7049c6f1..7f496464 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/req/team/TeamFinanceReq.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/req/team/TeamFinanceReq.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/resp/team/PayRecordList.java b/zayac-admin-agent/src/main/java/com/zayac/admin/resp/team/PayRecordList.java index ba0879ce..b8b22f17 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/resp/team/PayRecordList.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/resp/team/PayRecordList.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/resp/team/TeamAccountFinance.java b/zayac-admin-agent/src/main/java/com/zayac/admin/resp/team/TeamAccountFinance.java index 273428a3..70e01331 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/resp/team/TeamAccountFinance.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/resp/team/TeamAccountFinance.java @@ -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 diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/resp/team/TeamFinancePagination.java b/zayac-admin-agent/src/main/java/com/zayac/admin/resp/team/TeamFinancePagination.java index a406ac7c..79440b9e 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/resp/team/TeamFinancePagination.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/resp/team/TeamFinancePagination.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/resp/team/TeamFinanceSum.java b/zayac-admin-agent/src/main/java/com/zayac/admin/resp/team/TeamFinanceSum.java index 21b6c026..7200e91e 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/resp/team/TeamFinanceSum.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/resp/team/TeamFinanceSum.java @@ -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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/schedule/CheckRegAndDep.java b/zayac-admin-agent/src/main/java/com/zayac/admin/schedule/CheckRegAndDep.java index bd73830d..9c231938 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/schedule/CheckRegAndDep.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/schedule/CheckRegAndDep.java @@ -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; @@ -44,6 +42,7 @@ import java.util.concurrent.Executor; import java.util.function.Function; import java.util.stream.Collectors; + /** * 检测注册跟新增的定时任务 */ @@ -51,13 +50,11 @@ import java.util.stream.Collectors; @Slf4j @Component @RequiredArgsConstructor +@Profile("dev") 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,30 +63,42 @@ 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 userIds = userRoleService.listUserIdByRoleId(minister.getId()); - userIds.forEach(userId -> { - processUser(userService.getById(userId), nowDate, nowDateTime).join(); + //查询用户角色为部长的部门所有用户 + List deptWithUsersAndAccounts = deptService.getDeptWithUsersAndAccounts(MINISTER_ROLE_CODE); + deptWithUsersAndAccounts.forEach(dept -> { + //根据用户角色对部门用户进行分组 + Map> 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 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 processUser(UserDO minister, LocalDate nowDate, LocalDateTime nowDateTime) { - List accounts = accountService.getAccountsByUserId(minister.getId(), DisEnableStatusEnum.ENABLE) - .stream() - .filter(AccountResp::getIsTeam) - .toList(); - + private CompletableFuture processUser(UserWithRolesAndAccountsResp minister, Map accountUsernameToUserMap, LocalDate nowDate, LocalDateTime nowDateTime) { + //根据总线用户的账号查询数据 + List accounts = minister.getAccounts(); List> futures = accounts.stream() - .map(account -> processTeamAccount(minister, account, nowDate, nowDateTime)) + .map(account -> processTeamAccount(minister, accountUsernameToUserMap, account, nowDate, nowDateTime)) .toList(); return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); } - private CompletableFuture processTeamAccount(UserDO minister, + private CompletableFuture processTeamAccount(UserWithRolesAndAccountsResp minister, + Map accountUsernameToUserMap, AccountResp account, LocalDate nowDate, LocalDateTime nowDateTime) { @@ -108,18 +117,17 @@ public class CheckRegAndDep { .collect(Collectors.toMap(TeamAccount::getAgentName, Function.identity())); CompletableFuture registrationProcess = registrationService - .processRegistration(minister, account, currentTeamInfo, prevTeamInfo, nowDate, teamAccountMap, asyncTaskExecutor) + .processRegistration(minister, account, accountUsernameToUserMap, teamAccountMap, currentTeamInfo, prevTeamInfo, nowDate, asyncTaskExecutor) .thenRunAsync(() -> log.info("Registration process completed"), asyncTaskExecutor); CompletableFuture depositProcess = depositService - .processDeposits(minister, account, currentTeamInfo, prevTeamInfo, nowDate, nowDateTime, 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); } } diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/schedule/DailyReport.java b/zayac-admin-agent/src/main/java/com/zayac/admin/schedule/DailyReport.java index 8ed8e92f..99b9c479 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/schedule/DailyReport.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/schedule/DailyReport.java @@ -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("dev") 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,30 +76,100 @@ 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 deptWithUsersAndAccounts = deptService.getDeptWithUsersAndAccounts(MINISTER_ROLE_CODE); + deptWithUsersAndAccounts.forEach(dept -> { + //根据用户角色对部门用户进行分组 + Map> 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.isEmpty(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 deptWithUsersAndAccounts = deptService.getDeptWithUsersAndAccounts(MINISTER_ROLE_CODE); + + deptWithUsersAndAccounts.forEach(dept -> { + //根据用户角色对部门用户进行分组 + Map> 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 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.isEmpty(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 deptWithUsersAndAccounts = deptService.getDeptWithUsersAndAccounts(MINISTER_ROLE_CODE); + + deptWithUsersAndAccounts.forEach(dept -> { + //根据用户角色对部门用户进行分组 + Map> 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); + var assistants = usersByRole.get(ASSISTANT_ROLE_CODE); + userWithRolesAndAccountsResps.forEach(ministerUser -> { + if (ministerUser.getNeedNotify() == DisEnableStatusEnum.ENABLE) { + generateAndSendTeamReport(ministerUser, nowDate.atStartOfDay(), nowDateTime, assistants); + } + } + ); + }); + } + + /** * 查询存款失败用户,并发送消息 * * @param date 日期 */ - private void getPayFailedMember(LocalDate date) { - RoleDO minister = roleService.getByCode(MINISTER_ROLE_CODE); - List userIds = userRoleService.listUserIdByRoleId(minister.getId()); + private void getPayFailedMember(UserWithRolesAndAccountsResp ministerUser, Map accountUsernameToUserMap, LocalDate date) { + TeamMemberReq memberListReq = TeamMemberReq.builder() .registerStartDate(date) .registerEndDate(date) @@ -110,124 +180,118 @@ public class DailyReport { .pageSize(100) .build(); - userIds.forEach(userId -> { - List>> futureList = new ArrayList<>(); - UserDO ministerUser = userService.getById(userId); - List accounts = accountService.getAccountsByUserId(userId, DisEnableStatusEnum.ENABLE) - .stream() - .filter(AccountResp::getIsTeam) - .toList(); + List>> futureList = new ArrayList<>(); + ministerUser.getAccounts().forEach(account -> { + CompletableFuture>> memberPaginationCompletableFuture = completableFutureWebClientService + .fetchDataForAccount(account, ApiPathConstants.MEMBER_TEAM_LIST_URL, memberListReq, new ParameterizedTypeReference<>() { + }); - accounts.forEach(account -> { - CompletableFuture>> memberPaginationCompletableFuture = completableFutureWebClientService - .fetchDataForAccount(account, ApiPathConstants.MEMBER_TEAM_LIST_URL, memberListReq, new ParameterizedTypeReference<>() { - }); + CompletableFuture> 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> memberFutures = membersWithoutDep.stream() + .map(memberWithoutDep -> { + PayRecordListReq req = PayRecordListReq.builder() + .startDate(date) + .endDate(date) + .pageSize(100) + .id(memberWithoutDep.getId()) + .build(); + CompletableFuture>> 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(); - CompletableFuture> 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> memberFutures = membersWithoutDep.stream() - .map(memberWithoutDep -> { - PayRecordListReq req = PayRecordListReq.builder() - .startDate(date) - .endDate(date) - .pageSize(100) - .id(memberWithoutDep.getId()) - .build(); - CompletableFuture>> 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> 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); - 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> 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 allFutures = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])); - allFutures.thenRunAsync(() -> { - // 主线下的所有的存款失败用户 - List allTeamMembers = futureList.stream() - .map(CompletableFuture::join) - .flatMap(List::stream) - .toList(); - Map> 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 (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; - }); + futureList.add(teamMembersFuture); }); - } - - private void sendDailyReport(LocalDate reportDate, LocalDateTime startDateTime, LocalDateTime endDateTime) { - RoleDO minister = roleService.getByCode(MINISTER_ROLE_CODE); - List userIds = userRoleService.listUserIdByRoleId(minister.getId()); - - userIds.forEach(userId -> { - UserDO ministerUser = userService.getById(userId); - List deptUsers = userService.getByDeptId(DisEnableStatusEnum.ENABLE, ministerUser.getDeptId()); - - List> tasks = new ArrayList<>(); - - if (ministerUser.getNeedNotify() == DisEnableStatusEnum.ENABLE) { - tasks.add(generateAndSendTeamReport(ministerUser, startDateTime, endDateTime)); + CompletableFuture allFutures = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])); + allFutures.thenRunAsync(() -> { + // 主线下的所有的存款失败用户 + List allTeamMembers = futureList.stream() + .map(CompletableFuture::join) + .flatMap(List::stream) + .toList(); + Map> 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 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 assistants, + List deptUsers) { + + List> 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 allTasks = CompletableFuture.allOf(tasks.toArray(new CompletableFuture[0])); + allTasks.join(); } /** @@ -239,17 +303,13 @@ public class DailyReport { * @return CompletableFuture */ - private CompletableFuture generateAndSendTeamReport(UserDO ministerUser, LocalDateTime - startDateTime, LocalDateTime endDateTime) { + private CompletableFuture generateAndSendTeamReport(UserWithRolesAndAccountsResp ministerUser, + LocalDateTime startDateTime, + LocalDateTime endDateTime, + List assistants) { return CompletableFuture.runAsync(() -> { List rows = new ArrayList<>(); - rows.add(new String[]{"平台", "注册", "新增", "转化率"}); - rows.add(new String[]{"----", "----", "----", "----"}); - - List accounts = accountService.getAccountsByUserId(ministerUser.getId(), DisEnableStatusEnum.ENABLE) - .stream() - .filter(AccountResp::getIsTeam) - .toList(); + List accounts = ministerUser.getAccounts(); int[] totals = {0, 0}; TeamInfoReq teamInfoReq = TeamInfoReq.builder().startDate(startDateTime).endDate(endDateTime).build(); @@ -258,25 +318,45 @@ public class DailyReport { .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(); + 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}); + rows.add(new String[]{accountResp.getPlatformName(), String.valueOf(totalNewMember), String + .valueOf(totalNewFirstDeposit), percent}); } - }, asyncTaskExecutor) - ) + }, asyncTaskExecutor)) .toList(); CompletableFuture 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 */ - private CompletableFuture processDeptUser(UserDO deptUser, AgentDataVisualListReq - agentDataVisualListReq, LocalDate reportDate, UserDO ministerUser) { + private CompletableFuture processDeptUser(UserWithRolesAndAccountsResp deptUser, + UserWithRolesAndAccountsResp ministerUser, + AgentDataVisualListReq agentDataVisualListReq, + LocalDate reportDate) { return CompletableFuture.runAsync(() -> { - List currUserAccounts = accountService.getAccountsByUserId(deptUser.getId(), DisEnableStatusEnum.ENABLE) - .stream() - .filter(accountResp -> !accountResp.getIsTeam()) - .toList(); + List currUserAccounts = deptUser.getAccounts(); List> futures = currUserAccounts.stream() - .map(currAccount -> agentDataVisualListService.getAgentDataVisualList(currAccount, agentDataVisualListReq) + .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")) - ) + .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 userStaticsFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + CompletableFuture userStaticsFuture = CompletableFuture.allOf(futures + .toArray(new CompletableFuture[0])); userStaticsFuture.thenRunAsync(() -> { - List 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 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,7 @@ public class DailyReport { }); } - - private void saveData(LocalDate reportDate) { + private void saveData(UserWithRolesAndAccountsResp ministerUser, List deptUsers, LocalDate reportDate) { // 获取传入年月 YearMonth inputYearMonth = YearMonth.from(reportDate); @@ -356,28 +433,20 @@ public class DailyReport { YearMonth previousYearMonth = currentYearMonth.minusMonths(1); if (inputYearMonth.equals(currentYearMonth) || inputYearMonth.equals(previousYearMonth)) { - RoleDO minister = roleService.getByCode(MINISTER_ROLE_CODE); - List userIds = userRoleService.listUserIdByRoleId(minister.getId()); - List> tasks = new ArrayList<>(); - userIds.forEach(userId -> { - UserDO ministerUser = userService.getById(userId); - List deptUsers = userService.getByDeptId(DisEnableStatusEnum.ENABLE, ministerUser.getDeptId()); - TeamFinanceReq teamFinanceReq = TeamFinanceReq.builder() - .pageNum(1) - .pageSize(999) - .commissionDate(reportDate) - .build(); - List ministerUserAccounts = accountService.getAccountsByUserId(ministerUser.getId(), DisEnableStatusEnum.ENABLE) - .stream() - .filter(AccountResp::getIsTeam) - .toList(); + TeamFinanceReq teamFinanceReq = TeamFinanceReq.builder() + .pageNum(1) + .pageSize(999) + .commissionDate(reportDate) + .build(); - // 异步处理 ministerUserAccounts - CompletableFuture ministerAccountsFuture = CompletableFuture.runAsync(() -> - ministerUserAccounts.parallelStream().forEach(accountResp -> { - TeamFinancePagination> financePagination = completableFutureFinanceService.getTeamFinance(accountResp, teamFinanceReq).join(); + + // 异步处理 ministerUserAccounts + CompletableFuture ministerAccountsFuture = CompletableFuture.runAsync(() -> { + List> accountFutures = ministerUser.getAccounts().stream() + .map(accountResp -> completableFutureFinanceService.getTeamFinance(accountResp, teamFinanceReq) + .thenAcceptAsync(financePagination -> { List financeReqList = financePagination.getList().stream().map(finance -> { FinanceDO financeDO = new FinanceDO(); BeanUtil.copyProperties(finance, financeDO); @@ -387,64 +456,66 @@ public class DailyReport { 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); - - // 异步处理 deptUsers - AgentDataVisualListReq agentDataVisualListReq = AgentDataVisualListReq.builder() - .monthDate(reportDate) - .build(); - - deptUsers.forEach(deptUser -> { - CompletableFuture deptUserFuture = CompletableFuture.runAsync(() -> { - List currUserAccounts = accountService.getAccountsByUserId(deptUser.getId(), DisEnableStatusEnum.ENABLE) - .stream() - .filter(accountResp -> !accountResp.getIsTeam()) - .toList(); - - List> 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 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); - }); + }, 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 deptUserFuture = CompletableFuture.runAsync(() -> { + List currUserAccounts = deptUser.getAccounts(); + List> 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 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); + }); CompletableFuture 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; diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/service/CompletableFutureFinanceService.java b/zayac-admin-agent/src/main/java/com/zayac/admin/service/CompletableFutureFinanceService.java index 9c0e01b7..9f6e41c7 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/service/CompletableFutureFinanceService.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/service/CompletableFutureFinanceService.java @@ -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>> getTeamFinance(AccountResp account, TeamFinanceReq teamFinanceReq) { + public CompletableFuture>> 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<>() { + }); } } diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/service/CompletableFutureWebClientService.java b/zayac-admin-agent/src/main/java/com/zayac/admin/service/CompletableFutureWebClientService.java index 61791bfa..1c9e08b5 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/service/CompletableFutureWebClientService.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/service/CompletableFutureWebClientService.java @@ -69,43 +69,43 @@ public class CompletableFutureWebClientService { ParameterizedTypeReference> 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 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 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(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 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 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(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) { diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/service/DepositService.java b/zayac-admin-agent/src/main/java/com/zayac/admin/service/DepositService.java index 804ff9e3..504586d9 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/service/DepositService.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/service/DepositService.java @@ -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; @@ -40,9 +55,9 @@ import java.util.stream.Collectors; public class DepositService { private final CompletableFutureWebClientService completableFutureWebClientService; private final TelegramMessageService telegramMessageService; - private final AccountService accountService; - public CompletableFuture processDeposits(UserDO minister, + public CompletableFuture processDeposits(UserWithRolesAndAccountsResp ministerUser, + Map accountUsernameToUserMap, AccountResp account, Team currentTeam, Team previousTeam, @@ -53,10 +68,11 @@ 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 processDepositRecords(UserDO minister, + private CompletableFuture processDepositRecords(UserWithRolesAndAccountsResp ministerUser, + Map accountUsernameToUserMap, AccountResp account, Team currentTeam, Team previousTeam, @@ -78,8 +94,7 @@ public class DepositService { .map(TeamAccountWithChange::getAgentName) .collect(Collectors.toSet()); return paginationCompletableFuture.thenApplyAsync(Pagination::getList, asyncTaskExecutor) - .thenComposeAsync(payRecords -> - processPayRecords(payRecords, changedTeamAccounts, changedAgentNames, minister, account, nowDateTime, asyncTaskExecutor), asyncTaskExecutor) + .thenComposeAsync(payRecords -> processPayRecords(payRecords, changedTeamAccounts, changedAgentNames, ministerUser, accountUsernameToUserMap, account, nowDateTime, asyncTaskExecutor), asyncTaskExecutor) .exceptionally(ex -> { log.error("Error processing deposits for account {}: {}", account.getId(), ex.getMessage()); return null; @@ -89,19 +104,21 @@ public class DepositService { private CompletableFuture processPayRecords(List payRecords, List changedTeamAccounts, Set changedAgentNames, - UserDO minister, + UserWithRolesAndAccountsResp minister, + Map accountUsernameToUserMap, AccountResp account, LocalDateTime nowDateTime, Executor asyncTaskExecutor) { Map> 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)))); + .collect(Collectors.groupingBy(PayRecord::getAgentName, Collectors.mapping(PayRecord::getName, Collectors + .collectingAndThen(Collectors.toSet(), ArrayList::new)))); - List> futures = agentNameWithNames.entrySet().stream() - .map(entry -> processAgentRecords(entry.getKey(), entry.getValue(), changedTeamAccounts, payRecords, minister, account, asyncTaskExecutor)) + List> futures = agentNameWithNames.entrySet() + .stream() + .map(entry -> processAgentRecords(entry.getKey(), entry + .getValue(), changedTeamAccounts, payRecords, minister, accountUsernameToUserMap, account, asyncTaskExecutor)) .toList(); return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); @@ -111,7 +128,8 @@ public class DepositService { List names, List changedTeamAccounts, List payRecords, - UserDO minister, + UserWithRolesAndAccountsResp minister, + Map accountUsernameToUserMap, AccountResp account, Executor asyncTaskExecutor) { StringBuilder depositResults = new StringBuilder(); @@ -123,33 +141,37 @@ public class DepositService { .orElseThrow(() -> new BusinessException(String.format("can not find agent name %s", agentName))); List> fetchFutures = names.stream() - .map(name -> fetchMemberDetails(account, name, LocalDate.now(), asyncTaskExecutor).thenAcceptAsync(member -> - payRecords.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())); + depositResults.append(telegramMessageService.buildDepositResultsMessage(member + .getName(), record.getScoreAmount())); } - }), asyncTaskExecutor).exceptionally(ex -> { - log.error("Error fetching details for member {}: {}", name, ex.getMessage()); - return null; - })) + }), asyncTaskExecutor) + .exceptionally(ex -> { + log.error("Error fetching details for member {}: {}", name, ex.getMessage()); + return null; + })) .toList(); CompletableFuture 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); + String notification = telegramMessageService.buildDepositMessage(agentName, targetTeamAccount + .getNewDepositNum(), depositResults.toString(), targetTeamAccount.getFirstDepositNum()); + var currUser = accountUsernameToUserMap.get(agentName); if (currUser != null && DisEnableStatusEnum.ENABLE.equals(currUser.getNeedNotify())) { - String botToken = StrUtil.isEmpty(currUser.getBotToken()) ? minister.getBotToken() : currUser.getBotToken(); + String botToken = StrUtil.isEmpty(currUser.getBotToken()) + ? minister.getBotToken() + : currUser.getBotToken(); telegramMessageService.sendMessage(botToken, currUser.getRegAndDepIds(), notification); } - telegramMessageService.sendMessage("6013830443:AAHUOS4v6Ln19ziZkH-L28-HZQLJrGcvhto", 6054562838L, notification); + telegramMessageService + .sendMessage("6013830443:AAHUOS4v6Ln19ziZkH-L28-HZQLJrGcvhto", 6054562838L, notification); } }, asyncTaskExecutor).exceptionally(ex -> { log.error("Error sending notification for account {}: {}", account.getUsername(), ex.getMessage()); @@ -157,7 +179,10 @@ public class DepositService { }); } - private CompletableFuture fetchMemberDetails(AccountResp account, String name, LocalDate nowDate, Executor asyncTaskExecutor) { + private CompletableFuture fetchMemberDetails(AccountResp account, + String name, + LocalDate nowDate, + Executor asyncTaskExecutor) { TeamMemberListReq memberListReq = TeamMemberListReq.builder() .name(name) .startDate(nowDate) @@ -170,7 +195,9 @@ public class DepositService { }); return memberFuture.thenApplyAsync(MemberPagination::getList, asyncTaskExecutor) - .thenApplyAsync(list -> list.stream().findFirst().orElseThrow(() -> new RuntimeException("没有找到匹配的成员信息")), asyncTaskExecutor) + .thenApplyAsync(list -> list.stream() + .findFirst() + .orElseThrow(() -> new RuntimeException("没有找到匹配的成员信息")), asyncTaskExecutor) .thenComposeAsync(member -> fetchDetailedMemberInfo(account, member.getId(), nowDate), asyncTaskExecutor); } diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/service/RegistrationService.java b/zayac-admin-agent/src/main/java/com/zayac/admin/service/RegistrationService.java index 0a77e985..bbe365ac 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/service/RegistrationService.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/service/RegistrationService.java @@ -24,8 +24,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.model.resp.UserWithRolesAndAccountsResp; import com.zayac.admin.system.service.AccountService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -50,12 +50,14 @@ public class RegistrationService { private final TelegramMessageService telegramMessageService; private final AccountService accountService; - public CompletableFuture processRegistration(UserDO minister, + public CompletableFuture processRegistration(UserWithRolesAndAccountsResp minister, AccountResp account, + Map accountUsernameToUserMap, + Map teamAccountMap, Team currentTeamInfo, Team prevTeamInfo, LocalDate nowDate, - Map teamAccountMap, + Executor asyncTaskExecutor) { if (prevTeamInfo != null && currentTeamInfo.getSubMemberCount() > prevTeamInfo.getSubMemberCount()) { int registerCount = currentTeamInfo.getSubMemberCount() - prevTeamInfo.getSubMemberCount(); @@ -70,24 +72,27 @@ public class RegistrationService { CompletableFuture>> 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> groupByTopAgentName = members.stream() - .collect(Collectors.groupingBy(TeamMember::getTopAgentName)); - 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); + return memberPaginationCompletableFuture.thenApplyAsync(MemberPagination::getList, asyncTaskExecutor) + .thenAcceptAsync(members -> { + log.info("Successfully get [{}] new registered members", members.size()); + if (!members.isEmpty()) { + Map> groupByTopAgentName = members.stream() + .collect(Collectors.groupingBy(TeamMember::getTopAgentName)); + groupByTopAgentName.forEach((accountName, accountMembers) -> { + String notification = telegramMessageService + .buildRegistrationMessage(accountName, accountMembers, teamAccountMap.get(accountName)); + 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); + }); } - telegramMessageService - .sendMessage("6013830443:AAHUOS4v6Ln19ziZkH-L28-HZQLJrGcvhto", 6054562838L, notification); - }); - } - }, asyncTaskExecutor); + }, asyncTaskExecutor); } return CompletableFuture.completedFuture(null); } diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/service/TeamService.java b/zayac-admin-agent/src/main/java/com/zayac/admin/service/TeamService.java index 005fb909..7a73638b 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/service/TeamService.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/service/TeamService.java @@ -59,6 +59,4 @@ public class TeamService { // Method intentionally left empty, used only for cache update } - - } diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/service/TelegramMessageService.java b/zayac-admin-agent/src/main/java/com/zayac/admin/service/TelegramMessageService.java index 034d7c08..0f8471e2 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/service/TelegramMessageService.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/service/TelegramMessageService.java @@ -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 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 convertStringToList(String str) { @@ -71,7 +69,7 @@ public class TelegramMessageService { private static String escapeMarkdown(String text) { List 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 accountMembers) { + public String buildFailedPayMessage(String accountName, List 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) { 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(); diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/service/WebClientService.java b/zayac-admin-agent/src/main/java/com/zayac/admin/service/WebClientService.java index 4587af52..f4f770da 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/service/WebClientService.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/service/WebClientService.java @@ -54,19 +54,19 @@ public class WebClientService { try { semaphore.acquire(); // 尝试获取许可 return this.webClient.post().uri(url).headers(httpHeaders -> { - Map 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 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 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 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 Mono monoRespHandler(ApiResponse apiResponse) { diff --git a/zayac-admin-agent/src/main/java/com/zayac/admin/utils/TableFormatter.java b/zayac-admin-agent/src/main/java/com/zayac/admin/utils/TableFormatter.java index f8369aa1..0e5a0d34 100644 --- a/zayac-admin-agent/src/main/java/com/zayac/admin/utils/TableFormatter.java +++ b/zayac-admin-agent/src/main/java/com/zayac/admin/utils/TableFormatter.java @@ -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 rows) { int[] colWidths = calculateColumnWidths(rows); StringBuilder table = new StringBuilder("
\n");
+        rows.add(0, new String[]{"平台", "注册", "新增", "转化率"});
+        rows.add(1, new String[]{"----", "----", "----", "----"});
         for (String[] row : rows) {
             table.append(formatRow(row, colWidths)).append("\n");
         }
diff --git a/zayac-admin-common/src/main/java/com/zayac/admin/common/config/mybatis/MyBatisPlusMetaObjectHandler.java b/zayac-admin-common/src/main/java/com/zayac/admin/common/config/mybatis/MyBatisPlusMetaObjectHandler.java
index 6d315977..9acbe6a4 100644
--- a/zayac-admin-common/src/main/java/com/zayac/admin/common/config/mybatis/MyBatisPlusMetaObjectHandler.java
+++ b/zayac-admin-common/src/main/java/com/zayac/admin/common/config/mybatis/MyBatisPlusMetaObjectHandler.java
@@ -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;
diff --git a/zayac-admin-common/src/main/java/com/zayac/admin/common/constant/CacheConstants.java b/zayac-admin-common/src/main/java/com/zayac/admin/common/constant/CacheConstants.java
index 869f7135..628f11c6 100644
--- a/zayac-admin-common/src/main/java/com/zayac/admin/common/constant/CacheConstants.java
+++ b/zayac-admin-common/src/main/java/com/zayac/admin/common/constant/CacheConstants.java
@@ -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() {
     }
 }
diff --git a/zayac-admin-common/src/main/java/com/zayac/admin/common/constant/RegexConstants.java b/zayac-admin-common/src/main/java/com/zayac/admin/common/constant/RegexConstants.java
index 7dc0220c..80b85d75 100644
--- a/zayac-admin-common/src/main/java/com/zayac/admin/common/constant/RegexConstants.java
+++ b/zayac-admin-common/src/main/java/com/zayac/admin/common/constant/RegexConstants.java
@@ -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 位,可以包含字母、数字、下划线,特殊字符,同时包含字母和数字)
      */
diff --git a/zayac-admin-generator/src/main/java/com/zayac/admin/generator/model/resp/GeneratePreviewResp.java b/zayac-admin-generator/src/main/java/com/zayac/admin/generator/model/resp/GeneratePreviewResp.java
index 4d07824d..7a66444c 100644
--- a/zayac-admin-generator/src/main/java/com/zayac/admin/generator/model/resp/GeneratePreviewResp.java
+++ b/zayac-admin-generator/src/main/java/com/zayac/admin/generator/model/resp/GeneratePreviewResp.java
@@ -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;
+
     /**
      * 文件名
      */
diff --git a/zayac-admin-generator/src/main/java/com/zayac/admin/generator/service/impl/GeneratorServiceImpl.java b/zayac-admin-generator/src/main/java/com/zayac/admin/generator/service/impl/GeneratorServiceImpl.java
index 42dc19fb..06905e98 100644
--- a/zayac-admin-generator/src/main/java/com/zayac/admin/generator/service/impl/GeneratorServiceImpl.java
+++ b/zayac-admin-generator/src/main/java/com/zayac/admin/generator/service/impl/GeneratorServiceImpl.java
@@ -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
+ */
+
 /**
  * 代码生成业务实现
  *
@@ -88,15 +95,15 @@ public class GeneratorServiceImpl implements GeneratorService {
             tableList.removeIf(table -> !StrUtil.containsAny(table.getTableName(), tableName));
         }
         tableList.removeIf(table -> StrUtil.equalsAnyIgnoreCase(table.getTableName(), generatorProperties
-            .getExcludeTables()));
+                .getExcludeTables()));
         CollUtil.sort(tableList, Comparator.comparing(Table::getCreateTime)
-            .thenComparing(table -> Optional.ofNullable(table.getUpdateTime()).orElse(table.getCreateTime()))
-            .reversed());
+                .thenComparing(table -> Optional.ofNullable(table.getUpdateTime()).orElse(table.getCreateTime()))
+                .reversed());
         List tableRespList = BeanUtil.copyToList(tableList, TableResp.class);
         PageResp pageResp = PageResp.build(pageQuery.getPage(), pageQuery.getSize(), tableRespList);
         for (TableResp tableResp : pageResp.getList()) {
             long count = genConfigMapper.selectCount(Wrappers.lambdaQuery(GenConfigDO.class)
-                .eq(GenConfigDO::getTableName, tableResp.getTableName()));
+                    .eq(GenConfigDO::getTableName, tableResp.getTableName()));
             tableResp.setIsConfiged(count > 0);
         }
         return pageResp;
@@ -118,8 +125,8 @@ public class GeneratorServiceImpl implements GeneratorService {
             }
             // 默认作者名称(上次保存使用的作者名称)
             GenConfigDO lastGenConfig = genConfigMapper.selectOne(Wrappers.lambdaQuery(GenConfigDO.class)
-                .orderByDesc(GenConfigDO::getCreateTime)
-                .last("LIMIT 1"));
+                    .orderByDesc(GenConfigDO::getCreateTime)
+                    .last("LIMIT 1"));
             if (null != lastGenConfig) {
                 genConfig.setAuthor(lastGenConfig.getAuthor());
             }
@@ -148,11 +155,11 @@ public class GeneratorServiceImpl implements GeneratorService {
         Set>> typeMappingEntrySet = typeMappingMap.entrySet();
         // 新增或更新字段配置
         Map fieldConfigMap = fieldConfigList.stream()
-            .collect(Collectors.toMap(FieldConfigDO::getColumnName, Function.identity(), (key1, key2) -> key2));
+                .collect(Collectors.toMap(FieldConfigDO::getColumnName, Function.identity(), (key1, key2) -> key2));
         int i = 1;
         for (Column column : columnList) {
             FieldConfigDO fieldConfig = Optional.ofNullable(fieldConfigMap.get(column.getName()))
-                .orElseGet(() -> new FieldConfigDO(column));
+                    .orElseGet(() -> new FieldConfigDO(column));
             // 更新已有字段配置
             if (null != fieldConfig.getCreateTime()) {
                 String columnType = StrUtil.splitToArray(column.getTypeName(), StringConstants.SPACE)[0].toLowerCase();
@@ -160,10 +167,10 @@ public class GeneratorServiceImpl implements GeneratorService {
                 fieldConfig.setColumnSize(Convert.toStr(column.getSize()));
             }
             String fieldType = typeMappingEntrySet.stream()
-                .filter(entry -> entry.getValue().contains(fieldConfig.getColumnType()))
-                .map(Map.Entry::getKey)
-                .findFirst()
-                .orElse(null);
+                    .filter(entry -> entry.getValue().contains(fieldConfig.getColumnType()))
+                    .map(Map.Entry::getKey)
+                    .findFirst()
+                    .orElse(null);
             fieldConfig.setFieldType(fieldType);
             fieldConfig.setFieldSort(i++);
             latestFieldConfigList.add(fieldConfig);
@@ -193,7 +200,7 @@ public class GeneratorServiceImpl implements GeneratorService {
             }
             // 既不在表单也不在查询中显示,不需要设置表单类型
             if (Boolean.FALSE.equals(fieldConfig.getShowInForm()) && Boolean.FALSE.equals(fieldConfig
-                .getShowInQuery())) {
+                    .getShowInQuery())) {
                 fieldConfig.setFormType(null);
             }
             fieldConfig.setTableName(tableName);
@@ -222,7 +229,7 @@ public class GeneratorServiceImpl implements GeneratorService {
         genConfigMap.put("date", DateUtil.date().toString("yyyy/MM/dd HH:mm"));
         String packageName = genConfig.getPackageName();
         String apiModuleName = StrUtil.subSuf(packageName, StrUtil
-            .lastIndexOfIgnoreCase(packageName, StringConstants.DOT) + 1);
+                .lastIndexOfIgnoreCase(packageName, StringConstants.DOT) + 1);
         genConfigMap.put("apiModuleName", apiModuleName);
         genConfigMap.put("apiName", StrUtil.lowerFirst(genConfig.getClassNamePrefix()));
         // 渲染代码
@@ -243,15 +250,43 @@ public class GeneratorServiceImpl implements GeneratorService {
                 generatePreview.setContent(TemplateUtils.render(templateConfig.getTemplatePath(), genConfigMap));
             } else {
                 generatePreview.setFileName(".vue".equals(extension) && "index".equals(templateConfigEntry.getKey())
-                    ? "index.vue"
-                    : this.getFrontendFileName(classNamePrefix, className, extension));
+                        ? "index.vue"
+                        : this.getFrontendFileName(classNamePrefix, className, extension));
                 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 tableNames, HttpServletRequest request, HttpServletResponse response) {
         try {
@@ -282,37 +317,11 @@ public class GeneratorServiceImpl implements GeneratorService {
      * @param genConfig           生成配置
      */
     private void generateCode(List 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 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));
     }
 
@@ -359,8 +367,8 @@ public class GeneratorServiceImpl implements GeneratorService {
         GeneratorProperties.TemplateConfig templateConfig = templateConfigEntry.getValue();
         // 移除需要忽略的字段
         List fieldConfigList = originFieldConfigList.stream()
-            .filter(fieldConfig -> !StrUtil.equalsAny(fieldConfig.getFieldName(), templateConfig.getExcludeFields()))
-            .toList();
+                .filter(fieldConfig -> !StrUtil.equalsAny(fieldConfig.getFieldName(), templateConfig.getExcludeFields()))
+                .toList();
         genConfigMap.put("fieldConfigs", fieldConfigList);
         // 统计部分特殊字段特征
         genConfigMap.put("hasLocalDateTime", false);
@@ -380,11 +388,11 @@ public class GeneratorServiceImpl implements GeneratorService {
             }
             QueryTypeEnum queryType = fieldConfig.getQueryType();
             if (null != queryType && StrUtil.equalsAny(queryType.name(), QueryTypeEnum.IN.name(), QueryTypeEnum.NOT_IN
-                .name(), QueryTypeEnum.BETWEEN.name())) {
+                    .name(), QueryTypeEnum.BETWEEN.name())) {
                 genConfigMap.put("hasListQueryField", true);
             }
         }
         String subPackageName = templateConfig.getPackageName();
         genConfigMap.put("subPackageName", subPackageName);
     }
-}
+}
\ No newline at end of file
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/auth/model/resp/UserInfoResp.java b/zayac-admin-system/src/main/java/com/zayac/admin/auth/model/resp/UserInfoResp.java
index fca2da81..9f715db1 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/auth/model/resp/UserInfoResp.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/auth/model/resp/UserInfoResp.java
@@ -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;
+
     /**
      * 创建时间
      */
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/auth/service/impl/LoginServiceImpl.java b/zayac-admin-system/src/main/java/com/zayac/admin/auth/service/impl/LoginServiceImpl.java
index e4a13639..30352955 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/auth/service/impl/LoginServiceImpl.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/auth/service/impl/LoginServiceImpl.java
@@ -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);
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/enums/FileTypeEnum.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/enums/FileTypeEnum.java
index abb2435c..194e2dbd 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/enums/FileTypeEnum.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/enums/FileTypeEnum.java
@@ -44,7 +44,7 @@ public enum FileTypeEnum implements IBaseEnum {
      * 图片
      */
     IMAGE(2, "图片", List
-        .of("jpg", "jpeg", "png", "gif", "bmp", "webp", "ico", "psd", "tiff", "dwg", "jxr", "apng", "xcf")),
+            .of("jpg", "jpeg", "png", "gif", "bmp", "webp", "ico", "psd", "tiff", "dwg", "jxr", "apng", "xcf")),
 
     /**
      * 文档
@@ -59,7 +59,8 @@ public enum FileTypeEnum implements IBaseEnum {
     /**
      * 音频
      */
-    AUDIO(5, "音频", List.of("mp3", "flac", "wav", "ogg", "midi", "m4a", "aac", "amr", "ac3", "aiff")),;
+    AUDIO(5, "音频", List.of("mp3", "flac", "wav", "ogg", "midi", "m4a", "aac", "amr", "ac3", "aiff")),
+    ;
 
     private final Integer value;
     private final String description;
@@ -73,8 +74,8 @@ public enum FileTypeEnum implements IBaseEnum {
      */
     public static FileTypeEnum getByExtension(String extension) {
         return Arrays.stream(FileTypeEnum.values())
-            .filter(t -> t.getExtensions().contains(StrUtil.emptyIfNull(extension).toLowerCase()))
-            .findFirst()
-            .orElse(FileTypeEnum.UNKNOWN);
+                .filter(t -> t.getExtensions().contains(StrUtil.emptyIfNull(extension).toLowerCase()))
+                .findFirst()
+                .orElse(FileTypeEnum.UNKNOWN);
     }
 }
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/enums/OptionCodeEnum.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/enums/OptionCodeEnum.java
new file mode 100644
index 00000000..ffea3419
--- /dev/null
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/enums/OptionCodeEnum.java
@@ -0,0 +1,63 @@
+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;
+}
\ No newline at end of file
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/AccountMapper.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/AccountMapper.java
index 2fea8855..e73a899e 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/AccountMapper.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/AccountMapper.java
@@ -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 {}
\ No newline at end of file
+public interface AccountMapper extends BaseMapper {
+    List selectAccountsByUserIds(List userIds);
+}
\ No newline at end of file
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/DeptMapper.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/DeptMapper.java
index aff8ee0d..32f1fe17 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/DeptMapper.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/DeptMapper.java
@@ -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 {
+    /**
+     * 查询指定指定角色下的所有所属部门用户
+     *
+     * @param roleCode 角色列表
+     * @return 部门用户列表
+     */
+    List selectDeptUsersByRoleCode(@Param("roleCode")String roleCode);
 }
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/RoleDeptMapper.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/RoleDeptMapper.java
index c2f8fd18..fee54a35 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/RoleDeptMapper.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/RoleDeptMapper.java
@@ -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 {
      */
     @Select("SELECT dept_id FROM sys_role_dept WHERE role_id = #{roleId}")
     List selectDeptIdByRoleId(@Param("roleId") Long roleId);
+
+    List selectRolesByDeptIds(List deptIds);
 }
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/UserMapper.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/UserMapper.java
index 7e6f0503..36c8db68 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/UserMapper.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/UserMapper.java
@@ -19,6 +19,8 @@ package com.zayac.admin.system.mapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.zayac.admin.system.model.resp.DeptUsersResp;
+import com.zayac.admin.system.model.resp.UserWithRolesAndAccountsResp;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import com.zayac.admin.common.config.mybatis.DataPermissionMapper;
@@ -26,6 +28,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 +100,13 @@ public interface UserMapper extends DataPermissionMapper {
      * @return 用户数量
      */
     Long selectCountByPhone(@FieldEncrypt @Param("phone") String phone, @Param("id") Long id);
+
+    /**
+     * 根据部门id查询直系用户
+     *
+     * @param deptIds 部门id
+     * @return 直系用户列表
+     */
+    List selectUsersByDeptIds(@Param("deptIds") List deptIds);
+
 }
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/UserRoleMapper.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/UserRoleMapper.java
index b56c4da5..a741a96a 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/UserRoleMapper.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/mapper/UserRoleMapper.java
@@ -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 {
      */
     @Select("SELECT user_id FROM sys_user_role WHERE role_id = #{roleId}")
     List selectUserIdByRoleId(@Param("roleId") Long roleId);
+
+    List selectRolesByUserIds(List userIds);
 }
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/model/entity/AccountDO.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/model/entity/AccountDO.java
index 70b912c3..9e2aed36 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/model/entity/AccountDO.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/model/entity/AccountDO.java
@@ -78,9 +78,4 @@ public class AccountDO extends BaseDO {
      * 是否为团队账号
      */
     private Boolean isTeam;
-
-    //    /**
-    //     * 消息通知对象IDs
-    //     */
-    //    private String receiverIds;
 }
\ No newline at end of file
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/model/entity/UserDO.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/model/entity/UserDO.java
index ebe96bdc..6b9a22eb 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/model/entity/UserDO.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/model/entity/UserDO.java
@@ -26,7 +26,6 @@ import top.continew.starter.security.crypto.annotation.FieldEncrypt;
 
 import java.io.Serial;
 import java.time.LocalDateTime;
-import java.util.List;
 
 /**
  * 用户实体
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/model/req/UserEmailUpdateRequest.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/model/req/UserEmailUpdateRequest.java
index 7e9fe291..9e077213 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/model/req/UserEmailUpdateRequest.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/model/req/UserEmailUpdateRequest.java
@@ -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;
 }
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/model/req/UserPhoneUpdateReq.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/model/req/UserPhoneUpdateReq.java
index 9c73fb5e..d163deb4 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/model/req/UserPhoneUpdateReq.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/model/req/UserPhoneUpdateReq.java
@@ -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;
 }
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/model/resp/DeptUsersResp.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/model/resp/DeptUsersResp.java
new file mode 100644
index 00000000..64037b18
--- /dev/null
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/model/resp/DeptUsersResp.java
@@ -0,0 +1,18 @@
+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 users;
+
+}
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/model/resp/UserWithRolesAndAccountsResp.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/model/resp/UserWithRolesAndAccountsResp.java
new file mode 100644
index 00000000..12b5559b
--- /dev/null
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/model/resp/UserWithRolesAndAccountsResp.java
@@ -0,0 +1,23 @@
+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 roles;
+    private List accounts;
+}
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/AccountService.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/AccountService.java
index 8b65df57..3af1a018 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/AccountService.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/AccountService.java
@@ -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 {
-    List getAccountsByUserId(Long id, DisEnableStatusEnum status);
-
-    UserDO getUserByAccountUsername(String username);
 
     void updateHeaders(String headers, Long id);
+
+    List getAccountsByUserIds(List userIds);
 }
\ No newline at end of file
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/DeptService.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/DeptService.java
index 611bb72f..05437bad 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/DeptService.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/DeptService.java
@@ -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 listChildren(Long id);
 
-    List getByName(String name);
+    List getDeptWithUsersAndAccounts(String roleCode);
 }
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/OptionService.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/OptionService.java
index e51d5956..0c90bde9 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/OptionService.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/OptionService.java
@@ -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 getValueByCode(OptionCodeEnum code, Function mapper);
 }
\ No newline at end of file
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/RoleDeptService.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/RoleDeptService.java
index 81149dba..4e0e3e49 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/RoleDeptService.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/RoleDeptService.java
@@ -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 listDeptIdByRoleId(Long roleId);
+
+    List getRolesByDeptIds(List deptIds);
 }
\ No newline at end of file
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/UserRoleService.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/UserRoleService.java
index 093c1733..c7af673f 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/UserRoleService.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/UserRoleService.java
@@ -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 roleIds);
 
-    List listUserIdByRoleId(Long roleId);
+    List getsUserByRoleCode(String roleCode);
+
+    List getRolesByUserIds(List collect);
 }
\ No newline at end of file
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/UserService.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/UserService.java
index 0ae25657..24686b61 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/UserService.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/UserService.java
@@ -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 deptIds);
 
+    /**
+     * 密码是否已过期
+     *
+     * @param pwdResetTime 上次重置密码时间
+     * @return 是否过期
+     */
+    Boolean isPasswordExpired(LocalDateTime pwdResetTime);
+
     /**
      * 构建字典
      *
@@ -148,16 +156,4 @@ public interface UserService extends BaseService> buildDict(List list);
 
-    /**
-     * 查询所有的可用用户
-     *
-     * @return 用户列表
-     */
-    List getAllEnabledUsers();
-
-    /**
-     * 查询所有部门用户
-     */
-    List getByDeptId(DisEnableStatusEnum status, Long deptId);
-
 }
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/AccountServiceImpl.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/AccountServiceImpl.java
index b20d09fa..5fb25b47 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/AccountServiceImpl.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/AccountServiceImpl.java
@@ -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 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 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 getAccountsByUserIds(List userIds) {
+        return baseMapper.selectAccountsByUserIds(userIds);
+    }
 }
\ No newline at end of file
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/DeptServiceImpl.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/DeptServiceImpl.java
index 6f1204d2..d3e9f220 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/DeptServiceImpl.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/DeptServiceImpl.java
@@ -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,19 +54,15 @@ public class DeptServiceImpl extends BaseServiceImpl listChildren(Long id) {
         DatabaseType databaseType = MetaUtils.getDatabaseTypeOrDefault(SpringUtil
-            .getBean(DynamicRoutingDataSource.class), DatabaseType.MYSQL);
+                .getBean(DynamicRoutingDataSource.class), DatabaseType.MYSQL);
         return baseMapper.lambdaQuery().apply(databaseType.findInSet(id, "ancestors")).list();
     }
 
-    @Override
-    public List getByName(String name) {
-        return baseMapper.lambdaQuery().eq(DeptDO::getName, name).list();
-    }
-
     @Override
     protected void beforeAdd(DeptReq req) {
         String name = req.getName();
@@ -92,13 +88,13 @@ public class DeptServiceImpl extends BaseServiceImpl children = this.listChildren(id);
             long enabledChildrenCount = children.stream()
-                .filter(d -> DisEnableStatusEnum.ENABLE.equals(d.getStatus()))
-                .count();
+                    .filter(d -> DisEnableStatusEnum.ENABLE.equals(d.getStatus()))
+                    .count();
             CheckUtils.throwIf(DisEnableStatusEnum.DISABLE
-                .equals(newStatus) && enabledChildrenCount > 0, "禁用 [{}] 前,请先禁用其所有下级部门", oldName);
+                    .equals(newStatus) && enabledChildrenCount > 0, "禁用 [{}] 前,请先禁用其所有下级部门", oldName);
             DeptDO oldParentDept = this.getByParentId(oldParentId);
             CheckUtils.throwIf(DisEnableStatusEnum.ENABLE.equals(newStatus) && DisEnableStatusEnum.DISABLE
-                .equals(oldParentDept.getStatus()), "启用 [{}] 前,请先启用其所有上级部门", oldName);
+                    .equals(oldParentDept.getStatus()), "启用 [{}] 前,请先启用其所有上级部门", oldName);
         }
         // 变更上级部门
         if (ObjectUtil.notEqual(req.getParentId(), oldParentId)) {
@@ -113,12 +109,12 @@ public class DeptServiceImpl extends BaseServiceImpl ids) {
         List list = baseMapper.lambdaQuery()
-            .select(DeptDO::getName, DeptDO::getIsSystem)
-            .in(DeptDO::getId, ids)
-            .list();
+                .select(DeptDO::getName, DeptDO::getIsSystem)
+                .in(DeptDO::getId, ids)
+                .list();
         Optional isSystemData = list.stream().filter(DeptDO::getIsSystem).findFirst();
         CheckUtils.throwIf(isSystemData::isPresent, "所选部门 [{}] 是系统内置部门,不允许删除", isSystemData.orElseGet(DeptDO::new)
-            .getName());
+                .getName());
         CheckUtils.throwIf(this.countChildren(ids) > 0, "所选部门存在下级部门,不允许删除");
         CheckUtils.throwIf(userService.countByDeptIds(ids) > 0, "所选部门存在用户关联,请解除关联后重试");
         // 删除角色和部门关联
@@ -135,10 +131,10 @@ public class DeptServiceImpl extends BaseServiceImpl baseMapper.lambdaQuery().apply(databaseType.findInSet(id, "ancestors")).count())
-            .sum();
+                .mapToLong(id -> baseMapper.lambdaQuery().apply(databaseType.findInSet(id, "ancestors")).count())
+                .sum();
     }
 
     /**
@@ -202,4 +198,11 @@ public class DeptServiceImpl extends BaseServiceImpl getDeptWithUsersAndAccounts(String roleCode) {
+        return baseMapper.selectDeptUsersByRoleCode(roleCode);
+    }
+
 }
+
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/OptionServiceImpl.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/OptionServiceImpl.java
index 055acc7c..d1478fb3 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/OptionServiceImpl.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/OptionServiceImpl.java
@@ -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 getValueByCode(OptionCodeEnum code, Function mapper) {
+        String value = RedisUtils.get(CacheConstants.OPTION_KEY_PREFIX + code.getValue());
+        if (StrUtil.isNotBlank(value)) {
+            return mapper.apply(value);
+        }
+        LambdaQueryWrapper queryWrapper = Wrappers.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);
+    }
+
 }
\ No newline at end of file
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/RoleDeptServiceImpl.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/RoleDeptServiceImpl.java
index 14d706dc..ead0025f 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/RoleDeptServiceImpl.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/RoleDeptServiceImpl.java
@@ -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 getRolesByDeptIds(List deptIds) {
+        return baseMapper.selectRolesByDeptIds(deptIds);
+    }
+
     @Override
     @ContainerMethod(namespace = ContainerConstants.ROLE_DEPT_ID_LIST, type = MappingType.ORDER_OF_KEYS)
     public List listDeptIdByRoleId(Long roleId) {
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/UserRoleServiceImpl.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/UserRoleServiceImpl.java
index 429b26af..b4566e0a 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/UserRoleServiceImpl.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/UserRoleServiceImpl.java
@@ -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,19 @@ public class UserRoleServiceImpl implements UserRoleService {
         return baseMapper.selectRoleIdByUserId(userId);
     }
 
-    @Override
-    @ContainerMethod(namespace = ContainerConstants.ROLE_USER_ID_LIST, type = MappingType.ORDER_OF_KEYS)
-    public List listUserIdByRoleId(Long roleId) {
-        return baseMapper.selectUserIdByRoleId(roleId);
-    }
 
     @Override
     public boolean isRoleIdExists(List roleIds) {
         return baseMapper.lambdaQuery().in(UserRoleDO::getRoleId, roleIds).exists();
     }
+
+    @Override
+    public List getsUserByRoleCode(String roleCode) {
+        return null;
+    }
+
+    @Override
+    public List getRolesByUserIds(List userIds) {
+        return baseMapper.selectRolesByUserIds(userIds);
+    }
 }
diff --git a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/UserServiceImpl.java b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/UserServiceImpl.java
index b0813c8e..685b4914 100644
--- a/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/UserServiceImpl.java
+++ b/zayac-admin-system/src/main/java/com/zayac/admin/system/service/impl/UserServiceImpl.java
@@ -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 disjunctionRoleIds = CollUtil.disjunction(req.getRoleIds(), userRoleService
-                .listRoleIdByUserId(id));
+                    .listRoleIdByUserId(id));
             CheckUtils.throwIfNotEmpty(disjunctionRoleIds, "[{}] 是系统内置用户,不允许变更角色", oldUser.getNickname());
         }
         // 更新信息
@@ -141,12 +147,12 @@ public class UserServiceImpl extends BaseServiceImpl ids) {
         CheckUtils.throwIf(CollUtil.contains(ids, LoginHelper.getUserId()), "不允许删除当前用户");
         List list = baseMapper.lambdaQuery()
-            .select(UserDO::getNickname, UserDO::getIsSystem)
-            .in(UserDO::getId, ids)
-            .list();
+                .select(UserDO::getNickname, UserDO::getIsSystem)
+                .in(UserDO::getId, ids)
+                .list();
         Optional isSystemData = list.stream().filter(UserDO::getIsSystem).findFirst();
         CheckUtils.throwIf(isSystemData::isPresent, "所选用户 [{}] 是系统内置用户,不允许删除", isSystemData.orElseGet(UserDO::new)
-            .getNickname());
+                .getNickname());
         // 删除用户和角色关联
         userRoleService.deleteByUserIds(ids);
         // 删除用户
@@ -158,7 +164,7 @@ public class UserServiceImpl extends BaseServiceImpl> buildDict(List list) {
-        if (CollUtil.isEmpty(list)) {
-            return new ArrayList<>(0);
-        }
-        return list.stream().map(r -> new LabelValueResp<>(r.getUsername(), r.getId())).toList();
-    }
-
-    @Override
-    public List 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 getByDeptId(DisEnableStatusEnum status, Long deptId) {
-        QueryWrapper wrapper = new QueryWrapper().eq(null != status, "status", status.getValue())
-            .and(q -> {
-                List deptIdList = deptService.listChildren(deptId)
-                    .stream()
-                    .map(DeptDO::getId)
-                    .collect(Collectors.toList());
-                deptIdList.add(deptId);
-                q.in("dept_id", deptIdList);
-            });
-        return baseMapper.selectList(wrapper);
-    }
-
     /**
      * 构建 QueryWrapper
      *
@@ -329,21 +366,21 @@ public class UserServiceImpl extends BaseServiceImpl createTimeList = query.getCreateTime();
         Long deptId = query.getDeptId();
         return new QueryWrapper().and(StrUtil.isNotBlank(description), q -> q.like("t1.username", description)
-            .or()
-            .like("t1.nickname", description)
-            .or()
-            .like("t1.description", description))
-            .eq(null != status, "t1.status", status)
-            .between(CollUtil.isNotEmpty(createTimeList), "t1.create_time", CollUtil.getFirst(createTimeList), CollUtil
-                .getLast(createTimeList))
-            .and(null != deptId, q -> {
-                List deptIdList = deptService.listChildren(deptId)
-                    .stream()
-                    .map(DeptDO::getId)
-                    .collect(Collectors.toList());
-                deptIdList.add(deptId);
-                q.in("t1.dept_id", deptIdList);
-            });
+                        .or()
+                        .like("t1.nickname", description)
+                        .or()
+                        .like("t1.description", description))
+                .eq(null != status, "t1.status", status)
+                .between(CollUtil.isNotEmpty(createTimeList), "t1.create_time", CollUtil.getFirst(createTimeList), CollUtil
+                        .getLast(createTimeList))
+                .and(null != deptId, q -> {
+                    List deptIdList = deptService.listChildren(deptId)
+                            .stream()
+                            .map(DeptDO::getId)
+                            .collect(Collectors.toList());
+                    deptIdList.add(deptId);
+                    q.in("t1.dept_id", deptIdList);
+                });
     }
 
     /**
@@ -380,4 +417,12 @@ public class UserServiceImpl extends BaseServiceImpl 0;
     }
-}
+
+    @Override
+    public List> buildDict(List list) {
+        if (CollUtil.isEmpty(list)) {
+            return new ArrayList<>(0);
+        }
+        return list.stream().map(r -> new LabelValueResp<>(r.getUsername(), r.getId())).toList();
+    }
+}
\ No newline at end of file
diff --git a/zayac-admin-system/src/main/resources/mapper/AccountMapper.xml b/zayac-admin-system/src/main/resources/mapper/AccountMapper.xml
new file mode 100644
index 00000000..4fe0fdb9
--- /dev/null
+++ b/zayac-admin-system/src/main/resources/mapper/AccountMapper.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+    
+
\ No newline at end of file
diff --git a/zayac-admin-system/src/main/resources/mapper/DeptMapper.xml b/zayac-admin-system/src/main/resources/mapper/DeptMapper.xml
index d272d629..562db1c6 100644
--- a/zayac-admin-system/src/main/resources/mapper/DeptMapper.xml
+++ b/zayac-admin-system/src/main/resources/mapper/DeptMapper.xml
@@ -1,4 +1,88 @@
 
 
+
 
-
\ No newline at end of file
+    
+
+    
+        
+        
+        
+            
+            
+            
+            
+            
+            
+            
+            
+            
+            
+            
+                
+                
+                
+            
+            
+                
+                
+                
+                
+                
+                
+                
+                
+            
+        
+    
+
diff --git a/zayac-admin-system/src/main/resources/mapper/RoleDeptMapper.xml b/zayac-admin-system/src/main/resources/mapper/RoleDeptMapper.xml
new file mode 100644
index 00000000..84d276c0
--- /dev/null
+++ b/zayac-admin-system/src/main/resources/mapper/RoleDeptMapper.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+    
+
\ No newline at end of file
diff --git a/zayac-admin-system/src/main/resources/mapper/RoleMapper.xml b/zayac-admin-system/src/main/resources/mapper/RoleMapper.xml
index 9ace45dd..bb658102 100644
--- a/zayac-admin-system/src/main/resources/mapper/RoleMapper.xml
+++ b/zayac-admin-system/src/main/resources/mapper/RoleMapper.xml
@@ -1,4 +1,14 @@
 
 
 
+    
 
\ No newline at end of file
diff --git a/zayac-admin-system/src/main/resources/mapper/UserMapper.xml b/zayac-admin-system/src/main/resources/mapper/UserMapper.xml
index 8449fe96..40a2a3ae 100644
--- a/zayac-admin-system/src/main/resources/mapper/UserMapper.xml
+++ b/zayac-admin-system/src/main/resources/mapper/UserMapper.xml
@@ -5,17 +5,17 @@
     
 
     
 
     
+        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
+        
+            #{userId}
+        ;
+    
+
+
\ No newline at end of file
diff --git a/zayac-admin-webapi/pom.xml b/zayac-admin-webapi/pom.xml
index 9bd83492..3ff2bc6a 100644
--- a/zayac-admin-webapi/pom.xml
+++ b/zayac-admin-webapi/pom.xml
@@ -15,7 +15,7 @@
     
         
         
-        zayac.admin.ContiNewAdminApplication
+        com.zayac.admin.ZayacAdminApplication
         
         bin
         
diff --git a/zayac-admin-webapi/src/main/java/com/zayac/admin/webapi/auth/AuthController.java b/zayac-admin-webapi/src/main/java/com/zayac/admin/webapi/auth/AuthController.java
index 170daa39..06d0ad09 100644
--- a/zayac-admin-webapi/src/main/java/com/zayac/admin/webapi/auth/AuthController.java
+++ b/zayac-admin-webapi/src/main/java/com/zayac/admin/webapi/auth/AuthController.java
@@ -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);
     }
 
diff --git a/zayac-admin-webapi/src/main/java/com/zayac/admin/webapi/system/UserCenterController.java b/zayac-admin-webapi/src/main/java/com/zayac/admin/webapi/system/UserCenterController.java
index 5d415d70..2ce3cb34 100644
--- a/zayac-admin-webapi/src/main/java/com/zayac/admin/webapi/system/UserCenterController.java
+++ b/zayac-admin-webapi/src/main/java/com/zayac/admin/webapi/system/UserCenterController.java
@@ -90,13 +90,13 @@ public class UserCenterController {
     @PatchMapping("/password")
     public R updatePassword(@Validated @RequestBody UserPasswordUpdateReq updateReq) {
         String rawOldPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq
-            .getOldPassword()));
+                .getOldPassword()));
         ValidationUtils.throwIfNull(rawOldPassword, DECRYPT_FAILED);
         String rawNewPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq
-            .getNewPassword()));
+                .getNewPassword()));
         ValidationUtils.throwIfNull(rawNewPassword, "新密码解密失败");
         ValidationUtils.throwIf(!ReUtil
-            .isMatch(RegexConstants.PASSWORD, rawNewPassword), "密码长度为 6 到 32 位,可以包含字母、数字、下划线,特殊字符,同时包含字母和数字");
+                .isMatch(RegexConstants.PASSWORD, rawNewPassword), "密码长度为 6 到 32 位,可以包含字母、数字、下划线,特殊字符,同时包含字母和数字");
         userService.updatePassword(rawOldPassword, rawNewPassword, LoginHelper.getUserId());
         return R.ok("修改成功,请牢记你的新密码");
     }
@@ -104,30 +104,30 @@ public class UserCenterController {
     @Operation(summary = "修改手机号", description = "修改手机号")
     @PatchMapping("/phone")
     public R 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 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("修改成功");
     }
 
diff --git a/zayac-admin-webapi/src/main/resources/config/application-dev.yml b/zayac-admin-webapi/src/main/resources/config/application-dev.yml
index 80c744fd..ceaac283 100644
--- a/zayac-admin-webapi/src/main/resources/config/application-dev.yml
+++ b/zayac-admin-webapi/src/main/resources/config/application-dev.yml
@@ -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
 
diff --git a/zayac-admin-webapi/src/main/resources/config/application-prod.yml b/zayac-admin-webapi/src/main/resources/config/application-prod.yml
index c301a56e..216b5de9 100644
--- a/zayac-admin-webapi/src/main/resources/config/application-prod.yml
+++ b/zayac-admin-webapi/src/main/resources/config/application-prod.yml
@@ -1,6 +1,6 @@
 --- ### 项目配置
 project:
-  # URL(跨域配置默认放行此 URL,请注意更改为你实际的前端 URL)
+  # URL(跨域配置默认放行此 URL,第三方登录回调默认使用此 URL 为前缀,请注意更改为你实际的前端 URL)
   url: http://localhost:5173
   # 是否为生产环境
   production: true
diff --git a/zayac-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_data.sql b/zayac-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_data.sql
index b6aac72c..08004ebc 100644
--- a/zayac-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_data.sql
+++ b/zayac-admin-webapi/src/main/resources/db/changelog/mysql/continew-admin_data.sql
@@ -114,7 +114,14 @@ VALUES
  'Copyright © 2022-present Charles7c  ContiNew Admin  津ICP备2022005864号-2',
  '用于显示登录页面的底部版权信息。', 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`
diff --git a/zayac-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_data.sql b/zayac-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_data.sql
index 67362bdd..718d2a74 100644
--- a/zayac-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_data.sql
+++ b/zayac-admin-webapi/src/main/resources/db/changelog/postgresql/continew-admin_data.sql
@@ -114,7 +114,14 @@ VALUES
  'Copyright © 2022-present Charles7c  ContiNew Admin  津ICP备2022005864号-2',
  '用于显示登录页面的底部版权信息。', 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"
diff --git a/zayac-admin-webapi/src/test/java/com/zayac/admin/ContiNewAdminApplicationTests.java b/zayac-admin-webapi/src/test/java/com/zayac/admin/ContiNewAdminApplicationTests.java
index 95667c41..17471bc7 100644
--- a/zayac-admin-webapi/src/test/java/com/zayac/admin/ContiNewAdminApplicationTests.java
+++ b/zayac-admin-webapi/src/test/java/com/zayac/admin/ContiNewAdminApplicationTests.java
@@ -16,13 +16,32 @@
 
 package com.zayac.admin;
 
+import com.zayac.admin.schedule.CheckRegAndDep;
+import com.zayac.admin.system.service.DeptService;
+import com.zayac.admin.system.service.UserService;
 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);
+    }
 }