commit e90729baaebf7db3dc4f66de751ba03498f8a1b4 Author: 18980591175 <470162950@qq.com> Date: Wed May 13 16:14:53 2026 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..97eebe6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml + +.idea +.vscode +.trae + +target +logs + +deployment.log.jsonl +/src/main/resources/prompts.txt + +prompts.txt +/src/main/resources/test.json + +*.pyc +pycache/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7971740 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/ +FROM bellsoft/liberica-openjdk-rocky:17.0.16-cds +#FROM bellsoft/liberica-openjdk-rocky:21.0.8-cds +#FROM findepi/graalvm:java17-native + +LABEL maintainer="shihongwei" + +RUN mkdir -p /hot-platform/server/logs \ + /hot-platform/server/temp \ + /hot-platform/skywalking/agent + +WORKDIR /hot-platform/server + +ENV SERVER_PORT=19090 SNAIL_PORT=28080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" TZ=Asia/Shanghai + +EXPOSE ${SERVER_PORT} +# 暴露 snail job 客户端端口 用于定时任务调度中心通信 +EXPOSE ${SNAIL_PORT} + +ADD ./target/hot-platform.jar ./app.jar + +SHELL ["/bin/bash", "-c"] + +ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \ + -Dsnail-job.port=${SNAIL_PORT} \ + -Duser.timezone=Asia/Shanghai \ + -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \ + -jar app.jar + diff --git a/README.md b/README.md new file mode 100644 index 0000000..ee31963 --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ +# HOT交通安全管理清单平台 后端服务 + +**项目概览** +- 基于 `Spring Boot 3.5.7`、`Java 17` 的后端服务,提供系统管理、代码生成、工作流等能力。 +- 项目坐标:`com.hotwj:hot-platform:1.0.0`(pom.xml:15-17),构件名:`hot-backend`(pom.xml:18)。 +- 默认打包为可执行 `jar`(build.finalName 为 `hot-platform`,pom.xml:409-411)。 + +**技术栈** +- Web与容器:`spring-boot-starter-web`(Undertow 容器,pom.xml:336-351),`spring-webmvc`(pom.xml:157-160)。 +- ORM与数据源:`mybatis-plus`(pom.xml:162-175)、`dynamic-datasource`(pom.xml:232-237)。 +- 安全与认证:`Sa-Token`(pom.xml:303-322),JWT 集成。 +- 缓存与并发:`redisson`(pom.xml:284-289)、`lock4j`(pom.xml:291-295)、`caffeine`(pom.xml:297-301)。 +- 文档与OpenAPI:`springdoc-openapi`(pom.xml:134-138、400-405)。 +- 任务与调度:`snail-job` 客户端(pom.xml:192-203)。 +- 工作流:`warm-flow`(pom.xml:377-386)。 +- 存储与工具:`AWS S3 SDK`(pom.xml:246-282)、`hutool`(pom.xml:115-119)、`mapstruct-plus`(pom.xml:121-125)。 +- 其他:`velocity` 模板引擎(pom.xml:358-363)、`ip2region`(pom.xml:127-132)、`JustAuth` 社交登录(pom.xml:330-334)、`jakarta.mail`(pom.xml:223-230)。 + +**环境要求** +- `JDK 17+`(pom.xml:26)。 +- `Maven 3.9+`。 +- 数据库:`MySQL 8.0.x`(pom.xml:58、72-77)。 + +**快速开始** +- 配置环境:`application.yml` 默认激活 `dev`(src/main/resources/application.yml:73)。 +- 开发运行: + - 使用 Maven 插件直接运行:`mvn spring-boot:run -Dspring-boot.run.profiles=dev` + - 或打包运行: + - `mvn clean package -DskipTests=true` + - `java -jar target/hot-platform.jar --spring.profiles.active=dev` +- 生产运行:`java -jar target/hot-platform.jar --spring.profiles.active=prod` + +**构建说明** +- 默认打包跳过测试(pom.xml:65-67),如需执行测试:`mvn clean package -DskipTests=false`。 +- 生成的可执行包名为 `hot-platform.jar`(由 `finalName` 决定,pom.xml:409-411)。 +- 支持 `spring-boot-maven-plugin` 重打包(pom.xml:412-422),可用 `spring-boot:run` 快速启动。 + +**配置要点** +- 端口与上下文:`server.port=8080`、`context-path=/`(src/main/resources/application.yml:3-7)。 +- Undertow 线程与缓冲配置(src/main/resources/application.yml:8-21)。 +- 日志:`logback-plus.xml`(src/main/resources/application.yml:43)。 +- 国际化:`spring.messages.basename=i18n/messages`(src/main/resources/application.yml:69-71)。 +- 模块扫描分组(系统/代码生成/工作流 等,src/main/resources/application.yml:211-216)。 +- 安全与令牌:`sa-token`(src/main/resources/application.yml:98-108)。 +- WebSocket/SSE:`sse.enabled`、`websocket.enabled`(src/main/resources/application.yml:239-259)。 +- 工作流:`warm-flow.*`(src/main/resources/application.yml:260-275)。 + +**常用端点** +- Actuator 健康检查:`/actuator/health`(src/main/resources/application.yml:268-279)。 +- OpenAPI 文档:`/v3/api-docs`,Swagger UI:`/swagger-ui/index.html`(由 springdoc 依赖启用,pom.xml:400-405)。 + +**Docker 运行** +- 项目提供 `Dockerfile`(Dockerfile:1-31),基础镜像为 `JDK 17`(Dockerfile:1)。 +- 镜像构建前需确保 `target` 下存在打包产物。 +- 当前 `Dockerfile` 的 `ADD` 指向 `ruoyi-admin.jar`(Dockerfile:17),如使用本项目构件,请将其改为 `hot-platform.jar` 或生成同名文件。 + +**常用命令** +- 清理并打包:`mvn clean package -DskipTests=true` +- 执行测试:`mvn test` +- 启动(开发):`mvn spring-boot:run -Dspring-boot.run.profiles=dev` +- 启动(Jar):`java -jar target/hot-platform.jar --spring.profiles.active=dev` + + diff --git a/REPORT_SYSTEM_GUIDE.md b/REPORT_SYSTEM_GUIDE.md new file mode 100644 index 0000000..73528bd --- /dev/null +++ b/REPORT_SYSTEM_GUIDE.md @@ -0,0 +1,139 @@ +# 报表打印系统架构与使用说明 + +本文档详细介绍了当前报表打印系统的架构设计、核心组件及其使用方法。 + +## 1. 架构设计概览 + +本系统采用 **Provider 模式** 与 **流式任务构建 (Builder Pattern)** 相结合的设计,旨在实现报表生成的解耦、灵活配置与高扩展性。 + +### 核心组件 + +1. **IReportDataProvider (报表数据提供者)** + * **职责**:定义单一报表的元数据(模版、标题、类型)和数据准备逻辑。 + * **优势**:将业务逻辑从 Service 层剥离,每个报表对应一个 Provider,互不干扰,易于维护。 + * **关键方法**: + * `getReportType()`: 唯一标识(如 `APPLICATION_FORM`)。 + * `prepareData(params)`: 准备模版所需的数据。 + * `getCoverProviderType()`: 定义关联的封面类型(支持自动封面)。 + +2. **ReportPrintJob (打印任务构建器)** + * **职责**:负责组装打印任务。它像一条流水线,可以按需添加封面、报表片段、设置公章/水印等。 + * **特性**: + * **链式调用**:`createJob().addCover().addBatch().render()`。 + * **混合布局**:自动处理横向 (Landscape) 与纵向 (Portrait) 页面的合并。 + * **上下文注入**:支持注入 CompanyId 用于公章/水印生成。 + +3. **ReportRenderService (渲染服务)** + * **职责**:底层的渲染引擎。 + * **功能**: + * 解析 Velocity 模版。 + * 调用 Gotenberg 进行 HTML 转 PDF。 + * 处理水印 (Watermark) 和电子公章 (Seal)。 + * 合并多个 PDF 片段。 + +## 2. 现有报表类型 + +目前系统已实现的 Provider 如下: + +| 报表类型 (Type) | Provider 类 | 描述 | +| :--- | :--- | :--- | +| `APPLICATION_FORM` | `DriverApplicationFormProvider` | 驾驶员应聘申请表 | +| `ID_CARD_SCAN` | `IdCardScanProvider` | 身份证扫描件 | +| `DRIVER_LICENSE_COPY` | `DriverLicenseCopyProvider` | 驾驶证复印件 | +| `QUALIFICATION_CERTIFICATE_COPY` | `QualificationCertificateCopyProvider` | 从业资格证复印件 | +| `QUALIFICATION_REVIEW` | `QualificationReviewProvider` | 资格审查及技能考核登记表 | +| `ACCIDENT_RECORD` | `AccidentRecordProvider` | 安全行车事故记录 (横向) | +| `VIOLATION_RECORD` | `ViolationRecordProvider` | 违章记录 (横向) | +| `ANNUAL_ASSESSMENT_RECORD` | `AnnualAssessmentRecordProvider` | 年度考核记录 | +| `MEDICAL_EXAMINATION_REPORT` | `MedicalExaminationReportProvider` | 体检报告 | +| `REWARD_PUNISHMENT_RECORD` | `RewardPunishmentRecordProvider` | 奖惩记录表 | + +## 3. 使用方法 (开发指南) + +### 3.1 创建新报表 + +1. **定义 Provider**: + 新建一个类实现 `IReportDataProvider`,并注册为 Spring Bean (`@Component`)。 + ```java + @Component + public class MyReportProvider implements IReportDataProvider { + @Override + public String getReportType() { return "MY_REPORT"; } + + @Override + public String getTemplateCode() { return "templates/my/report.html.vm"; } + + @Override + public Map prepareData(Map params) { + // 准备数据... + return Map.of("data", "value"); + } + } + ``` + +2. **创建 Velocity 模版**: + 在 `src/main/resources/templates/my/report.html.vm` 中编写 HTML。 + +### 3.2 调用打印服务 + +在业务 Service 中注入 `ReportRenderService` 和 `providerMap`,然后使用链式调用: + +#### 场景 A:单个报表打印 (带自动封面) + +```java +public byte[] printMyReport(List ids) { + // 1. 获取 Provider + IReportDataProvider provider = providerMap.get("MY_REPORT"); + + // 2. 构建任务 + return reportRenderService.createJob(providerMap::get) // 传入查找器以支持自动封面 + .withCompanyId(companyId) // 设置公司ID(用于公章) + .addCover(provider) // 自动添加关联封面 + .addBatch(provider, ids, "id") // 批量添加报表内容 + .useSeal(true) // 启用公章 + .render(); // 渲染并返回 PDF +} +``` + +#### 场景 B:一键打印 (多报表合并) + +```java +public byte[] printAll(List driverIds, List types) { + var job = reportRenderService.createJob(providerMap::get) + .withCompanyId(companyId) + .useSeal(false); // 全局公章开关 + + for (String id : driverIds) { + // 1. 为每个驾驶员添加一个独立封面 + job.addCover("COMMON_COVER", Map.of( + "title", "驾驶员档案", + "subtitle", "张三" + )); + + // 2. 依次添加选中的报表 + for (String type : types) { + job.add(providerMap.get(type), Map.of("driverId", id)); + } + } + return job.render(); +} +``` + +### 3.3 进阶配置 + +* **横向布局**:在 Provider 中重写 `isLandscape()` 返回 `true`,或者在 Job 中调用 `.isLandscape(true)` (通常由 Provider + 决定)。 +* **自定义封面**:Provider 可重写 `getCoverProviderType()` 指定特定封面,或重写 `getCoverData()` 自定义封面参数。 +* **水印控制**:`job.useWatermark(true).watermarkText("自定义水印")`。 + +## 4. 模版工具支持 + +模版中内置了 `$tool` 工具类 (`ReportVelocityTool`),提供常用功能: + +* `$tool.getDictLabel('type', value)`: 字典翻译 +* `$tool.formatDate(date, 'yyyy-MM-dd')`: 日期格式化 +* `$tool.getOssUrls(urlStr)`: 解析 OSS 图片链接 +* `$tool.numberToChinese(num)`: 数字转中文大写 + +--- +*文档生成日期:2026-03-01* diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..b0173d7 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,43 @@ +ls#!/bin/bash + +# 定义变量 +APP_NAME="hot-platform" +IMAGE_NAME="hot-platform" +PORT_HOST=50004 +PORT_CONTAINER=19090 +LOG_DIR="/opt/hot-platform/logs" + +# 停止并删除旧容器 +if [ "$(docker ps -q -f name=$APP_NAME)" ]; then + echo "Stopping existing container..." + docker stop $APP_NAME +fi + +if [ "$(docker ps -aq -f name=$APP_NAME)" ]; then + echo "Removing existing container..." + docker rm $APP_NAME +fi + +# 删除旧镜像(可选,如果每次都构建新镜像的话建议开启) +# if [ "$(docker images -q $IMAGE_NAME)" ]; then +# echo "Removing old image..." +# docker rmi $IMAGE_NAME +# fi + +# 构建新镜像 +echo "Building new image..." +docker build -t $IMAGE_NAME . + +# 启动新容器 +echo "Starting new container..." +docker run -d \ + --name $APP_NAME \ + --restart always \ + --add-host=host.docker.internal:host-gateway \ + -p $PORT_HOST:$PORT_CONTAINER \ + -v $LOG_DIR:/hot-platform/server/logs \ + -e JAVA_OPTS="-Dspring.profiles.active=test" \ + $IMAGE_NAME + +echo "Container started successfully!" +docker logs -f --tail 100 $APP_NAME diff --git a/docs/TrainingProgressLogic.md b/docs/TrainingProgressLogic.md new file mode 100644 index 0000000..a35627a --- /dev/null +++ b/docs/TrainingProgressLogic.md @@ -0,0 +1,119 @@ +# 培训计划学员进度计算逻辑说明 + +本文档说明了在生成“在线教育培训月度统计表”时,学员完成培训进度的计算逻辑及相关数据库表关联关系。 + +## 1. 总体进度计算逻辑 + +学员的培训进度取决于 `hot_training_participant` 表中的状态。 + +### 1.1 已完成学员 +如果学员在 `hot_training_participant` 表中的 `is_completed` 字段为 `1`(或 `complete_time` 不为空),则该学员的进度直接记为 **100%**。 +这意味着学员已经完成了所有的课程学习以及综合检测(如果有)。 + +### 1.2 未完成学员 +如果学员尚未完成(`is_completed = 0`),则进度计算公式如下: + +$$ +\text{总进度} = \frac{\sum \text{各课程进度} + \text{综合检测进度}}{\text{课程数量} + 1} +$$ + +* **课程数量**:当前培训计划包含的课程总数(来自 `hot_training_course_config`)。 +* **综合检测进度**:对于未完成学员,综合检测部分默认记为 **0%**。 +* **各课程进度**:见下文详细计算。 + +--- + +## 2. 单门课程进度计算逻辑 + +对于培训计划中的每一门课程,其进度计算取决于该课程是否包含**随堂练习**(题目)。 + +### 2.1 判断课程是否有随堂练习 +1. 通过 `hot_training_course_config` 表找到对应的 `course_id`。 +2. 查询 `hot_course_resource` 表,条件为 `course_id` 且 `resource_type = 3`(题目)。 +3. 如果存在记录,则认为该课程包含随堂练习;否则认为不包含。 + +### 2.2 课程进度计算公式 + +#### 情况 A:无随堂练习 +$$ +\text{课程进度} = \text{视频播放进度} +$$ +* **视频播放进度**:取 `hot_training_course_record` 表中该用户该课程的 `progress_rate`(百分比值,最大100)。 + +#### 情况 B:有随堂练习 +$$ +\text{课程进度} = (\text{视频播放进度} \times 90\%) + (\text{随堂练习得分} \times 10\%) +$$ +* **视频播放进度**:同上。 +* **随堂练习得分**: + * 检查 `hot_training_course_record` 表中的 `check_status` 字段。 + * 如果 `check_status = 1`(已完成),则得分为 **100**。 + * 否则,得分为 **0**。 + +--- + +## 3. 课程时长计算逻辑(分钟) + +“教育培训内容”区域除展示课程名称外,同时展示每门课程的视频总时长(分钟)。 + +### 3.1 计算步骤 + +1. 通过 `hot_training_course_config` 获取当前培训计划的课程列表(按 `sort_no` 排序),得到每门课的 `course_id`。 +2. 查询 `hot_course_resource` 表中该 `course_id` 下的视频资源: + - 条件:`course_id = ? AND resource_type = 1`(视频) + - 得到视频资源 ID 列表:`resource_id`(对应 `hot_media_resource.id`) +3. 查询 `hot_media_resource` 表,按资源 ID 集合汇总视频时长: + - 汇总字段:`SUM(duration_seconds)` +4. 将秒数转换为分钟并向上取整: + - `duration_min = ceil(duration_seconds / 60)` + +### 3.2 展示规则 + +- 若课程无视频资源或汇总时长为空,则时长列显示空白。 +- 否则显示 `X分钟`。 + +--- + +## 4. 学习记录表 - 学习时长计算逻辑 + +在“学习记录表”中,会显示“学习分钟数/计划分钟数”。 + +### 4.1 计划分钟数 +逻辑同第3节“课程时长计算逻辑”,为该培训计划下所有课程视频时长的总和(分钟,向上取整)。 + +### 4.2 学习分钟数 +取 `hot_training_course_record` 表中的 `learn_duration_min` 字段。 +**注意**:尽管字段名为 `min`,但在实际业务中存储的是**秒**。 +因此计算逻辑为: +1. 获取该用户在该培训计划下所有课程的学习记录。 +2. 若同一课程有多条记录,取 `learn_duration_min` 最大值。 +3. **对于每一门课程,将其学习时长与该课程的计划视频时长进行比较,取较小值(即学习时长不大于课程时长)。** +4. 累加所有课程的**处理后时长**(秒)。 +5. 将总秒数除以 60 并向上取整,得到最终的学习分钟数。 + +--- + +## 5. 数据库表关联关系 + +以下是涉及的关键数据库表及其关联方式: + +1. **hot_training (培训计划主表)** + * `id`: 培训计划ID + +2. **hot_training_participant (培训参与人员表)** + * `training_id`: 关联 `hot_training.id` + * `user_id`: 学员ID + * `is_completed`: 是否完成培训 (1=是, 0=否) + * `complete_time`: 完成时间 + +3. **hot_training_course_config (培训课程配置表)** + * `training_id`: 关联 `hot_training.id` + * `course_id`: 关联 `hot_course.id` (实际课程ID) + * `id`: 培训课程配置ID (用于关联学习记录) + +4. **hot_course_resource (课程资源关联表)** + * `course_id`: 关联 `hot_course.id` + * `resource_type`: 资源类型 (1=视频 2=音频 3=题目) + * 用于判断课程是否有随堂练习。 + +5. **hot_training_course_record (培训课程学习记录表)** diff --git a/docs/dev_specs.md b/docs/dev_specs.md new file mode 100644 index 0000000..12cecee --- /dev/null +++ b/docs/dev_specs.md @@ -0,0 +1,99 @@ +# 车辆档案打印功能开发规范 + +## 1. 核心技术栈 +* **模板引擎**: Velocity (`.html.vm`) +* **PDF生成**: Gotenberg (HTML转PDF) +* **水印处理**: PDFBox (`PdfWatermarkUtil`) +* **存储**: OSS (图片URL处理) + +## 2. 通用样式与布局规范 (Template) +所有打印模板位于 `src/main/resources/templates/vehicle/` 目录下。 + +### 2.1 页面布局 +* **纸张设置**: A4 纵向 (Vertical)。 +* **容器宽度**: `.page-content` 宽度建议设置为 **99%** 或留出微小边距,防止浏览器预览或打印时**右侧边框**被截断。 +* **表格样式**: + * 必须使用标准 HTML ``。 + * 边框合并: `border-collapse: collapse;`。 + * 边框线条: 统一使用细线 (`1px solid #000` 或 `#333`),避免出现粗细不一的视觉效果。 + * **禁止**使用 `
` 模拟表格边框,防止分页截断错位。 + +### 2.2 图片展示 (核心优化) +* **容器**: 图片必须放置在表格单元格 (`
+ + + #if($r.optionA) + + + + #end + #if($r.optionB) + + + + #end + #if($r.optionC) + + + + #end + #if($r.optionD) + + + + #end + #if($r.optionE) + + + + #end + #if($r.optionF) + + + + #end + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/securityManage/studyRecord.html.vm b/src/main/resources/templates/securityManage/studyRecord.html.vm new file mode 100644 index 0000000..7ab1f61 --- /dev/null +++ b/src/main/resources/templates/securityManage/studyRecord.html.vm @@ -0,0 +1,125 @@ +
+ $!{training.planName}——学习记录表 +
+ +
`) 内部。 +* **布局方式**: 使用 Flex 布局 (`display: flex; flex-wrap: wrap;`)。 +* **动态宽度逻辑** (Velocity): + * **单张图片**: 宽度设为 **98%** (占满整行),居中显示。 + * **多张图片**: 宽度设为 **48%** (一行两张),保留 1% 间距。 +* **样式代码参考**: + ```html +
+ #if($images.size() == 1) + #set($widthStyle = "width: 98%;") + #else + #set($widthStyle = "width: 48%;") + #end + #foreach($img in $images) +
+ +
+ #end +
+ ``` + +### 2.3 水印设置 +* **位置调整**: 为防止 A4 纵向打印时最后一行水印被截断,Y 轴偏移量需增加。 +* **代码位置**: `PdfWatermarkUtil.java` +* **参数**: `y + 100` (原为 `y + 50`)。 + +--- + +## 3. 后端接口开发规范 (Java) + +### 3.1 接口定义 +* **Controller**: `VehicleFileController` +* **Service**: `IVehicleFilePrintService` +* **请求方式**: `POST` (支持批量打印) +* **参数**: `List vehicleIds` +* **响应**: `void` (写入 `HttpServletResponse` 流,Content-Type: `application/pdf`) + +### 3.2 数据查询逻辑 +所有打印业务需遵循“**取最新一条记录**”的原则: +1. **查询条件**: `vehicle_id` +2. **排序**: 按业务时间或完成时间 **倒序** (`orderByDesc`) +3. **限制**: 取第一条 (`LIMIT 1`) +4. **容错**: + * 如果记录不存在 (`null`),跳过该车辆 (`continue`)。 + * **数据补全**: 如果业务表中缺失关键信息(如 `plateNumber`),**必须**回查 `hot_vehicle` 基础表进行补全。 + +### 3.3 图片URL处理 +* 数据库中图片通常以逗号分隔字符串存储 (`img1.jpg,img2.jpg`)。 +* **必须**使用 `splitUrls` 方法分割为 `List`。 +* **必须**通过 `ossService.selectUrlByIds` 处理(如需签名或转换)。 + +--- + +## 4. 具体业务模块规范 + +### 4.1 车辆年审报告 (Inspection Report) +* **接口地址**: `/vehicle/file/inspectionReport` +* **数据源表**: `hot_vehicle_annual_review` +* **关键字段**: `review_date` (年审日期), `image_urls` (附件) +* **模板文件**: `inspectionReport.html.vm` +* **特殊要求**: + * 评定等级需转义 (1=一级, 2=二级)。 + * 所有附件图片需放入表格行中。 + +### 4.2 车辆二级维护记录 (Maintenance Record) +* **接口地址**: `/vehicle/file/maintenanceRecord` +* **数据源表**: `hot_vehicle_maintenance` +* **关键字段**: + * `finish_time` (完成时间 - 排序依据) + * `entry_inspect_image_urls` (进厂检验单) + * `process_inspect_image_urls` (过程检验单) + * `final_inspect_image_urls` (竣工检验单) + * `factory_cert_image_urls` (出厂合格证) +* **模板文件**: `maintenanceRecord.html.vm` +* **特殊要求**: + * **纯表格布局**: 整个页面(包括标题、信息、四类图片)全部在 `` 内部。 + * **标题样式**: 表格内的分类标题(如“竣工检验单”)**不需要**背景色,字体加粗居中。 + * **图片布局**: 严格执行“单张全宽、多张半宽”的动态布局规则。 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..51e9f25 --- /dev/null +++ b/pom.xml @@ -0,0 +1,542 @@ + + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.5.7 + + + + com.hotwj + hot-platform + 1.0.0 + hot-backend + hot平台后端 + + + + UTF-8 + UTF-8 + + 17 + yyyy-MM-dd'T'HH:mm:ssXXX + + 2.8.13 + 0.15.0 + 1.3.0 + 2.3 + 1.44.0 + 3.5.14 + 3.9.1 + 5.8.40 + 3.5.5 + 3.51.0 + 2.2.7 + 4.3.1 + 1.8.0 + 1.5.0 + 0.2.0 + 1.18.40 + 1.80 + 1.16.7 + + 2.7.0 + + 2.28.22 + + 3.3.5 + + 2.0.60 + + 8.7.2-20250603 + + 1.8.2 + 9.5.0 + + + 1.15.0 + + + 3.4.2 + 3.4.0 + 3.14.0 + 3.5.3 + 1.3.0 + + true + + + + + + + + com.mysql + mysql-connector-j + ${mysql.version} + + + + + org.apache.commons + commons-lang3 + 3.18.0 + + + + + com.twelvemonkeys.imageio + imageio-jpeg + 3.12.0 + + + com.twelvemonkeys.imageio + imageio-tiff + 3.12.0 + + + com.twelvemonkeys.imageio + imageio-webp + 3.12.0 + + + + + cn.hutool + hutool-all + ${hutool.version} + + + + + io.github.linpeilie + mapstruct-plus-spring-boot-starter + ${mapstruct-plus.version} + + + + + org.lionsoul + ip2region + ${ip2region.version} + + + + + org.bouncycastle + bcprov-jdk15to18 + ${bouncycastle.version} + + + + + com.baomidou + mybatis-plus-spring-boot3-starter + ${mybatis-plus.version} + true + + + + + com.baomidou + mybatis-plus-jsqlparser + ${mybatis-plus.version} + + + + + cn.idev.excel + fastexcel + ${fastexcel.version} + + + + + org.springframework.boot + spring-boot-starter-websocket + + + + + com.aizuda + snail-job-client-starter + ${snailjob.version} + + + + + com.aizuda + snail-job-client-job-core + ${snailjob.version} + + + + + org.projectlombok + lombok + ${lombok.version} + + + + + jakarta.mail + jakarta.mail-api + + + org.eclipse.angus + jakarta.mail + + + + + com.baomidou + dynamic-datasource-spring-boot3-starter + ${dynamic-ds.version} + + + + + p6spy + p6spy + ${p6spy.version} + + + + + software.amazon.awssdk + s3 + ${aws.sdk.version} + + + + software.amazon.awssdk + aws-crt-client + + + + software.amazon.awssdk + apache-client + + + + software.amazon.awssdk + url-connection-client + + + + + + + software.amazon.awssdk + netty-nio-client + ${aws.sdk.version} + + + + + software.amazon.awssdk + s3-transfer-manager + ${aws.sdk.version} + + + + + org.redisson + redisson-spring-boot-starter + ${redisson.version} + + + + + com.baomidou + lock4j-redisson-spring-boot-starter + ${lock4j.version} + + + + + com.github.ben-manes.caffeine + caffeine + + + + + + cn.dev33 + sa-token-spring-boot3-starter + ${satoken.version} + + + + + cn.dev33 + sa-token-jwt + ${satoken.version} + + + + + org.dromara.sms4j + sms4j-spring-boot-starter + ${sms4j.version} + + + + + me.zhyd.oauth + JustAuth + ${justauth.version} + + + + + org.springframework.boot + spring-boot-starter-web + + + spring-boot-starter-tomcat + org.springframework.boot + + + + + + org.springframework.boot + spring-boot-starter-undertow + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + net.jthink + jaudiotagger + 3.0.1 + + + + + org.mp4parser + isoparser + 1.9.41 + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.mockito + mockito-inline + 5.2.0 + test + + + + + org.apache.velocity + velocity-engine-core + ${velocity.version} + + + + + org.anyline + anyline-environment-spring-data-jdbc + ${anyline.version} + + + + + org.anyline + anyline-data-jdbc-mysql + ${anyline.version} + + + + + org.dromara.warm + warm-flow-mybatis-plus-sb3-starter + ${warm-flow.version} + + + + org.dromara.warm + warm-flow-plugin-ui-sb-web + ${warm-flow.version} + + + + org.jetbrains + annotations + 24.1.0 + + + + + com.alibaba.fastjson2 + fastjson2 + 2.0.60 + + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${springdoc.version} + + + + + com.belerweb + pinyin4j + 2.5.0 + + + + + com.google.zxing + core + 3.5.2 + + + com.google.zxing + javase + 3.5.2 + + + + + org.apache.pdfbox + pdfbox + 2.0.31 + + + + + org.apache.poi + poi + 5.2.3 + + + org.apache.poi + poi-ooxml + 5.2.3 + + + org.apache.poi + poi-scratchpad + 5.2.3 + + + + org.apache.commons + commons-text + ${commons-text.version} + + + + + + ${project.artifactId} + + + org.codehaus.mojo + build-helper-maven-plugin + 3.6.0 + + + timestamp-property + + timestamp-property + + + local.build.time + yyyy-MM-dd HH:mm:ss + Asia/Shanghai + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + ${project.version} + ${local.build.time} + + + + + + repackage + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + + + io.github.linpeilie + mapstruct-plus-processor + ${mapstruct-plus.version} + + + org.mapstruct + mapstruct-processor + 1.5.5.Final + + + org.projectlombok + lombok + ${lombok.version} + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + + true + + + ${project.version} + ${local.build.time} + + + + + + org.apache.maven.plugins + maven-war-plugin + ${maven-war-plugin.version} + + false + ${project.artifactId} + + + + + + diff --git a/sql/1289/1.sql b/sql/1289/1.sql new file mode 100644 index 0000000..aeaeb3d --- /dev/null +++ b/sql/1289/1.sql @@ -0,0 +1,827 @@ +-- 1) 资金账户表(对应“账户余额:0元”) +CREATE TABLE `hot_fund_account` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `user_id` BIGINT UNSIGNED NULL COMMENT '用户ID', + `account_no` VARCHAR(32) NULL COMMENT '账户编号', + `account_type` TINYINT NULL DEFAULT 1 COMMENT '账户类型:1=个人 2=企业', + `balance_amount` DECIMAL(18, 2) NULL DEFAULT 0.00 COMMENT '可用余额', + `status` TINYINT NULL DEFAULT 1 COMMENT '状态:1=正常 0=禁用', + `version` INT NULL DEFAULT 0 COMMENT '乐观锁版本号', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + UNIQUE KEY `uk_account_no` (`account_no`), + KEY `idx_company_id` (`company_id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_company_user_del_status` (`company_id`, `user_id`, `is_deleted`, `status`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='资金账户表'; + + +-- 2) 充值订单表(对应:指定充值金额 + 支付方式) +CREATE TABLE `hot_fund_recharge_order` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `account_id` BIGINT UNSIGNED NULL COMMENT '账户ID(逻辑外键)', + `user_id` BIGINT UNSIGNED NULL COMMENT '用户ID', + `recharge_order_no` VARCHAR(32) NULL COMMENT '充值订单号', + `recharge_amount` DECIMAL(18, 2) NULL COMMENT '充值金额(如5.00)', + `pay_method` TINYINT NULL COMMENT '支付方式:1=微信 2=支付宝 3=银行卡', + `pay_scene` TINYINT NULL DEFAULT 1 COMMENT '支付场景:1=H5 2=小程序 3=APP 4=PC', + `pay_status` TINYINT NULL DEFAULT 1 COMMENT '支付状态:1=待支付 2=支付中 3=支付成功 4=支付失败 5=已关闭 6=已退款', + `third_trade_no` VARCHAR(64) NULL COMMENT '三方支付流水号', + `paid_time` DATETIME NULL COMMENT '支付成功时间', + `fail_reason` VARCHAR(255) NULL COMMENT '失败原因', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + UNIQUE KEY `uk_recharge_order_no` (`recharge_order_no`), + KEY `idx_company_id` (`company_id`), + KEY `idx_account_id` (`account_id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_pay_status` (`pay_status`), + KEY `idx_paid_time` (`paid_time`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='充值订单表'; + + +-- 3) 订单管理表(对应列表:订单号/用户名/消费型企业/订单类型/产品名称/应付金额/退款金额/实付金额/支付方式/下单时间/支付时间/支付状态) +CREATE TABLE `hot_fund_trade_order` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `account_id` BIGINT UNSIGNED NULL COMMENT '账户ID(逻辑外键)', + `user_id` BIGINT UNSIGNED NULL COMMENT '用户ID', + `user_name` VARCHAR(64) NULL COMMENT '用户名', + `consumer_company_id` BIGINT UNSIGNED NULL COMMENT '消费型企业ID', + `consumer_company_name` VARCHAR(128) NULL COMMENT '消费型企业名称', + `order_no` VARCHAR(32) NULL COMMENT '订单号', + `order_type` TINYINT NULL COMMENT '订单类型:1=拓客订单 2=学习订单 3=短信订单 4=岗前培训订单 5=其他', + `biz_scene` TINYINT NULL COMMENT '业务场景:1=学时套餐 2=短信套餐 3=岗前培训套餐 4=岗前活动套餐 5=充值 6=其他', + `product_name` VARCHAR(100) NULL COMMENT '产品名称', + `package_id` BIGINT UNSIGNED NULL COMMENT '学时套餐ID', + `package_name` VARCHAR(100) NULL COMMENT '学时套餐名称(快照)', + `hour_type` TINYINT NULL COMMENT '学时类型:1=通用学时 2=其他', + `hour_count` INT NULL COMMENT '学时数量(如1/50/100)', + `sms_package_id` BIGINT UNSIGNED NULL COMMENT '短信套餐ID', + `sms_package_name` VARCHAR(100) NULL COMMENT '短信套餐名称(快照)', + `sms_count` INT NULL COMMENT '短信条数(如1000/3000/5000)', + `pre_job_package_id` BIGINT UNSIGNED NULL COMMENT '岗前培训套餐ID', + `pre_job_package_name` VARCHAR(100) NULL COMMENT '岗前培训套餐名称(快照)', + `pre_job_activity_id` BIGINT UNSIGNED NULL COMMENT '岗前活动ID', + `pre_job_activity_name` VARCHAR(100) NULL COMMENT '岗前活动名称(快照)', + `unit_price` DECIMAL(18, 2) NULL COMMENT '单价(元)', + `quantity` INT NULL DEFAULT 1 COMMENT '购买数量', + `payable_amount` DECIMAL(18, 2) NULL COMMENT '应付金额(元)', + `discount_amount` DECIMAL(18, 2) NULL DEFAULT 0.00 COMMENT '优惠金额(元)', + `refund_amount` DECIMAL(18, 2) NULL DEFAULT 0.00 COMMENT '退款金额(元)', + `paid_amount` DECIMAL(18, 2) NULL COMMENT '实付金额(元)', + `pay_method` TINYINT NULL COMMENT '支付方式:1=微信 2=支付宝 3=银行卡 4=余额', + `third_trade_no` VARCHAR(64) NULL COMMENT '三方支付流水号', + `order_create_time` DATETIME NULL COMMENT '下单时间', + `pay_time` DATETIME NULL COMMENT '支付时间', + `pay_status` TINYINT NULL DEFAULT 1 COMMENT '支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭', + `refund_status` TINYINT NULL DEFAULT 0 COMMENT '退款状态:0=无退款 1=退款中 2=部分退款 3=全额退款 4=退款失败', + `refund_time` DATETIME NULL COMMENT '退款完成时间', + `cancel_time` DATETIME NULL COMMENT '取消时间', + `close_time` DATETIME NULL COMMENT '关闭时间', + `contract_id` BIGINT UNSIGNED NULL COMMENT '合同ID', + `contract_no` VARCHAR(64) NULL COMMENT '合同编号', + `contract_status` TINYINT NULL COMMENT '合同状态:1=草稿 2=已签订 3=已生效 4=已终止 5=已作废', + `contract_sign_time` DATETIME NULL COMMENT '合同签订时间', + `order_source` VARCHAR(64) NULL COMMENT '订单来源(如渠道/来源ID)', + `product_snapshot` TEXT NULL COMMENT '产品快照(JSON)', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + UNIQUE KEY `uk_order_no` (`order_no`), + KEY `idx_account_id` (`account_id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_third_trade_no` (`third_trade_no`), + KEY `idx_contract_no` (`contract_no`), + KEY `idx_company_time_status` (`company_id`, `order_create_time`, `pay_status`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='订单管理表'; + + +-- 4) 学时套餐表(对应页面:培训学时套餐配置) +CREATE TABLE `hot_hour_package` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `package_code` VARCHAR(32) NULL COMMENT '套餐编码', + `package_name` VARCHAR(100) NULL COMMENT '套餐名称', + `hour_type` TINYINT NULL DEFAULT 1 COMMENT '学时类型:1=通用学时 2=其他', + `hour_count` INT NULL COMMENT '学时数(如1/50/100)', + `origin_price` DECIMAL(18, 2) NULL COMMENT '原价格(元)', + `package_price` DECIMAL(18, 2) NULL COMMENT '套餐价格(元)', + `unit_price` DECIMAL(18, 2) NULL COMMENT '单价(元)', + `valid_start_time` DATETIME NULL COMMENT '活动有效开始时间', + `valid_end_time` DATETIME NULL COMMENT '活动有效结束时间', + `applicable_types` VARCHAR(255) NULL COMMENT '适用类型(多选),逗号拼接', + `package_desc` VARCHAR(500) NULL COMMENT '套餐说明', + `status` TINYINT NULL DEFAULT 1 COMMENT '状态:1=启用 0=停用', + `sort_no` INT NULL DEFAULT 0 COMMENT '排序号', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + UNIQUE KEY `uk_package_code` (`package_code`), + KEY `idx_company_id` (`company_id`), + KEY `idx_hour_type` (`hour_type`), + KEY `idx_status` (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='学时套餐表'; + + +-- 5) 学时套餐购买记录表(对应页面下单动作) +CREATE TABLE `hot_hour_package_purchase` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `account_id` BIGINT UNSIGNED NULL COMMENT '账户ID(逻辑外键)', + `user_id` BIGINT UNSIGNED NULL COMMENT '用户ID', + `order_id` BIGINT UNSIGNED NULL COMMENT '订单管理表ID(逻辑外键)', + `order_no` VARCHAR(32) NULL COMMENT '订单号', + `package_id` BIGINT UNSIGNED NULL COMMENT '学时套餐ID', + `package_name` VARCHAR(100) NULL COMMENT '学时套餐名称(快照)', + `hour_type` TINYINT NULL COMMENT '学时类型:1=通用学时 2=其他', + `hour_count` INT NULL COMMENT '学时数', + `origin_price` DECIMAL(18, 2) NULL COMMENT '原价格(元)', + `package_price` DECIMAL(18, 2) NULL COMMENT '套餐价格(元)', + `unit_price` DECIMAL(18, 2) NULL COMMENT '单价(元)', + `quantity` INT NULL DEFAULT 1 COMMENT '购买数量', + `total_hours` INT NULL COMMENT '总课时数', + `total_amount` DECIMAL(18, 2) NULL COMMENT '总金额(元)', + `pay_method` TINYINT NULL COMMENT '支付方式:1=微信 2=支付宝 3=银行卡 4=余额', + `wallet_type` TINYINT NULL COMMENT '钱包类型:1=个人钱包 2=企业钱包', + `wallet_id` BIGINT UNSIGNED NULL COMMENT '钱包ID', + `wallet_transaction_id` BIGINT UNSIGNED NULL COMMENT '钱包流水ID', + `wallet_transaction_no` VARCHAR(64) NULL COMMENT '钱包流水号', + `pay_status` TINYINT NULL DEFAULT 1 COMMENT '支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭', + `order_create_time` DATETIME NULL COMMENT '下单时间', + `pay_time` DATETIME NULL COMMENT '支付时间', + `package_snapshot` TEXT NULL COMMENT '套餐快照(JSON)', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + KEY `idx_company_id` (`company_id`), + KEY `idx_account_id` (`account_id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_order_id` (`order_id`), + KEY `idx_order_no` (`order_no`), + KEY `idx_package_id` (`package_id`), + KEY `idx_wallet_id` (`wallet_id`), + KEY `idx_wallet_transaction_id` (`wallet_transaction_id`), + KEY `idx_pay_status` (`pay_status`), + KEY `idx_order_create_time` (`order_create_time`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='学时套餐购买记录表'; + + +-- 6) 短信套餐表(对应页面:短信套餐配置) +CREATE TABLE `hot_sms_package` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `package_code` VARCHAR(32) NULL COMMENT '套餐编码', + `package_name` VARCHAR(100) NULL COMMENT '套餐名称', + `sms_count` INT NULL COMMENT '短信条数(如1000/3000/5000)', + `package_price` DECIMAL(18, 2) NULL COMMENT '套餐价(元)', + `unit_price` DECIMAL(18, 4) NULL COMMENT '单条单价(元)', + `status` TINYINT NULL DEFAULT 1 COMMENT '状态:1=启用 0=停用', + `sort_no` INT NULL DEFAULT 0 COMMENT '排序号', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + UNIQUE KEY `uk_package_code` (`package_code`), + KEY `idx_company_id` (`company_id`), + KEY `idx_status` (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='短信套餐表'; + + +-- 7) 短信套餐购买记录表(对应页面下单动作) +CREATE TABLE `hot_sms_package_purchase` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `account_id` BIGINT UNSIGNED NULL COMMENT '账户ID(逻辑外键)', + `user_id` BIGINT UNSIGNED NULL COMMENT '用户ID', + `consumer_company_id` BIGINT UNSIGNED NULL COMMENT '充值企业ID', + `consumer_company_name` VARCHAR(128) NULL COMMENT '充值企业名称', + `order_id` BIGINT UNSIGNED NULL COMMENT '订单管理表ID(逻辑外键)', + `order_no` VARCHAR(32) NULL COMMENT '订单号', + `package_id` BIGINT UNSIGNED NULL COMMENT '短信套餐ID', + `package_name` VARCHAR(100) NULL COMMENT '短信套餐名称(快照)', + `sms_count` INT NULL COMMENT '短信条数', + `unit_price` DECIMAL(18, 4) NULL COMMENT '单条单价(元)', + `quantity` INT NULL DEFAULT 1 COMMENT '购买数量', + `total_amount` DECIMAL(18, 2) NULL COMMENT '总金额(元)', + `pay_method` TINYINT NULL COMMENT '支付方式:1=微信 2=支付宝 3=银行卡 4=余额', + `pay_status` TINYINT NULL DEFAULT 1 COMMENT '支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭', + `order_create_time` DATETIME NULL COMMENT '下单时间', + `pay_time` DATETIME NULL COMMENT '支付时间', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + KEY `idx_company_id` (`company_id`), + KEY `idx_account_id` (`account_id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_consumer_company_id` (`consumer_company_id`), + KEY `idx_order_id` (`order_id`), + KEY `idx_order_no` (`order_no`), + KEY `idx_package_id` (`package_id`), + KEY `idx_pay_status` (`pay_status`), + KEY `idx_order_create_time` (`order_create_time`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='短信套餐购买记录表'; + + +-- 8) 岗前培训套餐表(对应页面:岗前培训套餐配置) +CREATE TABLE `hot_pre_job_package` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `package_code` VARCHAR(32) NULL COMMENT '套餐编码', + `package_name` VARCHAR(100) NULL COMMENT '套餐名称', + `package_content` VARCHAR(255) NULL COMMENT '套餐内容', + `package_type` TINYINT NULL DEFAULT 1 COMMENT '套餐类型:1=常规套餐 2=活动套餐', + `activity_id` BIGINT UNSIGNED NULL COMMENT '活动ID', + `activity_name` VARCHAR(100) NULL COMMENT '活动名称', + `origin_price` DECIMAL(18, 2) NULL COMMENT '原价(元)', + `activity_price` DECIMAL(18, 2) NULL COMMENT '活动价(元)', + `unit_price` DECIMAL(18, 2) NULL COMMENT '生效单价(元)', + `valid_start_time` DATETIME NULL COMMENT '活动有效开始时间', + `valid_end_time` DATETIME NULL COMMENT '活动有效结束时间', + `status` TINYINT NULL DEFAULT 1 COMMENT '状态:1=启用 0=停用', + `sort_no` INT NULL DEFAULT 0 COMMENT '排序号', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + UNIQUE KEY `uk_package_code` (`package_code`), + KEY `idx_company_id` (`company_id`), + KEY `idx_package_type` (`package_type`), + KEY `idx_activity_id` (`activity_id`), + KEY `idx_status` (`status`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='岗前培训套餐表'; + + +-- 9) 岗前培训套餐购买记录表(对应页面下单动作) +CREATE TABLE `hot_pre_job_package_purchase` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `account_id` BIGINT UNSIGNED NULL COMMENT '账户ID(逻辑外键)', + `user_id` BIGINT UNSIGNED NULL COMMENT '用户ID', + `consumer_company_id` BIGINT UNSIGNED NULL COMMENT '消费型企业ID', + `consumer_company_name` VARCHAR(128) NULL COMMENT '消费型企业名称', + `order_id` BIGINT UNSIGNED NULL COMMENT '订单管理表ID(逻辑外键)', + `order_no` VARCHAR(32) NULL COMMENT '订单号', + `package_id` BIGINT UNSIGNED NULL COMMENT '岗前培训套餐ID', + `package_name` VARCHAR(100) NULL COMMENT '岗前培训套餐名称(快照)', + `package_content` VARCHAR(255) NULL COMMENT '套餐内容(快照)', + `package_type` TINYINT NULL COMMENT '套餐类型:1=常规套餐 2=活动套餐', + `activity_id` BIGINT UNSIGNED NULL COMMENT '活动ID', + `activity_name` VARCHAR(100) NULL COMMENT '活动名称(快照)', + `origin_price` DECIMAL(18, 2) NULL COMMENT '原价(元)', + `activity_price` DECIMAL(18, 2) NULL COMMENT '活动价(元)', + `unit_price` DECIMAL(18, 2) NULL COMMENT '成交单价(元)', + `quantity` INT NULL DEFAULT 1 COMMENT '购买数量', + `total_amount` DECIMAL(18, 2) NULL COMMENT '总金额(元)', + `pay_method` TINYINT NULL COMMENT '支付方式:1=微信 2=支付宝 3=银行卡 4=余额', + `pay_status` TINYINT NULL DEFAULT 1 COMMENT '支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭', + `order_create_time` DATETIME NULL COMMENT '下单时间', + `pay_time` DATETIME NULL COMMENT '支付时间', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + KEY `idx_company_id` (`company_id`), + KEY `idx_account_id` (`account_id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_consumer_company_id` (`consumer_company_id`), + KEY `idx_order_id` (`order_id`), + KEY `idx_order_no` (`order_no`), + KEY `idx_package_id` (`package_id`), + KEY `idx_activity_id` (`activity_id`), + KEY `idx_pay_status` (`pay_status`), + KEY `idx_order_create_time` (`order_create_time`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='岗前培训套餐购买记录表'; + + +-- 10) 合同管理表(数据来源:支付时签订的协议) +CREATE TABLE `hot_contract_manage` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `contract_no` VARCHAR(64) NULL COMMENT '合同编号', + `contract_name` VARCHAR(128) NULL COMMENT '合同名称', + `contract_amount` DECIMAL(18, 2) NULL COMMENT '合同金额(元)', + `party_a_name` VARCHAR(128) NULL COMMENT '甲方名称', + `party_b_name` VARCHAR(128) NULL COMMENT '乙方名称', + `party_b_owner` VARCHAR(64) NULL COMMENT '乙方责任人', + `contact_phone` VARCHAR(32) NULL COMMENT '联系电话', + `contract_sign_time` DATETIME NULL COMMENT '合同签订时间', + `contract_effective_time` DATETIME NULL COMMENT '合同生效时间', + `contract_expire_time` DATETIME NULL COMMENT '合同到期时间', + `contract_status` TINYINT NULL DEFAULT 2 COMMENT '合同状态:1=草稿 2=已签订 3=已生效 4=已终止 5=已作废', + `sign_channel` TINYINT NULL COMMENT '签约渠道:1=微信 2=支付宝 3=银行卡 4=余额 5=线下', + `agreement_url` VARCHAR(1024) NULL COMMENT '协议文件URL', + `source_order_id` BIGINT UNSIGNED NULL COMMENT '来源订单ID(支付订单)', + `source_order_no` VARCHAR(32) NULL COMMENT '来源订单号(支付订单)', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + UNIQUE KEY `uk_contract_no` (`contract_no`), + KEY `idx_company_id` (`company_id`), + KEY `idx_party_b_name` (`party_b_name`), + KEY `idx_contract_status` (`contract_status`), + KEY `idx_contract_expire_time` (`contract_expire_time`), + KEY `idx_source_order_id` (`source_order_id`), + KEY `idx_source_order_no` (`source_order_no`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='合同管理表'; + + +-- 11) 培训学时购买明细表(对应页面:套餐名称/适用类型/购买人/购买数量/已使用/剩余) +CREATE TABLE `hot_hour_purchase_detail` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `account_id` BIGINT UNSIGNED NULL COMMENT '账户ID(逻辑外键)', + `user_id` BIGINT UNSIGNED NULL COMMENT '用户ID', + `buyer_name` VARCHAR(64) NULL COMMENT '购买人', + `buyer_company_id` BIGINT UNSIGNED NULL COMMENT '购买企业ID', + `buyer_company_name` VARCHAR(128) NULL COMMENT '购买企业名称', + `purchase_id` BIGINT UNSIGNED NULL COMMENT '学时套餐购买记录ID', + `order_id` BIGINT UNSIGNED NULL COMMENT '订单管理表ID(逻辑外键)', + `order_no` VARCHAR(32) NULL COMMENT '订单号', + `package_id` BIGINT UNSIGNED NULL COMMENT '学时套餐ID', + `package_name` VARCHAR(100) NULL COMMENT '套餐名称(快照)', + `applicable_types` VARCHAR(255) NULL COMMENT '适用类型(如日常培训、事故培训等)', + `purchase_count` INT NULL DEFAULT 1 COMMENT '购买数量', + `total_hours` INT NULL COMMENT '总课时数', + `used_hours` INT NULL DEFAULT 0 COMMENT '已使用学时数', + `remaining_hours` INT NULL COMMENT '剩余学时数', + `wallet_type` TINYINT NULL COMMENT '钱包类型:1=个人钱包 2=企业钱包', + `wallet_id` BIGINT UNSIGNED NULL COMMENT '钱包ID', + `version` INT NULL DEFAULT 0 COMMENT '乐观锁版本号', + `status` TINYINT NULL DEFAULT 1 COMMENT '状态:1=生效 2=已用完 3=已过期 4=已作废', + `last_use_time` DATETIME NULL COMMENT '最近使用时间', + `expire_time` DATETIME NULL COMMENT '到期时间', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + KEY `idx_company_id` (`company_id`), + KEY `idx_account_id` (`account_id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_order_id` (`order_id`), + KEY `idx_order_no` (`order_no`), + KEY `idx_purchase_id` (`purchase_id`), + KEY `idx_package_id` (`package_id`), + KEY `idx_wallet_id` (`wallet_id`), + KEY `idx_status` (`status`), + KEY `idx_expire_time` (`expire_time`), + KEY `idx_remaining_hours` (`remaining_hours`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='培训学时购买明细表'; + + +-- 12) 培训学时使用明细表(对应页面:使用人员/使用类型/套餐名称/使用学时数量/使用日期) +CREATE TABLE `hot_hour_usage_detail` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `purchase_detail_id` BIGINT UNSIGNED NULL COMMENT '购买明细ID(逻辑外键)', + `account_id` BIGINT UNSIGNED NULL COMMENT '账户ID(逻辑外键)', + `user_id` BIGINT UNSIGNED NULL COMMENT '使用人员ID', + `user_name` VARCHAR(64) NULL COMMENT '使用人员', + `operator_id` BIGINT UNSIGNED NULL COMMENT '操作人ID', + `operator_name` VARCHAR(64) NULL COMMENT '操作人', + `use_type` VARCHAR(64) NULL COMMENT '使用类型(如日常培训/违章培训)', + `package_id` BIGINT UNSIGNED NULL COMMENT '套餐ID', + `package_name` VARCHAR(100) NULL COMMENT '套餐名称(快照)', + `used_hours` INT NULL COMMENT '使用学时数量(正数存储,展示可加负号)', + `before_remaining_hours` INT NULL COMMENT '扣减前剩余学时', + `after_remaining_hours` INT NULL COMMENT '扣减后剩余学时', + `remaining_hours` INT NULL COMMENT '使用后剩余学时', + `use_date` DATE NULL COMMENT '使用日期', + `biz_order_id` BIGINT UNSIGNED NULL COMMENT '业务单据ID', + `biz_order_no` VARCHAR(32) NULL COMMENT '业务单号', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + KEY `idx_company_id` (`company_id`), + KEY `idx_purchase_detail_id` (`purchase_detail_id`), + KEY `idx_account_id` (`account_id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_operator_id` (`operator_id`), + KEY `idx_package_id` (`package_id`), + KEY `idx_use_type` (`use_type`), + KEY `idx_use_date` (`use_date`), + KEY `idx_biz_order_no` (`biz_order_no`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='培训学时使用明细表'; + + +-- 13) 短信企业统计表(对应页面:企业名称/总充值条数/总发送条数/总成功到达量/总计费量/总充值金额/剩余短信) +CREATE TABLE `hot_sms_company_stat` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `enterprise_id` BIGINT UNSIGNED NULL COMMENT '企业ID', + `enterprise_name` VARCHAR(128) NULL COMMENT '企业名称', + `total_recharge_count` INT NULL DEFAULT 0 COMMENT '总充值条数', + `total_send_count` INT NULL DEFAULT 0 COMMENT '总发送条数', + `total_success_count` INT NULL DEFAULT 0 COMMENT '总成功到达量', + `total_billable_count` INT NULL DEFAULT 0 COMMENT '总计费量', + `total_recharge_amount` DECIMAL(18, 2) NULL DEFAULT 0.00 COMMENT '总充值金额(元)', + `remaining_sms_count` INT NULL DEFAULT 0 COMMENT '剩余短信', + `last_stat_time` DATETIME NULL COMMENT '最近统计时间', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + UNIQUE KEY `uk_enterprise_id` (`enterprise_id`), + KEY `idx_company_id` (`company_id`), + KEY `idx_enterprise_name` (`enterprise_name`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='短信企业统计表'; + + +-- 14) 短信按日统计表(对应页面:日期/总发送量/成功到达量/计费量) +CREATE TABLE `hot_sms_daily_stat` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `enterprise_id` BIGINT UNSIGNED NULL COMMENT '企业ID', + `enterprise_name` VARCHAR(128) NULL COMMENT '企业名称', + `stat_date` DATE NULL COMMENT '统计日期', + `total_send_count` INT NULL DEFAULT 0 COMMENT '总发送量', + `success_count` INT NULL DEFAULT 0 COMMENT '成功到达量', + `billable_count` INT NULL DEFAULT 0 COMMENT '计费量', + `fail_count` INT NULL DEFAULT 0 COMMENT '失败量', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + UNIQUE KEY `uk_enterprise_stat_date` (`enterprise_id`, `stat_date`), + KEY `idx_company_id` (`company_id`), + KEY `idx_stat_date` (`stat_date`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='短信按日统计表'; + + +-- 15) 短信充值记录表(对应页面:充值时间/充值企业/充值金额/充值条数/过期时间/操作人) +CREATE TABLE `hot_sms_recharge_record` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `enterprise_id` BIGINT UNSIGNED NULL COMMENT '充值企业ID', + `enterprise_name` VARCHAR(128) NULL COMMENT '充值企业', + `recharge_order_id` BIGINT UNSIGNED NULL COMMENT '短信购买记录ID(逻辑外键)', + `recharge_order_no` VARCHAR(32) NULL COMMENT '充值订单号', + `recharge_time` DATETIME NULL COMMENT '充值时间', + `recharge_amount` DECIMAL(18, 2) NULL COMMENT '充值金额(元)', + `recharge_sms_count` INT NULL COMMENT '充值条数', + `expire_time` DATETIME NULL COMMENT '过期时间', + `operator_id` BIGINT UNSIGNED NULL COMMENT '操作人ID', + `operator_name` VARCHAR(64) NULL COMMENT '操作人', + `status` TINYINT NULL DEFAULT 1 COMMENT '状态:1=生效 2=已过期 3=已作废', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + UNIQUE KEY `uk_recharge_order_no` (`recharge_order_no`), + KEY `idx_company_id` (`company_id`), + KEY `idx_enterprise_id` (`enterprise_id`), + KEY `idx_recharge_time` (`recharge_time`), + KEY `idx_expire_time` (`expire_time`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='短信充值记录表'; + + +-- 16) 短信发送明细表(对应页面:短信类型/姓名/手机号/岗位/是否收到/详情) +CREATE TABLE `hot_sms_send_detail` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `enterprise_id` BIGINT UNSIGNED NULL COMMENT '企业ID', + `enterprise_name` VARCHAR(128) NULL COMMENT '企业名称', + `biz_type` VARCHAR(64) NULL COMMENT '所属类型(如驾驶员管理)', + `sms_type` VARCHAR(64) NULL COMMENT '短信类型', + `receiver_id` BIGINT UNSIGNED NULL COMMENT '接收人ID', + `receiver_name` VARCHAR(64) NULL COMMENT '姓名', + `mobile` VARCHAR(32) NULL COMMENT '手机号', + `receiver_role` VARCHAR(64) NULL COMMENT '岗位/接收人类型', + `notice_content` TEXT NULL COMMENT '通知内容', + `notify_time` DATETIME NULL COMMENT '通知时间', + `notify_status` TINYINT NULL DEFAULT 1 COMMENT '通知状态:1=待发送 2=已发送 3=发送失败', + `is_received` TINYINT NULL DEFAULT 0 COMMENT '是否收到:0=否 1=是', + `receive_time` DATETIME NULL COMMENT '收到时间', + `send_channel` TINYINT NULL DEFAULT 1 COMMENT '发送渠道:1=短信 2=站内信', + `sms_count` INT NULL DEFAULT 1 COMMENT '消耗短信条数', + `billable_count` INT NULL DEFAULT 1 COMMENT '计费条数', + `provider_msg_id` VARCHAR(64) NULL COMMENT '供应商消息ID', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + KEY `idx_company_id` (`company_id`), + KEY `idx_enterprise_id` (`enterprise_id`), + KEY `idx_receiver_id` (`receiver_id`), + KEY `idx_mobile` (`mobile`), + KEY `idx_sms_type` (`sms_type`), + KEY `idx_notify_time` (`notify_time`), + KEY `idx_is_received` (`is_received`), + KEY `idx_notify_status` (`notify_status`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='短信发送明细表'; + + +-- 17) 发票管理表(对应页面:订单号/购买者/订单类型/订单金额/支付方式/开票状态/开票时间) +CREATE TABLE `hot_invoice_manage` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `order_id` BIGINT UNSIGNED NULL COMMENT '订单ID(逻辑外键)', + `order_no` VARCHAR(32) NULL COMMENT '订单号', + `buyer_company_id` BIGINT UNSIGNED NULL COMMENT '购买者企业ID', + `buyer_company_name` VARCHAR(128) NULL COMMENT '购买者(企业名称)', + `order_type` TINYINT NULL COMMENT '订单类型:1=拓客订单 2=学习订单 3=短信订单 4=岗前培训订单 5=其他', + `order_amount` DECIMAL(18, 2) NULL COMMENT '订单金额(元)', + `pay_method` TINYINT NULL COMMENT '支付方式:1=微信 2=支付宝 3=银行卡 4=余额', + `pay_time` DATETIME NULL COMMENT '支付时间', + + `invoice_amount` DECIMAL(18, 2) NULL COMMENT '开票金额(元)', + `invoice_type` TINYINT NULL COMMENT '发票类型:1=增值税电子普通发票 2=增值税电子专用发票', + `invoice_title` VARCHAR(200) NULL COMMENT '发票抬头', + `tax_no` VARCHAR(64) NULL COMMENT '税号', + `bank_name` VARCHAR(100) NULL COMMENT '开户银行', + `bank_account` VARCHAR(64) NULL COMMENT '银行账号', + `company_address` VARCHAR(255) NULL COMMENT '企业地址', + `company_phone` VARCHAR(32) NULL COMMENT '企业电话', + + `invoice_status` TINYINT NULL DEFAULT 1 COMMENT '开票状态:1=未开票 2=申请开票 3=已开票 4=驳回 5=已作废', + `apply_time` DATETIME NULL COMMENT '申请时间', + `invoice_time` DATETIME NULL COMMENT '开票时间', + `invoice_code` VARCHAR(32) NULL COMMENT '发票代码', + `invoice_no` VARCHAR(64) NULL COMMENT '发票号码(可按年月+随机号)', + `invoice_url` VARCHAR(1024) NULL COMMENT '电子发票文件URL', + `reject_reason` VARCHAR(255) NULL COMMENT '驳回原因', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='发票管理表'; + + +-- 18) 培训反馈主表(对应页面:反馈列表/我要反馈) +CREATE TABLE `hot_training_feedback` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `feedback_no` VARCHAR(32) NULL COMMENT '反馈单号', + `feedback_user_id` BIGINT UNSIGNED NULL COMMENT '反馈人ID', + `feedback_user_name` VARCHAR(64) NULL COMMENT '反馈人姓名', + `feedback_keyword` VARCHAR(100) NULL COMMENT '反馈关键词', + `feedback_content` TEXT NULL COMMENT '反馈内容', + `attachment_urls` TEXT NULL COMMENT '附件URL列表(逗号分隔,对应sys_oss)', + `feedback_time` DATETIME NULL COMMENT '反馈时间', + `status` TINYINT NULL DEFAULT 1 COMMENT '处理状态:1=待处理 2=处理中 3=已回复 4=已关闭', + `reply_time` DATETIME NULL COMMENT '最新回复时间', + `reply_count` INT NULL DEFAULT 0 COMMENT '回复条数', + `last_reply_content` VARCHAR(255) NULL COMMENT '最新回复内容摘要', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='培训反馈主表'; + + +-- 19) 培训反馈回复表(对应页面:反馈详情时间线“我的反馈/回复”) +CREATE TABLE `hot_training_feedback_reply` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(应用层保证必填)', + + `feedback_id` BIGINT UNSIGNED NULL COMMENT '反馈主表ID(逻辑外键)', + `feedback_no` VARCHAR(32) NULL COMMENT '反馈单号', + `reply_role` TINYINT NULL COMMENT '回复角色:1=反馈人 2=处理人', + `reply_user_id` BIGINT UNSIGNED NULL COMMENT '回复人ID', + `reply_user_name` VARCHAR(64) NULL COMMENT '回复人姓名', + `reply_content` TEXT NULL COMMENT '回复内容', + `reply_time` DATETIME NULL COMMENT '回复时间', + `status` TINYINT NULL DEFAULT 1 COMMENT '状态:1=有效 0=无效', + `remark` VARCHAR(255) NULL COMMENT '备注', + + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='培训反馈回复表'; diff --git a/sql/1289/2_pay_module.sql b/sql/1289/2_pay_module.sql new file mode 100644 index 0000000..4bfe1aa --- /dev/null +++ b/sql/1289/2_pay_module.sql @@ -0,0 +1,241 @@ +-- 支付模块基础表 +CREATE TABLE IF NOT EXISTS `pay_app` +( + `id` + BIGINT + UNSIGNED + NOT + NULL + AUTO_INCREMENT + COMMENT + '应用编号', + `app_key` + VARCHAR +( + 64 +) NOT NULL COMMENT '应用标识', + `name` VARCHAR +( + 100 +) NOT NULL COMMENT '应用名称', + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1启用 0停用', + `remark` VARCHAR +( + 255 +) DEFAULT NULL COMMENT '备注', + `order_notify_url` VARCHAR +( + 255 +) DEFAULT NULL COMMENT '业务回调地址', + `create_by` BIGINT DEFAULT NULL, + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, + `update_by` BIGINT DEFAULT NULL, + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '0正常 1删除', + PRIMARY KEY +( + `id` +), + UNIQUE KEY `uk_pay_app_key` +( + `app_key` +) + ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='支付应用'; + +CREATE TABLE IF NOT EXISTS `pay_channel` +( + `id` + BIGINT + UNSIGNED + NOT + NULL + AUTO_INCREMENT + COMMENT + '渠道编号', + `code` + VARCHAR +( + 64 +) NOT NULL COMMENT '渠道编码', + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1启用 0停用', + `remark` VARCHAR +( + 255 +) DEFAULT NULL COMMENT '备注', + `app_id` BIGINT UNSIGNED NOT NULL COMMENT '应用编号', + `config` LONGTEXT NOT NULL COMMENT '渠道配置(JSON)', + `create_by` BIGINT DEFAULT NULL, + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, + `update_by` BIGINT DEFAULT NULL, + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '0正常 1删除', + PRIMARY KEY +( + `id` +), + UNIQUE KEY `uk_pay_channel_app_code` +( + `app_id`, + `code` +) + ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='支付渠道'; + +CREATE TABLE IF NOT EXISTS `pay_order` +( + `id` + BIGINT + UNSIGNED + NOT + NULL + AUTO_INCREMENT + COMMENT + '支付订单编号', + `app_id` + BIGINT + UNSIGNED + NOT + NULL + COMMENT + '应用编号', + `merchant_order_no` + VARCHAR +( + 64 +) DEFAULT NULL COMMENT '业务订单号', + `biz_order_type` VARCHAR +( + 64 +) DEFAULT NULL COMMENT '业务类型', + `biz_order_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '业务主键', + `subject` VARCHAR +( + 128 +) DEFAULT NULL COMMENT '订单标题', + `body` VARCHAR +( + 255 +) DEFAULT NULL COMMENT '订单描述', + `channel_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '渠道编号', + `channel_code` VARCHAR +( + 64 +) DEFAULT NULL COMMENT '渠道编码', + `notify_url` VARCHAR +( + 255 +) DEFAULT NULL COMMENT '异步回调地址', + `return_url` VARCHAR +( + 255 +) DEFAULT NULL COMMENT '同步跳转地址', + `price` DECIMAL +( + 18, + 2 +) NOT NULL COMMENT '支付金额(元)', + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1待支付 2支付中 3成功 4失败 5关闭', + `user_ip` VARCHAR +( + 64 +) DEFAULT NULL COMMENT '用户IP', + `expire_time` DATETIME DEFAULT NULL COMMENT '失效时间', + `success_time` DATETIME DEFAULT NULL COMMENT '支付成功时间', + `extension_id` BIGINT UNSIGNED DEFAULT NULL COMMENT '支付成功拓展单编号', + `no` VARCHAR +( + 64 +) DEFAULT NULL COMMENT '平台支付订单号', + `channel_user_id` VARCHAR +( + 64 +) DEFAULT NULL COMMENT '渠道用户编号', + `channel_order_no` VARCHAR +( + 64 +) DEFAULT NULL COMMENT '渠道订单号', + `create_by` BIGINT DEFAULT NULL, + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, + `update_by` BIGINT DEFAULT NULL, + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '0正常 1删除', + PRIMARY KEY +( + `id` +), + UNIQUE KEY `uk_pay_order_no` +( + `no` +), + KEY `idx_pay_order_biz` +( + `biz_order_type`, + `biz_order_id` +) + ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='支付订单'; + +CREATE TABLE IF NOT EXISTS `pay_order_extension` +( + `id` + BIGINT + UNSIGNED + NOT + NULL + AUTO_INCREMENT + COMMENT + '拓展单编号', + `no` + VARCHAR +( + 64 +) NOT NULL COMMENT '三方侧商户单号', + `order_id` BIGINT UNSIGNED NOT NULL COMMENT '支付订单编号', + `channel_id` BIGINT UNSIGNED NOT NULL COMMENT '渠道编号', + `channel_code` VARCHAR +( + 64 +) NOT NULL COMMENT '渠道编码', + `user_ip` VARCHAR +( + 64 +) DEFAULT NULL COMMENT '用户IP', + `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1待支付 2支付中 3成功 4失败 5关闭', + `channel_extras` LONGTEXT DEFAULT NULL COMMENT '渠道扩展信息', + `channel_error_code` VARCHAR +( + 64 +) DEFAULT NULL COMMENT '渠道错误码', + `channel_error_msg` VARCHAR +( + 255 +) DEFAULT NULL COMMENT '渠道错误信息', + `channel_notify_data` LONGTEXT DEFAULT NULL COMMENT '渠道回调原文', + `create_by` BIGINT DEFAULT NULL, + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, + `update_by` BIGINT DEFAULT NULL, + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '0正常 1删除', + PRIMARY KEY +( + `id` +), + UNIQUE KEY `uk_pay_order_extension_no` +( + `no` +), + KEY `idx_pay_order_extension_order` +( + `order_id` +) + ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT ='支付订单拓展单'; + +ALTER TABLE `hot_fund_recharge_order` + MODIFY COLUMN `recharge_amount` DECIMAL (18, 2) NULL COMMENT '充值金额(元)', + MODIFY COLUMN `pay_status` TINYINT NULL DEFAULT 1 COMMENT '支付状态:1待支付 2支付中 3支付成功 4支付失败 5已关闭 6已退款'; + +-- 初始化建议 +-- INSERT INTO pay_app(app_key, name, status, remark) VALUES ('wallet-recharge', '钱包充值', 1, '钱包充值支付应用'); +-- 渠道编码建议: +-- WECHAT_QR / WECHAT_H5 / WECHAT_APP / ALIPAY_QR / ALIPAY_H5 / ALIPAY_APP +-- 支付渠道 config 示例: +-- 支付宝:{"appId":"xxx","privateKey":"-----BEGIN PRIVATE KEY-----...","alipayPublicKey":"-----BEGIN PUBLIC KEY-----...","gateway":"https://openapi.alipay.com/gateway.do"} +-- 微信:{"appId":"wxxxx","merchantId":"1xxxx","merchantSerialNo":"xxxx","privateKey":"-----BEGIN PRIVATE KEY-----...","apiV3Key":"32位密钥","platformPublicKey":"-----BEGIN PUBLIC KEY-----...","gateway":"https://api.mch.weixin.qq.com"} diff --git a/sql/add_avatar_to_sys_user_login_port.sql b/sql/add_avatar_to_sys_user_login_port.sql new file mode 100644 index 0000000..3fa4d34 --- /dev/null +++ b/sql/add_avatar_to_sys_user_login_port.sql @@ -0,0 +1,2 @@ +ALTER TABLE sys_user_login_port + ADD COLUMN avatar VARCHAR(500) COMMENT '头像'; diff --git a/sql/add_hot_company_notice_read.sql b/sql/add_hot_company_notice_read.sql new file mode 100644 index 0000000..d4d6ae2 --- /dev/null +++ b/sql/add_hot_company_notice_read.sql @@ -0,0 +1,20 @@ +CREATE TABLE `hot_company_notice_read` +( + `id` bigint NOT NULL COMMENT '主键', + `notice_id` bigint DEFAULT NULL COMMENT '公告ID', + `company_id` bigint DEFAULT NULL COMMENT '公司ID', + `reader_id` varchar(64) DEFAULT NULL COMMENT '阅读人ID', + `read_time` datetime DEFAULT NULL COMMENT '阅读时间', + `is_deleted` bigint DEFAULT 0 COMMENT '0=正常, 1=已删除', + `create_dept` bigint DEFAULT NULL COMMENT '创建部门', + `create_by` bigint DEFAULT NULL COMMENT '创建者', + `create_by_name` varchar(255) DEFAULT NULL COMMENT '创建者姓名', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` bigint DEFAULT NULL COMMENT '更新者', + `update_by_name` varchar(255) DEFAULT NULL COMMENT '更新者姓名', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_notice_reader` (`notice_id`, `reader_id`), + KEY `idx_company_reader` (`company_id`, `reader_id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 COMMENT ='公司公告阅读记录'; diff --git a/sql/alter_hot_hour_tables_20260511.sql b/sql/alter_hot_hour_tables_20260511.sql new file mode 100644 index 0000000..d814ce9 --- /dev/null +++ b/sql/alter_hot_hour_tables_20260511.sql @@ -0,0 +1,41 @@ +-- hot_hour 相关表结构升级(2026-05-11) +-- 目的: +-- 1) 支持总部配置学时套餐(原价、活动价、套餐说明、课时时长、适用类型) +-- 2) 支持企业端/移动端通过企业钱包或个人钱包购买并记录钱包流水关联 +-- 3) 支持课时扣减链路(总/已用/剩余、使用时间、扣减前后值、并发控制) + +ALTER TABLE `hot_hour_package` + ADD COLUMN `origin_price` DECIMAL(18, 2) NULL COMMENT '原价格(元)' AFTER `hour_count`, + ADD COLUMN `package_price` DECIMAL(18, 2) NULL COMMENT '套餐价格(元)' AFTER `origin_price`, + ADD COLUMN `valid_start_time` DATETIME NULL COMMENT '活动有效开始时间' AFTER `unit_price`, + ADD COLUMN `valid_end_time` DATETIME NULL COMMENT '活动有效结束时间' AFTER `valid_start_time`, + ADD COLUMN `applicable_types` VARCHAR(255) NULL COMMENT '适用类型(多选),逗号拼接' AFTER `valid_end_time`, + ADD COLUMN `package_desc` VARCHAR(500) NULL COMMENT '套餐说明' AFTER `applicable_types`; + +ALTER TABLE `hot_hour_package_purchase` + ADD COLUMN `origin_price` DECIMAL(18, 2) NULL COMMENT '原价格(元)' AFTER `hour_count`, + ADD COLUMN `package_price` DECIMAL(18, 2) NULL COMMENT '套餐价格(元)' AFTER `origin_price`, + ADD COLUMN `total_hours` INT NULL COMMENT '总课时数' AFTER `quantity`, + ADD COLUMN `wallet_type` TINYINT NULL COMMENT '钱包类型:1=个人钱包 2=企业钱包' AFTER `pay_method`, + ADD COLUMN `wallet_id` BIGINT UNSIGNED NULL COMMENT '钱包ID' AFTER `wallet_type`, + ADD COLUMN `wallet_transaction_id` BIGINT UNSIGNED NULL COMMENT '钱包流水ID' AFTER `wallet_id`, + ADD COLUMN `wallet_transaction_no` VARCHAR(64) NULL COMMENT '钱包流水号' AFTER `wallet_transaction_id`, + ADD COLUMN `package_snapshot` TEXT NULL COMMENT '套餐快照(JSON)' AFTER `pay_time`, + ADD KEY `idx_wallet_id` (`wallet_id`), + ADD KEY `idx_wallet_transaction_id` (`wallet_transaction_id`); + +ALTER TABLE `hot_hour_purchase_detail` + ADD COLUMN `purchase_id` BIGINT UNSIGNED NULL COMMENT '学时套餐购买记录ID' AFTER `buyer_company_name`, + ADD COLUMN `total_hours` INT NULL COMMENT '总课时数' AFTER `purchase_count`, + ADD COLUMN `wallet_type` TINYINT NULL COMMENT '钱包类型:1=个人钱包 2=企业钱包' AFTER `remaining_hours`, + ADD COLUMN `wallet_id` BIGINT UNSIGNED NULL COMMENT '钱包ID' AFTER `wallet_type`, + ADD COLUMN `version` INT NULL DEFAULT 0 COMMENT '乐观锁版本号' AFTER `wallet_id`, + ADD KEY `idx_purchase_id` (`purchase_id`), + ADD KEY `idx_wallet_id` (`wallet_id`); + +ALTER TABLE `hot_hour_usage_detail` + ADD COLUMN `operator_id` BIGINT UNSIGNED NULL COMMENT '操作人ID' AFTER `user_name`, + ADD COLUMN `operator_name` VARCHAR(64) NULL COMMENT '操作人' AFTER `operator_id`, + ADD COLUMN `before_remaining_hours` INT NULL COMMENT '扣减前剩余学时' AFTER `used_hours`, + ADD COLUMN `after_remaining_hours` INT NULL COMMENT '扣减后剩余学时' AFTER `before_remaining_hours`, + ADD KEY `idx_operator_id` (`operator_id`); diff --git a/sql/alter_hot_training_course_record_hour_package_20260512.sql b/sql/alter_hot_training_course_record_hour_package_20260512.sql new file mode 100644 index 0000000..ed7d46c --- /dev/null +++ b/sql/alter_hot_training_course_record_hour_package_20260512.sql @@ -0,0 +1,4 @@ +-- 安全学习使用学时套餐字段(2026-05-12) +ALTER TABLE `hot_training_course_record` + ADD COLUMN `is_use_hour_package` TINYINT NULL DEFAULT 0 COMMENT '是否已使用购买课时套餐:0=否 1=是' AFTER `signature_oss_id`, + ADD COLUMN `hour_package_used_hours` BIGINT NULL DEFAULT 0 COMMENT '已扣减课时数' AFTER `is_use_hour_package`; diff --git a/sql/alter_pay_wallet_freeze_status_20260509.sql b/sql/alter_pay_wallet_freeze_status_20260509.sql new file mode 100644 index 0000000..b2684d0 --- /dev/null +++ b/sql/alter_pay_wallet_freeze_status_20260509.sql @@ -0,0 +1,5 @@ +-- pay_wallet 新增冻结状态字段 +-- 0=正常 1=冻结 + +ALTER TABLE `pay_wallet` + ADD COLUMN `freeze_status` TINYINT NULL DEFAULT 0 COMMENT '冻结状态:0=正常 1=冻结' AFTER `wallet_type`; diff --git a/sql/flow_init.sql b/sql/flow_init.sql new file mode 100644 index 0000000..3ae78f6 --- /dev/null +++ b/sql/flow_init.sql @@ -0,0 +1,57 @@ +-- 流程实例表 +DROP TABLE IF EXISTS `sys_flow_instance`; +CREATE TABLE `sys_flow_instance` +( + `instance_id` varchar(32) NOT NULL COMMENT '主键ID (UUID)', + `flow_code` varchar(64) DEFAULT NULL COMMENT '流程编码', + `business_id` varchar(64) DEFAULT NULL COMMENT '业务单据ID', + `status` int DEFAULT NULL COMMENT '状态: 0-进行中, 1-已完成, 2-已撤销, 9-已终止', + `company_id` bigint DEFAULT NULL COMMENT '租户/公司ID', + `initiator_id` varchar(64) DEFAULT NULL COMMENT '发起人ID', + `finish_time` datetime DEFAULT NULL COMMENT '结束时间', + `create_by` varchar(64) DEFAULT NULL COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) DEFAULT NULL COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`instance_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='流程实例表'; + +-- 流程待办任务表 +DROP TABLE IF EXISTS `sys_flow_task`; +CREATE TABLE `sys_flow_task` +( + `task_id` varchar(32) NOT NULL COMMENT '任务ID (UUID)', + `instance_id` varchar(32) DEFAULT NULL COMMENT '关联流程实例ID', + `node_code` varchar(64) DEFAULT NULL COMMENT '节点编码', + `node_name` varchar(64) DEFAULT NULL COMMENT '节点名称', + `approver_id` varchar(64) DEFAULT NULL COMMENT '待审批人ID', + `company_id` bigint DEFAULT NULL COMMENT '租户/公司ID', + `create_by` varchar(64) DEFAULT NULL COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) DEFAULT NULL COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', + PRIMARY KEY (`task_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='流程待办任务表'; + +-- 流程处理历史表 +DROP TABLE IF EXISTS `sys_flow_task_his`; +CREATE TABLE `sys_flow_task_his` +( + `his_id` varchar(32) NOT NULL COMMENT '历史记录ID (UUID)', + `instance_id` varchar(32) DEFAULT NULL COMMENT '关联流程实例ID', + `task_id` varchar(32) DEFAULT NULL COMMENT '关联原待办任务ID', + `node_code` varchar(64) DEFAULT NULL COMMENT '节点编码', + `node_name` varchar(64) DEFAULT NULL COMMENT '节点名称', + `approver_id` varchar(64) DEFAULT NULL COMMENT '实际处理人ID', + `status` int DEFAULT NULL COMMENT '审批结果: 1-通过, 2-驳回, 3-转办, 9-终止', + `comment` varchar(500) DEFAULT NULL COMMENT '审批意见', + `audit_time` datetime DEFAULT NULL COMMENT '审批时间', + `company_id` bigint DEFAULT NULL COMMENT '租户/公司ID', + `create_by` varchar(64) DEFAULT NULL COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) DEFAULT NULL COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`his_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='流程处理历史表'; diff --git a/sql/flow_update_read.sql b/sql/flow_update_read.sql new file mode 100644 index 0000000..67719f0 --- /dev/null +++ b/sql/flow_update_read.sql @@ -0,0 +1,4 @@ +ALTER TABLE sys_flow_task + ADD COLUMN is_read TINYINT(1) DEFAULT 0 COMMENT '是否已读'; +ALTER TABLE sys_flow_task_his + ADD COLUMN is_read TINYINT(1) DEFAULT 0 COMMENT '是否已读'; diff --git a/sql/modify_avatar_type_in_sys_user_login_port.sql b/sql/modify_avatar_type_in_sys_user_login_port.sql new file mode 100644 index 0000000..54ac3ea --- /dev/null +++ b/sql/modify_avatar_type_in_sys_user_login_port.sql @@ -0,0 +1 @@ +ALTER TABLE sys_user_login_port MODIFY COLUMN avatar BIGINT COMMENT '头像(OSS ID)'; diff --git a/sql/update_hidden_danger_inspection_flow.sql b/sql/update_hidden_danger_inspection_flow.sql new file mode 100644 index 0000000..7cc4a64 --- /dev/null +++ b/sql/update_hidden_danger_inspection_flow.sql @@ -0,0 +1,3 @@ +ALTER TABLE `hot_hidden_danger_inspection` + ADD COLUMN `instance_id` varchar(64) NULL COMMENT '流程实例ID' AFTER `project_type`, +ADD COLUMN `flow_status` varchar(32) NULL COMMENT '流程状态' AFTER `instance_id`; diff --git a/sql/update_hot_hidden_danger_flow.sql b/sql/update_hot_hidden_danger_flow.sql new file mode 100644 index 0000000..86e7ce1 --- /dev/null +++ b/sql/update_hot_hidden_danger_flow.sql @@ -0,0 +1,3 @@ +ALTER TABLE `hot_hidden_danger` + ADD COLUMN `instance_id` varchar(64) NULL COMMENT '流程实例ID' AFTER `is_deleted`, + ADD COLUMN `flow_status` varchar(32) NULL COMMENT '流程状态' AFTER `instance_id`; diff --git a/src/main/java/com/hotwj/platform/common/controller/RegionController.java b/src/main/java/com/hotwj/platform/common/controller/RegionController.java new file mode 100644 index 0000000..082d528 --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/controller/RegionController.java @@ -0,0 +1,36 @@ +package com.hotwj.platform.common.controller; + +import com.hotwj.platform.common.mapper.PublicMapper; +import com.hotwj.platform.common.vo.AddressVo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RequiredArgsConstructor +@RestController +@RequestMapping("/common/region") +@Tag(name = "区域管理", description = "区域树查询接口") +public class RegionController { + + private final PublicMapper publicMapper; + + /** + * 查询区域树列表 + */ + @GetMapping("/tree") + @Operation(summary = "查询区域树列表") + public R> tree() { + // 查询数据库,查询所有区域树 + List addressVoList = publicMapper.selectList(); + // 组装成树 + List tree = AddressVo.buildTree(addressVoList); + + return R.ok(tree); + } +} diff --git a/src/main/java/com/hotwj/platform/common/helper/DriverLoginContextHelper.java b/src/main/java/com/hotwj/platform/common/helper/DriverLoginContextHelper.java new file mode 100644 index 0000000..608551a --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/helper/DriverLoginContextHelper.java @@ -0,0 +1,80 @@ +package com.hotwj.platform.common.helper; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysUserLoginPortVo; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.satoken.utils.LoginHelper; + +import java.util.List; + +/** + * 驾驶员登录上下文帮助类 + * + * @author shihongwei + * @date 2026/02/22 + */ +public final class DriverLoginContextHelper { + + private DriverLoginContextHelper() { + } + + public static boolean isDriverPort() { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null) { + return false; + } + return StringUtils.equals(loginUser.getLoginPort(), ISysUserLoginPortService.DRIVER_PORT); + } + + public static HotDriver getCurrentDriver() { + if (!isDriverPort()) { + throw new ServiceException("当前登录端口不是驾驶员端"); + } + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null) { + throw new ServiceException("登录信息不存在"); + } + String businessUserId = loginUser.getBusinessUserId(); + if (StringUtils.isBlank(businessUserId)) { + Long userId = loginUser.getUserId(); + if (userId == null) { + throw new ServiceException("用户ID不存在"); + } + ISysUserLoginPortService userLoginPortService = SpringUtils.getBean(ISysUserLoginPortService.class); + List ports = userLoginPortService.selectLoginPortListByPort(userId, ISysUserLoginPortService.DRIVER_PORT); + Long companyId = ports.stream() + .map(SysUserLoginPortVo::getCompanyId) + .filter(id -> id != null) + .findFirst() + .orElse(null); + if (companyId == null) { + throw new ServiceException("未找到驾驶员所属企业信息"); + } + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.eq(HotDriver::getCompanyId, companyId); + lqw.eq(HotDriver::getId, userId); + lqw.eq(HotDriver::getStatus, 1L); + lqw.eq(HotDriver::getAuditStatus, 1); + lqw.orderByDesc(HotDriver::getCreateTime); + lqw.last("limit 1"); + HotDriverMapper hotDriverMapper = SpringUtils.getBean(HotDriverMapper.class); + HotDriver driver = hotDriverMapper.selectOne(lqw); + if (driver == null) { + throw new ServiceException("未找到当前登录用户对应的驾驶员信息"); + } + return driver; + } + HotDriverMapper hotDriverMapper = SpringUtils.getBean(HotDriverMapper.class); + HotDriver driver = hotDriverMapper.selectById(businessUserId); + if (driver == null) { + throw new ServiceException("驾驶员信息不存在"); + } + return driver; + } +} diff --git a/src/main/java/com/hotwj/platform/common/mapper/PublicMapper.java b/src/main/java/com/hotwj/platform/common/mapper/PublicMapper.java new file mode 100644 index 0000000..05816cb --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/mapper/PublicMapper.java @@ -0,0 +1,40 @@ +package com.hotwj.platform.common.mapper; + +import com.hotwj.platform.common.vo.AddressVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface PublicMapper { + + /** + * 查询所有区域树,目前只查四川省的 + */ + List selectList(); + + /** + * 单SQL透视:根据6位区划代码返回“省 市 区”全名 + */ + String selectRegionFullNameByCode(String code); + + /** + * 根据code查询 + */ + AddressVo selectAddressByCode(String code); + + /** + * 根据用户ID列表查询用户名称 (逗号分隔) + */ + String selectUserNamesByIds(@org.apache.ibatis.annotations.Param("userIds") List userIds); + + /** + * 根据课程ID列表查询课程名称 (逗号分隔) + */ + String selectCourseNamesByIds(@org.apache.ibatis.annotations.Param("courseIds") List courseIds); + + /** + * 根据ID列表查询参会人员姓名(驾驶员 + 安全管理员),按中文顿号拼接 + */ + String selectAttendeeNamesByIds(@org.apache.ibatis.annotations.Param("ids") List ids); +} diff --git a/src/main/java/com/hotwj/platform/common/photoEvidence/controller/PhotoEvidenceController.java b/src/main/java/com/hotwj/platform/common/photoEvidence/controller/PhotoEvidenceController.java new file mode 100644 index 0000000..31ea232 --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/photoEvidence/controller/PhotoEvidenceController.java @@ -0,0 +1,44 @@ +package com.hotwj.platform.common.photoEvidence.controller; + +import com.hotwj.platform.common.photoEvidence.domain.bo.PhotoEvidenceCreateBo; +import com.hotwj.platform.common.photoEvidence.domain.vo.PhotoEvidenceVo; +import com.hotwj.platform.common.photoEvidence.service.IPhotoEvidenceService; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Validated +@RestController +@RequestMapping("/common/photoEvidence") +public class PhotoEvidenceController extends BaseController { + + @Resource + private IPhotoEvidenceService photoEvidenceService; + + @RepeatSubmit + @Log(title = "照片取证水印生成", businessType = BusinessType.INSERT) + @PostMapping("/create") + @Operation(summary = "生成带时间地点水印的照片取证记录") + public R create(@Valid @RequestBody PhotoEvidenceCreateBo bo) { + LoginUser loginUser = LoginHelper.getLoginUser(); + Long companyId = loginUser.getCompanyId(); + bo.setCompanyId(companyId); + bo.setOperatorId(loginUser.getBusinessUserId()); + bo.setOperatorName(loginUser.getUsername()); + + PhotoEvidenceVo vo = photoEvidenceService.create(bo); + return R.ok(vo); + } +} diff --git a/src/main/java/com/hotwj/platform/common/photoEvidence/domain/HotPhotoEvidence.java b/src/main/java/com/hotwj/platform/common/photoEvidence/domain/HotPhotoEvidence.java new file mode 100644 index 0000000..90f5f5f --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/photoEvidence/domain/HotPhotoEvidence.java @@ -0,0 +1,58 @@ +package com.hotwj.platform.common.photoEvidence.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_photo_evidence") +public class HotPhotoEvidence extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @TableId(value = "id") + private Long id; + + private Long originalOssId; + + private Long watermarkedOssId; + + private Date captureTimeClient; + + private Date captureTimeServer; + + private Double latitude; + + private Double longitude; + + private String addressTextServer; + + private String addressTextClient; + + private String addressSource; + + private String bizType; + + private String bizId; + + private Long companyId; + + private String operatorId; + + private String operatorName; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; +} + diff --git a/src/main/java/com/hotwj/platform/common/photoEvidence/domain/bo/PhotoEvidenceCreateBo.java b/src/main/java/com/hotwj/platform/common/photoEvidence/domain/bo/PhotoEvidenceCreateBo.java new file mode 100644 index 0000000..987579b --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/photoEvidence/domain/bo/PhotoEvidenceCreateBo.java @@ -0,0 +1,37 @@ +package com.hotwj.platform.common.photoEvidence.domain.bo; + +import com.baomidou.mybatisplus.annotation.TableLogic; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class PhotoEvidenceCreateBo { + + @NotBlank(message = "originalOssId不能为空") + private String originalOssId; + + @NotNull(message = "latitude不能为空") + private Double latitude; + + @NotNull(message = "longitude不能为空") + private Double longitude; + + private String addressTextClient; + + @NotBlank(message = "bizType不能为空") + private String bizType; + + private String bizId; + + private Long companyId; + + private String operatorId; + + private String operatorName; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; +} diff --git a/src/main/java/com/hotwj/platform/common/photoEvidence/domain/vo/PhotoEvidenceVo.java b/src/main/java/com/hotwj/platform/common/photoEvidence/domain/vo/PhotoEvidenceVo.java new file mode 100644 index 0000000..bfd7958 --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/photoEvidence/domain/vo/PhotoEvidenceVo.java @@ -0,0 +1,92 @@ +package com.hotwj.platform.common.photoEvidence.domain.vo; + +import com.baomidou.mybatisplus.annotation.TableLogic; +import lombok.Data; + +/** + * 照片取证水印生成的返回视图对象 + * 对应 hot_photo_evidence 表记录返回给前端的字段 + */ +@Data +public class PhotoEvidenceVo { + + /** + * 取证记录主键ID + */ + private String evidenceId; + + /** + * 原始图片 OSS 记录主键(ossId) + */ + private String originalOssId; + + /** + * 生成的水印图片 OSS 记录主键(ossId) + */ + private String watermarkedOssId; + + /** + * 客户端拍摄时间字符串(yyyy-MM-dd HH:mm:ss) + */ + private String captureTimeClient; + + /** + * 服务端生成取证记录时间字符串 + */ + private String captureTimeServer; + + /** + * 拍摄位置纬度 + */ + private Double latitude; + + /** + * 拍摄位置经度 + */ + private Double longitude; + + /** + * 服务端(高德逆地理)解析到的地址 + */ + private String addressTextServer; + + /** + * 客户端上传的地址文本 + */ + private String addressTextClient; + + /** + * 地址来源:SERVER(服务端解析)/ CLIENT(客户端上传) + */ + private String addressSource; + + /** + * 业务类型标识 + */ + private String bizType; + + /** + * 业务主键或业务单号 + */ + private String bizId; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 操作人ID + */ + private String operatorId; + + /** + * 操作人姓名 + */ + private String operatorName; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; +} diff --git a/src/main/java/com/hotwj/platform/common/photoEvidence/mapper/HotPhotoEvidenceMapper.java b/src/main/java/com/hotwj/platform/common/photoEvidence/mapper/HotPhotoEvidenceMapper.java new file mode 100644 index 0000000..aff6b4a --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/photoEvidence/mapper/HotPhotoEvidenceMapper.java @@ -0,0 +1,10 @@ +package com.hotwj.platform.common.photoEvidence.mapper; + +import com.hotwj.platform.common.photoEvidence.domain.HotPhotoEvidence; +import com.hotwj.platform.common.photoEvidence.domain.vo.PhotoEvidenceVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +@Mapper +public interface HotPhotoEvidenceMapper extends BaseMapperPlus { +} diff --git a/src/main/java/com/hotwj/platform/common/photoEvidence/service/IPhotoEvidenceService.java b/src/main/java/com/hotwj/platform/common/photoEvidence/service/IPhotoEvidenceService.java new file mode 100644 index 0000000..9e3372f --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/photoEvidence/service/IPhotoEvidenceService.java @@ -0,0 +1,10 @@ +package com.hotwj.platform.common.photoEvidence.service; + +import com.hotwj.platform.common.photoEvidence.domain.bo.PhotoEvidenceCreateBo; +import com.hotwj.platform.common.photoEvidence.domain.vo.PhotoEvidenceVo; + +public interface IPhotoEvidenceService { + + PhotoEvidenceVo create(PhotoEvidenceCreateBo bo); +} + diff --git a/src/main/java/com/hotwj/platform/common/photoEvidence/service/impl/PhotoEvidenceServiceImpl.java b/src/main/java/com/hotwj/platform/common/photoEvidence/service/impl/PhotoEvidenceServiceImpl.java new file mode 100644 index 0000000..03a5ba5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/photoEvidence/service/impl/PhotoEvidenceServiceImpl.java @@ -0,0 +1,335 @@ +package com.hotwj.platform.common.photoEvidence.service.impl; + +import com.fasterxml.jackson.databind.JsonNode; +import com.hotwj.platform.common.photoEvidence.domain.HotPhotoEvidence; +import com.hotwj.platform.common.photoEvidence.domain.bo.PhotoEvidenceCreateBo; +import com.hotwj.platform.common.photoEvidence.domain.vo.PhotoEvidenceVo; +import com.hotwj.platform.common.photoEvidence.mapper.HotPhotoEvidenceMapper; +import com.hotwj.platform.common.photoEvidence.service.IPhotoEvidenceService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.enums.FormatsType; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.DateUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.common.oss.core.OssClient; +import org.dromara.common.oss.factory.OssFactory; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.system.domain.vo.SysOssVo; +import org.dromara.system.service.ISysOssService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@Slf4j +@Service +public class PhotoEvidenceServiceImpl implements IPhotoEvidenceService { + + @Resource + private HotPhotoEvidenceMapper hotPhotoEvidenceMapper; + + @Resource + private ISysOssService ossService; + + @Value("${map.gd.key:}") + private String gdKey; + + @Override + @Transactional(rollbackFor = Exception.class) + public PhotoEvidenceVo create(PhotoEvidenceCreateBo bo) { + Long originalOssId = parseOssId(bo.getOriginalOssId()); + SysOssVo originalOss = ossService.getById(originalOssId); + if (originalOss == null) { + throw new ServiceException("原始图片不存在"); + } + + Date captureTimeClient = new Date(); + Date captureTimeServer = captureTimeClient; + + String addressServer = reverseGeocode(bo.getLatitude(), bo.getLongitude()); + String addressSource; + String addressForWatermark; + if (StringUtils.isNotBlank(addressServer)) { + addressSource = "SERVER"; + addressForWatermark = addressServer; + } else { + addressSource = "CLIENT"; + addressForWatermark = bo.getAddressTextClient(); + } + + Long watermarkedOssId = generateAndUploadWatermarkedImage( + originalOss, + captureTimeClient, + addressForWatermark + ); + String operatorName = bo.getOperatorName(); + if (StringUtils.isBlank(operatorName)) { + operatorName = LoginHelper.getUsername(); + } + + HotPhotoEvidence entity = new HotPhotoEvidence(); + entity.setOriginalOssId(originalOssId); + entity.setWatermarkedOssId(watermarkedOssId); + entity.setCaptureTimeClient(captureTimeClient); + entity.setCaptureTimeServer(captureTimeServer); + entity.setLatitude(bo.getLatitude()); + entity.setLongitude(bo.getLongitude()); + entity.setAddressTextServer(addressServer); + entity.setAddressTextClient(bo.getAddressTextClient()); + entity.setAddressSource(addressSource); + entity.setBizType(bo.getBizType()); + entity.setBizId(bo.getBizId()); + entity.setCompanyId(bo.getCompanyId()); + entity.setOperatorId(bo.getOperatorId()); + entity.setOperatorName(operatorName); + + hotPhotoEvidenceMapper.insert(entity); + + PhotoEvidenceVo vo = new PhotoEvidenceVo(); + vo.setEvidenceId(entity.getId() != null ? String.valueOf(entity.getId()) : null); + vo.setOriginalOssId(String.valueOf(originalOssId)); + vo.setWatermarkedOssId(watermarkedOssId != null ? String.valueOf(watermarkedOssId) : null); + vo.setCaptureTimeClient(DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, captureTimeClient)); + vo.setCaptureTimeServer(DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, captureTimeServer)); + vo.setLatitude(bo.getLatitude()); + vo.setLongitude(bo.getLongitude()); + vo.setAddressTextServer(addressServer); + vo.setAddressTextClient(bo.getAddressTextClient()); + vo.setAddressSource(addressSource); + vo.setBizType(bo.getBizType()); + vo.setBizId(bo.getBizId()); + vo.setCompanyId(bo.getCompanyId()); + vo.setOperatorId(bo.getOperatorId()); + vo.setOperatorName(operatorName); + + return vo; + } + + private Long parseOssId(String ossIdStr) { + try { + return Long.valueOf(ossIdStr); + } catch (NumberFormatException e) { + throw new ServiceException("originalOssId格式不正确"); + } + } + + private Long generateAndUploadWatermarkedImage(SysOssVo originalOss, + Date captureTimeClient, + String address) { + byte[] originalBytes = downloadOssFile(originalOss); + BufferedImage originalImage = readImage(originalBytes); + BufferedImage watermarked = addWatermark(originalImage, captureTimeClient, address); + + File tempFile = null; + try { + String suffix = originalOss.getFileSuffix(); + if (StringUtils.isBlank(suffix)) { + suffix = "jpg"; + } + if (suffix.startsWith(".")) { + suffix = suffix.substring(1); + } + tempFile = File.createTempFile("photo_evidence_", "." + suffix); + try (OutputStream os = new FileOutputStream(tempFile)) { + ImageIO.write(watermarked, suffix, os); + } + return ossService.upload(tempFile).getOssId(); + } catch (IOException e) { + throw new ServiceException("水印图片生成失败"); + } finally { + if (tempFile != null && tempFile.exists()) { + if (!tempFile.delete()) { + log.warn("临时文件删除失败: {}", tempFile.getAbsolutePath()); + } + } + } + } + + private byte[] downloadOssFile(SysOssVo oss) { + OssClient storage = OssFactory.instance(oss.getService()); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + storage.download(oss.getFileName(), baos, null); + return baos.toByteArray(); + } catch (Exception e) { + throw new ServiceException("原始图片下载失败"); + } + } + + private BufferedImage readImage(byte[] bytes) { + try (InputStream is = new ByteArrayInputStream(bytes)) { + BufferedImage image = ImageIO.read(is); + if (image == null) { + String hex = ""; + if (bytes.length >= 4) { + hex = String.format("%02X%02X%02X%02X", bytes[0], bytes[1], bytes[2], bytes[3]); + } + log.error("ImageIO.read returned null. Header: {}", hex); + throw new ServiceException("原始图片格式不支持 (Header: " + hex + ")"); + } + return image; + } catch (IOException e) { + log.error("图片解析失败", e); + throw new ServiceException("原始图片解析失败: " + e.getMessage()); + } + } + + private BufferedImage addWatermark(BufferedImage original, + Date captureTimeClient, + String address) { + int width = original.getWidth(); + int height = original.getHeight(); + + // 强制最小画布宽度,防止文字区域过窄 + int minWidth = 600; + if (width < minWidth) { + int newWidth = minWidth; + int newHeight = (int) Math.round((double) height * newWidth / width); + BufferedImage resized = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB); + Graphics2D gResize = resized.createGraphics(); + gResize.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + gResize.drawImage(original, 0, 0, newWidth, newHeight, null); + gResize.dispose(); + original = resized; + width = newWidth; + height = newHeight; + } + + // 字体大小随宽度自适应(设置最小值) + int fontSize = Math.max(20, width / 30); + int padding = Math.max(fontSize, 16); + + String timeText = DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, captureTimeClient); + String addressText = "地址:" + (address != null ? address : ""); + + // 构建字体并测量 + Font font; + try (InputStream is = getClass().getResourceAsStream("/fonts/simsun.ttc")) { + if (is == null) { + font = new Font("SansSerif", Font.PLAIN, fontSize); + } else { + font = Font.createFont(Font.TRUETYPE_FONT, is).deriveFont(Font.PLAIN, fontSize); + } + } catch (Exception e) { + log.warn("Failed to load simsun font, fallback to SansSerif", e); + font = new Font("SansSerif", Font.PLAIN, fontSize); + } + + // 使用临时图形上下文计算文字宽度与行高 + BufferedImage tmp = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); + Graphics2D gMeasure = tmp.createGraphics(); + gMeasure.setFont(font); + FontMetrics fm = gMeasure.getFontMetrics(); + int contentWidth = Math.max(1, width - padding * 2); + + // 构造文本行:第一行时间,后续为地址按宽度换行 + List lines = new ArrayList<>(); + lines.add(timeText); + if (fm.stringWidth(addressText) <= contentWidth) { + lines.add(addressText); + } else { + StringBuilder current = new StringBuilder(); + for (char c : addressText.toCharArray()) { + if (fm.stringWidth(current.toString() + c) > contentWidth) { + lines.add(current.toString()); + current = new StringBuilder(); + } + current.append(c); + } + if (current.length() > 0) { + lines.add(current.toString()); + } + } + int lineHeight = fm.getHeight() + fontSize / 4; + gMeasure.dispose(); + + // 计算白条高度,确保所有行完整显示 + int barHeight = Math.max(lineHeight + padding * 2, lines.size() * lineHeight + padding * 2); + + // 输出结果图 + BufferedImage result = new BufferedImage(width, height + barHeight, BufferedImage.TYPE_INT_RGB); + Graphics2D g = result.createGraphics(); + try { + // 绘制原图 + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.drawImage(original, 0, 0, null); + + // 绘制底部白条 + g.setColor(Color.WHITE); + g.fillRect(0, height, width, barHeight); + + // 绘制文本 + g.setFont(font); + g.setColor(new Color(51, 51, 51)); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + int y = height + padding + fm.getAscent(); + for (String line : lines) { + g.drawString(line, padding, y); + y += lineHeight; + } + return result; + } finally { + g.dispose(); + } + } + + private String reverseGeocode(Double latitude, Double longitude) { + if (latitude == null || longitude == null) { + return null; + } + if (StringUtils.isBlank(gdKey)) { + return null; + } + String location = longitude + "," + latitude; + String url = "https://restapi.amap.com/v3/geocode/regeo?output=JSON&location=" + + location + "&key=" + gdKey + "&radius=1000&extensions=base"; + HttpURLConnection connection = null; + try { + URL u = new URL(url); + connection = (HttpURLConnection) u.openConnection(); + connection.setConnectTimeout(3000); + connection.setReadTimeout(3000); + connection.setRequestMethod("GET"); + int code = connection.getResponseCode(); + if (code != HttpURLConnection.HTTP_OK) { + return null; + } + try (InputStream is = connection.getInputStream()) { + String body = new String(is.readAllBytes(), StandardCharsets.UTF_8); + JsonNode root = JsonUtils.getObjectMapper().readTree(body); + if (root == null) { + return null; + } + if (!"1".equals(root.path("status").asText())) { + return null; + } + JsonNode regeocode = root.path("regeocode"); + String formatted = regeocode.path("formatted_address").asText(); + if (StringUtils.isNotBlank(formatted)) { + return formatted; + } + return null; + } + } catch (Exception e) { + log.warn("高德逆地理解析失败: {},{}", latitude, longitude); + return null; + } finally { + if (connection != null) { + connection.disconnect(); + } + } + } +} + diff --git a/src/main/java/com/hotwj/platform/common/service/ITemplateService.java b/src/main/java/com/hotwj/platform/common/service/ITemplateService.java new file mode 100644 index 0000000..9335e6f --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/service/ITemplateService.java @@ -0,0 +1,29 @@ +package com.hotwj.platform.common.service; + +import java.util.Map; + +/** + * 通用模板填充服务 + * + * @author shihongwei + */ +public interface ITemplateService { + + /** + * 根据模板内容和数据映射,填充模板变量 + * + * @param templateContent 包含 {变量名} 的模板字符串 + * @param dataMap 变量名到实际数据的映射 + * @return 填充后的字符串 + */ + String fillTemplate(String templateContent, Map dataMap); + + /** + * 为指定的签订文件明细记录准备模板所需的数据映射 + * + * @param detailId 签订文件明细ID (hot_sign_file_detail.id) + * @return 包含所有可替换变量的Map + */ + Map prepareDataMapForSignDetail(Long detailId); + +} diff --git a/src/main/java/com/hotwj/platform/common/service/impl/TemplateServiceImpl.java b/src/main/java/com/hotwj/platform/common/service/impl/TemplateServiceImpl.java new file mode 100644 index 0000000..9682874 --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/service/impl/TemplateServiceImpl.java @@ -0,0 +1,141 @@ +package com.hotwj.platform.common.service.impl; + +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.common.service.ITemplateService; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.noticeManagerment.signFileDetail.domain.vo.HotSignFileDetailVo; +import com.hotwj.platform.noticeManagerment.signFileDetail.mapper.HotSignFileDetailMapper; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import lombok.RequiredArgsConstructor; +import org.apache.commons.text.StringSubstitutor; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Service; + +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class TemplateServiceImpl implements ITemplateService { + + private static final DateTimeFormatter SIGN_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy年MM月dd日"); + + private final HotSignFileDetailMapper signFileDetailMapper; + private final ISysCompanyService companyService; + private final HotCompanySafetyManagerMapper safetyManagerMapper; + private final HotDriverMapper driverMapper; + private final OssService ossService; + + @Override + public String fillTemplate(String templateContent, Map dataMap) { + if (StrUtil.isBlank(templateContent)) { + return ""; + } + Map renderMap = dataMap == null ? new HashMap<>() : dataMap; + StringSubstitutor substitutor = new StringSubstitutor(renderMap, "{", "}"); + substitutor.setEnableUndefinedVariableException(false); + String rendered = substitutor.replace(templateContent); + return rendered.replaceAll("\\{[^{}]+\\}", ""); + } + + @Override + public Map prepareDataMapForSignDetail(Long detailId) { + Map dataMap = new HashMap<>(); + HotSignFileDetailVo detail = signFileDetailMapper.selectVoById(detailId); + if (detail == null) { + return dataMap; + } + String signOssId = detail.getSignOssId(); + + if (detail.getCompanyId() != null) { + SysCompanyVo company = companyService.queryById(detail.getCompanyId()); + if (company != null) { + dataMap.put("企业名字", StringUtils.defaultString(company.getCompanyName())); + dataMap.put("信用代码", StringUtils.defaultString(company.getCreditCode())); + String sealUrl = resolveOssUrl(company.getSealUrl()); + if (StrUtil.isNotBlank(sealUrl)) { + dataMap.put("电子公章", buildSealImgTag(sealUrl)); + } + } + } + if (StrUtil.isNotBlank(detail.getStaffId()) && NumberUtil.isLong(detail.getStaffId())) { + HotCompanySafetyManager manager; + try { + manager = safetyManagerMapper.selectById(Long.valueOf(detail.getStaffId())); + } catch (Exception e) { + manager = null; + } + if (manager != null && manager.getId() != null) { + dataMap.put("管理员姓名", StringUtils.defaultString(manager.getName())); + dataMap.put("管理员身份证号码", StringUtils.defaultString(manager.getIdCardNo())); + dataMap.put("管理员手机号码", StringUtils.defaultString(manager.getPhone())); + dataMap.put("管理员岗位", StringUtils.defaultString(manager.getJobName())); + if (StrUtil.isNotBlank(signOssId)) { + String signImg = resolveOssUrl(signOssId); + dataMap.put("管理员签名", buildSignImgTag(signImg)); + if (detail.getSignTime() != null) { + String signDate = detail.getSignTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().format(SIGN_DATE_FORMATTER); + dataMap.put("管理员签字日期", signDate); + } + } + } + } else if (StrUtil.isNotBlank(detail.getStaffId()) && !NumberUtil.isLong(detail.getStaffId())) { + HotDriver driver = driverMapper.selectById(detail.getStaffId()); + if (driver != null && driver.getId() != null) { + dataMap.put("驾驶员姓名", StringUtils.defaultString(driver.getName())); + dataMap.put("驾驶员身份证号码", StringUtils.defaultString(driver.getIdCardNo())); + dataMap.put("驾驶员手机号码", StringUtils.defaultString(driver.getPhone())); + dataMap.put("驾驶员从业资格证号码", StringUtils.defaultString(driver.getQualificationNo())); + dataMap.put("驾驶员驾驶车牌号", StringUtils.defaultString(driver.getPlateNumber())); + if (StrUtil.isNotBlank(signOssId)) { + String signImg = resolveOssUrl(signOssId); + dataMap.put("驾驶员签名", buildSignImgTag(signImg)); + if (detail.getSignTime() != null) { + String signDate = detail.getSignTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().format(SIGN_DATE_FORMATTER); + dataMap.put("驾驶员签字日期", signDate); + } + } + } + } + + + return dataMap; + } + + private String resolveOssUrl(String source) { + if (StrUtil.isBlank(source)) { + return null; + } + if (source.startsWith("http://") || source.startsWith("https://")) { + return source; + } + String urls = ossService.selectUrlByIds(source); + if (StrUtil.isBlank(urls)) { + return source; + } + if (urls.contains(",")) { + for (String part : urls.split(",")) { + if (StrUtil.isNotBlank(part)) { + return part.trim(); + } + } + } + return urls; + } + + private String buildSignImgTag(String url) { + return ""; + } + + private String buildSealImgTag(String url) { + return ""; + } +} diff --git a/src/main/java/com/hotwj/platform/common/utils/FileTextExtractor.java b/src/main/java/com/hotwj/platform/common/utils/FileTextExtractor.java new file mode 100644 index 0000000..1dd7eb5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/utils/FileTextExtractor.java @@ -0,0 +1,89 @@ +package com.hotwj.platform.common.utils; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.text.PDFTextStripper; +import org.apache.poi.hslf.usermodel.HSLFSlideShow; +import org.apache.poi.hssf.extractor.ExcelExtractor; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.hwpf.HWPFDocument; +import org.apache.poi.hwpf.extractor.WordExtractor; +import org.apache.poi.sl.extractor.SlideShowExtractor; +import org.apache.poi.xslf.usermodel.XMLSlideShow; +import org.apache.poi.xssf.extractor.XSSFExcelExtractor; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.poi.xwpf.extractor.XWPFWordExtractor; +import org.apache.poi.xwpf.usermodel.XWPFDocument; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +@Slf4j +public class FileTextExtractor { + + public static String extractText(InputStream is, String fileExtension) { + if (is == null || StrUtil.isBlank(fileExtension)) { + return ""; + } + + String ext = fileExtension.toLowerCase(); + // Remove dot if present + if (ext.startsWith(".")) { + ext = ext.substring(1); + } + + try { + switch (ext) { + case "txt": + return IoUtil.read(is, StandardCharsets.UTF_8); + case "pdf": + try (PDDocument document = PDDocument.load(is)) { + PDFTextStripper stripper = new PDFTextStripper(); + return stripper.getText(document); + } + case "doc": + try (HWPFDocument doc = new HWPFDocument(is); + WordExtractor extractor = new WordExtractor(doc)) { + return extractor.getText(); + } + case "docx": + try (XWPFDocument doc = new XWPFDocument(is); + XWPFWordExtractor extractor = new XWPFWordExtractor(doc)) { + return extractor.getText(); + } + case "xls": + try (HSSFWorkbook wb = new HSSFWorkbook(is); + ExcelExtractor extractor = new ExcelExtractor(wb)) { + extractor.setFormulasNotResults(false); + extractor.setIncludeSheetNames(false); + return extractor.getText(); + } + case "xlsx": + try (XSSFWorkbook wb = new XSSFWorkbook(is); + XSSFExcelExtractor extractor = new XSSFExcelExtractor(wb)) { + extractor.setFormulasNotResults(false); + extractor.setIncludeSheetNames(false); + return extractor.getText(); + } + case "ppt": + try (HSLFSlideShow ppt = new HSLFSlideShow(is); + SlideShowExtractor extractor = new SlideShowExtractor(ppt)) { + return extractor.getText(); + } + case "pptx": + try (XMLSlideShow ppt = new XMLSlideShow(is); + SlideShowExtractor extractor = new SlideShowExtractor(ppt)) { + return extractor.getText(); + } + default: + log.warn("Unsupported file extension for text extraction: {}", ext); + return ""; + } + } catch (Exception e) { + log.error("Failed to extract text from file type: " + ext, e); + return ""; + } + } +} diff --git a/src/main/java/com/hotwj/platform/common/utils/PasswordUtils.java b/src/main/java/com/hotwj/platform/common/utils/PasswordUtils.java new file mode 100644 index 0000000..f0069ca --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/utils/PasswordUtils.java @@ -0,0 +1,34 @@ +package com.hotwj.platform.common.utils; + +import cn.hutool.crypto.digest.BCrypt; +import lombok.extern.slf4j.Slf4j; + +/** + * 密码工具类 + * + * @author shihongwei + * @date 2025-12-10 + */ +@Slf4j +public class PasswordUtils { + + /** + * 默认密码规则:手机号+hot + * + * @param phone + * @return + */ + public static String createDefaultPassword(String phone) { + return BCrypt.hashpw(phone + "hot"); + } + + /** + * 通过明文创建密文 + * + * @param password + * @return + */ + public static String createPassword(String password) { + return BCrypt.hashpw(password); + } +} diff --git a/src/main/java/com/hotwj/platform/common/utils/PdfWatermarkUtil.java b/src/main/java/com/hotwj/platform/common/utils/PdfWatermarkUtil.java new file mode 100644 index 0000000..2990583 --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/utils/PdfWatermarkUtil.java @@ -0,0 +1,381 @@ +package com.hotwj.platform.common.utils; + +import lombok.extern.slf4j.Slf4j; +import org.apache.fontbox.ttf.TrueTypeCollection; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDType0Font; +import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; +import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState; +import org.apache.pdfbox.rendering.PDFRenderer; +import org.apache.pdfbox.util.Matrix; +import org.springframework.core.io.ClassPathResource; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * PDF Watermark Utility + * Adds text watermarks to PDF files using PDFBox. + * + * @author shihongwei + * @date 2026-01-02 + */ +@Slf4j +public class PdfWatermarkUtil { + + private static final String DEFAULT_APP_NAME = "HOT交通安全管理清单平台"; + private static final String DEFAULT_WATERMARK_TEXT = "HOT交通安全\n管理清单平台"; + private static final float WATERMARK_ALPHA = 0.15f; + private static final float PAGE_NUMBER_ALPHA = 1.0f; + private static final int WATERMARK_FONT_SIZE = 18; + private static final float PAGE_NUMBER_FONT_SIZE = 7.5f; + private static final int WATERMARK_ANGLE = -30; + private static final int WATERMARK_GAP = 200; + + private static volatile byte[] cachedFontBytes; + + /** + * Lazy loads the font bytes from classpath + */ + private static byte[] getFontBytes() { + if (cachedFontBytes != null) { + return cachedFontBytes; + } + synchronized (PdfWatermarkUtil.class) { + if (cachedFontBytes == null) { + try { + ClassPathResource fontResource = new ClassPathResource("fonts/simsun.ttc"); + if (fontResource.exists()) { + try (InputStream is = fontResource.getInputStream()) { + cachedFontBytes = is.readAllBytes(); + } + } else { + log.warn("Watermark font file not found at fonts/simsun.ttc"); + } + } catch (IOException e) { + log.error("Failed to load watermark font", e); + } + } + } + return cachedFontBytes; + } + + /** + * Adds a text watermark to the provided PDF bytes (optimized for Landscape A4). + * + * @param pdfBytes The original PDF bytes + * @param appName The text to use as watermark + * @return The PDF bytes with watermark added + */ + public static byte[] addWatermarkLandscape(byte[] pdfBytes, String appName) { + return addWatermark(pdfBytes, appName); + } + + /** + * Adds a text watermark to the provided PDF bytes. + * + * @param pdfBytes The original PDF bytes + * @param appName The text to use as watermark (usually application name) + * @return The PDF bytes with watermark added + */ + public static byte[] addWatermark(byte[] pdfBytes, String appName) { + byte[] fontBytes = getFontBytes(); + if (fontBytes == null) { + log.warn("Watermark font not loaded, skipping watermark."); + return pdfBytes; + } + + String wmText = formatWatermarkText(appName); + + try (PDDocument doc = PDDocument.load(pdfBytes)) { + PDFont font = loadFont(doc, fontBytes); + if (font == null) { + return pdfBytes; + } + + PDExtendedGraphicsState r0 = new PDExtendedGraphicsState(); + r0.setNonStrokingAlphaConstant(WATERMARK_ALPHA); + + String[] lines = wmText.split("\n|\\\\n"); + + for (PDPage page : doc.getPages()) { + try (PDPageContentStream cs = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true, true)) { + cs.setGraphicsStateParameters(r0); + cs.setFont(font, WATERMARK_FONT_SIZE); + cs.setNonStrokingColor(Color.BLACK); + + float pageWidth = page.getMediaBox().getWidth(); + float pageHeight = page.getMediaBox().getHeight(); + + for (int x = 0; x < pageWidth; x += WATERMARK_GAP) { + for (int y = 0; y < pageHeight; y += WATERMARK_GAP) { + drawWatermarkText(cs, lines, x, y); + } + } + } + } + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + doc.save(baos); + return baos.toByteArray(); + } catch (Exception e) { + throw new RuntimeException("Failed to add watermark to PDF", e); + } + } + + private static PDFont loadFont(PDDocument doc, byte[] fontBytes) { + try (InputStream is = new ByteArrayInputStream(fontBytes)) { + TrueTypeCollection ttc = new TrueTypeCollection(is); + return PDType0Font.load(doc, ttc.getFontByName("SimSun"), true); + } catch (Exception e) { + log.error("Error loading font from cached bytes", e); + return null; + } + } + + private static void drawWatermarkText(PDPageContentStream cs, String[] lines, float x, float y) throws IOException { + cs.beginText(); + // Add offset to center within the grid cell roughly + // Shift y up by 100 (instead of 50) to prevent bottom row cutoff + Matrix matrix = Matrix.getRotateInstance(Math.toRadians(WATERMARK_ANGLE), x + 50, y + 100); + cs.setTextMatrix(matrix); + + float leading = 20f; + for (String line : lines) { + cs.showText(line); + cs.newLineAtOffset(0, -leading); + } + cs.endText(); + } + + private static String formatWatermarkText(String appName) { + String wmText = appName; + if (wmText == null || wmText.isEmpty() || DEFAULT_APP_NAME.equals(wmText)) { + return DEFAULT_WATERMARK_TEXT; + } + if (wmText.length() > 10 && !wmText.contains("
") && !wmText.contains("\n")) { + int mid = wmText.length() / 2; + return wmText.substring(0, mid) + "\n" + wmText.substring(mid); + } + return wmText; + } + + public static byte[] addSealBottomRight(byte[] pdfBytes, byte[] sealImageBytes) { + if (pdfBytes == null || sealImageBytes == null || sealImageBytes.length == 0) { + return pdfBytes; + } + try (PDDocument doc = PDDocument.load(pdfBytes)) { + PDImageXObject image = PDImageXObject.createFromByteArray(doc, sealImageBytes, "seal"); + PDFRenderer renderer = new PDFRenderer(doc); + float imageWidth = image.getWidth(); + float imageHeight = image.getHeight(); + int pageIndex = 0; + for (PDPage page : doc.getPages()) { + float pageWidth = page.getMediaBox().getWidth(); + float pageHeight = page.getMediaBox().getHeight(); + float targetWidth = pageWidth * 0.18f; + float scale = targetWidth / imageWidth; + float targetHeight = imageHeight * scale; + float marginX = pageWidth * 0.04f; + float marginY = pageHeight * 0.04f; + float x = pageWidth - targetWidth - marginX; + float y = marginY; + + try { + BufferedImage pageImage = renderer.renderImageWithDPI(pageIndex, 110); + int width = pageImage.getWidth(); + int height = pageImage.getHeight(); + int denseRowThreshold = Math.max(20, (int) (width * 0.30f)); + int denseColThreshold = Math.max(12, (int) (height * 0.06f)); + int lastDenseRow = -1; + int rightDenseCol = -1; + + for (int iy = 0; iy < height; iy++) { + int darkCount = 0; + for (int ix = 0; ix < width; ix++) { + int rgb = pageImage.getRGB(ix, iy); + int r = (rgb >> 16) & 0xff; + int g = (rgb >> 8) & 0xff; + int b = rgb & 0xff; + if (r < 120 || g < 120 || b < 120) { + darkCount++; + } + } + if (darkCount >= denseRowThreshold) { + lastDenseRow = iy; + } + } + + for (int ix = 0; ix < width; ix++) { + int darkCount = 0; + for (int iy = 0; iy < height; iy++) { + int rgb = pageImage.getRGB(ix, iy); + int r = (rgb >> 16) & 0xff; + int g = (rgb >> 8) & 0xff; + int b = rgb & 0xff; + if (r < 120 || g < 120 || b < 120) { + darkCount++; + } + } + if (darkCount >= denseColThreshold) { + rightDenseCol = ix; + } + } + + if (lastDenseRow >= 0 && rightDenseCol >= 0) { + float contentRight = (rightDenseCol + 1f) * pageWidth / width; + float contentBottom = pageHeight - ((lastDenseRow + 1f) * pageHeight / height); + float insetX = targetWidth * 0.06f; + float insetY = targetHeight * 0.06f; + float sealX = contentRight - targetWidth - insetX; + float sealY = contentBottom + insetY; + float maxX = pageWidth - targetWidth - marginX; + float maxY = pageHeight - targetHeight - marginY; + float minX = marginX; + float minY = marginY; + x = Math.max(minX, Math.min(sealX, maxX)); + y = Math.max(minY, Math.min(sealY, maxY)); + } + } catch (Exception e) { + log.warn("Failed to detect table edge for seal placement on page {}", pageIndex + 1, e); + } + try (PDPageContentStream cs = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true, true)) { + cs.drawImage(image, x, y, targetWidth, targetHeight); + } + pageIndex++; + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + doc.save(baos); + return baos.toByteArray(); + } catch (Exception e) { + log.error("Failed to add seal to PDF", e); + return pdfBytes; + } + } + + public static byte[] addPageNumbers(byte[] pdfBytes) { + byte[] fontBytes = getFontBytes(); + if (fontBytes == null) { + return pdfBytes; + } + try (PDDocument doc = PDDocument.load(pdfBytes)) { + PDFont font = loadFont(doc, fontBytes); + if (font == null) { + return pdfBytes; + } + int total = doc.getNumberOfPages(); + PDExtendedGraphicsState r0 = new PDExtendedGraphicsState(); + r0.setNonStrokingAlphaConstant(PAGE_NUMBER_ALPHA); + int index = 1; + for (PDPage page : doc.getPages()) { + try (PDPageContentStream cs = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true, true)) { + cs.setGraphicsStateParameters(r0); + cs.setFont(font, PAGE_NUMBER_FONT_SIZE); + float pageWidth = page.getMediaBox().getWidth(); + + float leftPaddingX = 28.35f; + float rightPaddingX = 18.0f; + float y = 24f; + + cs.setNonStrokingColor(new Color(0x88, 0x88, 0x88)); + cs.beginText(); + cs.newLineAtOffset(leftPaddingX, y); + cs.showText("平台信息依实为准,信息使用自判"); + cs.endText(); + + String pageLabel = "第 " + index + "/" + total + " 页"; + float pageLabelWidth = font.getStringWidth(pageLabel) / 1000f * PAGE_NUMBER_FONT_SIZE; + float x = pageWidth - rightPaddingX - pageLabelWidth; + cs.setNonStrokingColor(Color.BLACK); + cs.beginText(); + cs.newLineAtOffset(x, y); + cs.showText(pageLabel); + cs.endText(); + } + index++; + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + doc.save(baos); + return baos.toByteArray(); + } catch (Exception e) { + return pdfBytes; + } + } + + public static byte[] addImageTopLeft(byte[] pdfBytes, byte[] imageBytes) { + return addImageTopLeft(pdfBytes, imageBytes, 0.18f, 0.04f); + } + + public static byte[] addImageTopLeft(byte[] pdfBytes, byte[] imageBytes, float widthRatio, float marginRatio) { + if (pdfBytes == null || imageBytes == null || imageBytes.length == 0) { + return pdfBytes; + } + try (PDDocument doc = PDDocument.load(pdfBytes)) { + PDImageXObject image = PDImageXObject.createFromByteArray(doc, imageBytes, "wm_top_left"); + float imageWidth = image.getWidth(); + float imageHeight = image.getHeight(); + for (PDPage page : doc.getPages()) { + float pageWidth = page.getMediaBox().getWidth(); + float pageHeight = page.getMediaBox().getHeight(); + float targetWidth = pageWidth * widthRatio; + float scale = targetWidth / imageWidth; + float targetHeight = imageHeight * scale; + float marginX = pageWidth * marginRatio; + float marginY = pageHeight * marginRatio; + float x = marginX; + float y = pageHeight - targetHeight - marginY; + try (PDPageContentStream cs = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true, true)) { + cs.drawImage(image, x, y, targetWidth, targetHeight); + } + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + doc.save(baos); + return baos.toByteArray(); + } catch (Exception e) { + log.error("Failed to add top-left image to PDF", e); + return pdfBytes; + } + } + + public static byte[] addImageTopRight(byte[] pdfBytes, byte[] imageBytes) { + return addImageTopRight(pdfBytes, imageBytes, 0.18f, 0.04f); + } + + public static byte[] addImageTopRight(byte[] pdfBytes, byte[] imageBytes, float widthRatio, float marginRatio) { + if (pdfBytes == null || imageBytes == null || imageBytes.length == 0) { + return pdfBytes; + } + try (PDDocument doc = PDDocument.load(pdfBytes)) { + PDImageXObject image = PDImageXObject.createFromByteArray(doc, imageBytes, "wm_top_right"); + float imageWidth = image.getWidth(); + float imageHeight = image.getHeight(); + for (PDPage page : doc.getPages()) { + float pageWidth = page.getMediaBox().getWidth(); + float pageHeight = page.getMediaBox().getHeight(); + float targetWidth = pageWidth * widthRatio; + float scale = targetWidth / imageWidth; + float targetHeight = imageHeight * scale; + float marginX = pageWidth * marginRatio; + float marginY = pageHeight * marginRatio; + float x = pageWidth - targetWidth - marginX; + float y = pageHeight - targetHeight - marginY; + try (PDPageContentStream cs = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true, true)) { + cs.drawImage(image, x, y, targetWidth, targetHeight); + } + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + doc.save(baos); + return baos.toByteArray(); + } catch (Exception e) { + log.error("Failed to add top-right image to PDF", e); + return pdfBytes; + } + } +} diff --git a/src/main/java/com/hotwj/platform/common/utils/TimeRangeUtil.java b/src/main/java/com/hotwj/platform/common/utils/TimeRangeUtil.java new file mode 100644 index 0000000..b703af2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/utils/TimeRangeUtil.java @@ -0,0 +1,94 @@ +package com.hotwj.platform.common.utils; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAdjusters; +import java.util.Date; + +/** + * 时间范围工具类 + */ +public class TimeRangeUtil { + + public static class TimeRange { + private final Date startTime; + private final Date endTime; + + public TimeRange(Date startTime, Date endTime) { + this.startTime = startTime; + this.endTime = endTime; + } + + public Date getStartTime() { + return startTime; + } + + public Date getEndTime() { + return endTime; + } + } + + public static TimeRange getCurrentWeek() { + LocalDate today = LocalDate.now(); + LocalDate startOfWeek = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); + LocalDate endOfWeek = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)); + return new TimeRange(toDate(startOfWeek), toDate(endOfWeek)); + } + + public static TimeRange getCurrentMonth() { + LocalDate today = LocalDate.now(); + LocalDate startOfMonth = today.with(TemporalAdjusters.firstDayOfMonth()); + LocalDate endOfMonth = today.with(TemporalAdjusters.lastDayOfMonth()); + return new TimeRange(toDate(startOfMonth), toDate(endOfMonth)); + } + + public static TimeRange getCurrentQuarter() { + LocalDate today = LocalDate.now(); + int month = today.getMonthValue(); + int quarterStartMonth = month - (month - 1) % 3; + LocalDate startOfQuarter = today.withMonth(quarterStartMonth).with(TemporalAdjusters.firstDayOfMonth()); + LocalDate endOfQuarter = startOfQuarter.plusMonths(2).with(TemporalAdjusters.lastDayOfMonth()); + return new TimeRange(toDate(startOfQuarter), toDate(endOfQuarter)); + } + + public static TimeRange getCurrentHalfYear() { + LocalDate today = LocalDate.now(); + int month = today.getMonthValue(); + LocalDate startOfHalfYear; + LocalDate endOfHalfYear; + if (month <= 6) { + startOfHalfYear = LocalDate.of(today.getYear(), 1, 1); + endOfHalfYear = LocalDate.of(today.getYear(), 6, 30); + } else { + startOfHalfYear = LocalDate.of(today.getYear(), 7, 1); + endOfHalfYear = LocalDate.of(today.getYear(), 12, 31); + } + return new TimeRange(toDate(startOfHalfYear), toDate(endOfHalfYear)); + } + + public static TimeRange getCurrentYear() { + LocalDate today = LocalDate.now(); + LocalDate startOfYear = today.with(TemporalAdjusters.firstDayOfYear()); + LocalDate endOfYear = today.with(TemporalAdjusters.lastDayOfYear()); + return new TimeRange(toDate(startOfYear), toDate(endOfYear)); + } + + public static int getCurrentQuarterNumber() { + return (LocalDate.now().getMonthValue() - 1) / 3 + 1; + } + + public static String getCurrentHalfYearDescription() { + return LocalDate.now().getMonthValue() <= 6 ? "上半年" : "下半年"; + } + + public static String formatDate(Date date, String pattern) { + if (date == null) return ""; + return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().format(DateTimeFormatter.ofPattern(pattern)); + } + + private static Date toDate(LocalDate localDate) { + return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + } +} diff --git a/src/main/java/com/hotwj/platform/common/vo/AddressVo.java b/src/main/java/com/hotwj/platform/common/vo/AddressVo.java new file mode 100644 index 0000000..0f821fe --- /dev/null +++ b/src/main/java/com/hotwj/platform/common/vo/AddressVo.java @@ -0,0 +1,62 @@ +package com.hotwj.platform.common.vo; + +import lombok.Data; + +import java.util.List; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +@Data +public class AddressVo { + private Long id; + private String label; + private String value; + private List children; + + public static List buildTree(List list) { + Map map = new HashMap<>(); + List roots = new ArrayList<>(); + for (AddressVo vo : list) { + if (vo != null && vo.getValue() != null) { + map.put(vo.getValue(), vo); + } + } + for (AddressVo vo : list) { + if (vo == null) { + continue; + } + String code = vo.getValue(); + String parent = parentCode(code); + AddressVo p = parent == null ? null : map.get(parent); + if (p == null) { + roots.add(vo); + } else { + List c = p.getChildren(); + if (c == null) { + c = new ArrayList<>(); + p.setChildren(c); + } + c.add(vo); + } + } + return roots; + } + + private static String parentCode(String code) { + if (code == null) { + return null; + } + String c = code.trim(); + if (c.length() == 6) { + if (c.endsWith("0000")) { + return null; + } + if (c.endsWith("00")) { + return c.substring(0, 2) + "0000"; + } + return c.substring(0, 4) + "00"; + } + return null; + } +} diff --git a/src/main/java/com/hotwj/platform/config/accidentLevelConfig/controller/HotAccidentLevelConfigController.java b/src/main/java/com/hotwj/platform/config/accidentLevelConfig/controller/HotAccidentLevelConfigController.java new file mode 100644 index 0000000..a8dda22 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentLevelConfig/controller/HotAccidentLevelConfigController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.config.accidentLevelConfig.controller; + +import com.hotwj.platform.config.accidentLevelConfig.domain.bo.HotAccidentLevelConfigBo; +import com.hotwj.platform.config.accidentLevelConfig.domain.vo.HotAccidentLevelConfigVo; +import com.hotwj.platform.config.accidentLevelConfig.service.IHotAccidentLevelConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 事故等级划分 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/accidentLevelConfig") +@Tag(name = "事故等级划分", description = "事故等级划分管理") +public class HotAccidentLevelConfigController extends BaseController { + + private final IHotAccidentLevelConfigService hotAccidentLevelConfigService; + + /** + * 查询事故等级划分列表 + */ + //@SaCheckPermission("config:accidentLevelConfig:list") + @GetMapping("/list") + @Operation(summary = "分页查询事故等级划分列表") + public TableDataInfo list(HotAccidentLevelConfigBo bo, PageQuery pageQuery) { + return hotAccidentLevelConfigService.queryPageList(bo, pageQuery); + } + + /** + * 导出事故等级划分列表 + */ + //@SaCheckPermission("config:accidentLevelConfig:export") + @Log(title = "事故等级划分", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出事故等级划分列表") + public void export(HotAccidentLevelConfigBo bo, HttpServletResponse response) { + List list = hotAccidentLevelConfigService.queryList(bo); + ExcelUtil.exportExcel(list, "事故等级划分", HotAccidentLevelConfigVo.class, response); + } + + /** + * 获取事故等级划分详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:accidentLevelConfig:query") + @GetMapping("/{id}") + @Operation(summary = "获取事故等级划分详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotAccidentLevelConfigService.queryById(id)); + } + + /** + * 新增事故等级划分 + */ + //@SaCheckPermission("config:accidentLevelConfig:add") + @Log(title = "事故等级划分", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增事故等级划分") + public R add(@Validated(AddGroup.class) @RequestBody HotAccidentLevelConfigBo bo) { + return toAjax(hotAccidentLevelConfigService.insertByBo(bo)); + } + + /** + * 修改事故等级划分 + */ + //@SaCheckPermission("config:accidentLevelConfig:edit") + @Log(title = "事故等级划分", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改事故等级划分") + public R edit(@Validated(EditGroup.class) @RequestBody HotAccidentLevelConfigBo bo) { + return toAjax(hotAccidentLevelConfigService.updateByBo(bo)); + } + + /** + * 删除事故等级划分 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:accidentLevelConfig:remove") + @Log(title = "事故等级划分", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除事故等级划分") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotAccidentLevelConfigService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/accidentLevelConfig/domain/HotAccidentLevelConfig.java b/src/main/java/com/hotwj/platform/config/accidentLevelConfig/domain/HotAccidentLevelConfig.java new file mode 100644 index 0000000..6567d29 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentLevelConfig/domain/HotAccidentLevelConfig.java @@ -0,0 +1,69 @@ +package com.hotwj.platform.config.accidentLevelConfig.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 事故等级划分对象 hot_accident_level_config + * + * @author shihongwei + * @date 2026-01-06 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_accident_level_config") +public class HotAccidentLevelConfig extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 风险值小值 + */ + private Long minRiskValue; + + /** + * 风险值大值 + */ + private Long maxRiskValue; + + /** + * 评价分级 + */ + private String levelName; + + /** + * 管控级别 + */ + private String controlLevel; + + /** + * 管控措施级别 + */ + private String controlMeasureLevel; + + /** + * 完成期限 + */ + private String deadlineDesc; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/accidentLevelConfig/domain/bo/HotAccidentLevelConfigBo.java b/src/main/java/com/hotwj/platform/config/accidentLevelConfig/domain/bo/HotAccidentLevelConfigBo.java new file mode 100644 index 0000000..45b1703 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentLevelConfig/domain/bo/HotAccidentLevelConfigBo.java @@ -0,0 +1,72 @@ +package com.hotwj.platform.config.accidentLevelConfig.domain.bo; + +import com.hotwj.platform.config.accidentLevelConfig.domain.HotAccidentLevelConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 事故等级划分业务对象 hot_accident_level_config + * + * @author shihongwei + * @date 2026-01-06 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotAccidentLevelConfig.class, reverseConvertGenerate = false) +public class HotAccidentLevelConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 风险值小值 + */ + @NotNull(message = "风险值小值不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long minRiskValue; + + /** + * 风险值大值 + */ + @NotNull(message = "风险值大值不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long maxRiskValue; + + /** + * 评价分级 + */ + @NotBlank(message = "评价分级不能为空", groups = {AddGroup.class, EditGroup.class}) + private String levelName; + + /** + * 管控级别 + */ + @NotBlank(message = "管控级别不能为空", groups = {AddGroup.class, EditGroup.class}) + private String controlLevel; + + /** + * 管控措施级别 + */ + @NotBlank(message = "管控措施级别不能为空", groups = {AddGroup.class, EditGroup.class}) + private String controlMeasureLevel; + + /** + * 完成期限 + */ + @NotBlank(message = "完成期限不能为空", groups = {AddGroup.class, EditGroup.class}) + private String deadlineDesc; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/accidentLevelConfig/domain/vo/HotAccidentLevelConfigVo.java b/src/main/java/com/hotwj/platform/config/accidentLevelConfig/domain/vo/HotAccidentLevelConfigVo.java new file mode 100644 index 0000000..0a3f15a --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentLevelConfig/domain/vo/HotAccidentLevelConfigVo.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.config.accidentLevelConfig.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.accidentLevelConfig.domain.HotAccidentLevelConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 事故等级划分视图对象 hot_accident_level_config + * + * @author shihongwei + * @date 2026-01-06 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotAccidentLevelConfig.class) +public class HotAccidentLevelConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 风险值小值 + */ + @ExcelProperty(value = "风险值小值") + private Long minRiskValue; + + /** + * 风险值大值 + */ + @ExcelProperty(value = "风险值大值") + private Long maxRiskValue; + + /** + * 评价分级 + */ + @ExcelProperty(value = "评价分级") + private String levelName; + + /** + * 管控级别 + */ + @ExcelProperty(value = "管控级别") + private String controlLevel; + + /** + * 管控措施级别 + */ + @ExcelProperty(value = "管控措施级别") + private String controlMeasureLevel; + + /** + * 完成期限 + */ + @ExcelProperty(value = "完成期限") + private String deadlineDesc; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/accidentLevelConfig/mapper/HotAccidentLevelConfigMapper.java b/src/main/java/com/hotwj/platform/config/accidentLevelConfig/mapper/HotAccidentLevelConfigMapper.java new file mode 100644 index 0000000..a5eba73 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentLevelConfig/mapper/HotAccidentLevelConfigMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.accidentLevelConfig.mapper; + +import com.hotwj.platform.config.accidentLevelConfig.domain.HotAccidentLevelConfig; +import com.hotwj.platform.config.accidentLevelConfig.domain.vo.HotAccidentLevelConfigVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 事故等级划分Mapper接口 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Mapper +public interface HotAccidentLevelConfigMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/accidentLevelConfig/service/IHotAccidentLevelConfigService.java b/src/main/java/com/hotwj/platform/config/accidentLevelConfig/service/IHotAccidentLevelConfigService.java new file mode 100644 index 0000000..c8a33e8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentLevelConfig/service/IHotAccidentLevelConfigService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.accidentLevelConfig.service; + +import com.hotwj.platform.config.accidentLevelConfig.domain.bo.HotAccidentLevelConfigBo; +import com.hotwj.platform.config.accidentLevelConfig.domain.vo.HotAccidentLevelConfigVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 事故等级划分Service接口 + * + * @author shihongwei + * @date 2026-01-06 + */ +public interface IHotAccidentLevelConfigService { + + /** + * 查询事故等级划分 + * + * @param id 主键 + * @return 事故等级划分 + */ + HotAccidentLevelConfigVo queryById(Long id); + + /** + * 分页查询事故等级划分列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 事故等级划分分页列表 + */ + TableDataInfo queryPageList(HotAccidentLevelConfigBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的事故等级划分列表 + * + * @param bo 查询条件 + * @return 事故等级划分列表 + */ + List queryList(HotAccidentLevelConfigBo bo); + + /** + * 新增事故等级划分 + * + * @param bo 事故等级划分 + * @return 是否新增成功 + */ + Boolean insertByBo(HotAccidentLevelConfigBo bo); + + /** + * 修改事故等级划分 + * + * @param bo 事故等级划分 + * @return 是否修改成功 + */ + Boolean updateByBo(HotAccidentLevelConfigBo bo); + + /** + * 校验并批量删除事故等级划分信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/accidentLevelConfig/service/impl/HotAccidentLevelConfigServiceImpl.java b/src/main/java/com/hotwj/platform/config/accidentLevelConfig/service/impl/HotAccidentLevelConfigServiceImpl.java new file mode 100644 index 0000000..62abed0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentLevelConfig/service/impl/HotAccidentLevelConfigServiceImpl.java @@ -0,0 +1,138 @@ +package com.hotwj.platform.config.accidentLevelConfig.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.accidentLevelConfig.domain.HotAccidentLevelConfig; +import com.hotwj.platform.config.accidentLevelConfig.domain.bo.HotAccidentLevelConfigBo; +import com.hotwj.platform.config.accidentLevelConfig.domain.vo.HotAccidentLevelConfigVo; +import com.hotwj.platform.config.accidentLevelConfig.mapper.HotAccidentLevelConfigMapper; +import com.hotwj.platform.config.accidentLevelConfig.service.IHotAccidentLevelConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 事故等级划分Service业务层处理 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotAccidentLevelConfigServiceImpl implements IHotAccidentLevelConfigService { + + private final HotAccidentLevelConfigMapper baseMapper; + + /** + * 查询事故等级划分 + * + * @param id 主键 + * @return 事故等级划分 + */ + @Override + public HotAccidentLevelConfigVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询事故等级划分列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 事故等级划分分页列表 + */ + @Override + public TableDataInfo queryPageList(HotAccidentLevelConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的事故等级划分列表 + * + * @param bo 查询条件 + * @return 事故等级划分列表 + */ + @Override + public List queryList(HotAccidentLevelConfigBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotAccidentLevelConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotAccidentLevelConfig::getId); + lqw.eq(bo.getMinRiskValue() != null, HotAccidentLevelConfig::getMinRiskValue, bo.getMinRiskValue()); + lqw.eq(bo.getMaxRiskValue() != null, HotAccidentLevelConfig::getMaxRiskValue, bo.getMaxRiskValue()); + lqw.like(StringUtils.isNotBlank(bo.getLevelName()), HotAccidentLevelConfig::getLevelName, bo.getLevelName()); + lqw.eq(StringUtils.isNotBlank(bo.getControlLevel()), HotAccidentLevelConfig::getControlLevel, bo.getControlLevel()); + lqw.eq(StringUtils.isNotBlank(bo.getControlMeasureLevel()), HotAccidentLevelConfig::getControlMeasureLevel, bo.getControlMeasureLevel()); + lqw.eq(StringUtils.isNotBlank(bo.getDeadlineDesc()), HotAccidentLevelConfig::getDeadlineDesc, bo.getDeadlineDesc()); + lqw.eq(bo.getIsDeleted() != null, HotAccidentLevelConfig::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增事故等级划分 + * + * @param bo 事故等级划分 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotAccidentLevelConfigBo bo) { + HotAccidentLevelConfig add = MapstructUtils.convert(bo, HotAccidentLevelConfig.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改事故等级划分 + * + * @param bo 事故等级划分 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotAccidentLevelConfigBo bo) { + HotAccidentLevelConfig update = MapstructUtils.convert(bo, HotAccidentLevelConfig.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotAccidentLevelConfig entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除事故等级划分信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/controller/HotAccidentLikelihoodLevelConfigController.java b/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/controller/HotAccidentLikelihoodLevelConfigController.java new file mode 100644 index 0000000..0250d5c --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/controller/HotAccidentLikelihoodLevelConfigController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.config.accidentLikelihoodLevelConfig.controller; + +import com.hotwj.platform.config.accidentLikelihoodLevelConfig.domain.bo.HotAccidentLikelihoodLevelConfigBo; +import com.hotwj.platform.config.accidentLikelihoodLevelConfig.domain.vo.HotAccidentLikelihoodLevelConfigVo; +import com.hotwj.platform.config.accidentLikelihoodLevelConfig.service.IHotAccidentLikelihoodLevelConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 事故等级划分(可能性) + * + * @author shihongwei + * @date 2026-01-05 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/accidentLikelihoodLevelConfig") +@Tag(name = "事故等级划分(可能性)", description = "事故等级划分(可能性)管理") +public class HotAccidentLikelihoodLevelConfigController extends BaseController { + + private final IHotAccidentLikelihoodLevelConfigService hotAccidentLikelihoodLevelConfigService; + + /** + * 查询事故等级划分(可能性)列表 + */ + //@SaCheckPermission("config:accidentLikelihoodLevelConfig:list") + @GetMapping("/list") + @Operation(summary = "分页查询事故等级划分(可能性)列表") + public TableDataInfo list(HotAccidentLikelihoodLevelConfigBo bo, PageQuery pageQuery) { + return hotAccidentLikelihoodLevelConfigService.queryPageList(bo, pageQuery); + } + + /** + * 导出事故等级划分(可能性)列表 + */ + //@SaCheckPermission("config:accidentLikelihoodLevelConfig:export") + @Log(title = "事故等级划分(可能性)", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出事故等级划分(可能性)列表") + public void export(HotAccidentLikelihoodLevelConfigBo bo, HttpServletResponse response) { + List list = hotAccidentLikelihoodLevelConfigService.queryList(bo); + ExcelUtil.exportExcel(list, "事故等级划分(可能性)", HotAccidentLikelihoodLevelConfigVo.class, response); + } + + /** + * 获取事故等级划分(可能性)详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:accidentLikelihoodLevelConfig:query") + @GetMapping("/{id}") + @Operation(summary = "获取事故等级划分(可能性)详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotAccidentLikelihoodLevelConfigService.queryById(id)); + } + + /** + * 新增事故等级划分(可能性) + */ + //@SaCheckPermission("config:accidentLikelihoodLevelConfig:add") + @Log(title = "事故等级划分(可能性)", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增事故等级划分(可能性)") + public R add(@Validated(AddGroup.class) @RequestBody HotAccidentLikelihoodLevelConfigBo bo) { + return toAjax(hotAccidentLikelihoodLevelConfigService.insertByBo(bo)); + } + + /** + * 修改事故等级划分(可能性) + */ + //@SaCheckPermission("config:accidentLikelihoodLevelConfig:edit") + @Log(title = "事故等级划分(可能性)", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改事故等级划分(可能性)") + public R edit(@Validated(EditGroup.class) @RequestBody HotAccidentLikelihoodLevelConfigBo bo) { + return toAjax(hotAccidentLikelihoodLevelConfigService.updateByBo(bo)); + } + + /** + * 删除事故等级划分(可能性) + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:accidentLikelihoodLevelConfig:remove") + @Log(title = "事故等级划分(可能性)", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除事故等级划分(可能性)") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotAccidentLikelihoodLevelConfigService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/domain/HotAccidentLikelihoodLevelConfig.java b/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/domain/HotAccidentLikelihoodLevelConfig.java new file mode 100644 index 0000000..176f690 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/domain/HotAccidentLikelihoodLevelConfig.java @@ -0,0 +1,49 @@ +package com.hotwj.platform.config.accidentLikelihoodLevelConfig.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 事故等级划分(可能性)对象 hot_accident_likelihood_level_config + * + * @author shihongwei + * @date 2026-01-05 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_accident_likelihood_level_config") +public class HotAccidentLikelihoodLevelConfig extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 取值(级别) + */ + private Long levelValue; + + /** + * 判定标准 + */ + private String criterion; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/domain/bo/HotAccidentLikelihoodLevelConfigBo.java b/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/domain/bo/HotAccidentLikelihoodLevelConfigBo.java new file mode 100644 index 0000000..c2f9c76 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/domain/bo/HotAccidentLikelihoodLevelConfigBo.java @@ -0,0 +1,48 @@ +package com.hotwj.platform.config.accidentLikelihoodLevelConfig.domain.bo; + +import com.hotwj.platform.config.accidentLikelihoodLevelConfig.domain.HotAccidentLikelihoodLevelConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 事故等级划分(可能性)业务对象 hot_accident_likelihood_level_config + * + * @author shihongwei + * @date 2026-01-05 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotAccidentLikelihoodLevelConfig.class, reverseConvertGenerate = false) +public class HotAccidentLikelihoodLevelConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 取值(级别) + */ + @NotNull(message = "取值(级别)不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long levelValue; + + /** + * 判定标准 + */ + @NotBlank(message = "判定标准不能为空", groups = {AddGroup.class, EditGroup.class}) + private String criterion; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/domain/vo/HotAccidentLikelihoodLevelConfigVo.java b/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/domain/vo/HotAccidentLikelihoodLevelConfigVo.java new file mode 100644 index 0000000..5200d94 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/domain/vo/HotAccidentLikelihoodLevelConfigVo.java @@ -0,0 +1,55 @@ +package com.hotwj.platform.config.accidentLikelihoodLevelConfig.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.accidentLikelihoodLevelConfig.domain.HotAccidentLikelihoodLevelConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 事故等级划分(可能性)视图对象 hot_accident_likelihood_level_config + * + * @author shihongwei + * @date 2026-01-05 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotAccidentLikelihoodLevelConfig.class) +public class HotAccidentLikelihoodLevelConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 取值(级别) + */ + @ExcelProperty(value = "取值", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "级=别") + private Long levelValue; + + /** + * 判定标准 + */ + @ExcelProperty(value = "判定标准") + private String criterion; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/mapper/HotAccidentLikelihoodLevelConfigMapper.java b/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/mapper/HotAccidentLikelihoodLevelConfigMapper.java new file mode 100644 index 0000000..da787fd --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/mapper/HotAccidentLikelihoodLevelConfigMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.accidentLikelihoodLevelConfig.mapper; + +import com.hotwj.platform.config.accidentLikelihoodLevelConfig.domain.HotAccidentLikelihoodLevelConfig; +import com.hotwj.platform.config.accidentLikelihoodLevelConfig.domain.vo.HotAccidentLikelihoodLevelConfigVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 事故等级划分(可能性)Mapper接口 + * + * @author shihongwei + * @date 2026-01-05 + */ +@Mapper +public interface HotAccidentLikelihoodLevelConfigMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/service/IHotAccidentLikelihoodLevelConfigService.java b/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/service/IHotAccidentLikelihoodLevelConfigService.java new file mode 100644 index 0000000..b364cab --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/service/IHotAccidentLikelihoodLevelConfigService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.accidentLikelihoodLevelConfig.service; + +import com.hotwj.platform.config.accidentLikelihoodLevelConfig.domain.bo.HotAccidentLikelihoodLevelConfigBo; +import com.hotwj.platform.config.accidentLikelihoodLevelConfig.domain.vo.HotAccidentLikelihoodLevelConfigVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 事故等级划分(可能性)Service接口 + * + * @author shihongwei + * @date 2026-01-05 + */ +public interface IHotAccidentLikelihoodLevelConfigService { + + /** + * 查询事故等级划分(可能性) + * + * @param id 主键 + * @return 事故等级划分(可能性) + */ + HotAccidentLikelihoodLevelConfigVo queryById(Long id); + + /** + * 分页查询事故等级划分(可能性)列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 事故等级划分(可能性)分页列表 + */ + TableDataInfo queryPageList(HotAccidentLikelihoodLevelConfigBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的事故等级划分(可能性)列表 + * + * @param bo 查询条件 + * @return 事故等级划分(可能性)列表 + */ + List queryList(HotAccidentLikelihoodLevelConfigBo bo); + + /** + * 新增事故等级划分(可能性) + * + * @param bo 事故等级划分(可能性) + * @return 是否新增成功 + */ + Boolean insertByBo(HotAccidentLikelihoodLevelConfigBo bo); + + /** + * 修改事故等级划分(可能性) + * + * @param bo 事故等级划分(可能性) + * @return 是否修改成功 + */ + Boolean updateByBo(HotAccidentLikelihoodLevelConfigBo bo); + + /** + * 校验并批量删除事故等级划分(可能性)信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/service/impl/HotAccidentLikelihoodLevelConfigServiceImpl.java b/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/service/impl/HotAccidentLikelihoodLevelConfigServiceImpl.java new file mode 100644 index 0000000..092ce7d --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentLikelihoodLevelConfig/service/impl/HotAccidentLikelihoodLevelConfigServiceImpl.java @@ -0,0 +1,134 @@ +package com.hotwj.platform.config.accidentLikelihoodLevelConfig.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.accidentLikelihoodLevelConfig.domain.HotAccidentLikelihoodLevelConfig; +import com.hotwj.platform.config.accidentLikelihoodLevelConfig.domain.bo.HotAccidentLikelihoodLevelConfigBo; +import com.hotwj.platform.config.accidentLikelihoodLevelConfig.domain.vo.HotAccidentLikelihoodLevelConfigVo; +import com.hotwj.platform.config.accidentLikelihoodLevelConfig.mapper.HotAccidentLikelihoodLevelConfigMapper; +import com.hotwj.platform.config.accidentLikelihoodLevelConfig.service.IHotAccidentLikelihoodLevelConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 事故等级划分(可能性)Service业务层处理 + * + * @author shihongwei + * @date 2026-01-05 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotAccidentLikelihoodLevelConfigServiceImpl implements IHotAccidentLikelihoodLevelConfigService { + + private final HotAccidentLikelihoodLevelConfigMapper baseMapper; + + /** + * 查询事故等级划分(可能性) + * + * @param id 主键 + * @return 事故等级划分(可能性) + */ + @Override + public HotAccidentLikelihoodLevelConfigVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询事故等级划分(可能性)列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 事故等级划分(可能性)分页列表 + */ + @Override + public TableDataInfo queryPageList(HotAccidentLikelihoodLevelConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的事故等级划分(可能性)列表 + * + * @param bo 查询条件 + * @return 事故等级划分(可能性)列表 + */ + @Override + public List queryList(HotAccidentLikelihoodLevelConfigBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotAccidentLikelihoodLevelConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotAccidentLikelihoodLevelConfig::getId); + lqw.eq(bo.getLevelValue() != null, HotAccidentLikelihoodLevelConfig::getLevelValue, bo.getLevelValue()); + lqw.eq(StringUtils.isNotBlank(bo.getCriterion()), HotAccidentLikelihoodLevelConfig::getCriterion, bo.getCriterion()); + lqw.eq(bo.getIsDeleted() != null, HotAccidentLikelihoodLevelConfig::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增事故等级划分(可能性) + * + * @param bo 事故等级划分(可能性) + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotAccidentLikelihoodLevelConfigBo bo) { + HotAccidentLikelihoodLevelConfig add = MapstructUtils.convert(bo, HotAccidentLikelihoodLevelConfig.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改事故等级划分(可能性) + * + * @param bo 事故等级划分(可能性) + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotAccidentLikelihoodLevelConfigBo bo) { + HotAccidentLikelihoodLevelConfig update = MapstructUtils.convert(bo, HotAccidentLikelihoodLevelConfig.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotAccidentLikelihoodLevelConfig entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除事故等级划分(可能性)信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/controller/HotAccidentSeverityLevelConfigController.java b/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/controller/HotAccidentSeverityLevelConfigController.java new file mode 100644 index 0000000..31e563b --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/controller/HotAccidentSeverityLevelConfigController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.config.accidentSeverityLevelConfig.controller; + +import com.hotwj.platform.config.accidentSeverityLevelConfig.domain.bo.HotAccidentSeverityLevelConfigBo; +import com.hotwj.platform.config.accidentSeverityLevelConfig.domain.vo.HotAccidentSeverityLevelConfigVo; +import com.hotwj.platform.config.accidentSeverityLevelConfig.service.IHotAccidentSeverityLevelConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 事故等级划分(严重程度) + * + * @author shihongwei + * @date 2026-01-05 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/accidentSeverityLevelConfig") +@Tag(name = "事故等级划分(严重程度)", description = "事故等级划分(严重程度)管理") +public class HotAccidentSeverityLevelConfigController extends BaseController { + + private final IHotAccidentSeverityLevelConfigService hotAccidentSeverityLevelConfigService; + + /** + * 查询事故等级划分(严重程度)列表 + */ + //@SaCheckPermission("config:accidentSeverityLevelConfig:list") + @GetMapping("/list") + @Operation(summary = "分页查询事故等级划分(严重程度)列表") + public TableDataInfo list(HotAccidentSeverityLevelConfigBo bo, PageQuery pageQuery) { + return hotAccidentSeverityLevelConfigService.queryPageList(bo, pageQuery); + } + + /** + * 导出事故等级划分(严重程度)列表 + */ + //@SaCheckPermission("config:accidentSeverityLevelConfig:export") + @Log(title = "事故等级划分(严重程度)", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出事故等级划分(严重程度)列表") + public void export(HotAccidentSeverityLevelConfigBo bo, HttpServletResponse response) { + List list = hotAccidentSeverityLevelConfigService.queryList(bo); + ExcelUtil.exportExcel(list, "事故等级划分(严重程度)", HotAccidentSeverityLevelConfigVo.class, response); + } + + /** + * 获取事故等级划分(严重程度)详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:accidentSeverityLevelConfig:query") + @GetMapping("/{id}") + @Operation(summary = "获取事故等级划分(严重程度)详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotAccidentSeverityLevelConfigService.queryById(id)); + } + + /** + * 新增事故等级划分(严重程度) + */ + //@SaCheckPermission("config:accidentSeverityLevelConfig:add") + @Log(title = "事故等级划分(严重程度)", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增事故等级划分(严重程度)") + public R add(@Validated(AddGroup.class) @RequestBody HotAccidentSeverityLevelConfigBo bo) { + return toAjax(hotAccidentSeverityLevelConfigService.insertByBo(bo)); + } + + /** + * 修改事故等级划分(严重程度) + */ + //@SaCheckPermission("config:accidentSeverityLevelConfig:edit") + @Log(title = "事故等级划分(严重程度)", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改事故等级划分(严重程度)") + public R edit(@Validated(EditGroup.class) @RequestBody HotAccidentSeverityLevelConfigBo bo) { + return toAjax(hotAccidentSeverityLevelConfigService.updateByBo(bo)); + } + + /** + * 删除事故等级划分(严重程度) + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:accidentSeverityLevelConfig:remove") + @Log(title = "事故等级划分(严重程度)", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除事故等级划分(严重程度)") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotAccidentSeverityLevelConfigService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/domain/HotAccidentSeverityLevelConfig.java b/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/domain/HotAccidentSeverityLevelConfig.java new file mode 100644 index 0000000..bedc96a --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/domain/HotAccidentSeverityLevelConfig.java @@ -0,0 +1,64 @@ +package com.hotwj.platform.config.accidentSeverityLevelConfig.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 事故等级划分(严重程度)对象 hot_accident_severity_level_config + * + * @author shihongwei + * @date 2026-01-05 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_accident_severity_level_config") +public class HotAccidentSeverityLevelConfig extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 取值(级别) + */ + private Long levelValue; + + /** + * 法律法规及其他要求 + */ + private String lawRequirement; + + /** + * 人员 + */ + private String personnelDescription; + + /** + * 直接经济损失 + */ + private String directEconomicLoss; + + /** + * 企业形象(社会影响) + */ + private String enterpriseReputation; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/domain/bo/HotAccidentSeverityLevelConfigBo.java b/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/domain/bo/HotAccidentSeverityLevelConfigBo.java new file mode 100644 index 0000000..1b24e6d --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/domain/bo/HotAccidentSeverityLevelConfigBo.java @@ -0,0 +1,66 @@ +package com.hotwj.platform.config.accidentSeverityLevelConfig.domain.bo; + +import com.hotwj.platform.config.accidentSeverityLevelConfig.domain.HotAccidentSeverityLevelConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 事故等级划分(严重程度)业务对象 hot_accident_severity_level_config + * + * @author shihongwei + * @date 2026-01-05 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotAccidentSeverityLevelConfig.class, reverseConvertGenerate = false) +public class HotAccidentSeverityLevelConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 取值(级别) + */ + @NotNull(message = "取值(级别)不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long levelValue; + + /** + * 法律法规及其他要求 + */ + @NotBlank(message = "法律法规及其他要求不能为空", groups = {AddGroup.class, EditGroup.class}) + private String lawRequirement; + + /** + * 人员 + */ + @NotBlank(message = "人员不能为空", groups = {AddGroup.class, EditGroup.class}) + private String personnelDescription; + + /** + * 直接经济损失 + */ + @NotBlank(message = "直接经济损失不能为空", groups = {AddGroup.class, EditGroup.class}) + private String directEconomicLoss; + + /** + * 企业形象(社会影响) + */ + @NotBlank(message = "企业形象(社会影响)不能为空", groups = {AddGroup.class, EditGroup.class}) + private String enterpriseReputation; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/domain/vo/HotAccidentSeverityLevelConfigVo.java b/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/domain/vo/HotAccidentSeverityLevelConfigVo.java new file mode 100644 index 0000000..58c597a --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/domain/vo/HotAccidentSeverityLevelConfigVo.java @@ -0,0 +1,74 @@ +package com.hotwj.platform.config.accidentSeverityLevelConfig.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.accidentSeverityLevelConfig.domain.HotAccidentSeverityLevelConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 事故等级划分(严重程度)视图对象 hot_accident_severity_level_config + * + * @author shihongwei + * @date 2026-01-05 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotAccidentSeverityLevelConfig.class) +public class HotAccidentSeverityLevelConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 取值(级别) + */ + @ExcelProperty(value = "取值", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "级=别") + private Long levelValue; + + /** + * 法律法规及其他要求 + */ + @ExcelProperty(value = "法律法规及其他要求") + private String lawRequirement; + + /** + * 人员 + */ + @ExcelProperty(value = "人员") + private String personnelDescription; + + /** + * 直接经济损失 + */ + @ExcelProperty(value = "直接经济损失") + private String directEconomicLoss; + + /** + * 企业形象(社会影响) + */ + @ExcelProperty(value = "企业形象", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "社=会影响") + private String enterpriseReputation; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/mapper/HotAccidentSeverityLevelConfigMapper.java b/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/mapper/HotAccidentSeverityLevelConfigMapper.java new file mode 100644 index 0000000..60c7200 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/mapper/HotAccidentSeverityLevelConfigMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.accidentSeverityLevelConfig.mapper; + +import com.hotwj.platform.config.accidentSeverityLevelConfig.domain.HotAccidentSeverityLevelConfig; +import com.hotwj.platform.config.accidentSeverityLevelConfig.domain.vo.HotAccidentSeverityLevelConfigVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 事故等级划分(严重程度)Mapper接口 + * + * @author shihongwei + * @date 2026-01-05 + */ +@Mapper +public interface HotAccidentSeverityLevelConfigMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/service/IHotAccidentSeverityLevelConfigService.java b/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/service/IHotAccidentSeverityLevelConfigService.java new file mode 100644 index 0000000..3e691dc --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/service/IHotAccidentSeverityLevelConfigService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.accidentSeverityLevelConfig.service; + +import com.hotwj.platform.config.accidentSeverityLevelConfig.domain.bo.HotAccidentSeverityLevelConfigBo; +import com.hotwj.platform.config.accidentSeverityLevelConfig.domain.vo.HotAccidentSeverityLevelConfigVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 事故等级划分(严重程度)Service接口 + * + * @author shihongwei + * @date 2026-01-05 + */ +public interface IHotAccidentSeverityLevelConfigService { + + /** + * 查询事故等级划分(严重程度) + * + * @param id 主键 + * @return 事故等级划分(严重程度) + */ + HotAccidentSeverityLevelConfigVo queryById(Long id); + + /** + * 分页查询事故等级划分(严重程度)列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 事故等级划分(严重程度)分页列表 + */ + TableDataInfo queryPageList(HotAccidentSeverityLevelConfigBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的事故等级划分(严重程度)列表 + * + * @param bo 查询条件 + * @return 事故等级划分(严重程度)列表 + */ + List queryList(HotAccidentSeverityLevelConfigBo bo); + + /** + * 新增事故等级划分(严重程度) + * + * @param bo 事故等级划分(严重程度) + * @return 是否新增成功 + */ + Boolean insertByBo(HotAccidentSeverityLevelConfigBo bo); + + /** + * 修改事故等级划分(严重程度) + * + * @param bo 事故等级划分(严重程度) + * @return 是否修改成功 + */ + Boolean updateByBo(HotAccidentSeverityLevelConfigBo bo); + + /** + * 校验并批量删除事故等级划分(严重程度)信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/service/impl/HotAccidentSeverityLevelConfigServiceImpl.java b/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/service/impl/HotAccidentSeverityLevelConfigServiceImpl.java new file mode 100644 index 0000000..0d4aeb8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/accidentSeverityLevelConfig/service/impl/HotAccidentSeverityLevelConfigServiceImpl.java @@ -0,0 +1,137 @@ +package com.hotwj.platform.config.accidentSeverityLevelConfig.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.accidentSeverityLevelConfig.domain.HotAccidentSeverityLevelConfig; +import com.hotwj.platform.config.accidentSeverityLevelConfig.domain.bo.HotAccidentSeverityLevelConfigBo; +import com.hotwj.platform.config.accidentSeverityLevelConfig.domain.vo.HotAccidentSeverityLevelConfigVo; +import com.hotwj.platform.config.accidentSeverityLevelConfig.mapper.HotAccidentSeverityLevelConfigMapper; +import com.hotwj.platform.config.accidentSeverityLevelConfig.service.IHotAccidentSeverityLevelConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 事故等级划分(严重程度)Service业务层处理 + * + * @author shihongwei + * @date 2026-01-05 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotAccidentSeverityLevelConfigServiceImpl implements IHotAccidentSeverityLevelConfigService { + + private final HotAccidentSeverityLevelConfigMapper baseMapper; + + /** + * 查询事故等级划分(严重程度) + * + * @param id 主键 + * @return 事故等级划分(严重程度) + */ + @Override + public HotAccidentSeverityLevelConfigVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询事故等级划分(严重程度)列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 事故等级划分(严重程度)分页列表 + */ + @Override + public TableDataInfo queryPageList(HotAccidentSeverityLevelConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的事故等级划分(严重程度)列表 + * + * @param bo 查询条件 + * @return 事故等级划分(严重程度)列表 + */ + @Override + public List queryList(HotAccidentSeverityLevelConfigBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotAccidentSeverityLevelConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotAccidentSeverityLevelConfig::getId); + lqw.eq(bo.getLevelValue() != null, HotAccidentSeverityLevelConfig::getLevelValue, bo.getLevelValue()); + lqw.eq(StringUtils.isNotBlank(bo.getLawRequirement()), HotAccidentSeverityLevelConfig::getLawRequirement, bo.getLawRequirement()); + lqw.eq(StringUtils.isNotBlank(bo.getPersonnelDescription()), HotAccidentSeverityLevelConfig::getPersonnelDescription, bo.getPersonnelDescription()); + lqw.eq(StringUtils.isNotBlank(bo.getDirectEconomicLoss()), HotAccidentSeverityLevelConfig::getDirectEconomicLoss, bo.getDirectEconomicLoss()); + lqw.eq(StringUtils.isNotBlank(bo.getEnterpriseReputation()), HotAccidentSeverityLevelConfig::getEnterpriseReputation, bo.getEnterpriseReputation()); + lqw.eq(bo.getIsDeleted() != null, HotAccidentSeverityLevelConfig::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增事故等级划分(严重程度) + * + * @param bo 事故等级划分(严重程度) + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotAccidentSeverityLevelConfigBo bo) { + HotAccidentSeverityLevelConfig add = MapstructUtils.convert(bo, HotAccidentSeverityLevelConfig.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改事故等级划分(严重程度) + * + * @param bo 事故等级划分(严重程度) + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotAccidentSeverityLevelConfigBo bo) { + HotAccidentSeverityLevelConfig update = MapstructUtils.convert(bo, HotAccidentSeverityLevelConfig.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotAccidentSeverityLevelConfig entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除事故等级划分(严重程度)信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/companyBasicConfig/controller/HotCompanyBasicConfigController.java b/src/main/java/com/hotwj/platform/config/companyBasicConfig/controller/HotCompanyBasicConfigController.java new file mode 100644 index 0000000..b8bd3b9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/companyBasicConfig/controller/HotCompanyBasicConfigController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.config.companyBasicConfig.controller; + +import com.hotwj.platform.config.companyBasicConfig.domain.bo.HotCompanyBasicConfigBo; +import com.hotwj.platform.config.companyBasicConfig.domain.vo.HotCompanyBasicConfigVo; +import com.hotwj.platform.config.companyBasicConfig.service.IHotCompanyBasicConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 企业基础配置 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/companyBasicConfig") +@Tag(name = "企业基础配置", description = "企业基础配置管理") +public class HotCompanyBasicConfigController extends BaseController { + + private final IHotCompanyBasicConfigService hotCompanyBasicConfigService; + + /** + * 查询企业基础配置列表 + */ + //@SaCheckPermission("config:companyBasicConfig:list") + @GetMapping("/list") + @Operation(summary = "分页查询企业基础配置列表") + public TableDataInfo list(HotCompanyBasicConfigBo bo, PageQuery pageQuery) { + return hotCompanyBasicConfigService.queryPageList(bo, pageQuery); + } + + /** + * 导出企业基础配置列表 + */ + //@SaCheckPermission("config:companyBasicConfig:export") + @Log(title = "企业基础配置", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出企业基础配置列表") + public void export(HotCompanyBasicConfigBo bo, HttpServletResponse response) { + List list = hotCompanyBasicConfigService.queryList(bo); + ExcelUtil.exportExcel(list, "企业基础配置", HotCompanyBasicConfigVo.class, response); + } + + /** + * 获取企业基础配置详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:companyBasicConfig:query") + @GetMapping("/{id}") + @Operation(summary = "获取企业基础配置详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotCompanyBasicConfigService.queryById(id)); + } + + /** + * 新增企业基础配置 + */ + //@SaCheckPermission("config:companyBasicConfig:add") + @Log(title = "企业基础配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增企业基础配置") + public R add(@Validated(AddGroup.class) @RequestBody HotCompanyBasicConfigBo bo) { + return toAjax(hotCompanyBasicConfigService.insertByBo(bo)); + } + + /** + * 修改企业基础配置 + */ + //@SaCheckPermission("config:companyBasicConfig:edit") + @Log(title = "企业基础配置", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改企业基础配置") + public R edit(@Validated(EditGroup.class) @RequestBody HotCompanyBasicConfigBo bo) { + return toAjax(hotCompanyBasicConfigService.updateByBo(bo)); + } + + /** + * 删除企业基础配置 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:companyBasicConfig:remove") + @Log(title = "企业基础配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除企业基础配置") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotCompanyBasicConfigService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/companyBasicConfig/domain/HotCompanyBasicConfig.java b/src/main/java/com/hotwj/platform/config/companyBasicConfig/domain/HotCompanyBasicConfig.java new file mode 100644 index 0000000..01bfe4a --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/companyBasicConfig/domain/HotCompanyBasicConfig.java @@ -0,0 +1,84 @@ +package com.hotwj.platform.config.companyBasicConfig.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 企业基础配置对象 hot_company_basic_config + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_company_basic_config") +public class HotCompanyBasicConfig extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 是否补学 0=关闭 1=开启 + */ + private Long makeUpStudyEnabled; + + /** + * 禁学区间是否开启 0=关闭 1=开启 + */ + private Long forbidPeriodEnabled; + + /** + * 禁学开始时间 + */ + private String forbidStartTime; + + /** + * 禁学结束时间 + */ + private String forbidEndTime; + + /** + * 学习短信提醒是否开启 0=关闭 1=开启 + */ + private Long studySmsEnabled; + + /** + * 剩余短信数量 + */ + private Long remainingSmsCount; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 隐患排查规则(1=周 2=月 3=季度 4=半年 5=年) + */ + private Long hiddenDangerRole; + + /** + * 隐患排查规则是否启用(0=否 1=是) + */ + private Long roleIsEnable; + + +} diff --git a/src/main/java/com/hotwj/platform/config/companyBasicConfig/domain/bo/HotCompanyBasicConfigBo.java b/src/main/java/com/hotwj/platform/config/companyBasicConfig/domain/bo/HotCompanyBasicConfigBo.java new file mode 100644 index 0000000..131849d --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/companyBasicConfig/domain/bo/HotCompanyBasicConfigBo.java @@ -0,0 +1,86 @@ +package com.hotwj.platform.config.companyBasicConfig.domain.bo; + +import com.hotwj.platform.config.companyBasicConfig.domain.HotCompanyBasicConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 企业基础配置业务对象 hot_company_basic_config + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotCompanyBasicConfig.class, reverseConvertGenerate = false) +public class HotCompanyBasicConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 是否补学 0=关闭 1=开启 + */ + @NotNull(message = "是否补学 0=关闭 1=开启不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long makeUpStudyEnabled; + + /** + * 禁学区间是否开启 0=关闭 1=开启 + */ + @NotNull(message = "禁学区间是否开启 0=关闭 1=开启不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long forbidPeriodEnabled; + + /** + * 禁学开始时间 + */ + private String forbidStartTime; + + /** + * 禁学结束时间 + */ + private String forbidEndTime; + + /** + * 学习短信提醒是否开启 0=关闭 1=开启 + */ + @NotNull(message = "学习短信提醒是否开启 0=关闭 1=开启不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long studySmsEnabled; + + /** + * 剩余短信数量 + */ + @NotNull(message = "剩余短信数量不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long remainingSmsCount; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + /** + * 隐患排查规则(1=周 2=月 3=季度 4=半年 5=年) + */ + private Long hiddenDangerRole; + + /** + * 隐患排查规则是否启用(0=否 1=是) + */ + private Long roleIsEnable; + + +} diff --git a/src/main/java/com/hotwj/platform/config/companyBasicConfig/domain/vo/HotCompanyBasicConfigVo.java b/src/main/java/com/hotwj/platform/config/companyBasicConfig/domain/vo/HotCompanyBasicConfigVo.java new file mode 100644 index 0000000..16da51b --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/companyBasicConfig/domain/vo/HotCompanyBasicConfigVo.java @@ -0,0 +1,91 @@ +package com.hotwj.platform.config.companyBasicConfig.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.companyBasicConfig.domain.HotCompanyBasicConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 企业基础配置视图对象 hot_company_basic_config + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotCompanyBasicConfig.class) +public class HotCompanyBasicConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 是否补学 0=关闭 1=开启 + */ + @ExcelProperty(value = "是否补学 0=关闭 1=开启") + private Long makeUpStudyEnabled; + + /** + * 禁学区间是否开启 0=关闭 1=开启 + */ + @ExcelProperty(value = "禁学区间是否开启 0=关闭 1=开启") + private Long forbidPeriodEnabled; + + /** + * 禁学开始时间 + */ + @ExcelProperty(value = "禁学开始时间") + private String forbidStartTime; + + /** + * 禁学结束时间 + */ + @ExcelProperty(value = "禁学结束时间") + private String forbidEndTime; + + /** + * 学习短信提醒是否开启 0=关闭 1=开启 + */ + @ExcelProperty(value = "学习短信提醒是否开启 0=关闭 1=开启") + private Long studySmsEnabled; + + /** + * 剩余短信数量 + */ + @ExcelProperty(value = "剩余短信数量") + private Long remainingSmsCount; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + +// private Long isChecked; + /** + * 隐患排查规则(1=周 2=月 3=季度 4=半年 5=年) + */ + private Long hiddenDangerRole; + + /** + * 隐患排查规则是否启用(0=否 1=是) + */ + private Long roleIsEnable; +} diff --git a/src/main/java/com/hotwj/platform/config/companyBasicConfig/mapper/HotCompanyBasicConfigMapper.java b/src/main/java/com/hotwj/platform/config/companyBasicConfig/mapper/HotCompanyBasicConfigMapper.java new file mode 100644 index 0000000..c66fec6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/companyBasicConfig/mapper/HotCompanyBasicConfigMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.companyBasicConfig.mapper; + +import com.hotwj.platform.config.companyBasicConfig.domain.HotCompanyBasicConfig; +import com.hotwj.platform.config.companyBasicConfig.domain.vo.HotCompanyBasicConfigVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 企业基础配置Mapper接口 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Mapper +public interface HotCompanyBasicConfigMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/companyBasicConfig/service/IHotCompanyBasicConfigService.java b/src/main/java/com/hotwj/platform/config/companyBasicConfig/service/IHotCompanyBasicConfigService.java new file mode 100644 index 0000000..3eee036 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/companyBasicConfig/service/IHotCompanyBasicConfigService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.companyBasicConfig.service; + +import com.hotwj.platform.config.companyBasicConfig.domain.bo.HotCompanyBasicConfigBo; +import com.hotwj.platform.config.companyBasicConfig.domain.vo.HotCompanyBasicConfigVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 企业基础配置Service接口 + * + * @author shihongwei + * @date 2025-12-29 + */ +public interface IHotCompanyBasicConfigService { + + /** + * 查询企业基础配置 + * + * @param id 主键 + * @return 企业基础配置 + */ + HotCompanyBasicConfigVo queryById(Long id); + + /** + * 分页查询企业基础配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 企业基础配置分页列表 + */ + TableDataInfo queryPageList(HotCompanyBasicConfigBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的企业基础配置列表 + * + * @param bo 查询条件 + * @return 企业基础配置列表 + */ + List queryList(HotCompanyBasicConfigBo bo); + + /** + * 新增企业基础配置 + * + * @param bo 企业基础配置 + * @return 是否新增成功 + */ + Boolean insertByBo(HotCompanyBasicConfigBo bo); + + /** + * 修改企业基础配置 + * + * @param bo 企业基础配置 + * @return 是否修改成功 + */ + Boolean updateByBo(HotCompanyBasicConfigBo bo); + + /** + * 校验并批量删除企业基础配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/companyBasicConfig/service/impl/HotCompanyBasicConfigServiceImpl.java b/src/main/java/com/hotwj/platform/config/companyBasicConfig/service/impl/HotCompanyBasicConfigServiceImpl.java new file mode 100644 index 0000000..0e380bf --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/companyBasicConfig/service/impl/HotCompanyBasicConfigServiceImpl.java @@ -0,0 +1,213 @@ +package com.hotwj.platform.config.companyBasicConfig.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.companyBasicConfig.domain.HotCompanyBasicConfig; +import com.hotwj.platform.config.companyBasicConfig.domain.bo.HotCompanyBasicConfigBo; +import com.hotwj.platform.config.companyBasicConfig.domain.vo.HotCompanyBasicConfigVo; +import com.hotwj.platform.config.companyBasicConfig.mapper.HotCompanyBasicConfigMapper; +import com.hotwj.platform.config.companyBasicConfig.service.IHotCompanyBasicConfigService; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 企业基础配置Service业务层处理 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotCompanyBasicConfigServiceImpl implements IHotCompanyBasicConfigService { + + private final HotCompanyBasicConfigMapper baseMapper; + private final HotCompanySafetyManagerMapper managerMapper; + private final HotDriverMapper driverMapper; + private final IHotSystemNotificationService notificationService; + + /** + * 查询企业基础配置 + * + * @param id 主键 + * @return 企业基础配置 + */ + @Override + public HotCompanyBasicConfigVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询企业基础配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 企业基础配置分页列表 + */ + @Override + public TableDataInfo queryPageList(HotCompanyBasicConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的企业基础配置列表 + * + * @param bo 查询条件 + * @return 企业基础配置列表 + */ + @Override + public List queryList(HotCompanyBasicConfigBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotCompanyBasicConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotCompanyBasicConfig::getId); + lqw.eq(bo.getCompanyId() != null, HotCompanyBasicConfig::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getMakeUpStudyEnabled() != null, HotCompanyBasicConfig::getMakeUpStudyEnabled, bo.getMakeUpStudyEnabled()); + lqw.eq(bo.getForbidPeriodEnabled() != null, HotCompanyBasicConfig::getForbidPeriodEnabled, bo.getForbidPeriodEnabled()); + lqw.eq(bo.getForbidStartTime() != null, HotCompanyBasicConfig::getForbidStartTime, bo.getForbidStartTime()); + lqw.eq(bo.getForbidEndTime() != null, HotCompanyBasicConfig::getForbidEndTime, bo.getForbidEndTime()); + lqw.eq(bo.getStudySmsEnabled() != null, HotCompanyBasicConfig::getStudySmsEnabled, bo.getStudySmsEnabled()); + lqw.eq(bo.getRemainingSmsCount() != null, HotCompanyBasicConfig::getRemainingSmsCount, bo.getRemainingSmsCount()); + lqw.eq(bo.getIsDeleted() != null, HotCompanyBasicConfig::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增企业基础配置 + * + * @param bo 企业基础配置 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotCompanyBasicConfigBo bo) { + HotCompanyBasicConfig add = MapstructUtils.convert(bo, HotCompanyBasicConfig.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + if (add.getCompanyId() != null + && add.getForbidPeriodEnabled() != null && Long.valueOf(1L).equals(add.getForbidPeriodEnabled()) + && add.getForbidStartTime() != null && add.getForbidEndTime() != null) { + notifyForbidPeriod(add.getCompanyId(), add.getForbidStartTime(), add.getForbidEndTime()); + } + } + return flag; + } + + /** + * 修改企业基础配置 + * + * @param bo 企业基础配置 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotCompanyBasicConfigBo bo) { + HotCompanyBasicConfig update = MapstructUtils.convert(bo, HotCompanyBasicConfig.class); + validEntityBeforeSave(update); + boolean flag = baseMapper.updateById(update) > 0; + if (flag) { + HotCompanyBasicConfig cfg = baseMapper.selectById(update.getId()); + if (cfg != null + && cfg.getCompanyId() != null + && cfg.getForbidPeriodEnabled() != null && Long.valueOf(1L).equals(cfg.getForbidPeriodEnabled()) + && cfg.getForbidStartTime() != null && cfg.getForbidEndTime() != null) { + notifyForbidPeriod(cfg.getCompanyId(), bo.getForbidStartTime(), bo.getForbidEndTime()); + } + } + return flag; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotCompanyBasicConfig entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除企业基础配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + //通知 + private void notifyForbidPeriod(Long companyId, String startTime, String endTime) { + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + List drivers = driverMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotDriver::getCompanyId, companyId) + .eq(HotDriver::getIsDeleted, 0L) + .eq(HotDriver::getStatus, 1L) + ); + String content = "企业已配置禁学期间:" + startTime + " 至 " + endTime + ",期间禁止学习。"; + + if (managers != null && !managers.isEmpty()) { + List receiverIds = managers.stream().map(m -> String.valueOf(m.getId())).toList(); + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("企业管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("禁学期间通知发送失败 companyId={} type=manager", companyId, e); + } + } + if (drivers != null && !drivers.isEmpty()) { + List receiverIds = drivers.stream().map(HotDriver::getId).toList(); + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("企业管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("驾驶员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("禁学期间通知发送失败 companyId={} type=driver", companyId, e); + } + } + } +} diff --git a/src/main/java/com/hotwj/platform/config/companyDeptConfig/controller/HotCompanyDeptConfigController.java b/src/main/java/com/hotwj/platform/config/companyDeptConfig/controller/HotCompanyDeptConfigController.java new file mode 100644 index 0000000..f7c2c5e --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/companyDeptConfig/controller/HotCompanyDeptConfigController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.config.companyDeptConfig.controller; + +import com.hotwj.platform.config.companyDeptConfig.domain.bo.HotCompanyDeptConfigBo; +import com.hotwj.platform.config.companyDeptConfig.domain.vo.HotCompanyDeptConfigVo; +import com.hotwj.platform.config.companyDeptConfig.service.IHotCompanyDeptConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 企业部门配置 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/companyDeptConfig") +@Tag(name = "企业部门配置", description = "企业部门配置管理") +public class HotCompanyDeptConfigController extends BaseController { + + private final IHotCompanyDeptConfigService hotCompanyDeptConfigService; + + /** + * 查询企业部门配置列表 + */ + //@SaCheckPermission("config:companyDeptConfig:list") + @GetMapping("/list") + @Operation(summary = "分页查询企业部门配置列表") + public TableDataInfo list(HotCompanyDeptConfigBo bo, PageQuery pageQuery) { + return hotCompanyDeptConfigService.queryPageList(bo, pageQuery); + } + + /** + * 导出企业部门配置列表 + */ + //@SaCheckPermission("config:companyDeptConfig:export") + @Log(title = "企业部门配置", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出企业部门配置列表") + public void export(HotCompanyDeptConfigBo bo, HttpServletResponse response) { + List list = hotCompanyDeptConfigService.queryList(bo); + ExcelUtil.exportExcel(list, "企业部门配置", HotCompanyDeptConfigVo.class, response); + } + + /** + * 获取企业部门配置详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:companyDeptConfig:query") + @GetMapping("/{id}") + @Operation(summary = "获取企业部门配置详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotCompanyDeptConfigService.queryById(id)); + } + + /** + * 新增企业部门配置 + */ + //@SaCheckPermission("config:companyDeptConfig:add") + @Log(title = "企业部门配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增企业部门配置") + public R add(@Validated(AddGroup.class) @RequestBody HotCompanyDeptConfigBo bo) { + return toAjax(hotCompanyDeptConfigService.insertByBo(bo)); + } + + /** + * 修改企业部门配置 + */ + //@SaCheckPermission("config:companyDeptConfig:edit") + @Log(title = "企业部门配置", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改企业部门配置") + public R edit(@Validated(EditGroup.class) @RequestBody HotCompanyDeptConfigBo bo) { + return toAjax(hotCompanyDeptConfigService.updateByBo(bo)); + } + + /** + * 删除企业部门配置 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:companyDeptConfig:remove") + @Log(title = "企业部门配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除企业部门配置") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotCompanyDeptConfigService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/companyDeptConfig/domain/HotCompanyDeptConfig.java b/src/main/java/com/hotwj/platform/config/companyDeptConfig/domain/HotCompanyDeptConfig.java new file mode 100644 index 0000000..cc13bc6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/companyDeptConfig/domain/HotCompanyDeptConfig.java @@ -0,0 +1,54 @@ +package com.hotwj.platform.config.companyDeptConfig.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 企业部门配置对象 hot_company_dept_config + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_company_dept_config") +public class HotCompanyDeptConfig extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 部门代码,要求大写字母 + */ + private String deptCode; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/companyDeptConfig/domain/bo/HotCompanyDeptConfigBo.java b/src/main/java/com/hotwj/platform/config/companyDeptConfig/domain/bo/HotCompanyDeptConfigBo.java new file mode 100644 index 0000000..4c29983 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/companyDeptConfig/domain/bo/HotCompanyDeptConfigBo.java @@ -0,0 +1,55 @@ +package com.hotwj.platform.config.companyDeptConfig.domain.bo; + +import com.hotwj.platform.config.companyDeptConfig.domain.HotCompanyDeptConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 企业部门配置业务对象 hot_company_dept_config + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotCompanyDeptConfig.class, reverseConvertGenerate = false) +public class HotCompanyDeptConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 部门名称 + */ + @NotBlank(message = "部门名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String deptName; + + /** + * 部门代码,要求大写字母 + */ + @NotBlank(message = "部门代码,要求大写字母不能为空", groups = {AddGroup.class, EditGroup.class}) + private String deptCode; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/companyDeptConfig/domain/vo/HotCompanyDeptConfigVo.java b/src/main/java/com/hotwj/platform/config/companyDeptConfig/domain/vo/HotCompanyDeptConfigVo.java new file mode 100644 index 0000000..3a8d26d --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/companyDeptConfig/domain/vo/HotCompanyDeptConfigVo.java @@ -0,0 +1,58 @@ +package com.hotwj.platform.config.companyDeptConfig.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.companyDeptConfig.domain.HotCompanyDeptConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 企业部门配置视图对象 hot_company_dept_config + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotCompanyDeptConfig.class) +public class HotCompanyDeptConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 部门名称 + */ + @ExcelProperty(value = "部门名称") + private String deptName; + + /** + * 部门代码,要求大写字母 + */ + @ExcelProperty(value = "部门代码,要求大写字母") + private String deptCode; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/companyDeptConfig/mapper/HotCompanyDeptConfigMapper.java b/src/main/java/com/hotwj/platform/config/companyDeptConfig/mapper/HotCompanyDeptConfigMapper.java new file mode 100644 index 0000000..30741e1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/companyDeptConfig/mapper/HotCompanyDeptConfigMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.companyDeptConfig.mapper; + +import com.hotwj.platform.config.companyDeptConfig.domain.HotCompanyDeptConfig; +import com.hotwj.platform.config.companyDeptConfig.domain.vo.HotCompanyDeptConfigVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 企业部门配置Mapper接口 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Mapper +public interface HotCompanyDeptConfigMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/companyDeptConfig/service/IHotCompanyDeptConfigService.java b/src/main/java/com/hotwj/platform/config/companyDeptConfig/service/IHotCompanyDeptConfigService.java new file mode 100644 index 0000000..4349d61 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/companyDeptConfig/service/IHotCompanyDeptConfigService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.companyDeptConfig.service; + +import com.hotwj.platform.config.companyDeptConfig.domain.bo.HotCompanyDeptConfigBo; +import com.hotwj.platform.config.companyDeptConfig.domain.vo.HotCompanyDeptConfigVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 企业部门配置Service接口 + * + * @author shihongwei + * @date 2025-12-29 + */ +public interface IHotCompanyDeptConfigService { + + /** + * 查询企业部门配置 + * + * @param id 主键 + * @return 企业部门配置 + */ + HotCompanyDeptConfigVo queryById(Long id); + + /** + * 分页查询企业部门配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 企业部门配置分页列表 + */ + TableDataInfo queryPageList(HotCompanyDeptConfigBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的企业部门配置列表 + * + * @param bo 查询条件 + * @return 企业部门配置列表 + */ + List queryList(HotCompanyDeptConfigBo bo); + + /** + * 新增企业部门配置 + * + * @param bo 企业部门配置 + * @return 是否新增成功 + */ + Boolean insertByBo(HotCompanyDeptConfigBo bo); + + /** + * 修改企业部门配置 + * + * @param bo 企业部门配置 + * @return 是否修改成功 + */ + Boolean updateByBo(HotCompanyDeptConfigBo bo); + + /** + * 校验并批量删除企业部门配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/companyDeptConfig/service/impl/HotCompanyDeptConfigServiceImpl.java b/src/main/java/com/hotwj/platform/config/companyDeptConfig/service/impl/HotCompanyDeptConfigServiceImpl.java new file mode 100644 index 0000000..c09b3bd --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/companyDeptConfig/service/impl/HotCompanyDeptConfigServiceImpl.java @@ -0,0 +1,144 @@ +package com.hotwj.platform.config.companyDeptConfig.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.companyDeptConfig.domain.HotCompanyDeptConfig; +import com.hotwj.platform.config.companyDeptConfig.domain.bo.HotCompanyDeptConfigBo; +import com.hotwj.platform.config.companyDeptConfig.domain.vo.HotCompanyDeptConfigVo; +import com.hotwj.platform.config.companyDeptConfig.mapper.HotCompanyDeptConfigMapper; +import com.hotwj.platform.config.companyDeptConfig.service.IHotCompanyDeptConfigService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 企业部门配置Service业务层处理 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotCompanyDeptConfigServiceImpl implements IHotCompanyDeptConfigService { + + private final HotCompanyDeptConfigMapper baseMapper; + private final HotCompanySafetyManagerMapper safetyManagerMapper; + + /** + * 查询企业部门配置 + * + * @param id 主键 + * @return 企业部门配置 + */ + @Override + public HotCompanyDeptConfigVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询企业部门配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 企业部门配置分页列表 + */ + @Override + public TableDataInfo queryPageList(HotCompanyDeptConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的企业部门配置列表 + * + * @param bo 查询条件 + * @return 企业部门配置列表 + */ + @Override + public List queryList(HotCompanyDeptConfigBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotCompanyDeptConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotCompanyDeptConfig::getId); + lqw.eq(bo.getCompanyId() != null, HotCompanyDeptConfig::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getDeptName()), HotCompanyDeptConfig::getDeptName, bo.getDeptName()); + lqw.eq(StringUtils.isNotBlank(bo.getDeptCode()), HotCompanyDeptConfig::getDeptCode, bo.getDeptCode()); + lqw.eq(bo.getIsDeleted() != null, HotCompanyDeptConfig::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增企业部门配置 + * + * @param bo 企业部门配置 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotCompanyDeptConfigBo bo) { + HotCompanyDeptConfig add = MapstructUtils.convert(bo, HotCompanyDeptConfig.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改企业部门配置 + * + * @param bo 企业部门配置 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotCompanyDeptConfigBo bo) { + HotCompanyDeptConfig update = MapstructUtils.convert(bo, HotCompanyDeptConfig.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotCompanyDeptConfig entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除企业部门配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + Long count = safetyManagerMapper.selectCount(Wrappers.lambdaQuery() + .in(HotCompanySafetyManager::getDeptId, ids) + .eq(HotCompanySafetyManager::getIsResigned, 0L)); + if (count > 0) { + throw new ServiceException("该部门有未离职人员不可删除"); + } + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/course/controller/HotCourseController.java b/src/main/java/com/hotwj/platform/config/course/controller/HotCourseController.java new file mode 100644 index 0000000..6285069 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/course/controller/HotCourseController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.config.course.controller; + +import com.hotwj.platform.config.course.domain.bo.HotCourseBo; +import com.hotwj.platform.config.course.domain.vo.HotCourseVo; +import com.hotwj.platform.config.course.service.IHotCourseService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 课程库 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/course") +@Tag(name = "课程库", description = "课程库管理") +public class HotCourseController extends BaseController { + + private final IHotCourseService hotCourseService; + + /** + * 查询课程库列表 + */ + //@SaCheckPermission("config:course:list") + @GetMapping("/list") + @Operation(summary = "分页查询课程库列表") + public TableDataInfo list(HotCourseBo bo, PageQuery pageQuery) { + return hotCourseService.queryPageList(bo, pageQuery); + } + + /** + * 导出课程库列表 + */ + //@SaCheckPermission("config:course:export") + @Log(title = "课程库", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出课程库列表") + public void export(HotCourseBo bo, HttpServletResponse response) { + List list = hotCourseService.queryList(bo); + ExcelUtil.exportExcel(list, "课程库", HotCourseVo.class, response); + } + + /** + * 获取课程库详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:course:query") + @GetMapping("/{id}") + @Operation(summary = "获取课程库详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotCourseService.queryById(id)); + } + + /** + * 新增课程库 + */ + //@SaCheckPermission("config:course:add") + @Log(title = "课程库", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增课程库") + public R add(@Validated(AddGroup.class) @RequestBody HotCourseBo bo) { + return toAjax(hotCourseService.insertByBo(bo)); + } + + /** + * 修改课程库 + */ + //@SaCheckPermission("config:course:edit") + @Log(title = "课程库", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改课程库") + public R edit(@Validated(EditGroup.class) @RequestBody HotCourseBo bo) { + return toAjax(hotCourseService.updateByBo(bo)); + } + + /** + * 删除课程库 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:course:remove") + @Log(title = "课程库", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除课程库") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotCourseService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/course/domain/HotCourse.java b/src/main/java/com/hotwj/platform/config/course/domain/HotCourse.java new file mode 100644 index 0000000..23a19fa --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/course/domain/HotCourse.java @@ -0,0 +1,100 @@ +package com.hotwj.platform.config.course.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 课程库对象 hot_course + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_course") +public class HotCourse extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 课程名称 + */ + private String name; + + /** + * 教育类型字典编码 + */ + private String trainingTypeCode; + + /** + * 教育类型子集编码集(JSON/逗号分隔) + */ + private String trainingSubtypeCodes; + + /** + * 课程描述 + */ + private String description; + + /** + * 封面/预览图URL + */ + private String coverUrl; + + /** + * 课程文件URL(PDF/图片) + */ + private String materialUrl; + + /** + * 文件学习时长(秒) + */ + private Long learningDuration; + + /** + * 课时 + */ + private BigDecimal classHours; + + /** + * 课程内容(富文本) + */ + private String content; + + /** + * 状态:1=启用 0=禁用 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/course/domain/bo/HotCourseBo.java b/src/main/java/com/hotwj/platform/config/course/domain/bo/HotCourseBo.java new file mode 100644 index 0000000..487a4a9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/course/domain/bo/HotCourseBo.java @@ -0,0 +1,96 @@ +package com.hotwj.platform.config.course.domain.bo; + +import com.hotwj.platform.config.course.domain.HotCourse; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.math.BigDecimal; + +/** + * 课程库业务对象 hot_course + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotCourse.class, reverseConvertGenerate = false) +public class HotCourseBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 课程名称 + */ + private String name; + + /** + * 教育类型字典编码 + */ + private String trainingTypeCode; + + /** + * 教育类型子集编码集(JSON/逗号分隔) + */ + private String trainingSubtypeCodes; + + /** + * 课程描述 + */ + private String description; + + /** + * 封面/预览图URL + */ + private String coverUrl; + + /** + * 课程文件URL(PDF/图片) + */ + private String materialUrl; + + /** + * 文件学习时长(秒) + */ + private Long learningDuration; + + /** + * 课时 + */ + private BigDecimal classHours; + + /** + * 课程内容(富文本) + */ + private String content; + + /** + * 状态:1=启用 0=禁用 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/course/domain/vo/HotCourseVo.java b/src/main/java/com/hotwj/platform/config/course/domain/vo/HotCourseVo.java new file mode 100644 index 0000000..5b2c3ce --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/course/domain/vo/HotCourseVo.java @@ -0,0 +1,115 @@ +package com.hotwj.platform.config.course.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.course.domain.HotCourse; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 课程库视图对象 hot_course + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotCourse.class) +public class HotCourseVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 课程名称 + */ + @ExcelProperty(value = "课程名称") + private String name; + + /** + * 教育类型字典编码 + */ + @ExcelProperty(value = "教育类型字典编码") + private String trainingTypeCode; + + /** + * 教育类型子集编码集(JSON/逗号分隔) + */ + @ExcelProperty(value = "教育类型子集编码集(JSON/逗号分隔)") + private String trainingSubtypeCodes; + + /** + * 课程描述 + */ + @ExcelProperty(value = "课程描述") + private String description; + + /** + * 封面/预览图URL + */ + @ExcelProperty(value = "封面/预览图URL") + private String coverUrl; + + /** + * 课程文件URL(PDF/图片) + */ + @ExcelProperty(value = "课程文件URL(PDF/图片)") + private String materialUrl; + + /** + * 文件学习时长(秒) + */ + @ExcelProperty(value = "文件学习时长(秒)") + private Long learningDuration; + + /** + * 课时 + */ + @ExcelProperty(value = "课时") + private BigDecimal classHours; + + /** + * 课程内容(富文本) + */ + @ExcelProperty(value = "课程内容(富文本)") + private String content; + + /** + * 状态:1=启用 0=禁用 + */ + @ExcelProperty(value = "状态:1=启用 0=禁用") + private Long status; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + private Date createTime; + +} diff --git a/src/main/java/com/hotwj/platform/config/course/mapper/HotCourseMapper.java b/src/main/java/com/hotwj/platform/config/course/mapper/HotCourseMapper.java new file mode 100644 index 0000000..3af8fec --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/course/mapper/HotCourseMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.course.mapper; + +import com.hotwj.platform.config.course.domain.HotCourse; +import com.hotwj.platform.config.course.domain.vo.HotCourseVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 课程库Mapper接口 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Mapper +public interface HotCourseMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/course/service/IHotCourseService.java b/src/main/java/com/hotwj/platform/config/course/service/IHotCourseService.java new file mode 100644 index 0000000..967f0ae --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/course/service/IHotCourseService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.course.service; + +import com.hotwj.platform.config.course.domain.bo.HotCourseBo; +import com.hotwj.platform.config.course.domain.vo.HotCourseVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 课程库Service接口 + * + * @author shihongwei + * @date 2025-12-23 + */ +public interface IHotCourseService { + + /** + * 查询课程库 + * + * @param id 主键 + * @return 课程库 + */ + HotCourseVo queryById(Long id); + + /** + * 分页查询课程库列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 课程库分页列表 + */ + TableDataInfo queryPageList(HotCourseBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的课程库列表 + * + * @param bo 查询条件 + * @return 课程库列表 + */ + List queryList(HotCourseBo bo); + + /** + * 新增课程库 + * + * @param bo 课程库 + * @return 是否新增成功 + */ + Boolean insertByBo(HotCourseBo bo); + + /** + * 修改课程库 + * + * @param bo 课程库 + * @return 是否修改成功 + */ + Boolean updateByBo(HotCourseBo bo); + + /** + * 校验并批量删除课程库信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/course/service/impl/HotCourseServiceImpl.java b/src/main/java/com/hotwj/platform/config/course/service/impl/HotCourseServiceImpl.java new file mode 100644 index 0000000..0c27958 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/course/service/impl/HotCourseServiceImpl.java @@ -0,0 +1,145 @@ +package com.hotwj.platform.config.course.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.course.domain.HotCourse; +import com.hotwj.platform.config.course.domain.bo.HotCourseBo; +import com.hotwj.platform.config.course.domain.vo.HotCourseVo; +import com.hotwj.platform.config.course.mapper.HotCourseMapper; +import com.hotwj.platform.config.course.service.IHotCourseService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 课程库Service业务层处理 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotCourseServiceImpl implements IHotCourseService { + + private final HotCourseMapper baseMapper; + + /** + * 查询课程库 + * + * @param id 主键 + * @return 课程库 + */ + @Override + public HotCourseVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询课程库列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 课程库分页列表 + */ + @Override + public TableDataInfo queryPageList(HotCourseBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的课程库列表 + * + * @param bo 查询条件 + * @return 课程库列表 + */ + @Override + public List queryList(HotCourseBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotCourseBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotCourse::getId); + lqw.eq(bo.getCompanyId() != null, HotCourse::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getName()), HotCourse::getName, bo.getName()); + lqw.eq(StringUtils.isNotBlank(bo.getTrainingTypeCode()), HotCourse::getTrainingTypeCode, bo.getTrainingTypeCode()); + lqw.eq(StringUtils.isNotBlank(bo.getTrainingSubtypeCodes()), HotCourse::getTrainingSubtypeCodes, bo.getTrainingSubtypeCodes()); + lqw.eq(StringUtils.isNotBlank(bo.getDescription()), HotCourse::getDescription, bo.getDescription()); + lqw.eq(StringUtils.isNotBlank(bo.getCoverUrl()), HotCourse::getCoverUrl, bo.getCoverUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getMaterialUrl()), HotCourse::getMaterialUrl, bo.getMaterialUrl()); + lqw.eq(bo.getLearningDuration() != null, HotCourse::getLearningDuration, bo.getLearningDuration()); + lqw.eq(bo.getClassHours() != null, HotCourse::getClassHours, bo.getClassHours()); + lqw.eq(StringUtils.isNotBlank(bo.getContent()), HotCourse::getContent, bo.getContent()); + lqw.eq(bo.getStatus() != null, HotCourse::getStatus, bo.getStatus()); + lqw.eq(bo.getIsDeleted() != null, HotCourse::getIsDeleted, bo.getIsDeleted()); + lqw.between(params.get("beginTime") != null && params.get("endTime") != null, + HotCourse::getCreateTime, params.get("beginTime"), params.get("endTime")); + return lqw; + } + + /** + * 新增课程库 + * + * @param bo 课程库 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotCourseBo bo) { + HotCourse add = MapstructUtils.convert(bo, HotCourse.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改课程库 + * + * @param bo 课程库 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotCourseBo bo) { + HotCourse update = MapstructUtils.convert(bo, HotCourse.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotCourse entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除课程库信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/courseResource/controller/HotCourseResourceController.java b/src/main/java/com/hotwj/platform/config/courseResource/controller/HotCourseResourceController.java new file mode 100644 index 0000000..2f94df8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/courseResource/controller/HotCourseResourceController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.config.courseResource.controller; + +import com.hotwj.platform.config.courseResource.domain.bo.HotCourseResourceBo; +import com.hotwj.platform.config.courseResource.domain.vo.HotCourseResourceVo; +import com.hotwj.platform.config.courseResource.service.IHotCourseResourceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 课程-资源关联 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/courseResource") +@Tag(name = "课程-资源关联", description = "课程-资源关联管理") +public class HotCourseResourceController extends BaseController { + + private final IHotCourseResourceService hotCourseResourceService; + + /** + * 查询课程-资源关联列表 + */ + //@SaCheckPermission("config:courseResource:list") + @GetMapping("/list") + @Operation(summary = "分页查询课程-资源关联列表") + public TableDataInfo list(HotCourseResourceBo bo, PageQuery pageQuery) { + return hotCourseResourceService.queryPageList(bo, pageQuery); + } + + /** + * 导出课程-资源关联列表 + */ + //@SaCheckPermission("config:courseResource:export") + @Log(title = "课程-资源关联", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出课程-资源关联列表") + public void export(HotCourseResourceBo bo, HttpServletResponse response) { + List list = hotCourseResourceService.queryList(bo); + ExcelUtil.exportExcel(list, "课程-资源关联", HotCourseResourceVo.class, response); + } + + /** + * 获取课程-资源关联详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:courseResource:query") + @GetMapping("/{id}") + @Operation(summary = "获取课程-资源关联详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotCourseResourceService.queryById(id)); + } + + /** + * 新增课程-资源关联 + */ + //@SaCheckPermission("config:courseResource:add") + @Log(title = "课程-资源关联", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增课程-资源关联") + public R add(@Validated(AddGroup.class) @RequestBody HotCourseResourceBo bo) { + return toAjax(hotCourseResourceService.insertByBo(bo)); + } + + /** + * 修改课程-资源关联 + */ + //@SaCheckPermission("config:courseResource:edit") + @Log(title = "课程-资源关联", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改课程-资源关联") + public R edit(@Validated(EditGroup.class) @RequestBody HotCourseResourceBo bo) { + return toAjax(hotCourseResourceService.updateByBo(bo)); + } + + /** + * 删除课程-资源关联 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:courseResource:remove") + @Log(title = "课程-资源关联", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除课程-资源关联") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotCourseResourceService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/courseResource/domain/HotCourseResource.java b/src/main/java/com/hotwj/platform/config/courseResource/domain/HotCourseResource.java new file mode 100644 index 0000000..c230392 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/courseResource/domain/HotCourseResource.java @@ -0,0 +1,57 @@ +package com.hotwj.platform.config.courseResource.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 课程-资源关联对象 hot_course_resource + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_course_resource") +public class HotCourseResource extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 课程ID + */ + private Long courseId; + + /** + * 资源类型:1=视频 2=音频 3=题目 + */ + private Long resourceType; + + /** + * 资源ID(根据类型关联不同表) + */ + private Long resourceId; + + /** + * 排序值 + */ + private Long sortOrder; + + +} diff --git a/src/main/java/com/hotwj/platform/config/courseResource/domain/bo/HotCourseResourceBo.java b/src/main/java/com/hotwj/platform/config/courseResource/domain/bo/HotCourseResourceBo.java new file mode 100644 index 0000000..4368453 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/courseResource/domain/bo/HotCourseResourceBo.java @@ -0,0 +1,58 @@ +package com.hotwj.platform.config.courseResource.domain.bo; + +import com.hotwj.platform.config.courseResource.domain.HotCourseResource; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 课程-资源关联业务对象 hot_course_resource + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotCourseResource.class, reverseConvertGenerate = false) +public class HotCourseResourceBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 课程ID + */ + @NotNull(message = "课程ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long courseId; + + /** + * 资源类型:1=视频 2=音频 3=题目 + */ + @NotNull(message = "资源类型:1=视频 2=音频 3=题目不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long resourceType; + + /** + * 资源ID(根据类型关联不同表) + */ + @NotNull(message = "资源ID(根据类型关联不同表)不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long resourceId; + + /** + * 排序值 + */ + private Long sortOrder; + + +} diff --git a/src/main/java/com/hotwj/platform/config/courseResource/domain/vo/HotCourseResourceVo.java b/src/main/java/com/hotwj/platform/config/courseResource/domain/vo/HotCourseResourceVo.java new file mode 100644 index 0000000..882502c --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/courseResource/domain/vo/HotCourseResourceVo.java @@ -0,0 +1,64 @@ +package com.hotwj.platform.config.courseResource.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.courseResource.domain.HotCourseResource; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 课程-资源关联视图对象 hot_course_resource + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotCourseResource.class) +public class HotCourseResourceVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 课程ID + */ + @ExcelProperty(value = "课程ID") + private Long courseId; + + /** + * 资源类型:1=视频 2=音频 3=题目 + */ + @ExcelProperty(value = "资源类型:1=视频 2=音频 3=题目") + private Long resourceType; + + /** + * 资源ID(根据类型关联不同表) + */ + @ExcelProperty(value = "资源ID(根据类型关联不同表)") + private Long resourceId; + + /** + * 排序值 + */ + @ExcelProperty(value = "排序值") + private Long sortOrder; + + +} diff --git a/src/main/java/com/hotwj/platform/config/courseResource/mapper/HotCourseResourceMapper.java b/src/main/java/com/hotwj/platform/config/courseResource/mapper/HotCourseResourceMapper.java new file mode 100644 index 0000000..ccc4b8d --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/courseResource/mapper/HotCourseResourceMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.courseResource.mapper; + +import com.hotwj.platform.config.courseResource.domain.HotCourseResource; +import com.hotwj.platform.config.courseResource.domain.vo.HotCourseResourceVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 课程-资源关联Mapper接口 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Mapper +public interface HotCourseResourceMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/courseResource/service/IHotCourseResourceService.java b/src/main/java/com/hotwj/platform/config/courseResource/service/IHotCourseResourceService.java new file mode 100644 index 0000000..51240f1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/courseResource/service/IHotCourseResourceService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.courseResource.service; + +import com.hotwj.platform.config.courseResource.domain.bo.HotCourseResourceBo; +import com.hotwj.platform.config.courseResource.domain.vo.HotCourseResourceVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 课程-资源关联Service接口 + * + * @author shihongwei + * @date 2025-12-23 + */ +public interface IHotCourseResourceService { + + /** + * 查询课程-资源关联 + * + * @param id 主键 + * @return 课程-资源关联 + */ + HotCourseResourceVo queryById(Long id); + + /** + * 分页查询课程-资源关联列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 课程-资源关联分页列表 + */ + TableDataInfo queryPageList(HotCourseResourceBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的课程-资源关联列表 + * + * @param bo 查询条件 + * @return 课程-资源关联列表 + */ + List queryList(HotCourseResourceBo bo); + + /** + * 新增课程-资源关联 + * + * @param bo 课程-资源关联 + * @return 是否新增成功 + */ + Boolean insertByBo(HotCourseResourceBo bo); + + /** + * 修改课程-资源关联 + * + * @param bo 课程-资源关联 + * @return 是否修改成功 + */ + Boolean updateByBo(HotCourseResourceBo bo); + + /** + * 校验并批量删除课程-资源关联信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/courseResource/service/impl/HotCourseResourceServiceImpl.java b/src/main/java/com/hotwj/platform/config/courseResource/service/impl/HotCourseResourceServiceImpl.java new file mode 100644 index 0000000..d2e4f3f --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/courseResource/service/impl/HotCourseResourceServiceImpl.java @@ -0,0 +1,135 @@ +package com.hotwj.platform.config.courseResource.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.courseResource.domain.HotCourseResource; +import com.hotwj.platform.config.courseResource.domain.bo.HotCourseResourceBo; +import com.hotwj.platform.config.courseResource.domain.vo.HotCourseResourceVo; +import com.hotwj.platform.config.courseResource.mapper.HotCourseResourceMapper; +import com.hotwj.platform.config.courseResource.service.IHotCourseResourceService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 课程-资源关联Service业务层处理 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotCourseResourceServiceImpl implements IHotCourseResourceService { + + private final HotCourseResourceMapper baseMapper; + + /** + * 查询课程-资源关联 + * + * @param id 主键 + * @return 课程-资源关联 + */ + @Override + public HotCourseResourceVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询课程-资源关联列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 课程-资源关联分页列表 + */ + @Override + public TableDataInfo queryPageList(HotCourseResourceBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的课程-资源关联列表 + * + * @param bo 查询条件 + * @return 课程-资源关联列表 + */ + @Override + public List queryList(HotCourseResourceBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotCourseResourceBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotCourseResource::getId); + lqw.eq(bo.getCompanyId() != null, HotCourseResource::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getCourseId() != null, HotCourseResource::getCourseId, bo.getCourseId()); + lqw.eq(bo.getResourceType() != null, HotCourseResource::getResourceType, bo.getResourceType()); + lqw.eq(bo.getResourceId() != null, HotCourseResource::getResourceId, bo.getResourceId()); + lqw.eq(bo.getSortOrder() != null, HotCourseResource::getSortOrder, bo.getSortOrder()); + return lqw; + } + + /** + * 新增课程-资源关联 + * + * @param bo 课程-资源关联 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotCourseResourceBo bo) { + HotCourseResource add = MapstructUtils.convert(bo, HotCourseResource.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改课程-资源关联 + * + * @param bo 课程-资源关联 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotCourseResourceBo bo) { + HotCourseResource update = MapstructUtils.convert(bo, HotCourseResource.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotCourseResource entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除课程-资源关联信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/dispatchTemplate/controller/HotDispatchTemplateController.java b/src/main/java/com/hotwj/platform/config/dispatchTemplate/controller/HotDispatchTemplateController.java new file mode 100644 index 0000000..a8a38a1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/dispatchTemplate/controller/HotDispatchTemplateController.java @@ -0,0 +1,125 @@ +package com.hotwj.platform.config.dispatchTemplate.controller; + +import com.hotwj.platform.config.dispatchTemplate.domain.bo.HotDispatchTemplateBo; +import com.hotwj.platform.config.dispatchTemplate.domain.vo.HotDispatchTemplateVo; +import com.hotwj.platform.config.dispatchTemplate.service.IHotDispatchTemplateService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 下发模板 + * + * @author shihongwei + * @date 2026-02-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/dispatchTemplate") +@Tag(name = "下发模板", description = "下发模板管理") +public class HotDispatchTemplateController extends BaseController { + + private final IHotDispatchTemplateService hotDispatchTemplateService; + + /** + * 查询下发模板列表 + */ + //@SaCheckPermission("config:dispatchTemplate:list") + @GetMapping("/list") + @Operation(summary = "分页查询下发模板列表") + public TableDataInfo list(HotDispatchTemplateBo bo, PageQuery pageQuery) { + return hotDispatchTemplateService.queryPageList(bo, pageQuery); + } + + /** + * 导出下发模板列表 + */ + //@SaCheckPermission("config:dispatchTemplate:export") + @Log(title = "下发模板", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出下发模板列表") + public void export(HotDispatchTemplateBo bo, HttpServletResponse response) { + List list = hotDispatchTemplateService.queryList(bo); + ExcelUtil.exportExcel(list, "下发模板", HotDispatchTemplateVo.class, response); + } + + /** + * 获取下发模板详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:dispatchTemplate:query") + @GetMapping("/{id}") + @Operation(summary = "获取下发模板详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotDispatchTemplateService.queryById(id)); + } + + /** + * 新增下发模板 + */ + //@SaCheckPermission("config:dispatchTemplate:add") + @Log(title = "下发模板", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增下发模板") + public R add(@Validated(AddGroup.class) @RequestBody HotDispatchTemplateBo bo) { + return toAjax(hotDispatchTemplateService.insertByBo(bo)); + } + + /** + * 修改下发模板 + */ + //@SaCheckPermission("config:dispatchTemplate:edit") + @Log(title = "下发模板", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改下发模板") + public R edit(@Validated(EditGroup.class) @RequestBody HotDispatchTemplateBo bo) { + return toAjax(hotDispatchTemplateService.updateByBo(bo)); + } + + /** + * 删除下发模板 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:dispatchTemplate:remove") + @Log(title = "下发模板", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除下发模板") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotDispatchTemplateService.deleteWithValidByIds(List.of(ids), true)); + } + + @Log(title = "下发模板", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/useDefault") + @Operation(summary = "采用默认配置") + public R useDefault() { + return toAjax(hotDispatchTemplateService.useDefault()); + } +} diff --git a/src/main/java/com/hotwj/platform/config/dispatchTemplate/domain/HotDispatchTemplate.java b/src/main/java/com/hotwj/platform/config/dispatchTemplate/domain/HotDispatchTemplate.java new file mode 100644 index 0000000..895eef6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/dispatchTemplate/domain/HotDispatchTemplate.java @@ -0,0 +1,64 @@ +package com.hotwj.platform.config.dispatchTemplate.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 下发模板对象 hot_dispatch_template + * + * @author shihongwei + * @date 2026-02-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_dispatch_template") +public class HotDispatchTemplate extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + private Long companyId; + + /** + * 模板名称 + */ + private String templateName; + + /** + * 模板类型(字典) + */ + private String templateType; + + /** + * 样式图OSS附件ID + */ + private String styleOssId; + + /** + * 内容(富文本) + */ + private String content; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/dispatchTemplate/domain/bo/HotDispatchTemplateBo.java b/src/main/java/com/hotwj/platform/config/dispatchTemplate/domain/bo/HotDispatchTemplateBo.java new file mode 100644 index 0000000..920020b --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/dispatchTemplate/domain/bo/HotDispatchTemplateBo.java @@ -0,0 +1,59 @@ +package com.hotwj.platform.config.dispatchTemplate.domain.bo; + +import com.hotwj.platform.config.dispatchTemplate.domain.HotDispatchTemplate; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 下发模板业务对象 hot_dispatch_template + * + * @author shihongwei + * @date 2026-02-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotDispatchTemplate.class, reverseConvertGenerate = false) +public class HotDispatchTemplateBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + private Long companyId; + + /** + * 模板名称 + */ + private String templateName; + + /** + * 模板类型(字典) + */ + private String templateType; + + /** + * 样式图OSS附件ID + */ + private String styleOssId; + + /** + * 内容(富文本) + */ + private String content; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/dispatchTemplate/domain/vo/HotDispatchTemplateVo.java b/src/main/java/com/hotwj/platform/config/dispatchTemplate/domain/vo/HotDispatchTemplateVo.java new file mode 100644 index 0000000..ea1de9d --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/dispatchTemplate/domain/vo/HotDispatchTemplateVo.java @@ -0,0 +1,87 @@ +package com.hotwj.platform.config.dispatchTemplate.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.dispatchTemplate.domain.HotDispatchTemplate; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 下发模板视图对象 hot_dispatch_template + * + * @author shihongwei + * @date 2026-02-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotDispatchTemplate.class) +public class HotDispatchTemplateVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "由=应用层保证必填") + private Long companyId; + + /** + * 模板名称 + */ + @ExcelProperty(value = "模板名称") + private String templateName; + + /** + * 模板类型(字典) + */ + @ExcelProperty(value = "模板类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "字=典") + private String templateType; + + /** + * 样式图OSS附件ID + */ + @ExcelProperty(value = "样式图OSS附件ID") + private String styleOssId; + + /** + * 内容(富文本) + */ + @ExcelProperty(value = "内容", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "富=文本") + private String content; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/dispatchTemplate/mapper/HotDispatchTemplateMapper.java b/src/main/java/com/hotwj/platform/config/dispatchTemplate/mapper/HotDispatchTemplateMapper.java new file mode 100644 index 0000000..50cd646 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/dispatchTemplate/mapper/HotDispatchTemplateMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.dispatchTemplate.mapper; + +import com.hotwj.platform.config.dispatchTemplate.domain.HotDispatchTemplate; +import com.hotwj.platform.config.dispatchTemplate.domain.vo.HotDispatchTemplateVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 下发模板Mapper接口 + * + * @author shihongwei + * @date 2026-02-15 + */ +@Mapper +public interface HotDispatchTemplateMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/dispatchTemplate/service/IHotDispatchTemplateService.java b/src/main/java/com/hotwj/platform/config/dispatchTemplate/service/IHotDispatchTemplateService.java new file mode 100644 index 0000000..b389d45 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/dispatchTemplate/service/IHotDispatchTemplateService.java @@ -0,0 +1,70 @@ +package com.hotwj.platform.config.dispatchTemplate.service; + +import com.hotwj.platform.config.dispatchTemplate.domain.bo.HotDispatchTemplateBo; +import com.hotwj.platform.config.dispatchTemplate.domain.vo.HotDispatchTemplateVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 下发模板Service接口 + * + * @author shihongwei + * @date 2026-02-15 + */ +public interface IHotDispatchTemplateService { + + /** + * 查询下发模板 + * + * @param id 主键 + * @return 下发模板 + */ + HotDispatchTemplateVo queryById(Long id); + + /** + * 分页查询下发模板列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 下发模板分页列表 + */ + TableDataInfo queryPageList(HotDispatchTemplateBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的下发模板列表 + * + * @param bo 查询条件 + * @return 下发模板列表 + */ + List queryList(HotDispatchTemplateBo bo); + + /** + * 新增下发模板 + * + * @param bo 下发模板 + * @return 是否新增成功 + */ + Boolean insertByBo(HotDispatchTemplateBo bo); + + /** + * 修改下发模板 + * + * @param bo 下发模板 + * @return 是否修改成功 + */ + Boolean updateByBo(HotDispatchTemplateBo bo); + + /** + * 校验并批量删除下发模板信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + Boolean useDefault(); +} diff --git a/src/main/java/com/hotwj/platform/config/dispatchTemplate/service/impl/HotDispatchTemplateServiceImpl.java b/src/main/java/com/hotwj/platform/config/dispatchTemplate/service/impl/HotDispatchTemplateServiceImpl.java new file mode 100644 index 0000000..63ffea7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/dispatchTemplate/service/impl/HotDispatchTemplateServiceImpl.java @@ -0,0 +1,186 @@ +package com.hotwj.platform.config.dispatchTemplate.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.dispatchTemplate.domain.HotDispatchTemplate; +import com.hotwj.platform.config.dispatchTemplate.domain.bo.HotDispatchTemplateBo; +import com.hotwj.platform.config.dispatchTemplate.domain.vo.HotDispatchTemplateVo; +import com.hotwj.platform.config.dispatchTemplate.mapper.HotDispatchTemplateMapper; +import com.hotwj.platform.config.dispatchTemplate.service.IHotDispatchTemplateService; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 下发模板Service业务层处理 + * + * @author shihongwei + * @date 2026-02-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotDispatchTemplateServiceImpl implements IHotDispatchTemplateService { + + private final HotDispatchTemplateMapper baseMapper; + private final ISysCompanyService sysCompanyService; + + /** + * 查询下发模板 + * + * @param id 主键 + * @return 下发模板 + */ + @Override + public HotDispatchTemplateVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询下发模板列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 下发模板分页列表 + */ + @Override + public TableDataInfo queryPageList(HotDispatchTemplateBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的下发模板列表 + * + * @param bo 查询条件 + * @return 下发模板列表 + */ + @Override + public List queryList(HotDispatchTemplateBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean useDefault() { + Long companyId = LoginHelper.getCompanyId(); + if (companyId == null) { + throw new ServiceException("当前登录用户无公司信息"); + } + SysCompanyVo company = sysCompanyService.queryById(companyId); + if (company == null) { + throw new ServiceException("公司不存在"); + } + Long count = baseMapper.selectCount( + Wrappers.lambdaQuery(HotDispatchTemplate.class) + .eq(HotDispatchTemplate::getCompanyId, companyId) + ); + if (count != null && count > 0) { + throw new ServiceException("当前公司已存在下发模板配置,请先清除已存在数据"); + } + List defaultConfigs = baseMapper.selectList( + Wrappers.lambdaQuery(HotDispatchTemplate.class) + .eq(HotDispatchTemplate::getCompanyId, 1L) + ); + if (CollUtil.isEmpty(defaultConfigs)) { + throw new ServiceException("默认配置不存在,请联系管理员"); + } + List newConfigs = new ArrayList<>(); + for (HotDispatchTemplate cfg : defaultConfigs) { + HotDispatchTemplate newCfg = new HotDispatchTemplate(); + newCfg.setCompanyId(companyId); + newCfg.setTemplateName(cfg.getTemplateName()); + newCfg.setTemplateType(cfg.getTemplateType()); + newCfg.setStyleOssId(cfg.getStyleOssId()); + newCfg.setContent(cfg.getContent()); + newCfg.setIsDeleted(0L); + newConfigs.add(newCfg); + } + return baseMapper.insertBatch(newConfigs); + } + + private LambdaQueryWrapper buildQueryWrapper(HotDispatchTemplateBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotDispatchTemplate::getId); + lqw.eq(bo.getCompanyId() != null, HotDispatchTemplate::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getTemplateName()), HotDispatchTemplate::getTemplateName, bo.getTemplateName()); + lqw.eq(StringUtils.isNotBlank(bo.getTemplateType()), HotDispatchTemplate::getTemplateType, bo.getTemplateType()); + lqw.eq(bo.getStyleOssId() != null, HotDispatchTemplate::getStyleOssId, bo.getStyleOssId()); + lqw.eq(StringUtils.isNotBlank(bo.getContent()), HotDispatchTemplate::getContent, bo.getContent()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotDispatchTemplate::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotDispatchTemplate::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotDispatchTemplate::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增下发模板 + * + * @param bo 下发模板 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotDispatchTemplateBo bo) { + HotDispatchTemplate add = MapstructUtils.convert(bo, HotDispatchTemplate.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改下发模板 + * + * @param bo 下发模板 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotDispatchTemplateBo bo) { + HotDispatchTemplate update = MapstructUtils.convert(bo, HotDispatchTemplate.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotDispatchTemplate entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除下发模板信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/controller/HotDriverAnnualAssessmentConfigController.java b/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/controller/HotDriverAnnualAssessmentConfigController.java new file mode 100644 index 0000000..d4a8859 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/controller/HotDriverAnnualAssessmentConfigController.java @@ -0,0 +1,129 @@ +package com.hotwj.platform.config.driverAnnualAssessmentConfig.controller; + +import com.hotwj.platform.config.driverAnnualAssessmentConfig.domain.bo.HotDriverAnnualAssessmentConfigBo; +import com.hotwj.platform.config.driverAnnualAssessmentConfig.domain.vo.HotDriverAnnualAssessmentConfigVo; +import com.hotwj.platform.config.driverAnnualAssessmentConfig.service.IHotDriverAnnualAssessmentConfigService; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 驾驶员年度考核配置 + * + * @author shihongwei + * @date 2025-12-16 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/driverAnnualAssessmentConfig") +@Tag(name = "驾驶员年度考核配置", description = "年度考核配置管理") +public class HotDriverAnnualAssessmentConfigController extends BaseController { + + private final IHotDriverAnnualAssessmentConfigService hotDriverAnnualAssessmentConfigService; + + /** + * 查询驾驶员年度考核配置列表 + */ + //@SaCheckPermission("config:driverAnnualAssessmentConfig:list") + @GetMapping("/list") + public TableDataInfo list(HotDriverAnnualAssessmentConfigBo bo, PageQuery pageQuery) { + return hotDriverAnnualAssessmentConfigService.queryPageList(bo, pageQuery); + } + + /** + * 导出驾驶员年度考核配置列表 + */ + //@SaCheckPermission("config:driverAnnualAssessmentConfig:export") + @Log(title = "驾驶员年度考核配置", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HotDriverAnnualAssessmentConfigBo bo, HttpServletResponse response) { + List list = hotDriverAnnualAssessmentConfigService.queryList(bo); + ExcelUtil.exportExcel(list, "驾驶员年度考核配置", HotDriverAnnualAssessmentConfigVo.class, response); + } + + /** + * 获取驾驶员年度考核配置详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:driverAnnualAssessmentConfig:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotDriverAnnualAssessmentConfigService.queryById(id)); + } + + /** + * 新增驾驶员年度考核配置 + */ + //@SaCheckPermission("config:driverAnnualAssessmentConfig:add") + @Log(title = "驾驶员年度考核配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody HotDriverAnnualAssessmentConfigBo bo) { + return toAjax(hotDriverAnnualAssessmentConfigService.insertByBo(bo)); + } + + /** + * 修改驾驶员年度考核配置 + */ + //@SaCheckPermission("config:driverAnnualAssessmentConfig:edit") + @Log(title = "驾驶员年度考核配置", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody HotDriverAnnualAssessmentConfigBo bo) { + return toAjax(hotDriverAnnualAssessmentConfigService.updateByBo(bo)); + } + + /** + * 删除驾驶员年度考核配置 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:driverAnnualAssessmentConfig:remove") + @Log(title = "驾驶员年度考核配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotDriverAnnualAssessmentConfigService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 采用默认配置 + */ + //@SaCheckPermission("config:driverAnnualAssessmentConfig:add") + @Log(title = "驾驶员年度考核配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/useDefault") + public R useDefault() { + return toAjax(hotDriverAnnualAssessmentConfigService.useDefault()); + } + + /** + * 一键清空 + */ + //@SaCheckPermission("config:driverAnnualAssessmentConfig:remove") + @Log(title = "驾驶员年度考核配置", businessType = BusinessType.DELETE) + @RepeatSubmit() + @PostMapping("/clearAll") + public R clearAll() { + return toAjax(hotDriverAnnualAssessmentConfigService.clearAll()); + } +} diff --git a/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/domain/HotDriverAnnualAssessmentConfig.java b/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/domain/HotDriverAnnualAssessmentConfig.java new file mode 100644 index 0000000..770102d --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/domain/HotDriverAnnualAssessmentConfig.java @@ -0,0 +1,62 @@ +package com.hotwj.platform.config.driverAnnualAssessmentConfig.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 驾驶员年度考核配置对象 hot_driver_annual_assessment_config + * + * @author shihongwei + * @date 2025-12-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_driver_annual_assessment_config") +public class HotDriverAnnualAssessmentConfig extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 考核内容 + */ + private String content; + + /** + * 分数 + */ + private Long score; + + /** + * 类型 + */ + private String category; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 是否启用 + */ + private Long isEnabled; +} diff --git a/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/domain/bo/HotDriverAnnualAssessmentConfigBo.java b/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/domain/bo/HotDriverAnnualAssessmentConfigBo.java new file mode 100644 index 0000000..6cc6087 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/domain/bo/HotDriverAnnualAssessmentConfigBo.java @@ -0,0 +1,58 @@ +package com.hotwj.platform.config.driverAnnualAssessmentConfig.domain.bo; + +import com.hotwj.platform.config.driverAnnualAssessmentConfig.domain.HotDriverAnnualAssessmentConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 驾驶员年度考核配置业务对象 hot_driver_annual_assessment_config + * + * @author shihongwei + * @date 2025-12-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotDriverAnnualAssessmentConfig.class, reverseConvertGenerate = false) +public class HotDriverAnnualAssessmentConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 考核内容 + */ + private String content; + + /** + * 分数 + */ + private Long score; + + /** + * 类型 + */ + private String category; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + + /** + * 是否启用 + */ + private Long isEnabled; +} diff --git a/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/domain/vo/HotDriverAnnualAssessmentConfigVo.java b/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/domain/vo/HotDriverAnnualAssessmentConfigVo.java new file mode 100644 index 0000000..24cf632 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/domain/vo/HotDriverAnnualAssessmentConfigVo.java @@ -0,0 +1,67 @@ +package com.hotwj.platform.config.driverAnnualAssessmentConfig.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.driverAnnualAssessmentConfig.domain.HotDriverAnnualAssessmentConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 驾驶员年度考核配置视图对象 hot_driver_annual_assessment_config + * + * @author shihongwei + * @date 2025-12-16 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotDriverAnnualAssessmentConfig.class) +public class HotDriverAnnualAssessmentConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 考核内容 + */ + @ExcelProperty(value = "考核内容") + private String content; + + /** + * 分数 + */ + @ExcelProperty(value = "分数") + private Long score; + + /** + * 类型 + */ + @ExcelProperty(value = "类型") + private String category; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 是否启用 + */ + private Long isEnabled; +} diff --git a/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/mapper/HotDriverAnnualAssessmentConfigMapper.java b/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/mapper/HotDriverAnnualAssessmentConfigMapper.java new file mode 100644 index 0000000..031914b --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/mapper/HotDriverAnnualAssessmentConfigMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.driverAnnualAssessmentConfig.mapper; + +import com.hotwj.platform.config.driverAnnualAssessmentConfig.domain.HotDriverAnnualAssessmentConfig; +import com.hotwj.platform.config.driverAnnualAssessmentConfig.domain.vo.HotDriverAnnualAssessmentConfigVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 驾驶员年度考核配置Mapper接口 + * + * @author shihongwei + * @date 2025-12-16 + */ +@Mapper +public interface HotDriverAnnualAssessmentConfigMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/service/IHotDriverAnnualAssessmentConfigService.java b/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/service/IHotDriverAnnualAssessmentConfigService.java new file mode 100644 index 0000000..46b3f35 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/service/IHotDriverAnnualAssessmentConfigService.java @@ -0,0 +1,82 @@ +package com.hotwj.platform.config.driverAnnualAssessmentConfig.service; + +import com.hotwj.platform.config.driverAnnualAssessmentConfig.domain.bo.HotDriverAnnualAssessmentConfigBo; +import com.hotwj.platform.config.driverAnnualAssessmentConfig.domain.vo.HotDriverAnnualAssessmentConfigVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 驾驶员年度考核配置Service接口 + * + * @author shihongwei + * @date 2025-12-16 + */ +public interface IHotDriverAnnualAssessmentConfigService { + + /** + * 查询驾驶员年度考核配置 + * + * @param id 主键 + * @return 驾驶员年度考核配置 + */ + HotDriverAnnualAssessmentConfigVo queryById(Long id); + + /** + * 分页查询驾驶员年度考核配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员年度考核配置分页列表 + */ + TableDataInfo queryPageList(HotDriverAnnualAssessmentConfigBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的驾驶员年度考核配置列表 + * + * @param bo 查询条件 + * @return 驾驶员年度考核配置列表 + */ + List queryList(HotDriverAnnualAssessmentConfigBo bo); + + /** + * 新增驾驶员年度考核配置 + * + * @param bo 驾驶员年度考核配置 + * @return 是否新增成功 + */ + Boolean insertByBo(HotDriverAnnualAssessmentConfigBo bo); + + /** + * 修改驾驶员年度考核配置 + * + * @param bo 驾驶员年度考核配置 + * @return 是否修改成功 + */ + Boolean updateByBo(HotDriverAnnualAssessmentConfigBo bo); + + /** + * 校验并批量删除驾驶员年度考核配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 采用默认配置 + * + * @return 结果 + */ + Boolean useDefault(); + + /** + * 清空当前公司所有配置 + * + * @return 是否成功 + */ + Boolean clearAll(); +} diff --git a/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/service/impl/HotDriverAnnualAssessmentConfigServiceImpl.java b/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/service/impl/HotDriverAnnualAssessmentConfigServiceImpl.java new file mode 100644 index 0000000..0a5bd39 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/driverAnnualAssessmentConfig/service/impl/HotDriverAnnualAssessmentConfigServiceImpl.java @@ -0,0 +1,268 @@ +package com.hotwj.platform.config.driverAnnualAssessmentConfig.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.driverAnnualAssessmentConfig.domain.HotDriverAnnualAssessmentConfig; +import com.hotwj.platform.config.driverAnnualAssessmentConfig.domain.bo.HotDriverAnnualAssessmentConfigBo; +import com.hotwj.platform.config.driverAnnualAssessmentConfig.domain.vo.HotDriverAnnualAssessmentConfigVo; +import com.hotwj.platform.config.driverAnnualAssessmentConfig.mapper.HotDriverAnnualAssessmentConfigMapper; +import com.hotwj.platform.config.driverAnnualAssessmentConfig.service.IHotDriverAnnualAssessmentConfigService; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员年度考核配置Service业务层处理 + * + * @author shihongwei + * @date 2025-12-16 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotDriverAnnualAssessmentConfigServiceImpl implements IHotDriverAnnualAssessmentConfigService { + + private static final long ENABLED_STATUS = 1L; + private static final long DISABLED_STATUS = 0L; + private static final long MAX_ENABLED_TOTAL_SCORE = 100L; + + private final HotDriverAnnualAssessmentConfigMapper baseMapper; + private final ISysCompanyService sysCompanyService; + + /** + * 查询驾驶员年度考核配置 + * + * @param id 主键 + * @return 驾驶员年度考核配置 + */ + @Override + public HotDriverAnnualAssessmentConfigVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean useDefault() { + Long companyId = LoginHelper.getCompanyId(); + if (companyId == null) { + throw new ServiceException("当前登录用户无公司信息"); + } + + SysCompanyVo company = sysCompanyService.queryById(companyId); + if (company == null) { + throw new ServiceException("公司不存在"); + } + + // 校验当前公司是否已有配置 + Long count = baseMapper.selectCount( + Wrappers.lambdaQuery(HotDriverAnnualAssessmentConfig.class) + .eq(HotDriverAnnualAssessmentConfig::getCompanyId, companyId) + ); + if (count != null && count > 0) { + throw new ServiceException("当前公司已存在考核配置,请先清除已存在数据"); + } + + // 获取总部配置 (companyId = 1) + List defaultConfigs = baseMapper.selectList( + Wrappers.lambdaQuery(HotDriverAnnualAssessmentConfig.class) + .eq(HotDriverAnnualAssessmentConfig::getCompanyId, 1L) + ); + + if (CollUtil.isEmpty(defaultConfigs)) { + throw new ServiceException("默认配置不存在,请联系管理员"); + } + + List newConfigs = new ArrayList<>(); + for (HotDriverAnnualAssessmentConfig cfg : defaultConfigs) { + HotDriverAnnualAssessmentConfig newCfg = new HotDriverAnnualAssessmentConfig(); + // 复制属性,排除ID和创建时间等 + newCfg.setCompanyId(companyId); + newCfg.setContent(cfg.getContent()); + newCfg.setScore(cfg.getScore()); + newCfg.setCategory(cfg.getCategory()); + newCfg.setIsDeleted(0L); + newConfigs.add(newCfg); + } + + return baseMapper.insertBatch(newConfigs); + } + + + /** + * 分页查询驾驶员年度考核配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员年度考核配置分页列表 + */ + @Override + public TableDataInfo queryPageList(HotDriverAnnualAssessmentConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的驾驶员年度考核配置列表 + * + * @param bo 查询条件 + * @return 驾驶员年度考核配置列表 + */ + @Override + public List queryList(HotDriverAnnualAssessmentConfigBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotDriverAnnualAssessmentConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotDriverAnnualAssessmentConfig::getId); + lqw.eq(bo.getCompanyId() != null, HotDriverAnnualAssessmentConfig::getCompanyId, bo.getCompanyId()); + lqw.eq(StringUtils.isNotBlank(bo.getContent()), HotDriverAnnualAssessmentConfig::getContent, bo.getContent()); + lqw.eq(bo.getScore() != null, HotDriverAnnualAssessmentConfig::getScore, bo.getScore()); + lqw.eq(StringUtils.isNotBlank(bo.getCategory()), HotDriverAnnualAssessmentConfig::getCategory, bo.getCategory()); + lqw.eq(bo.getIsDeleted() != null, HotDriverAnnualAssessmentConfig::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增驾驶员年度考核配置 + * + * @param bo 驾驶员年度考核配置 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotDriverAnnualAssessmentConfigBo bo) { + HotDriverAnnualAssessmentConfig add = MapstructUtils.convert(bo, HotDriverAnnualAssessmentConfig.class); + Long companyId = resolveCompanyId(add.getCompanyId()); + add.setCompanyId(companyId); + applyInsertEnabledRule(add, companyId); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改驾驶员年度考核配置 + * + * @param bo 驾驶员年度考核配置 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotDriverAnnualAssessmentConfigBo bo) { + HotDriverAnnualAssessmentConfig update = MapstructUtils.convert(bo, HotDriverAnnualAssessmentConfig.class); + Long companyId = resolveCompanyId(update.getCompanyId()); + update.setCompanyId(companyId); + validateUpdateEnabledRule(update, companyId); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotDriverAnnualAssessmentConfig entity) { + //TODO 做一些数据校验,如唯一约束 + } + + private Long resolveCompanyId(Long companyId) { + if (companyId != null) { + return companyId; + } + Long loginCompanyId = LoginHelper.getCompanyId(); + if (loginCompanyId == null) { + throw new ServiceException("当前登录用户无公司信息"); + } + return loginCompanyId; + } + + private void applyInsertEnabledRule(HotDriverAnnualAssessmentConfig entity, Long companyId) { + Long isEnabled = entity.getIsEnabled(); + if (isEnabled == null || isEnabled != ENABLED_STATUS) { + if (isEnabled == null) { + entity.setIsEnabled(DISABLED_STATUS); + } + return; + } + long enabledTotalScore = getEnabledTotalScore(companyId, null) + safeScore(entity.getScore()); + if (enabledTotalScore > MAX_ENABLED_TOTAL_SCORE) { + entity.setIsEnabled(DISABLED_STATUS); + } + } + + private void validateUpdateEnabledRule(HotDriverAnnualAssessmentConfig entity, Long companyId) { + if (entity.getIsEnabled() == null || entity.getIsEnabled() != ENABLED_STATUS) { + return; + } + long enabledTotalScore = getEnabledTotalScore(companyId, entity.getId()) + safeScore(entity.getScore()); + if (enabledTotalScore > MAX_ENABLED_TOTAL_SCORE) { + throw new ServiceException("启用失败:当前启用项总分值不能超过100分"); + } + } + + private long getEnabledTotalScore(Long companyId, Long excludeId) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(HotDriverAnnualAssessmentConfig::getCompanyId, companyId); + lqw.eq(HotDriverAnnualAssessmentConfig::getIsEnabled, ENABLED_STATUS); + lqw.ne(excludeId != null, HotDriverAnnualAssessmentConfig::getId, excludeId); + List enabledConfigs = baseMapper.selectList(lqw); + return enabledConfigs.stream() + .map(HotDriverAnnualAssessmentConfig::getScore) + .filter(score -> score != null) + .mapToLong(Long::longValue) + .sum(); + } + + private long safeScore(Long score) { + return score == null ? 0L : score; + } + + /** + * 校验并批量删除驾驶员年度考核配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean clearAll() { + Long companyId = LoginHelper.getCompanyId(); + if (companyId == null) { + throw new ServiceException("当前登录用户无公司信息"); + } + // 逻辑删除当前公司所有配置 + return baseMapper.delete( + Wrappers.lambdaQuery(HotDriverAnnualAssessmentConfig.class) + .eq(HotDriverAnnualAssessmentConfig::getCompanyId, companyId) + ) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/examPaper/controller/HotExamPaperController.java b/src/main/java/com/hotwj/platform/config/examPaper/controller/HotExamPaperController.java new file mode 100644 index 0000000..dc18b8a --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaper/controller/HotExamPaperController.java @@ -0,0 +1,127 @@ +package com.hotwj.platform.config.examPaper.controller; + +import com.hotwj.platform.config.examPaper.domain.bo.ExamPaperSaveQuestionsBo; +import com.hotwj.platform.config.examPaper.domain.bo.HotExamPaperBo; +import com.hotwj.platform.config.examPaper.domain.vo.ExamPaperSaveQuestionsResultVo; +import com.hotwj.platform.config.examPaper.domain.vo.HotExamPaperVo; +import com.hotwj.platform.config.examPaper.service.IHotExamPaperService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 试卷库 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/examPaper") +@Tag(name = "试卷库", description = "试卷库管理") +public class HotExamPaperController extends BaseController { + + private final IHotExamPaperService hotExamPaperService; + + /** + * 查询试卷库列表 + */ + //@SaCheckPermission("config:examPaper:list") + @GetMapping("/list") + @Operation(summary = "分页查询试卷库列表") + public TableDataInfo list(HotExamPaperBo bo, PageQuery pageQuery) { + return hotExamPaperService.queryPageList(bo, pageQuery); + } + + /** + * 导出试卷库列表 + */ + //@SaCheckPermission("config:examPaper:export") + @Log(title = "试卷库", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出试卷库列表") + public void export(HotExamPaperBo bo, HttpServletResponse response) { + List list = hotExamPaperService.queryList(bo); + ExcelUtil.exportExcel(list, "试卷库", HotExamPaperVo.class, response); + } + + /** + * 获取试卷库详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:examPaper:query") + @GetMapping("/{id}") + @Operation(summary = "获取试卷库详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotExamPaperService.queryById(id)); + } + + /** + * 新增试卷库 + */ + //@SaCheckPermission("config:examPaper:add") + @Log(title = "试卷库", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增试卷库") + public R add(@Validated(AddGroup.class) @RequestBody HotExamPaperBo bo) { + return toAjax(hotExamPaperService.insertByBo(bo)); + } + + /** + * 修改试卷库 + */ + //@SaCheckPermission("config:examPaper:edit") + @Log(title = "试卷库", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改试卷库") + public R edit(@Validated(EditGroup.class) @RequestBody HotExamPaperBo bo) { + return toAjax(hotExamPaperService.updateByBo(bo)); + } + + @Log(title = "试卷库", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/saveQuestions") + @Operation(summary = "试卷题目批量保存") + public R saveQuestions(@Validated @RequestBody ExamPaperSaveQuestionsBo bo) { + return R.ok(hotExamPaperService.saveQuestions(bo)); + } + + /** + * 删除试卷库 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:examPaper:remove") + @Log(title = "试卷库", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除试卷库") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotExamPaperService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/examPaper/domain/HotExamPaper.java b/src/main/java/com/hotwj/platform/config/examPaper/domain/HotExamPaper.java new file mode 100644 index 0000000..0ba8387 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaper/domain/HotExamPaper.java @@ -0,0 +1,89 @@ +package com.hotwj.platform.config.examPaper.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 试卷库对象 hot_exam_paper + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_exam_paper") +public class HotExamPaper extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 试卷名称 + */ + private String name; + + /** + * 试卷分类字典编码 + */ + private String categoryCode; + + /** + * 教育类型字典编码 + */ + private String trainingTypeCode; + + /** + * 教育类型子集编码集(JSON/逗号分隔) + */ + private String trainingSubtypeCodes; + + /** + * 检测题数 + */ + private Long questionCount; + + /** + * 试卷总分 + */ + private Long totalScore; + + /** + * 及格分数 + */ + private Long passScore; + + /** + * 状态:1=启用 0=禁用 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/examPaper/domain/bo/ExamPaperSaveQuestionsBo.java b/src/main/java/com/hotwj/platform/config/examPaper/domain/bo/ExamPaperSaveQuestionsBo.java new file mode 100644 index 0000000..6294feb --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaper/domain/bo/ExamPaperSaveQuestionsBo.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.examPaper.domain.bo; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Data +public class ExamPaperSaveQuestionsBo { + @NotNull(message = "试卷ID不能为空") + private Long paperId; + + @NotNull(message = "题目类型不能为空") + private String questionType; + + private List questions; +} diff --git a/src/main/java/com/hotwj/platform/config/examPaper/domain/bo/HotExamPaperBo.java b/src/main/java/com/hotwj/platform/config/examPaper/domain/bo/HotExamPaperBo.java new file mode 100644 index 0000000..59fb032 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaper/domain/bo/HotExamPaperBo.java @@ -0,0 +1,84 @@ +package com.hotwj.platform.config.examPaper.domain.bo; + +import com.hotwj.platform.config.examPaper.domain.HotExamPaper; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 试卷库业务对象 hot_exam_paper + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotExamPaper.class, reverseConvertGenerate = false) +public class HotExamPaperBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 试卷名称 + */ + private String name; + + /** + * 试卷分类字典编码 + */ + private String categoryCode; + + /** + * 教育类型字典编码 + */ + private String trainingTypeCode; + + /** + * 教育类型子集编码集(JSON/逗号分隔) + */ + private String trainingSubtypeCodes; + + /** + * 检测题数 + */ + private Long questionCount; + + /** + * 试卷总分 + */ + private Long totalScore; + + /** + * 及格分数 + */ + private Long passScore; + + /** + * 状态:1=启用 0=禁用 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/examPaper/domain/vo/ExamPaperSaveQuestionsResultVo.java b/src/main/java/com/hotwj/platform/config/examPaper/domain/vo/ExamPaperSaveQuestionsResultVo.java new file mode 100644 index 0000000..dc6cfa3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaper/domain/vo/ExamPaperSaveQuestionsResultVo.java @@ -0,0 +1,16 @@ +package com.hotwj.platform.config.examPaper.domain.vo; + +import lombok.Data; + +@Data +public class ExamPaperSaveQuestionsResultVo { + private Long totalScore; + private Long passScore; + private Long questionCount; + private Long singleChoiceCount; + private Long multiChoiceCount; + private Long judgeCount; + private Long singleChoiceScore; + private Long multiChoiceScore; + private Long judgeScore; +} diff --git a/src/main/java/com/hotwj/platform/config/examPaper/domain/vo/HotExamPaperVo.java b/src/main/java/com/hotwj/platform/config/examPaper/domain/vo/HotExamPaperVo.java new file mode 100644 index 0000000..361f147 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaper/domain/vo/HotExamPaperVo.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.config.examPaper.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.examPaper.domain.HotExamPaper; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 试卷库视图对象 hot_exam_paper + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotExamPaper.class) +public class HotExamPaperVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 试卷名称 + */ + @ExcelProperty(value = "试卷名称") + private String name; + + /** + * 试卷分类字典编码 + */ + @ExcelProperty(value = "试卷分类字典编码") + private String categoryCode; + + /** + * 教育类型字典编码 + */ + @ExcelProperty(value = "教育类型字典编码") + private String trainingTypeCode; + + /** + * 教育类型子集编码集(JSON/逗号分隔) + */ + @ExcelProperty(value = "教育类型子集编码集(JSON/逗号分隔)") + private String trainingSubtypeCodes; + + /** + * 检测题数 + */ + @ExcelProperty(value = "检测题数") + private Long questionCount; + + /** + * 试卷总分 + */ + @ExcelProperty(value = "试卷总分") + private Long totalScore; + + /** + * 及格分数 + */ + @ExcelProperty(value = "及格分数") + private Long passScore; + + /** + * 状态:1=启用 0=禁用 + */ + @ExcelProperty(value = "状态:1=启用 0=禁用") + private Long status; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + private Date createTime; + + private Long singleChoiceCount; + + private Long multiChoiceCount; + + private Long judgeCount; + + private Long singleChoiceScore; + + private Long multiChoiceScore; + + private Long judgeScore; + +} diff --git a/src/main/java/com/hotwj/platform/config/examPaper/mapper/HotExamPaperMapper.java b/src/main/java/com/hotwj/platform/config/examPaper/mapper/HotExamPaperMapper.java new file mode 100644 index 0000000..bcf1fcf --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaper/mapper/HotExamPaperMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.examPaper.mapper; + +import com.hotwj.platform.config.examPaper.domain.HotExamPaper; +import com.hotwj.platform.config.examPaper.domain.vo.HotExamPaperVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 试卷库Mapper接口 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Mapper +public interface HotExamPaperMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/examPaper/service/IHotExamPaperService.java b/src/main/java/com/hotwj/platform/config/examPaper/service/IHotExamPaperService.java new file mode 100644 index 0000000..f8689d1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaper/service/IHotExamPaperService.java @@ -0,0 +1,72 @@ +package com.hotwj.platform.config.examPaper.service; + +import com.hotwj.platform.config.examPaper.domain.bo.ExamPaperSaveQuestionsBo; +import com.hotwj.platform.config.examPaper.domain.bo.HotExamPaperBo; +import com.hotwj.platform.config.examPaper.domain.vo.ExamPaperSaveQuestionsResultVo; +import com.hotwj.platform.config.examPaper.domain.vo.HotExamPaperVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 试卷库Service接口 + * + * @author shihongwei + * @date 2025-12-23 + */ +public interface IHotExamPaperService { + + /** + * 查询试卷库 + * + * @param id 主键 + * @return 试卷库 + */ + HotExamPaperVo queryById(Long id); + + /** + * 分页查询试卷库列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 试卷库分页列表 + */ + TableDataInfo queryPageList(HotExamPaperBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的试卷库列表 + * + * @param bo 查询条件 + * @return 试卷库列表 + */ + List queryList(HotExamPaperBo bo); + + /** + * 新增试卷库 + * + * @param bo 试卷库 + * @return 是否新增成功 + */ + Boolean insertByBo(HotExamPaperBo bo); + + /** + * 修改试卷库 + * + * @param bo 试卷库 + * @return 是否修改成功 + */ + Boolean updateByBo(HotExamPaperBo bo); + + ExamPaperSaveQuestionsResultVo saveQuestions(ExamPaperSaveQuestionsBo bo); + + /** + * 校验并批量删除试卷库信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/examPaper/service/impl/HotExamPaperServiceImpl.java b/src/main/java/com/hotwj/platform/config/examPaper/service/impl/HotExamPaperServiceImpl.java new file mode 100644 index 0000000..c7456f6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaper/service/impl/HotExamPaperServiceImpl.java @@ -0,0 +1,333 @@ +package com.hotwj.platform.config.examPaper.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.examPaper.domain.HotExamPaper; +import com.hotwj.platform.config.examPaper.domain.bo.ExamPaperSaveQuestionsBo; +import com.hotwj.platform.config.examPaper.domain.bo.HotExamPaperBo; +import com.hotwj.platform.config.examPaper.domain.vo.ExamPaperSaveQuestionsResultVo; +import com.hotwj.platform.config.examPaper.domain.vo.HotExamPaperVo; +import com.hotwj.platform.config.examPaper.mapper.HotExamPaperMapper; +import com.hotwj.platform.config.examPaper.service.IHotExamPaperService; +import com.hotwj.platform.config.examPaperQuestion.domain.HotExamPaperQuestion; +import com.hotwj.platform.config.examPaperQuestion.mapper.HotExamPaperQuestionMapper; +import com.hotwj.platform.config.questionBank.domain.HotQuestionBank; +import com.hotwj.platform.config.questionBank.mapper.HotQuestionBankMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 试卷库Service业务层处理 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotExamPaperServiceImpl implements IHotExamPaperService { + + private final HotExamPaperMapper baseMapper; + private final HotExamPaperQuestionMapper examPaperQuestionMapper; + private final HotQuestionBankMapper questionBankMapper; + + /** + * 查询试卷库 + * + * @param id 主键 + * @return 试卷库 + */ + @Override + public HotExamPaperVo queryById(Long id) { + HotExamPaperVo vo = baseMapper.selectVoById(id); + if (vo == null || vo.getId() == null) { + return vo; + } + + List relations = examPaperQuestionMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotExamPaperQuestion::getPaperId, vo.getId()) + ); + if (relations == null || relations.isEmpty()) { + vo.setSingleChoiceCount(0L); + vo.setMultiChoiceCount(0L); + vo.setJudgeCount(0L); + vo.setSingleChoiceScore(0L); + vo.setMultiChoiceScore(0L); + vo.setJudgeScore(0L); + return vo; + } + + long singleCount = 0L; + long multiCount = 0L; + long judgeCount = 0L; + long singleScore = 0L; + long multiScore = 0L; + long judgeScore = 0L; + + for (HotExamPaperQuestion r : relations) { + if (r == null) { + continue; + } + long score = r.getScore() == null ? 0L : r.getScore(); + String t = r.getQuestionType(); + if ("1".equals(t)) { + singleCount++; + singleScore += score; + } else if ("2".equals(t)) { + multiCount++; + multiScore += score; + } else if ("3".equals(t)) { + judgeCount++; + judgeScore += score; + } + } + + vo.setSingleChoiceCount(singleCount); + vo.setMultiChoiceCount(multiCount); + vo.setJudgeCount(judgeCount); + vo.setSingleChoiceScore(singleScore); + vo.setMultiChoiceScore(multiScore); + vo.setJudgeScore(judgeScore); + return vo; + } + + /** + * 分页查询试卷库列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 试卷库分页列表 + */ + @Override + public TableDataInfo queryPageList(HotExamPaperBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的试卷库列表 + * + * @param bo 查询条件 + * @return 试卷库列表 + */ + @Override + public List queryList(HotExamPaperBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotExamPaperBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotExamPaper::getId); + // lqw.eq(bo.getCompanyId() != null, HotExamPaper::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getName()), HotExamPaper::getName, bo.getName()); + lqw.eq(StringUtils.isNotBlank(bo.getCategoryCode()), HotExamPaper::getCategoryCode, bo.getCategoryCode()); + lqw.eq(StringUtils.isNotBlank(bo.getTrainingTypeCode()), HotExamPaper::getTrainingTypeCode, bo.getTrainingTypeCode()); + lqw.eq(StringUtils.isNotBlank(bo.getTrainingSubtypeCodes()), HotExamPaper::getTrainingSubtypeCodes, bo.getTrainingSubtypeCodes()); + lqw.eq(bo.getQuestionCount() != null, HotExamPaper::getQuestionCount, bo.getQuestionCount()); + lqw.eq(bo.getTotalScore() != null, HotExamPaper::getTotalScore, bo.getTotalScore()); + lqw.eq(bo.getPassScore() != null, HotExamPaper::getPassScore, bo.getPassScore()); + lqw.eq(bo.getStatus() != null, HotExamPaper::getStatus, bo.getStatus()); + lqw.eq(bo.getIsDeleted() != null, HotExamPaper::getIsDeleted, bo.getIsDeleted()); + lqw.between(params.get("beginTime") != null && params.get("endTime") != null, + HotExamPaper::getCreateTime, params.get("beginTime"), params.get("endTime")); + return lqw; + } + + /** + * 新增试卷库 + * + * @param bo 试卷库 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotExamPaperBo bo) { + HotExamPaper add = MapstructUtils.convert(bo, HotExamPaper.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改试卷库 + * + * @param bo 试卷库 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotExamPaperBo bo) { + HotExamPaper update = MapstructUtils.convert(bo, HotExamPaper.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public ExamPaperSaveQuestionsResultVo saveQuestions(ExamPaperSaveQuestionsBo bo) { + if (bo == null || bo.getPaperId() == null) { + throw new ServiceException("试卷ID不能为空"); + } + if (StringUtils.isBlank(bo.getQuestionType())) { + throw new ServiceException("题目类型不能为空"); + } + String targetType = bo.getQuestionType(); + if (!"1".equals(targetType) && !"2".equals(targetType) && !"3".equals(targetType)) { + throw new ServiceException("题目类型不合法"); + } + + HotExamPaper paper = baseMapper.selectById(bo.getPaperId()); + if (paper == null || (paper.getIsDeleted() != null && paper.getIsDeleted() == 1L)) { + throw new ServiceException("试卷不存在"); + } + + Set questionIdSet = new LinkedHashSet<>(); + if (bo.getQuestions() != null) { + for (Long id : bo.getQuestions()) { + if (id != null) { + questionIdSet.add(id); + } + } + } + + List questionIds = questionIdSet.stream().toList(); + Map questionMap = questionIds.isEmpty() ? Map.of() : + questionBankMapper.selectList( + Wrappers.lambdaQuery() + .in(HotQuestionBank::getId, questionIds) + .eq(HotQuestionBank::getIsDeleted, 0L) + .eq(paper.getCompanyId() != null, HotQuestionBank::getCompanyId, paper.getCompanyId()) + ).stream().filter(Objects::nonNull).collect(Collectors.toMap(HotQuestionBank::getId, q -> q, (a, b) -> a)); + + if (questionMap.size() != questionIds.size()) { + throw new ServiceException("题目列表包含不存在或已删除的题目"); + } + + for (HotQuestionBank q : questionMap.values()) { + if (q == null || q.getQuestionType() == null) { + throw new ServiceException("题目类型不能为空"); + } + if (!targetType.equals(String.valueOf(q.getQuestionType()))) { + throw new ServiceException("题目列表包含非当前题型的题目"); + } + } + + examPaperQuestionMapper.delete( + Wrappers.lambdaQuery() + .eq(HotExamPaperQuestion::getPaperId, paper.getId()) + .eq(HotExamPaperQuestion::getQuestionType, targetType) + ); + + List relations = new ArrayList<>(questionIds.size()); + long sortOrder = 1L; + for (Long questionId : questionIds) { + HotQuestionBank q = questionMap.get(questionId); + long score = q.getScore() == null ? 0L : q.getScore(); + + HotExamPaperQuestion r = new HotExamPaperQuestion(); + r.setCompanyId(paper.getCompanyId()); + r.setPaperId(paper.getId()); + r.setQuestionId(questionId); + r.setQuestionType(targetType); + r.setSortOrder(sortOrder++); + r.setScore(score); + relations.add(r); + } + + if (!relations.isEmpty()) { + examPaperQuestionMapper.insertBatch(relations); + } + + List allRelations = examPaperQuestionMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotExamPaperQuestion::getPaperId, paper.getId()) + ); + + long questionCount = 0L; + long totalScore = 0L; + long singleCount = 0L; + long multiCount = 0L; + long judgeCount = 0L; + long singleChoiceScore = 0L; + long multiChoiceScore = 0L; + long judgeScore = 0L; + if (allRelations != null) { + for (HotExamPaperQuestion r : allRelations) { + if (r == null) { + continue; + } + questionCount++; + long score = r.getScore() == null ? 0L : r.getScore(); + totalScore += score; + if ("1".equals(r.getQuestionType())) { + singleCount++; + singleChoiceScore += score; + } else if ("2".equals(r.getQuestionType())) { + multiCount++; + multiChoiceScore += score; + } else if ("3".equals(r.getQuestionType())) { + judgeCount++; + judgeScore += score; + } + } + } + + baseMapper.update( + null, + Wrappers.lambdaUpdate() + .set(HotExamPaper::getQuestionCount, questionCount) + .set(HotExamPaper::getTotalScore, totalScore) + .eq(HotExamPaper::getId, paper.getId()) + ); + + ExamPaperSaveQuestionsResultVo result = new ExamPaperSaveQuestionsResultVo(); + result.setQuestionCount(questionCount); + result.setTotalScore(totalScore); + result.setPassScore(paper.getPassScore()); + result.setSingleChoiceCount(singleCount); + result.setMultiChoiceCount(multiCount); + result.setJudgeCount(judgeCount); + result.setSingleChoiceScore(singleChoiceScore); + result.setMultiChoiceScore(multiChoiceScore); + result.setJudgeScore(judgeScore); + return result; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotExamPaper entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除试卷库信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/examPaperQuestion/controller/HotExamPaperQuestionController.java b/src/main/java/com/hotwj/platform/config/examPaperQuestion/controller/HotExamPaperQuestionController.java new file mode 100644 index 0000000..85e724a --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaperQuestion/controller/HotExamPaperQuestionController.java @@ -0,0 +1,124 @@ +package com.hotwj.platform.config.examPaperQuestion.controller; + +import com.hotwj.platform.config.examPaperQuestion.domain.bo.HotExamPaperQuestionBo; +import com.hotwj.platform.config.examPaperQuestion.domain.vo.ExamPaperQuestionsGroupedVo; +import com.hotwj.platform.config.examPaperQuestion.domain.vo.HotExamPaperQuestionVo; +import com.hotwj.platform.config.examPaperQuestion.service.IHotExamPaperQuestionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 试卷-题目关联 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/examPaperQuestion") +@Tag(name = "试卷-题目关联", description = "试卷-题目关联管理") +public class HotExamPaperQuestionController extends BaseController { + + private final IHotExamPaperQuestionService hotExamPaperQuestionService; + + /** + * 查询试卷-题目关联列表 + */ + //@SaCheckPermission("config:examPaperQuestion:list") + @GetMapping("/list") + @Operation(summary = "分页查询试卷-题目关联列表") + public TableDataInfo list(HotExamPaperQuestionBo bo, PageQuery pageQuery) { + return hotExamPaperQuestionService.queryPageList(bo, pageQuery); + } + + /** + * 导出试卷-题目关联列表 + */ + //@SaCheckPermission("config:examPaperQuestion:export") + @Log(title = "试卷-题目关联", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出试卷-题目关联列表") + public void export(HotExamPaperQuestionBo bo, HttpServletResponse response) { + List list = hotExamPaperQuestionService.queryList(bo); + ExcelUtil.exportExcel(list, "试卷-题目关联", HotExamPaperQuestionVo.class, response); + } + + /** + * 获取试卷-题目关联详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:examPaperQuestion:query") + @GetMapping("/{id}") + @Operation(summary = "获取试卷-题目关联详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotExamPaperQuestionService.queryById(id)); + } + + /** + * 新增试卷-题目关联 + */ + //@SaCheckPermission("config:examPaperQuestion:add") + @Log(title = "试卷-题目关联", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增试卷-题目关联") + public R add(@Validated(AddGroup.class) @RequestBody HotExamPaperQuestionBo bo) { + return toAjax(hotExamPaperQuestionService.insertByBo(bo)); + } + + /** + * 修改试卷-题目关联 + */ + //@SaCheckPermission("config:examPaperQuestion:edit") + @Log(title = "试卷-题目关联", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改试卷-题目关联") + public R edit(@Validated(EditGroup.class) @RequestBody HotExamPaperQuestionBo bo) { + return toAjax(hotExamPaperQuestionService.updateByBo(bo)); + } + + /** + * 删除试卷-题目关联 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:examPaperQuestion:remove") + @Log(title = "试卷-题目关联", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除试卷-题目关联") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotExamPaperQuestionService.deleteWithValidByIds(List.of(ids), true)); + } + + @GetMapping("/groupedQuestionBank") + @Operation(summary = "按试卷查询题库题目并按题型分组") + public R groupedQuestionBank(@RequestParam Long paperId) { + return R.ok(hotExamPaperQuestionService.queryGroupedQuestionBankByPaperId(paperId)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/examPaperQuestion/domain/HotExamPaperQuestion.java b/src/main/java/com/hotwj/platform/config/examPaperQuestion/domain/HotExamPaperQuestion.java new file mode 100644 index 0000000..a27981b --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaperQuestion/domain/HotExamPaperQuestion.java @@ -0,0 +1,62 @@ +package com.hotwj.platform.config.examPaperQuestion.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 试卷-题目关联对象 hot_exam_paper_question + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_exam_paper_question") +public class HotExamPaperQuestion extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 试卷ID + */ + private Long paperId; + + /** + * 题目ID + */ + private Long questionId; + + /** + * 问题类型 + */ + private String questionType; + + /** + * 排序值 + */ + private Long sortOrder; + + /** + * 题目分值(若为空则使用原题分值) + */ + private Long score; + + +} diff --git a/src/main/java/com/hotwj/platform/config/examPaperQuestion/domain/bo/HotExamPaperQuestionBo.java b/src/main/java/com/hotwj/platform/config/examPaperQuestion/domain/bo/HotExamPaperQuestionBo.java new file mode 100644 index 0000000..b477a08 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaperQuestion/domain/bo/HotExamPaperQuestionBo.java @@ -0,0 +1,62 @@ +package com.hotwj.platform.config.examPaperQuestion.domain.bo; + +import com.hotwj.platform.config.examPaperQuestion.domain.HotExamPaperQuestion; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 试卷-题目关联业务对象 hot_exam_paper_question + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotExamPaperQuestion.class, reverseConvertGenerate = false) +public class HotExamPaperQuestionBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 试卷ID + */ + @NotNull(message = "试卷ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long paperId; + + /** + * 题目ID + */ + @NotNull(message = "题目ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long questionId; + + /** + * 问题类型 + */ + private String questionType; + + /** + * 排序值 + */ + private Long sortOrder; + + /** + * 题目分值(若为空则使用原题分值) + */ + private Long score; + + +} diff --git a/src/main/java/com/hotwj/platform/config/examPaperQuestion/domain/vo/ExamPaperQuestionDetailVo.java b/src/main/java/com/hotwj/platform/config/examPaperQuestion/domain/vo/ExamPaperQuestionDetailVo.java new file mode 100644 index 0000000..468cd11 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaperQuestion/domain/vo/ExamPaperQuestionDetailVo.java @@ -0,0 +1,122 @@ +package com.hotwj.platform.config.examPaperQuestion.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +@Data +public class ExamPaperQuestionDetailVo implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 试卷-题目关联表主键(hot_exam_paper_question.id) + */ + private Long relationId; + + /** + * 试卷ID(hot_exam_paper_question.paper_id) + */ + private Long paperId; + + /** + * 题目ID(hot_exam_paper_question.question_id / hot_question_bank.id) + */ + private Long questionId; + + /** + * 题目类型(关联表字段,1=单选 2=多选 3=判断) + */ + private String questionType; + + /** + * 题目排序值(hot_exam_paper_question.sort_order) + */ + private Long sortOrder; + + /** + * 题目分值(关联表字段,hot_exam_paper_question.score) + */ + private Long score; + + /** + * 难度:1=普通 2=困难 3=极难 + */ + private Long difficulty; + + /** + * 题库题目所属公司ID(hot_question_bank.company_id) + */ + private Long questionCompanyId; + + /** + * 题库题目类型(题库字段,hot_question_bank.question_type) + */ + private Long questionTypeNum; + + /** + * 教育类型字典编码(hot_question_bank.training_type_code) + */ + private String trainingTypeCode; + + /** + * 教育类型子集编码集(hot_question_bank.training_subtype_codes) + */ + private String trainingSubtypeCodes; + + /** + * 题干描述(hot_question_bank.question_desc) + */ + private String questionDesc; + + /** + * 题目插图URL(hot_question_bank.image_url) + */ + private String imageUrl; + + /** + * 选项A(hot_question_bank.option_a) + */ + private String optionA; + + /** + * 选项B(hot_question_bank.option_b) + */ + private String optionB; + + /** + * 选项C(hot_question_bank.option_c) + */ + private String optionC; + + /** + * 选项D(hot_question_bank.option_d) + */ + private String optionD; + + /** + * 选项E(hot_question_bank.option_e) + */ + private String optionE; + + /** + * 选项F(hot_question_bank.option_f) + */ + private String optionF; + + /** + * 标准答案(hot_question_bank.correct_answers) + */ + private String correctAnswers; + + /** + * 题库题目分值(hot_question_bank.score) + */ + private Long questionScore; + + /** + * 题库题目逻辑删除标记(hot_question_bank.is_deleted) + */ + private Long questionIsDeleted; +} diff --git a/src/main/java/com/hotwj/platform/config/examPaperQuestion/domain/vo/ExamPaperQuestionsGroupedVo.java b/src/main/java/com/hotwj/platform/config/examPaperQuestion/domain/vo/ExamPaperQuestionsGroupedVo.java new file mode 100644 index 0000000..5081d87 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaperQuestion/domain/vo/ExamPaperQuestionsGroupedVo.java @@ -0,0 +1,12 @@ +package com.hotwj.platform.config.examPaperQuestion.domain.vo; + +import lombok.Data; + +import java.util.List; + +@Data +public class ExamPaperQuestionsGroupedVo { + private List singleChoiceList; + private List multiChoiceList; + private List judgeList; +} diff --git a/src/main/java/com/hotwj/platform/config/examPaperQuestion/domain/vo/HotExamPaperQuestionVo.java b/src/main/java/com/hotwj/platform/config/examPaperQuestion/domain/vo/HotExamPaperQuestionVo.java new file mode 100644 index 0000000..655ca1c --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaperQuestion/domain/vo/HotExamPaperQuestionVo.java @@ -0,0 +1,69 @@ +package com.hotwj.platform.config.examPaperQuestion.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.examPaperQuestion.domain.HotExamPaperQuestion; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 试卷-题目关联视图对象 hot_exam_paper_question + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotExamPaperQuestion.class) +public class HotExamPaperQuestionVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 试卷ID + */ + @ExcelProperty(value = "试卷ID") + private Long paperId; + + /** + * 题目ID + */ + @ExcelProperty(value = "题目ID") + private Long questionId; + + /** + * 问题类型 + */ + private String questionType; + + /** + * 排序值 + */ + @ExcelProperty(value = "排序值") + private Long sortOrder; + + /** + * 题目分值(若为空则使用原题分值) + */ + @ExcelProperty(value = "题目分值(若为空则使用原题分值)") + private Long score; + + +} diff --git a/src/main/java/com/hotwj/platform/config/examPaperQuestion/mapper/HotExamPaperQuestionMapper.java b/src/main/java/com/hotwj/platform/config/examPaperQuestion/mapper/HotExamPaperQuestionMapper.java new file mode 100644 index 0000000..aba45bf --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaperQuestion/mapper/HotExamPaperQuestionMapper.java @@ -0,0 +1,22 @@ +package com.hotwj.platform.config.examPaperQuestion.mapper; + +import com.hotwj.platform.config.examPaperQuestion.domain.HotExamPaperQuestion; +import com.hotwj.platform.config.examPaperQuestion.domain.vo.ExamPaperQuestionDetailVo; +import com.hotwj.platform.config.examPaperQuestion.domain.vo.HotExamPaperQuestionVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +import java.util.List; + +/** + * 试卷-题目关联Mapper接口 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Mapper +public interface HotExamPaperQuestionMapper extends BaseMapperPlus { + + List selectPaperQuestionDetailList(@Param("paperId") Long paperId); +} diff --git a/src/main/java/com/hotwj/platform/config/examPaperQuestion/service/IHotExamPaperQuestionService.java b/src/main/java/com/hotwj/platform/config/examPaperQuestion/service/IHotExamPaperQuestionService.java new file mode 100644 index 0000000..c135aac --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaperQuestion/service/IHotExamPaperQuestionService.java @@ -0,0 +1,71 @@ +package com.hotwj.platform.config.examPaperQuestion.service; + +import com.hotwj.platform.config.examPaperQuestion.domain.bo.HotExamPaperQuestionBo; +import com.hotwj.platform.config.examPaperQuestion.domain.vo.ExamPaperQuestionsGroupedVo; +import com.hotwj.platform.config.examPaperQuestion.domain.vo.HotExamPaperQuestionVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 试卷-题目关联Service接口 + * + * @author shihongwei + * @date 2025-12-23 + */ +public interface IHotExamPaperQuestionService { + + /** + * 查询试卷-题目关联 + * + * @param id 主键 + * @return 试卷-题目关联 + */ + HotExamPaperQuestionVo queryById(Long id); + + /** + * 分页查询试卷-题目关联列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 试卷-题目关联分页列表 + */ + TableDataInfo queryPageList(HotExamPaperQuestionBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的试卷-题目关联列表 + * + * @param bo 查询条件 + * @return 试卷-题目关联列表 + */ + List queryList(HotExamPaperQuestionBo bo); + + /** + * 新增试卷-题目关联 + * + * @param bo 试卷-题目关联 + * @return 是否新增成功 + */ + Boolean insertByBo(HotExamPaperQuestionBo bo); + + /** + * 修改试卷-题目关联 + * + * @param bo 试卷-题目关联 + * @return 是否修改成功 + */ + Boolean updateByBo(HotExamPaperQuestionBo bo); + + /** + * 校验并批量删除试卷-题目关联信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + ExamPaperQuestionsGroupedVo queryGroupedQuestionBankByPaperId(Long paperId); +} diff --git a/src/main/java/com/hotwj/platform/config/examPaperQuestion/service/impl/HotExamPaperQuestionServiceImpl.java b/src/main/java/com/hotwj/platform/config/examPaperQuestion/service/impl/HotExamPaperQuestionServiceImpl.java new file mode 100644 index 0000000..42fc356 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/examPaperQuestion/service/impl/HotExamPaperQuestionServiceImpl.java @@ -0,0 +1,174 @@ +package com.hotwj.platform.config.examPaperQuestion.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.examPaperQuestion.domain.HotExamPaperQuestion; +import com.hotwj.platform.config.examPaperQuestion.domain.bo.HotExamPaperQuestionBo; +import com.hotwj.platform.config.examPaperQuestion.domain.vo.ExamPaperQuestionDetailVo; +import com.hotwj.platform.config.examPaperQuestion.domain.vo.ExamPaperQuestionsGroupedVo; +import com.hotwj.platform.config.examPaperQuestion.domain.vo.HotExamPaperQuestionVo; +import com.hotwj.platform.config.examPaperQuestion.mapper.HotExamPaperQuestionMapper; +import com.hotwj.platform.config.examPaperQuestion.service.IHotExamPaperQuestionService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 试卷-题目关联Service业务层处理 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotExamPaperQuestionServiceImpl implements IHotExamPaperQuestionService { + + private final HotExamPaperQuestionMapper baseMapper; + + /** + * 查询试卷-题目关联 + * + * @param id 主键 + * @return 试卷-题目关联 + */ + @Override + public HotExamPaperQuestionVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询试卷-题目关联列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 试卷-题目关联分页列表 + */ + @Override + public TableDataInfo queryPageList(HotExamPaperQuestionBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的试卷-题目关联列表 + * + * @param bo 查询条件 + * @return 试卷-题目关联列表 + */ + @Override + public List queryList(HotExamPaperQuestionBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotExamPaperQuestionBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotExamPaperQuestion::getId); + lqw.eq(bo.getCompanyId() != null, HotExamPaperQuestion::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getQuestionType() != null, HotExamPaperQuestion::getQuestionType, bo.getQuestionType()); + lqw.eq(bo.getPaperId() != null, HotExamPaperQuestion::getPaperId, bo.getPaperId()); + lqw.eq(bo.getQuestionId() != null, HotExamPaperQuestion::getQuestionId, bo.getQuestionId()); + lqw.eq(bo.getSortOrder() != null, HotExamPaperQuestion::getSortOrder, bo.getSortOrder()); + lqw.eq(bo.getScore() != null, HotExamPaperQuestion::getScore, bo.getScore()); + return lqw; + } + + /** + * 新增试卷-题目关联 + * + * @param bo 试卷-题目关联 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotExamPaperQuestionBo bo) { + HotExamPaperQuestion add = MapstructUtils.convert(bo, HotExamPaperQuestion.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改试卷-题目关联 + * + * @param bo 试卷-题目关联 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotExamPaperQuestionBo bo) { + HotExamPaperQuestion update = MapstructUtils.convert(bo, HotExamPaperQuestion.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotExamPaperQuestion entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除试卷-题目关联信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + public ExamPaperQuestionsGroupedVo queryGroupedQuestionBankByPaperId(Long paperId) { + if (paperId == null) { + throw new ServiceException("试卷ID不能为空"); + } + List list = baseMapper.selectPaperQuestionDetailList(paperId); + List single = new ArrayList<>(); + List multi = new ArrayList<>(); + List judge = new ArrayList<>(); + if (list != null) { + for (ExamPaperQuestionDetailVo vo : list) { + if (vo == null) { + continue; + } + String t = vo.getQuestionType(); + if ("1".equals(t)) { + single.add(vo); + } else if ("2".equals(t)) { + multi.add(vo); + } else if ("3".equals(t)) { + judge.add(vo); + } else if (StringUtils.isNotBlank(t)) { + single.add(vo); + } + } + } + ExamPaperQuestionsGroupedVo resp = new ExamPaperQuestionsGroupedVo(); + resp.setSingleChoiceList(single); + resp.setMultiChoiceList(multi); + resp.setJudgeList(judge); + return resp; + } +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDanger/strategy/HiddenDangerCheckCallback.java b/src/main/java/com/hotwj/platform/config/hiddenDanger/strategy/HiddenDangerCheckCallback.java new file mode 100644 index 0000000..62e9131 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDanger/strategy/HiddenDangerCheckCallback.java @@ -0,0 +1,110 @@ +package com.hotwj.platform.config.hiddenDanger.strategy; + +import com.hotwj.platform.flow.callback.IFlowCallback; +import com.hotwj.platform.flow.event.StartFlowEvent; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.HotHiddenDanger; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.bo.HotHiddenDangerBo; +import com.hotwj.platform.securityManagement.hiddenDanger.mapper.HotHiddenDangerMapper; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.HotHiddenDangerInspection; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.mapper.HotHiddenDangerInspectionMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Component; + +/** + * 隐患排查流程回调 + * + * @author shihongwei + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class HiddenDangerCheckCallback implements IFlowCallback { + + private final HotHiddenDangerInspectionMapper inspectMapper; + private final HotHiddenDangerMapper hiddenDangerMapper; + private final ApplicationEventPublisher eventPublisher; + + @Override + public String getFlowCode() { + return "HIDDEN_DANGER_CHECK"; + } + + @Override + public void onProcessEnd(String instanceId, String businessId, String flowCode, boolean success) { + log.info("隐患排查流程结束回调: businessId={}, success={}", businessId, success); + Long id = Long.parseLong(businessId); + HotHiddenDangerInspection inspection = inspectMapper.selectById(id); + if (inspection != null) { + if (success) { + inspection.setStatus(3L); + } else { + inspection.setStatus(6L); + log.info("隐患排查流程被驳回或非正常结束: {}", businessId); + } + if (inspection.getAuditHasDanger() != null && inspection.getAuditHasDanger() == 1L) { + createHiddenDangerFlow(inspection); + } + inspectMapper.updateById(inspection); + } + } + + /** + * 创建隐患治理流程 + */ + private void createHiddenDangerFlow(HotHiddenDangerInspection inspection) { + log.info("隐患排查发现隐患,自动创建隐患治理流程,业务ID: {}", inspection.getId()); + + HotHiddenDangerBo dangerBo = new HotHiddenDangerBo(); + dangerBo.setCompanyId(inspection.getCompanyId()); + dangerBo.setCheckTime(inspection.getCheckDate()); + dangerBo.setCheckerId(inspection.getCheckerId()); + dangerBo.setCheckerName(inspection.getCheckerName()); + dangerBo.setDescription(inspection.getDangerDesc()); + dangerBo.setEvidenceUrls(inspection.getAttachmentUrl()); + + // 生成隐患编号 YH-时间戳 + dangerBo.setDangerNo("YH-" + System.currentTimeMillis() / 1000); + + // 设置评估人 + dangerBo.setEvaluatorId(inspection.getEvaluatorId()); + dangerBo.setEvaluatorName(inspection.getEvaluatorName()); + + // 映射隐患来源类型 + dangerBo.setSourceName(inspection.getProjectName()); + dangerBo.setSourceId(String.valueOf(inspection.getId())); + dangerBo.setSourceType(inspection.getProjectType()); + dangerBo.setDataSource(1L); // 1=日常排查 + + // 隐患名称 + String dangerName = "隐患排查"; + dangerBo.setDangerName(dangerName); + dangerBo.setStatus(0L); // 0=待评估 + + HotHiddenDanger danger = MapstructUtils.convert(dangerBo, HotHiddenDanger.class); + hiddenDangerMapper.insert(danger); + + // 2. 启动隐患治理流程 + String initiator = LoginHelper.getBusinessUserId(); + String rectifierId = String.valueOf(inspection.getEvaluatorId()); + + // 使用事件发布解耦 + StartFlowEvent startFlowEvent = new StartFlowEvent(this, + "HIDDEN_DANGER_GOVERNANCE", + String.valueOf(danger.getId()), + inspection.getCompanyId(), + initiator, + rectifierId + ); + eventPublisher.publishEvent(startFlowEvent); + + if (startFlowEvent.getInstanceId() != null) { + danger.setInstanceId(startFlowEvent.getInstanceId()); + danger.setFlowStatus("SUBMITTED"); + hiddenDangerMapper.updateById(danger); + } + } +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDanger/strategy/HiddenDangerCheckStrategy.java b/src/main/java/com/hotwj/platform/config/hiddenDanger/strategy/HiddenDangerCheckStrategy.java new file mode 100644 index 0000000..6dfba88 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDanger/strategy/HiddenDangerCheckStrategy.java @@ -0,0 +1,87 @@ +package com.hotwj.platform.config.hiddenDanger.strategy; + +import com.hotwj.platform.flow.domain.SysFlowInstance; +import com.hotwj.platform.flow.domain.dto.FlowNextDto; +import com.hotwj.platform.flow.strategy.IFlowStrategy; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.HotHiddenDangerInspection; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.mapper.HotHiddenDangerInspectionMapper; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.HotHiddenDangerPlan; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.mapper.HotHiddenDangerPlanMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +/** + * 隐患排查流程策略 + * + * @author shihongwei + * @date 2026-01-25 + */ +@Component +public class HiddenDangerCheckStrategy implements IFlowStrategy { + + @Resource + private HotHiddenDangerInspectionMapper inspectMapper; + + @Resource + private HotHiddenDangerPlanMapper planMapper; + + @Override + public String getFlowCode() { + return "HIDDEN_DANGER_CHECK"; + } + + @Override + public String getFlowName() { + return "隐患排查"; + } + + @Override + public FlowNextDto next(SysFlowInstance instance, String currentNodeCode) { + // 简单流程:只有一级审批,审批通过即结束 + return null; + } + + + @Override + public FlowNextDto getInitialNode(String businessId) { + return getNextNode(businessId); + } + + private FlowNextDto getNextNode(String id) { + // 待办名称规则:(排查计划名称)(排查项名称)审核 + + String inspectItemName = "未知排查项"; + String planName = "未知计划"; + + try { + if (id != null) { + HotHiddenDangerInspection inspection = inspectMapper.selectById(id); + if (inspection != null) { + // 处理排查项名称 + if (inspection.getProjectName() != null) { + if (Long.valueOf(3L).equals(inspection.getProjectType())) { + // 刘浩然(513019199308195132) + // 人员特殊处理,保护隐私 ,支持中英文括号 + inspectItemName = inspection.getProjectName().replaceAll("[\\((].*?[\\))]", ""); + } else { + inspectItemName = inspection.getProjectName(); + } + } + + // 处理计划名称 + if (inspection.getPlanId() != null) { + HotHiddenDangerPlan plan = planMapper.selectById(inspection.getPlanId()); + if (plan != null && plan.getPlanName() != null) { + planName = plan.getPlanName(); + } + } + } + } + } catch (Exception e) { + // 忽略异常,使用默认值 + } + + String nodeName = String.format("(%s)(%s)审核", planName, inspectItemName); + return new FlowNextDto("NODE_CHECK_AUDIT", nodeName, null); + } +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDanger/strategy/HiddenDangerGovernanceStrategy.java b/src/main/java/com/hotwj/platform/config/hiddenDanger/strategy/HiddenDangerGovernanceStrategy.java new file mode 100644 index 0000000..1dc72a1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDanger/strategy/HiddenDangerGovernanceStrategy.java @@ -0,0 +1,125 @@ +package com.hotwj.platform.config.hiddenDanger.strategy; + +import com.hotwj.platform.flow.domain.SysFlowInstance; +import com.hotwj.platform.flow.domain.dto.FlowNextDto; +import com.hotwj.platform.flow.strategy.IFlowStrategy; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.HotHiddenDanger; +import com.hotwj.platform.securityManagement.hiddenDanger.mapper.HotHiddenDangerMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +/** + * 隐患治理流程策略 + * + * @author shihongwei + * @date 2026-01-25 + */ +@Component +public class HiddenDangerGovernanceStrategy implements IFlowStrategy { + + @Resource + private HotHiddenDangerMapper mapper; + + @Override + public String getFlowCode() { + return "HIDDEN_DANGER_GOVERNANCE"; + } + + @Override + public String getFlowName() { + return "隐患治理"; + } + + @Override + public FlowNextDto next(SysFlowInstance instance, String currentNodeCode) { + // 简单流程:只有一级审批,审批通过即结束 + return getNextNode(instance.getBusinessId(), currentNodeCode, false); + } + + @Override + public FlowNextDto reject(SysFlowInstance instance, String currentNodeCode) { + // 如果是复查不通过,退回到治理 + HotHiddenDanger danger = getHiddenDanger(instance.getBusinessId()); + String sourceName = danger != null ? danger.getSourceName() : "未知来源"; + + if ("NODE_GOVERNANCE_AUDIT_FC".equals(currentNodeCode)) { + // 回退给治理人 + String approverId = danger != null && danger.getGovernPersonId() != null ? String.valueOf(danger.getGovernPersonId()) : null; + return new FlowNextDto("NODE_GOVERNANCE_ZL", String.format("(%s)%s", sourceName, "治理"), approverId); + } else { + return null; + } + } + + @Override + public FlowNextDto getInitialNode(String businessId) { + // 根据不同类型返回不同的名称 + return getNextNode(businessId, null, true); + } + + + private FlowNextDto getNextNode(String businessId, String currentNodeCode, boolean initialNode) { + HotHiddenDanger danger = getHiddenDanger(businessId); + String sourceName = danger != null ? danger.getSourceName() : "未知来源"; + + // 初始节点 + if (initialNode) { + return new FlowNextDto("NODE_GOVERNANCE_AUDIT_PG", + String.format("(%s)评估", sourceName), null); + } + + // 流程流转:评估 -> 治理 -> 复查 + String nodeCode = null; + String state = null; + String approverId = null; + + if ("NODE_GOVERNANCE_AUDIT_PG".equals(currentNodeCode)) { + nodeCode = "NODE_GOVERNANCE_ZL"; + state = "治理"; + // 待办给治理人 + if (danger != null && danger.getGovernPersonId() != null) { + approverId = String.valueOf(danger.getGovernPersonId()); + } + } else if ("NODE_GOVERNANCE_ZL".equals(currentNodeCode)) { + nodeCode = "NODE_GOVERNANCE_AUDIT_FC"; + state = "复查"; + // 待办给复查人 + if (danger != null && danger.getReviewerId() != null) { + approverId = String.valueOf(danger.getReviewerId()); + } + } else if ("NODE_GOVERNANCE_AUDIT_FC".equals(currentNodeCode)) { + // 流程结束 + return null; + } + + if (nodeCode != null) { + return new FlowNextDto(nodeCode, + String.format("(%s)%s", sourceName, state), approverId); + } + + return null; + } + + /** + * 获取隐患对象 + */ + private HotHiddenDanger getHiddenDanger(String businessId) { + if (businessId == null) { + return null; + } + try { + Long id = Long.parseLong(businessId); + return mapper.selectById(id); + } catch (Exception e) { + return null; + } + } + + /** + * 获取来源名称 (安全获取) + */ + private String getSourceName(String businessId) { + HotHiddenDanger danger = getHiddenDanger(businessId); + return danger != null && danger.getSourceName() != null ? danger.getSourceName() : "未知来源"; + } +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/controller/HotHiddenDangerCheckConfigController.java b/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/controller/HotHiddenDangerCheckConfigController.java new file mode 100644 index 0000000..be2f8af --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/controller/HotHiddenDangerCheckConfigController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.config.hiddenDangerCheckConfig.controller; + +import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.bo.HotHiddenDangerCheckConfigBo; +import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.vo.HotHiddenDangerCheckConfigVo; +import com.hotwj.platform.config.hiddenDangerCheckConfig.service.IHotHiddenDangerCheckConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 隐患排查检查项配置 + * + * @author shihongwei + * @date 2025-12-30 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/hiddenDangerCheckConfig") +@Tag(name = "隐患排查检查项配置", description = "隐患排查检查项配置管理") +public class HotHiddenDangerCheckConfigController extends BaseController { + + private final IHotHiddenDangerCheckConfigService hotHiddenDangerCheckConfigService; + + /** + * 查询隐患排查检查项配置列表 + */ + //@SaCheckPermission("config:hiddenDangerCheckConfig:list") + @GetMapping("/list") + @Operation(summary = "分页查询隐患排查检查项配置列表") + public TableDataInfo list(HotHiddenDangerCheckConfigBo bo, PageQuery pageQuery) { + return hotHiddenDangerCheckConfigService.queryPageList(bo, pageQuery); + } + + /** + * 导出隐患排查检查项配置列表 + */ + //@SaCheckPermission("config:hiddenDangerCheckConfig:export") + @Log(title = "隐患排查检查项配置", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出隐患排查检查项配置列表") + public void export(HotHiddenDangerCheckConfigBo bo, HttpServletResponse response) { + List list = hotHiddenDangerCheckConfigService.queryList(bo); + ExcelUtil.exportExcel(list, "隐患排查检查项配置", HotHiddenDangerCheckConfigVo.class, response); + } + + /** + * 获取隐患排查检查项配置详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:hiddenDangerCheckConfig:query") + @GetMapping("/{id}") + @Operation(summary = "获取隐患排查检查项配置详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotHiddenDangerCheckConfigService.queryById(id)); + } + + /** + * 新增隐患排查检查项配置 + */ + //@SaCheckPermission("config:hiddenDangerCheckConfig:add") + @Log(title = "隐患排查检查项配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增隐患排查检查项配置") + public R add(@Validated(AddGroup.class) @RequestBody HotHiddenDangerCheckConfigBo bo) { + return toAjax(hotHiddenDangerCheckConfigService.insertByBo(bo)); + } + + /** + * 修改隐患排查检查项配置 + */ + //@SaCheckPermission("config:hiddenDangerCheckConfig:edit") + @Log(title = "隐患排查检查项配置", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改隐患排查检查项配置") + public R edit(@Validated(EditGroup.class) @RequestBody HotHiddenDangerCheckConfigBo bo) { + return toAjax(hotHiddenDangerCheckConfigService.updateByBo(bo)); + } + + /** + * 删除隐患排查检查项配置 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:hiddenDangerCheckConfig:remove") + @Log(title = "隐患排查检查项配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除隐患排查检查项配置") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotHiddenDangerCheckConfigService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/domain/HotHiddenDangerCheckConfig.java b/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/domain/HotHiddenDangerCheckConfig.java new file mode 100644 index 0000000..611b215 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/domain/HotHiddenDangerCheckConfig.java @@ -0,0 +1,69 @@ +package com.hotwj.platform.config.hiddenDangerCheckConfig.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 隐患排查检查项配置对象 hot_hidden_danger_check_config + * + * @author shihongwei + * @date 2025-12-30 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_hidden_danger_check_config") +public class HotHiddenDangerCheckConfig extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 类型名称 + */ + private String typeName; + + /** + * 是否默认 0=否 1=是 + */ + private Long isDefault; + + /** + * 企业检查项目JSON + */ + private String companyInspectJson; + + /** + * 车辆检查项目JSON + */ + private String vehicleInspectJson; + + /** + * 人员检查项目JSON + */ + private String personInspectJson; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/domain/bo/HotHiddenDangerCheckConfigBo.java b/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/domain/bo/HotHiddenDangerCheckConfigBo.java new file mode 100644 index 0000000..f111b76 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/domain/bo/HotHiddenDangerCheckConfigBo.java @@ -0,0 +1,70 @@ +package com.hotwj.platform.config.hiddenDangerCheckConfig.domain.bo; + +import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.HotHiddenDangerCheckConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 隐患排查检查项配置业务对象 hot_hidden_danger_check_config + * + * @author shihongwei + * @date 2025-12-30 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotHiddenDangerCheckConfig.class, reverseConvertGenerate = false) +public class HotHiddenDangerCheckConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 类型名称 + */ + @NotBlank(message = "类型名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String typeName; + + /** + * 是否默认 0=否 1=是 + */ + @NotNull(message = "是否默认 0=否 1=是不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDefault; + + /** + * 企业检查项目JSON + */ + private String companyInspectJson; + + /** + * 车辆检查项目JSON + */ + private String vehicleInspectJson; + + /** + * 人员检查项目JSON + */ + private String personInspectJson; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/domain/vo/HotHiddenDangerCheckConfigVo.java b/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/domain/vo/HotHiddenDangerCheckConfigVo.java new file mode 100644 index 0000000..978c6ec --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/domain/vo/HotHiddenDangerCheckConfigVo.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.config.hiddenDangerCheckConfig.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.HotHiddenDangerCheckConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 隐患排查检查项配置视图对象 hot_hidden_danger_check_config + * + * @author shihongwei + * @date 2025-12-30 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotHiddenDangerCheckConfig.class) +public class HotHiddenDangerCheckConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 类型名称 + */ + @ExcelProperty(value = "类型名称") + private String typeName; + + /** + * 是否默认 0=否 1=是 + */ + @ExcelProperty(value = "是否默认 0=否 1=是") + private Long isDefault; + + /** + * 企业检查项目JSON + */ + @ExcelProperty(value = "企业检查项目JSON") + private String companyInspectJson; + + /** + * 车辆检查项目JSON + */ + @ExcelProperty(value = "车辆检查项目JSON") + private String vehicleInspectJson; + + /** + * 人员检查项目JSON + */ + @ExcelProperty(value = "人员检查项目JSON") + private String personInspectJson; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/mapper/HotHiddenDangerCheckConfigMapper.java b/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/mapper/HotHiddenDangerCheckConfigMapper.java new file mode 100644 index 0000000..58ac1ff --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/mapper/HotHiddenDangerCheckConfigMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.hiddenDangerCheckConfig.mapper; + +import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.HotHiddenDangerCheckConfig; +import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.vo.HotHiddenDangerCheckConfigVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 隐患排查检查项配置Mapper接口 + * + * @author shihongwei + * @date 2025-12-30 + */ +@Mapper +public interface HotHiddenDangerCheckConfigMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/service/IHotHiddenDangerCheckConfigService.java b/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/service/IHotHiddenDangerCheckConfigService.java new file mode 100644 index 0000000..521930c --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/service/IHotHiddenDangerCheckConfigService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.hiddenDangerCheckConfig.service; + +import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.bo.HotHiddenDangerCheckConfigBo; +import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.vo.HotHiddenDangerCheckConfigVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 隐患排查检查项配置Service接口 + * + * @author shihongwei + * @date 2025-12-30 + */ +public interface IHotHiddenDangerCheckConfigService { + + /** + * 查询隐患排查检查项配置 + * + * @param id 主键 + * @return 隐患排查检查项配置 + */ + HotHiddenDangerCheckConfigVo queryById(Long id); + + /** + * 分页查询隐患排查检查项配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 隐患排查检查项配置分页列表 + */ + TableDataInfo queryPageList(HotHiddenDangerCheckConfigBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的隐患排查检查项配置列表 + * + * @param bo 查询条件 + * @return 隐患排查检查项配置列表 + */ + List queryList(HotHiddenDangerCheckConfigBo bo); + + /** + * 新增隐患排查检查项配置 + * + * @param bo 隐患排查检查项配置 + * @return 是否新增成功 + */ + Boolean insertByBo(HotHiddenDangerCheckConfigBo bo); + + /** + * 修改隐患排查检查项配置 + * + * @param bo 隐患排查检查项配置 + * @return 是否修改成功 + */ + Boolean updateByBo(HotHiddenDangerCheckConfigBo bo); + + /** + * 校验并批量删除隐患排查检查项配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/service/impl/HotHiddenDangerCheckConfigServiceImpl.java b/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/service/impl/HotHiddenDangerCheckConfigServiceImpl.java new file mode 100644 index 0000000..0f28bb2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDangerCheckConfig/service/impl/HotHiddenDangerCheckConfigServiceImpl.java @@ -0,0 +1,138 @@ +package com.hotwj.platform.config.hiddenDangerCheckConfig.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.HotHiddenDangerCheckConfig; +import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.bo.HotHiddenDangerCheckConfigBo; +import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.vo.HotHiddenDangerCheckConfigVo; +import com.hotwj.platform.config.hiddenDangerCheckConfig.mapper.HotHiddenDangerCheckConfigMapper; +import com.hotwj.platform.config.hiddenDangerCheckConfig.service.IHotHiddenDangerCheckConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 隐患排查检查项配置Service业务层处理 + * + * @author shihongwei + * @date 2025-12-30 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotHiddenDangerCheckConfigServiceImpl implements IHotHiddenDangerCheckConfigService { + + private final HotHiddenDangerCheckConfigMapper baseMapper; + + /** + * 查询隐患排查检查项配置 + * + * @param id 主键 + * @return 隐患排查检查项配置 + */ + @Override + public HotHiddenDangerCheckConfigVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询隐患排查检查项配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 隐患排查检查项配置分页列表 + */ + @Override + public TableDataInfo queryPageList(HotHiddenDangerCheckConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的隐患排查检查项配置列表 + * + * @param bo 查询条件 + * @return 隐患排查检查项配置列表 + */ + @Override + public List queryList(HotHiddenDangerCheckConfigBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotHiddenDangerCheckConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotHiddenDangerCheckConfig::getId); + lqw.eq(bo.getCompanyId() != null, HotHiddenDangerCheckConfig::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getTypeName()), HotHiddenDangerCheckConfig::getTypeName, bo.getTypeName()); + lqw.eq(bo.getIsDefault() != null, HotHiddenDangerCheckConfig::getIsDefault, bo.getIsDefault()); + lqw.eq(StringUtils.isNotBlank(bo.getCompanyInspectJson()), HotHiddenDangerCheckConfig::getCompanyInspectJson, bo.getCompanyInspectJson()); + lqw.eq(StringUtils.isNotBlank(bo.getVehicleInspectJson()), HotHiddenDangerCheckConfig::getVehicleInspectJson, bo.getVehicleInspectJson()); + lqw.eq(StringUtils.isNotBlank(bo.getPersonInspectJson()), HotHiddenDangerCheckConfig::getPersonInspectJson, bo.getPersonInspectJson()); + lqw.eq(bo.getIsDeleted() != null, HotHiddenDangerCheckConfig::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增隐患排查检查项配置 + * + * @param bo 隐患排查检查项配置 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotHiddenDangerCheckConfigBo bo) { + HotHiddenDangerCheckConfig add = MapstructUtils.convert(bo, HotHiddenDangerCheckConfig.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改隐患排查检查项配置 + * + * @param bo 隐患排查检查项配置 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotHiddenDangerCheckConfigBo bo) { + HotHiddenDangerCheckConfig update = MapstructUtils.convert(bo, HotHiddenDangerCheckConfig.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotHiddenDangerCheckConfig entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除隐患排查检查项配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/controller/HotHiddenDangerInspectStoreController.java b/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/controller/HotHiddenDangerInspectStoreController.java new file mode 100644 index 0000000..d132cac --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/controller/HotHiddenDangerInspectStoreController.java @@ -0,0 +1,163 @@ +package com.hotwj.platform.config.hiddenDangerInspectStore.controller; + +import com.hotwj.platform.config.hiddenDangerInspectStore.domain.bo.HotHiddenDangerInspectStoreBo; +import com.hotwj.platform.config.hiddenDangerInspectStore.domain.vo.HotHiddenDangerInspectStoreVo; +import com.hotwj.platform.config.hiddenDangerInspectStore.service.IHotHiddenDangerInspectStoreService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 隐患排查检查库 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/hiddenDangerInspectStore") +@Tag(name = "隐患排查检查库", description = "隐患排查检查库管理") +public class HotHiddenDangerInspectStoreController extends BaseController { + + private final IHotHiddenDangerInspectStoreService hotHiddenDangerInspectStoreService; + + /** + * 查询隐患排查检查库列表 + */ + //@SaCheckPermission("config:hiddenDangerInspectStore:list") + @GetMapping("/list") + @Operation(summary = "分页查询隐患排查检查库列表") + public TableDataInfo list(HotHiddenDangerInspectStoreBo bo, PageQuery pageQuery) { + return hotHiddenDangerInspectStoreService.queryPageList(bo, pageQuery); + } + + /** + * 导出隐患排查检查库列表 + */ + //@SaCheckPermission("config:hiddenDangerInspectStore:export") + @Log(title = "隐患排查检查库", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出隐患排查检查库列表") + public void export(HotHiddenDangerInspectStoreBo bo, HttpServletResponse response) { + List list = hotHiddenDangerInspectStoreService.queryList(bo); + ExcelUtil.exportExcel(list, "隐患排查检查库", HotHiddenDangerInspectStoreVo.class, response); + } + + /** + * 获取隐患排查检查库详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:hiddenDangerInspectStore:query") + @GetMapping("/{id}") + @Operation(summary = "获取隐患排查检查库详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotHiddenDangerInspectStoreService.queryById(id)); + } + + /** + * 新增隐患排查检查库 + */ + //@SaCheckPermission("config:hiddenDangerInspectStore:add") + @Log(title = "隐患排查检查库", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增隐患排查检查库") + public R add(@Validated(AddGroup.class) @RequestBody HotHiddenDangerInspectStoreBo bo) { + return toAjax(hotHiddenDangerInspectStoreService.insertByBo(bo)); + } + + /** + * 修改隐患排查检查库 + */ + //@SaCheckPermission("config:hiddenDangerInspectStore:edit") + @Log(title = "隐患排查检查库", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改隐患排查检查库") + public R edit(@Validated(EditGroup.class) @RequestBody HotHiddenDangerInspectStoreBo bo) { + return toAjax(hotHiddenDangerInspectStoreService.updateByBo(bo)); + } + + /** + * 删除隐患排查检查库 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:hiddenDangerInspectStore:remove") + @Log(title = "隐患排查检查库", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除隐患排查检查库") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotHiddenDangerInspectStoreService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 采用默认配置 + */ + //@SaCheckPermission("config:hiddenDangerInspectStore:add") + @Log(title = "隐患排查检查库", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/useDefault") + @Operation(summary = "采用默认配置") + public R useDefault() { + return toAjax(hotHiddenDangerInspectStoreService.useDefault()); + } + + /** + * 一键清空 + */ + //@SaCheckPermission("config:hiddenDangerInspectStore:remove") + @Log(title = "隐患排查检查库", businessType = BusinessType.DELETE) + @RepeatSubmit() + @PostMapping("/clearAll") + @Operation(summary = "一键清空") + public R clearAll() { + return toAjax(hotHiddenDangerInspectStoreService.clearAll()); + } + + /** + * 根据公司ID与主键ID串查询隐患排查检查库列表 + */ + //@SaCheckPermission("config:hiddenDangerInspectStore:list") + @GetMapping("/listByIds") + @Operation(summary = "根据公司ID与ID串查询隐患排查检查库列表") + public R> listByIds( + @RequestParam Long companyId, + @RequestParam String ids + ) { + if (companyId == null) { + return R.ok(List.of()); + } + java.util.List idList = java.util.Arrays.stream(ids.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .map(Long::valueOf) + .distinct() + .toList(); + return R.ok(hotHiddenDangerInspectStoreService.queryByCompanyIdAndIds(companyId, idList)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/domain/HotHiddenDangerInspectStore.java b/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/domain/HotHiddenDangerInspectStore.java new file mode 100644 index 0000000..4fdaf60 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/domain/HotHiddenDangerInspectStore.java @@ -0,0 +1,69 @@ +package com.hotwj.platform.config.hiddenDangerInspectStore.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 隐患排查检查库对象 hot_hidden_danger_inspect_store + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_hidden_danger_inspect_store") +public class HotHiddenDangerInspectStore extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 检查类型 1=企业 2=车辆 3=人员 + */ + private Long inspectType; + + /** + * 隐患排查内容 + */ + private String content; + + /** + * 正常项 + */ + private String normalText; + + /** + * 异常项 + */ + private String abnormalText; + + /** + * 代码 + */ + private String code; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/domain/bo/HotHiddenDangerInspectStoreBo.java b/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/domain/bo/HotHiddenDangerInspectStoreBo.java new file mode 100644 index 0000000..f2e3254 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/domain/bo/HotHiddenDangerInspectStoreBo.java @@ -0,0 +1,73 @@ +package com.hotwj.platform.config.hiddenDangerInspectStore.domain.bo; + +import com.hotwj.platform.config.hiddenDangerInspectStore.domain.HotHiddenDangerInspectStore; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 隐患排查检查库业务对象 hot_hidden_danger_inspect_store + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotHiddenDangerInspectStore.class, reverseConvertGenerate = false) +public class HotHiddenDangerInspectStoreBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 检查类型 1=企业 2=车辆 3=人员 + */ + @NotNull(message = "检查类型 1=企业 2=车辆 3=人员不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long inspectType; + + /** + * 隐患排查内容 + */ + @NotBlank(message = "隐患排查内容不能为空", groups = {AddGroup.class, EditGroup.class}) + private String content; + + /** + * 正常项 + */ + @NotBlank(message = "正常项不能为空", groups = {AddGroup.class, EditGroup.class}) + private String normalText; + + /** + * 异常项 + */ + @NotBlank(message = "异常项不能为空", groups = {AddGroup.class, EditGroup.class}) + private String abnormalText; + + /** + * 代码 + */ + @NotBlank(message = "代码不能为空", groups = {AddGroup.class, EditGroup.class}) + private String code; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/domain/vo/HotHiddenDangerInspectStoreVo.java b/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/domain/vo/HotHiddenDangerInspectStoreVo.java new file mode 100644 index 0000000..bfe19fa --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/domain/vo/HotHiddenDangerInspectStoreVo.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.config.hiddenDangerInspectStore.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.hiddenDangerInspectStore.domain.HotHiddenDangerInspectStore; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 隐患排查检查库视图对象 hot_hidden_danger_inspect_store + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotHiddenDangerInspectStore.class) +public class HotHiddenDangerInspectStoreVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 检查类型 1=企业 2=车辆 3=人员 + */ + @ExcelProperty(value = "检查类型 1=企业 2=车辆 3=人员") + private Long inspectType; + + /** + * 隐患排查内容 + */ + @ExcelProperty(value = "隐患排查内容") + private String content; + + /** + * 正常项 + */ + @ExcelProperty(value = "正常项") + private String normalText; + + /** + * 异常项 + */ + @ExcelProperty(value = "异常项") + private String abnormalText; + + /** + * 代码 + */ + @ExcelProperty(value = "代码") + private String code; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/mapper/HotHiddenDangerInspectStoreMapper.java b/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/mapper/HotHiddenDangerInspectStoreMapper.java new file mode 100644 index 0000000..e8d831f --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/mapper/HotHiddenDangerInspectStoreMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.hiddenDangerInspectStore.mapper; + +import com.hotwj.platform.config.hiddenDangerInspectStore.domain.HotHiddenDangerInspectStore; +import com.hotwj.platform.config.hiddenDangerInspectStore.domain.vo.HotHiddenDangerInspectStoreVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 隐患排查检查库Mapper接口 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Mapper +public interface HotHiddenDangerInspectStoreMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/service/IHotHiddenDangerInspectStoreService.java b/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/service/IHotHiddenDangerInspectStoreService.java new file mode 100644 index 0000000..9d5338d --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/service/IHotHiddenDangerInspectStoreService.java @@ -0,0 +1,91 @@ +package com.hotwj.platform.config.hiddenDangerInspectStore.service; + +import com.hotwj.platform.config.hiddenDangerInspectStore.domain.bo.HotHiddenDangerInspectStoreBo; +import com.hotwj.platform.config.hiddenDangerInspectStore.domain.vo.HotHiddenDangerInspectStoreVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 隐患排查检查库Service接口 + * + * @author shihongwei + * @date 2025-12-29 + */ +public interface IHotHiddenDangerInspectStoreService { + + /** + * 查询隐患排查检查库 + * + * @param id 主键 + * @return 隐患排查检查库 + */ + HotHiddenDangerInspectStoreVo queryById(Long id); + + /** + * 分页查询隐患排查检查库列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 隐患排查检查库分页列表 + */ + TableDataInfo queryPageList(HotHiddenDangerInspectStoreBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的隐患排查检查库列表 + * + * @param bo 查询条件 + * @return 隐患排查检查库列表 + */ + List queryList(HotHiddenDangerInspectStoreBo bo); + + /** + * 根据公司ID和ID列表查询隐患排查检查库 + * + * @param companyId 公司ID + * @param ids 主键ID列表 + * @return 结果列表 + */ + List queryByCompanyIdAndIds(Long companyId, List ids); + + /** + * 新增隐患排查检查库 + * + * @param bo 隐患排查检查库 + * @return 是否新增成功 + */ + Boolean insertByBo(HotHiddenDangerInspectStoreBo bo); + + /** + * 修改隐患排查检查库 + * + * @param bo 隐患排查检查库 + * @return 是否修改成功 + */ + Boolean updateByBo(HotHiddenDangerInspectStoreBo bo); + + /** + * 校验并批量删除隐患排查检查库信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 采用默认配置 + * + * @return 结果 + */ + Boolean useDefault(); + + /** + * 清空当前公司所有配置 + * + * @return 是否成功 + */ + Boolean clearAll(); +} diff --git a/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/service/impl/HotHiddenDangerInspectStoreServiceImpl.java b/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/service/impl/HotHiddenDangerInspectStoreServiceImpl.java new file mode 100644 index 0000000..3d20def --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/hiddenDangerInspectStore/service/impl/HotHiddenDangerInspectStoreServiceImpl.java @@ -0,0 +1,235 @@ +package com.hotwj.platform.config.hiddenDangerInspectStore.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.hiddenDangerInspectStore.domain.HotHiddenDangerInspectStore; +import com.hotwj.platform.config.hiddenDangerInspectStore.domain.bo.HotHiddenDangerInspectStoreBo; +import com.hotwj.platform.config.hiddenDangerInspectStore.domain.vo.HotHiddenDangerInspectStoreVo; +import com.hotwj.platform.config.hiddenDangerInspectStore.mapper.HotHiddenDangerInspectStoreMapper; +import com.hotwj.platform.config.hiddenDangerInspectStore.service.IHotHiddenDangerInspectStoreService; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 隐患排查检查库Service业务层处理 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotHiddenDangerInspectStoreServiceImpl implements IHotHiddenDangerInspectStoreService { + + private final HotHiddenDangerInspectStoreMapper baseMapper; + private final ISysCompanyService sysCompanyService; + + /** + * 查询隐患排查检查库 + * + * @param id 主键 + * @return 隐患排查检查库 + */ + @Override + public HotHiddenDangerInspectStoreVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean useDefault() { + Long companyId = LoginHelper.getCompanyId(); + if (companyId == null) { + throw new ServiceException("当前登录用户无公司信息"); + } + + SysCompanyVo company = sysCompanyService.queryById(companyId); + if (company == null) { + throw new ServiceException("公司不存在"); + } + + // 校验当前公司是否已有配置 + Long count = baseMapper.selectCount( + Wrappers.lambdaQuery(HotHiddenDangerInspectStore.class) + .eq(HotHiddenDangerInspectStore::getCompanyId, companyId) + ); + if (count != null && count > 0) { + throw new ServiceException("当前公司已存在隐患排查配置,请先清除已存在数据"); + } + + // 获取总部配置 (companyId = 1) + List defaultConfigs = baseMapper.selectList( + Wrappers.lambdaQuery(HotHiddenDangerInspectStore.class) + .eq(HotHiddenDangerInspectStore::getCompanyId, 1L) + ); + + if (CollUtil.isEmpty(defaultConfigs)) { + throw new ServiceException("默认配置不存在,请联系管理员"); + } + + List newConfigs = new ArrayList<>(); + for (HotHiddenDangerInspectStore cfg : defaultConfigs) { + HotHiddenDangerInspectStore newCfg = new HotHiddenDangerInspectStore(); + // 复制属性,排除ID等 + newCfg.setCompanyId(companyId); + newCfg.setInspectType(cfg.getInspectType()); + newCfg.setContent(cfg.getContent()); + newCfg.setNormalText(cfg.getNormalText()); + newCfg.setAbnormalText(cfg.getAbnormalText()); + newCfg.setCode(cfg.getCode()); + newCfg.setIsDeleted(0L); + newConfigs.add(newCfg); + } + + return baseMapper.insertBatch(newConfigs); + } + + + /** + * 分页查询隐患排查检查库列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 隐患排查检查库分页列表 + */ + @Override + public TableDataInfo queryPageList(HotHiddenDangerInspectStoreBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的隐患排查检查库列表 + * + * @param bo 查询条件 + * @return 隐患排查检查库列表 + */ + @Override + public List queryList(HotHiddenDangerInspectStoreBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + @Override + public List queryByCompanyIdAndIds(Long companyId, List ids) { + if (companyId == null || ids == null || ids.isEmpty()) { + return new ArrayList<>(); + } + LambdaQueryWrapper lqw = Wrappers.lambdaQuery() + .eq(HotHiddenDangerInspectStore::getCompanyId, companyId) + .in(HotHiddenDangerInspectStore::getId, ids); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotHiddenDangerInspectStoreBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotHiddenDangerInspectStore::getId); + lqw.eq(bo.getCompanyId() != null, HotHiddenDangerInspectStore::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getInspectType() != null, HotHiddenDangerInspectStore::getInspectType, bo.getInspectType()); + lqw.eq(StringUtils.isNotBlank(bo.getContent()), HotHiddenDangerInspectStore::getContent, bo.getContent()); + lqw.eq(StringUtils.isNotBlank(bo.getNormalText()), HotHiddenDangerInspectStore::getNormalText, bo.getNormalText()); + lqw.eq(StringUtils.isNotBlank(bo.getAbnormalText()), HotHiddenDangerInspectStore::getAbnormalText, bo.getAbnormalText()); + lqw.eq(StringUtils.isNotBlank(bo.getCode()), HotHiddenDangerInspectStore::getCode, bo.getCode()); + lqw.eq(bo.getIsDeleted() != null, HotHiddenDangerInspectStore::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增隐患排查检查库 + * + * @param bo 隐患排查检查库 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotHiddenDangerInspectStoreBo bo) { + HotHiddenDangerInspectStore add = MapstructUtils.convert(bo, HotHiddenDangerInspectStore.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改隐患排查检查库 + * + * @param bo 隐患排查检查库 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotHiddenDangerInspectStoreBo bo) { + HotHiddenDangerInspectStore update = MapstructUtils.convert(bo, HotHiddenDangerInspectStore.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotHiddenDangerInspectStore entity) { + if (entity.getCompanyId() == null || StringUtils.isBlank(entity.getCode())) { + return; + } + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(HotHiddenDangerInspectStore::getCompanyId, entity.getCompanyId()); + lqw.eq(HotHiddenDangerInspectStore::getCode, entity.getCode()); + lqw.eq(HotHiddenDangerInspectStore::getInspectType, entity.getInspectType()); + lqw.eq(HotHiddenDangerInspectStore::getIsDeleted, 0L); + if (entity.getId() != null) { + lqw.ne(HotHiddenDangerInspectStore::getId, entity.getId()); + } + Long count = baseMapper.selectCount(lqw); + if (count != null && count > 0) { + throw new ServiceException("同一公司下隐患排查检查库代码已存在,请修改后重试"); + } + } + + /** + * 校验并批量删除隐患排查检查库信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean clearAll() { + Long companyId = LoginHelper.getCompanyId(); + if (companyId == null) { + throw new ServiceException("当前登录用户无公司信息"); + } + // 逻辑删除当前公司所有配置 + return baseMapper.delete( + Wrappers.lambdaQuery(HotHiddenDangerInspectStore.class) + .eq(HotHiddenDangerInspectStore::getCompanyId, companyId) + ) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/mediaResource/controller/HotMediaResourceController.java b/src/main/java/com/hotwj/platform/config/mediaResource/controller/HotMediaResourceController.java new file mode 100644 index 0000000..ca1e465 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/mediaResource/controller/HotMediaResourceController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.config.mediaResource.controller; + +import com.hotwj.platform.config.mediaResource.domain.bo.HotMediaResourceBo; +import com.hotwj.platform.config.mediaResource.domain.vo.HotMediaResourceVo; +import com.hotwj.platform.config.mediaResource.service.IHotMediaResourceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 视频/音频资源库 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/mediaResource") +@Tag(name = "视频/音频资源库", description = "视频/音频资源库管理") +public class HotMediaResourceController extends BaseController { + + private final IHotMediaResourceService hotMediaResourceService; + + /** + * 查询视频/音频资源库列表 + */ + //@SaCheckPermission("config:mediaResource:list") + @GetMapping("/list") + @Operation(summary = "分页查询视频/音频资源库列表") + public TableDataInfo list(HotMediaResourceBo bo, PageQuery pageQuery) { + return hotMediaResourceService.queryPageList(bo, pageQuery); + } + + /** + * 导出视频/音频资源库列表 + */ + //@SaCheckPermission("config:mediaResource:export") + @Log(title = "视频/音频资源库", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出视频/音频资源库列表") + public void export(HotMediaResourceBo bo, HttpServletResponse response) { + List list = hotMediaResourceService.queryList(bo); + ExcelUtil.exportExcel(list, "视频/音频资源库", HotMediaResourceVo.class, response); + } + + /** + * 获取视频/音频资源库详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:mediaResource:query") + @GetMapping("/{id}") + @Operation(summary = "获取视频/音频资源库详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotMediaResourceService.queryById(id)); + } + + /** + * 新增视频/音频资源库 + */ + //@SaCheckPermission("config:mediaResource:add") + @Log(title = "视频/音频资源库", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增视频/音频资源库") + public R add(@Validated(AddGroup.class) @RequestBody HotMediaResourceBo bo) { + return toAjax(hotMediaResourceService.insertByBo(bo)); + } + + /** + * 修改视频/音频资源库 + */ + //@SaCheckPermission("config:mediaResource:edit") + @Log(title = "视频/音频资源库", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改视频/音频资源库") + public R edit(@Validated(EditGroup.class) @RequestBody HotMediaResourceBo bo) { + return toAjax(hotMediaResourceService.updateByBo(bo)); + } + + /** + * 删除视频/音频资源库 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:mediaResource:remove") + @Log(title = "视频/音频资源库", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除视频/音频资源库") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotMediaResourceService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/mediaResource/domain/HotMediaResource.java b/src/main/java/com/hotwj/platform/config/mediaResource/domain/HotMediaResource.java new file mode 100644 index 0000000..a7346fc --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/mediaResource/domain/HotMediaResource.java @@ -0,0 +1,94 @@ +package com.hotwj.platform.config.mediaResource.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 视频/音频资源库对象 hot_media_resource + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_media_resource") +public class HotMediaResource extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 资源名称 + */ + private String name; + + /** + * 视频分类字典编码 + */ + private String categoryCode; + + /** + * 资源类型:1=视频 2=音频 + */ + private Long mediaType; + + /** + * 教育类型 + */ + private String trainingTypeCode; + + /** + * 教育类型子集编码集(JSON/逗号分隔) + */ + private String trainingSubtypeCodes; + + /** + * 资源文件URL + */ + private String fileUrl; + + /** + * 资源大小(字节) + */ + private Long sizeBytes; + + /** + * 视频时长(秒) + */ + private Long durationSeconds; + + /** + * 状态:1=启用 0=禁用 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/mediaResource/domain/bo/HotMediaResourceBo.java b/src/main/java/com/hotwj/platform/config/mediaResource/domain/bo/HotMediaResourceBo.java new file mode 100644 index 0000000..ae312e8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/mediaResource/domain/bo/HotMediaResourceBo.java @@ -0,0 +1,89 @@ +package com.hotwj.platform.config.mediaResource.domain.bo; + +import com.hotwj.platform.config.mediaResource.domain.HotMediaResource; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 视频/音频资源库业务对象 hot_media_resource + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotMediaResource.class, reverseConvertGenerate = false) +public class HotMediaResourceBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 资源名称 + */ + private String name; + + /** + * 视频分类字典编码 + */ + private String categoryCode; + + /** + * 资源类型:1=视频 2=音频 + */ + private Long mediaType; + + /** + * 教育类型 + */ + private String trainingTypeCode; + + /** + * 教育类型子集编码集(JSON/逗号分隔) + */ + private String trainingSubtypeCodes; + + /** + * 资源文件URL + */ + private String fileUrl; + + /** + * 资源大小(字节) + */ + private Long sizeBytes; + + /** + * 视频时长(秒) + */ + private Long durationSeconds; + + /** + * 状态:1=启用 0=禁用 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/mediaResource/domain/vo/HotMediaResourceVo.java b/src/main/java/com/hotwj/platform/config/mediaResource/domain/vo/HotMediaResourceVo.java new file mode 100644 index 0000000..2845c86 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/mediaResource/domain/vo/HotMediaResourceVo.java @@ -0,0 +1,103 @@ +package com.hotwj.platform.config.mediaResource.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.mediaResource.domain.HotMediaResource; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 视频/音频资源库视图对象 hot_media_resource + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotMediaResource.class) +public class HotMediaResourceVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 资源名称 + */ + @ExcelProperty(value = "资源名称") + private String name; + + /** + * 视频分类字典编码 + */ + @ExcelProperty(value = "视频分类字典编码") + private String categoryCode; + + /** + * 资源类型:1=视频 2=音频 + */ + @ExcelProperty(value = "资源类型:1=视频 2=音频") + private Long mediaType; + + /** + * 教育类型 + */ + @ExcelProperty(value = "教育类型") + private String trainingTypeCode; + + /** + * 教育类型子集编码集(JSON/逗号分隔) + */ + @ExcelProperty(value = "教育类型子集编码集(JSON/逗号分隔)") + private String trainingSubtypeCodes; + + /** + * 资源文件URL + */ + @ExcelProperty(value = "资源文件URL") + private String fileUrl; + + /** + * 资源大小(字节) + */ + @ExcelProperty(value = "资源大小(字节)") + private Long sizeBytes; + + /** + * 视频时长(秒) + */ + @ExcelProperty(value = "视频时长(秒)") + private Long durationSeconds; + + /** + * 状态:1=启用 0=禁用 + */ + @ExcelProperty(value = "状态:1=启用 0=禁用") + private Long status; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + private Long isDeleted; + + private Date createTime; +} diff --git a/src/main/java/com/hotwj/platform/config/mediaResource/mapper/HotMediaResourceMapper.java b/src/main/java/com/hotwj/platform/config/mediaResource/mapper/HotMediaResourceMapper.java new file mode 100644 index 0000000..0c396a6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/mediaResource/mapper/HotMediaResourceMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.mediaResource.mapper; + +import com.hotwj.platform.config.mediaResource.domain.HotMediaResource; +import com.hotwj.platform.config.mediaResource.domain.vo.HotMediaResourceVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 视频/音频资源库Mapper接口 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Mapper +public interface HotMediaResourceMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/mediaResource/service/IHotMediaResourceService.java b/src/main/java/com/hotwj/platform/config/mediaResource/service/IHotMediaResourceService.java new file mode 100644 index 0000000..11c402d --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/mediaResource/service/IHotMediaResourceService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.mediaResource.service; + +import com.hotwj.platform.config.mediaResource.domain.bo.HotMediaResourceBo; +import com.hotwj.platform.config.mediaResource.domain.vo.HotMediaResourceVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 视频/音频资源库Service接口 + * + * @author shihongwei + * @date 2025-12-23 + */ +public interface IHotMediaResourceService { + + /** + * 查询视频/音频资源库 + * + * @param id 主键 + * @return 视频/音频资源库 + */ + HotMediaResourceVo queryById(Long id); + + /** + * 分页查询视频/音频资源库列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 视频/音频资源库分页列表 + */ + TableDataInfo queryPageList(HotMediaResourceBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的视频/音频资源库列表 + * + * @param bo 查询条件 + * @return 视频/音频资源库列表 + */ + List queryList(HotMediaResourceBo bo); + + /** + * 新增视频/音频资源库 + * + * @param bo 视频/音频资源库 + * @return 是否新增成功 + */ + Boolean insertByBo(HotMediaResourceBo bo); + + /** + * 修改视频/音频资源库 + * + * @param bo 视频/音频资源库 + * @return 是否修改成功 + */ + Boolean updateByBo(HotMediaResourceBo bo); + + /** + * 校验并批量删除视频/音频资源库信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/mediaResource/service/impl/HotMediaResourceServiceImpl.java b/src/main/java/com/hotwj/platform/config/mediaResource/service/impl/HotMediaResourceServiceImpl.java new file mode 100644 index 0000000..ded8b5d --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/mediaResource/service/impl/HotMediaResourceServiceImpl.java @@ -0,0 +1,142 @@ +package com.hotwj.platform.config.mediaResource.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.mediaResource.domain.HotMediaResource; +import com.hotwj.platform.config.mediaResource.domain.bo.HotMediaResourceBo; +import com.hotwj.platform.config.mediaResource.domain.vo.HotMediaResourceVo; +import com.hotwj.platform.config.mediaResource.mapper.HotMediaResourceMapper; +import com.hotwj.platform.config.mediaResource.service.IHotMediaResourceService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 视频/音频资源库Service业务层处理 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotMediaResourceServiceImpl implements IHotMediaResourceService { + + private final HotMediaResourceMapper baseMapper; + + /** + * 查询视频/音频资源库 + * + * @param id 主键 + * @return 视频/音频资源库 + */ + @Override + public HotMediaResourceVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询视频/音频资源库列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 视频/音频资源库分页列表 + */ + @Override + public TableDataInfo queryPageList(HotMediaResourceBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的视频/音频资源库列表 + * + * @param bo 查询条件 + * @return 视频/音频资源库列表 + */ + @Override + public List queryList(HotMediaResourceBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotMediaResourceBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotMediaResource::getId); +// lqw.eq(bo.getCompanyId() != null, HotMediaResource::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getName()), HotMediaResource::getName, bo.getName()); + lqw.like(StringUtils.isNotBlank(bo.getCategoryCode()), HotMediaResource::getCategoryCode, bo.getCategoryCode()); + lqw.eq(bo.getMediaType() != null, HotMediaResource::getMediaType, bo.getMediaType()); + lqw.eq(StringUtils.isNotBlank(bo.getFileUrl()), HotMediaResource::getFileUrl, bo.getFileUrl()); + lqw.eq(bo.getSizeBytes() != null, HotMediaResource::getSizeBytes, bo.getSizeBytes()); + lqw.eq(bo.getDurationSeconds() != null, HotMediaResource::getDurationSeconds, bo.getDurationSeconds()); + lqw.eq(bo.getStatus() != null, HotMediaResource::getStatus, bo.getStatus()); + lqw.eq(bo.getCreateTime() != null, HotMediaResource::getCreateTime, bo.getCreateTime()); + lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, + HotMediaResource::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); + return lqw; + } + + /** + * 新增视频/音频资源库 + * + * @param bo 视频/音频资源库 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotMediaResourceBo bo) { + HotMediaResource add = MapstructUtils.convert(bo, HotMediaResource.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改视频/音频资源库 + * + * @param bo 视频/音频资源库 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotMediaResourceBo bo) { + HotMediaResource update = MapstructUtils.convert(bo, HotMediaResource.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotMediaResource entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除视频/音频资源库信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/questionBank/controller/HotQuestionBankController.java b/src/main/java/com/hotwj/platform/config/questionBank/controller/HotQuestionBankController.java new file mode 100644 index 0000000..eff6918 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/questionBank/controller/HotQuestionBankController.java @@ -0,0 +1,148 @@ +package com.hotwj.platform.config.questionBank.controller; + +import com.hotwj.platform.config.questionBank.domain.bo.HotQuestionBankBo; +import com.hotwj.platform.config.questionBank.domain.vo.HotQuestionBankImportVo; +import com.hotwj.platform.config.questionBank.domain.vo.HotQuestionBankVo; +import com.hotwj.platform.config.questionBank.listener.HotQuestionBankImportListener; +import com.hotwj.platform.config.questionBank.service.IHotQuestionBankService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.ArrayList; +import java.util.List; + +/** + * 题库 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/questionBank") +@Tag(name = "题库", description = "题库管理") +public class HotQuestionBankController extends BaseController { + + private final IHotQuestionBankService hotQuestionBankService; + + /** + * 查询题库列表 + */ + //@SaCheckPermission("config:questionBank:list") + @GetMapping("/list") + @Operation(summary = "分页查询题库列表") + public TableDataInfo list(HotQuestionBankBo bo, PageQuery pageQuery) { + return hotQuestionBankService.queryPageList(bo, pageQuery); + } + + /** + * 导出题库列表 + */ + //@SaCheckPermission("config:questionBank:export") + @Log(title = "题库", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出题库列表") + public void export(HotQuestionBankBo bo, HttpServletResponse response) { + List list = hotQuestionBankService.queryList(bo); + ExcelUtil.exportExcel(list, "题库", HotQuestionBankVo.class, response); + } + + /** + * 导入数据 + * + * @param file 导入文件 + */ + @Log(title = "题库", businessType = BusinessType.IMPORT) + //@SaCheckPermission("config:questionBank:import") + @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @Operation(summary = "导入题库数据") + public R importData(@RequestPart("file") MultipartFile file) throws Exception { + var result = ExcelUtil.importExcel( + file.getInputStream(), HotQuestionBankImportVo.class, + new HotQuestionBankImportListener() + ); + return R.ok(result.getAnalysis()); + } + + /** + * 获取导入模板 + */ + @PostMapping("/importTemplate") + @Operation(summary = "下载题库导入模板") + public void importTemplate(HttpServletResponse response) { + ExcelUtil.exportExcel(new ArrayList<>(), "题库导入模板", HotQuestionBankImportVo.class, response); + } + + /** + * 获取题库详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:questionBank:query") + @GetMapping("/{id}") + @Operation(summary = "获取题库详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotQuestionBankService.queryById(id)); + } + + /** + * 新增题库 + */ + //@SaCheckPermission("config:questionBank:add") + @Log(title = "题库", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增题库") + public R add(@Validated(AddGroup.class) @RequestBody HotQuestionBankBo bo) { + return toAjax(hotQuestionBankService.insertByBo(bo)); + } + + /** + * 修改题库 + */ + //@SaCheckPermission("config:questionBank:edit") + @Log(title = "题库", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改题库") + public R edit(@Validated(EditGroup.class) @RequestBody HotQuestionBankBo bo) { + return toAjax(hotQuestionBankService.updateByBo(bo)); + } + + /** + * 删除题库 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:questionBank:remove") + @Log(title = "题库", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除题库") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotQuestionBankService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/questionBank/domain/HotQuestionBank.java b/src/main/java/com/hotwj/platform/config/questionBank/domain/HotQuestionBank.java new file mode 100644 index 0000000..e9b681f --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/questionBank/domain/HotQuestionBank.java @@ -0,0 +1,129 @@ +package com.hotwj.platform.config.questionBank.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 题库对象 hot_question_bank + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_question_bank") +public class HotQuestionBank extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 题型:1=单选 2=多选 3=判断 + */ + private Long questionType; + + /** + * 教育类型字典编码 + */ + private String trainingTypeCode; + + /** + * 教育类型子集编码集(JSON/逗号分隔) + */ + private String trainingSubtypeCodes; + + /** + * 问题描述 + */ + private String questionDesc; + + /** + * 插图URL + */ + private String imageUrl; + + /** + * 选项A + */ + private String optionA; + + /** + * 选项B + */ + private String optionB; + + /** + * 选项C + */ + private String optionC; + + /** + * 选项D + */ + private String optionD; + + /** + * 选项E + */ + private String optionE; + + /** + * 选项F + */ + private String optionF; + + /** + * 正确答案(如 A,B) + */ + private String correctAnswers; + + /** + * 分值 + */ + private Long score; + + /** + * 难度:1=普通 2=困难 3=极难 + */ + private Long difficulty; + + /** + * 案例描述 + */ + private String caseDesc; + + /** + * 状态:1=启用 0=禁用 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/questionBank/domain/bo/HotQuestionBankBo.java b/src/main/java/com/hotwj/platform/config/questionBank/domain/bo/HotQuestionBankBo.java new file mode 100644 index 0000000..67135a1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/questionBank/domain/bo/HotQuestionBankBo.java @@ -0,0 +1,123 @@ +package com.hotwj.platform.config.questionBank.domain.bo; + +import com.hotwj.platform.config.questionBank.domain.HotQuestionBank; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 题库业务对象 hot_question_bank + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotQuestionBank.class, reverseConvertGenerate = false) +public class HotQuestionBankBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 题型:1=单选 2=多选 3=判断 + */ + private Long questionType; + + /** + * 教育类型字典编码 + */ + private String trainingTypeCode; + + /** + * 教育类型子集编码集(JSON/逗号分隔) + */ + private String trainingSubtypeCodes; + + /** + * 问题描述 + */ + private String questionDesc; + + /** + * 插图URL + */ + private String imageUrl; + + /** + * 选项A + */ + private String optionA; + + /** + * 选项B + */ + private String optionB; + + /** + * 选项C + */ + private String optionC; + + /** + * 选项D + */ + private String optionD; + + /** + * 选项E + */ + private String optionE; + + /** + * 选项F + */ + private String optionF; + + /** + * 正确答案(如 A,B) + */ + private String correctAnswers; + + /** + * 分值 + */ + private Long score; + + /** + * 难度:1=普通 2=困难 3=极难 + */ + private Long difficulty; + + /** + * 案例描述 + */ + private String caseDesc; + + /** + * 状态:1=启用 0=禁用 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + + /** + * 题目IDs(逗号分隔) + */ + private String ids; +} diff --git a/src/main/java/com/hotwj/platform/config/questionBank/domain/vo/HotQuestionBankImportVo.java b/src/main/java/com/hotwj/platform/config/questionBank/domain/vo/HotQuestionBankImportVo.java new file mode 100644 index 0000000..131574a --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/questionBank/domain/vo/HotQuestionBankImportVo.java @@ -0,0 +1,63 @@ +package com.hotwj.platform.config.questionBank.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; + +@Data +@NoArgsConstructor +@ExcelIgnoreUnannotated +public class HotQuestionBankImportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty(value = "检测题类型", index = 0, converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1=单选,2=多选,3=判断") + private Long questionType; + + @ExcelProperty(value = "问题描述", index = 1) + private String questionDesc; + + @ExcelProperty(value = "选项A", index = 2) + private String optionA; + + @ExcelProperty(value = "选项B", index = 3) + private String optionB; + + @ExcelProperty(value = "选项C", index = 4) + private String optionC; + + @ExcelProperty(value = "选项D", index = 5) + private String optionD; + + @ExcelProperty(value = "选项E", index = 6) + private String optionE; + + @ExcelProperty(value = "选项F", index = 7) + private String optionF; + + @ExcelProperty(value = "正确答案", index = 8) + private String correctAnswers; + + @ExcelProperty(value = "分数", index = 9) + private Long score; + + @ExcelProperty(value = "难度等级", index = 10, converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1=普通,2=困难,3=极难") + private Long difficulty; + + @ExcelProperty(value = "案例描述", index = 11) + private String caseDesc; + + @ExcelProperty(value = "题型分类", index = 12, converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_training_type") + private String trainingTypeCode; +} + diff --git a/src/main/java/com/hotwj/platform/config/questionBank/domain/vo/HotQuestionBankVo.java b/src/main/java/com/hotwj/platform/config/questionBank/domain/vo/HotQuestionBankVo.java new file mode 100644 index 0000000..61264f4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/questionBank/domain/vo/HotQuestionBankVo.java @@ -0,0 +1,140 @@ +package com.hotwj.platform.config.questionBank.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import com.hotwj.platform.config.questionBank.domain.HotQuestionBank; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 题库视图对象 hot_question_bank + * + * @author shihongwei + * @date 2025-12-23 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotQuestionBank.class) +public class HotQuestionBankVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 题型:1=单选 2=多选 3=判断 + */ + @ExcelProperty(value = "检测题类型", index = 1, converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1=单选,2=多选,3=判断") + private Long questionType; + + /** + * 教育类型字典编码 + */ + @ExcelProperty(value = "分类", index = 0, converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_training_type") + private String trainingTypeCode; + + /** + * 教育类型子集编码集(JSON/逗号分隔) + */ + private String trainingSubtypeCodes; + + /** + * 问题描述 + */ + @ExcelProperty(value = "问题描述", index = 2) + private String questionDesc; + + /** + * 插图URL + */ + private String imageUrl; + + /** + * 选项A + */ + @ExcelProperty(value = "选项A", index = 3) + private String optionA; + + /** + * 选项B + */ + @ExcelProperty(value = "选项B", index = 4) + private String optionB; + + /** + * 选项C + */ + @ExcelProperty(value = "选项C", index = 5) + private String optionC; + + /** + * 选项D + */ + @ExcelProperty(value = "选项D", index = 6) + private String optionD; + + /** + * 选项E + */ + @ExcelProperty(value = "选项E", index = 7) + private String optionE; + + /** + * 选项F + */ + @ExcelProperty(value = "选项F", index = 8) + private String optionF; + + /** + * 正确答案(如 A,B) + */ + @ExcelProperty(value = "正确答案", index = 9) + private String correctAnswers; + + /** + * 分值 + */ + @ExcelProperty(value = "分数", index = 10) + private Long score; + + + + /** + * 难度:1=普通 2=困难 3=极难 + */ + @ExcelProperty(value = "难度等级", index = 12) + private Long difficulty; + + /** + * 案例描述 + */ + private String caseDesc; + + /** + * 状态:1=启用 0=禁用 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + private Date createTime; + +} diff --git a/src/main/java/com/hotwj/platform/config/questionBank/listener/HotQuestionBankImportListener.java b/src/main/java/com/hotwj/platform/config/questionBank/listener/HotQuestionBankImportListener.java new file mode 100644 index 0000000..e09665e --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/questionBank/listener/HotQuestionBankImportListener.java @@ -0,0 +1,77 @@ +package com.hotwj.platform.config.questionBank.listener; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.idev.excel.context.AnalysisContext; +import cn.idev.excel.event.AnalysisEventListener; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.excel.core.ExcelListener; +import org.dromara.common.excel.core.ExcelResult; +import com.hotwj.platform.config.questionBank.domain.bo.HotQuestionBankBo; +import com.hotwj.platform.config.questionBank.domain.vo.HotQuestionBankImportVo; +import com.hotwj.platform.config.questionBank.service.IHotQuestionBankService; + +import java.util.List; + +@Slf4j +public class HotQuestionBankImportListener extends AnalysisEventListener implements ExcelListener { + + private final IHotQuestionBankService questionBankService; + + private int successNum = 0; + private int failureNum = 0; + private final StringBuilder successMsg = new StringBuilder(); + private final StringBuilder failureMsg = new StringBuilder(); + + public HotQuestionBankImportListener() { + this.questionBankService = SpringUtils.getBean(IHotQuestionBankService.class); + } + + @Override + public void invoke(HotQuestionBankImportVo vo, AnalysisContext context) { + try { + HotQuestionBankBo bo = BeanUtil.toBean(vo, HotQuestionBankBo.class); + questionBankService.insertByBo(bo); + successNum++; + successMsg.append("
").append(successNum).append("、题目 ").append(ObjectUtil.defaultIfNull(vo.getQuestionDesc(), "")).append(" 导入成功"); + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、题目 导入失败:"; + failureMsg.append(msg).append(e.getMessage()); + log.error(msg, e); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + } + + @Override + public ExcelResult getExcelResult() { + return new ExcelResult<>() { + @Override + public String getAnalysis() { + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + + @Override + public List getList() { + return null; + } + + @Override + public List getErrorList() { + return null; + } + }; + } +} + diff --git a/src/main/java/com/hotwj/platform/config/questionBank/mapper/HotQuestionBankMapper.java b/src/main/java/com/hotwj/platform/config/questionBank/mapper/HotQuestionBankMapper.java new file mode 100644 index 0000000..37d90e8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/questionBank/mapper/HotQuestionBankMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.questionBank.mapper; + +import com.hotwj.platform.config.questionBank.domain.HotQuestionBank; +import com.hotwj.platform.config.questionBank.domain.vo.HotQuestionBankVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 题库Mapper接口 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Mapper +public interface HotQuestionBankMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/questionBank/service/IHotQuestionBankService.java b/src/main/java/com/hotwj/platform/config/questionBank/service/IHotQuestionBankService.java new file mode 100644 index 0000000..4d36e19 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/questionBank/service/IHotQuestionBankService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.questionBank.service; + +import com.hotwj.platform.config.questionBank.domain.bo.HotQuestionBankBo; +import com.hotwj.platform.config.questionBank.domain.vo.HotQuestionBankVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 题库Service接口 + * + * @author shihongwei + * @date 2025-12-23 + */ +public interface IHotQuestionBankService { + + /** + * 查询题库 + * + * @param id 主键 + * @return 题库 + */ + HotQuestionBankVo queryById(Long id); + + /** + * 分页查询题库列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 题库分页列表 + */ + TableDataInfo queryPageList(HotQuestionBankBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的题库列表 + * + * @param bo 查询条件 + * @return 题库列表 + */ + List queryList(HotQuestionBankBo bo); + + /** + * 新增题库 + * + * @param bo 题库 + * @return 是否新增成功 + */ + Boolean insertByBo(HotQuestionBankBo bo); + + /** + * 修改题库 + * + * @param bo 题库 + * @return 是否修改成功 + */ + Boolean updateByBo(HotQuestionBankBo bo); + + /** + * 校验并批量删除题库信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/questionBank/service/impl/HotQuestionBankServiceImpl.java b/src/main/java/com/hotwj/platform/config/questionBank/service/impl/HotQuestionBankServiceImpl.java new file mode 100644 index 0000000..44ec579 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/questionBank/service/impl/HotQuestionBankServiceImpl.java @@ -0,0 +1,152 @@ +package com.hotwj.platform.config.questionBank.service.impl; + +import cn.hutool.core.convert.Convert; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.questionBank.domain.HotQuestionBank; +import com.hotwj.platform.config.questionBank.domain.bo.HotQuestionBankBo; +import com.hotwj.platform.config.questionBank.domain.vo.HotQuestionBankVo; +import com.hotwj.platform.config.questionBank.mapper.HotQuestionBankMapper; +import com.hotwj.platform.config.questionBank.service.IHotQuestionBankService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 题库Service业务层处理 + * + * @author shihongwei + * @date 2025-12-23 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotQuestionBankServiceImpl implements IHotQuestionBankService { + + private final HotQuestionBankMapper baseMapper; + + /** + * 查询题库 + * + * @param id 主键 + * @return 题库 + */ + @Override + public HotQuestionBankVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询题库列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 题库分页列表 + */ + @Override + public TableDataInfo queryPageList(HotQuestionBankBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的题库列表 + * + * @param bo 查询条件 + * @return 题库列表 + */ + @Override + public List queryList(HotQuestionBankBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotQuestionBankBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotQuestionBank::getId); + lqw.in(StringUtils.isNotBlank(bo.getIds()), HotQuestionBank::getId, StringUtils.splitTo(bo.getIds(), Convert::toLong)); +// lqw.eq(bo.getCompanyId() != null, HotQuestionBank::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getQuestionType() != null, HotQuestionBank::getQuestionType, bo.getQuestionType()); + lqw.eq(StringUtils.isNotBlank(bo.getTrainingTypeCode()), HotQuestionBank::getTrainingTypeCode, bo.getTrainingTypeCode()); + lqw.eq(StringUtils.isNotBlank(bo.getTrainingSubtypeCodes()), HotQuestionBank::getTrainingSubtypeCodes, bo.getTrainingSubtypeCodes()); + lqw.like(StringUtils.isNotBlank(bo.getQuestionDesc()), HotQuestionBank::getQuestionDesc, bo.getQuestionDesc()); + lqw.eq(StringUtils.isNotBlank(bo.getImageUrl()), HotQuestionBank::getImageUrl, bo.getImageUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getOptionA()), HotQuestionBank::getOptionA, bo.getOptionA()); + lqw.eq(StringUtils.isNotBlank(bo.getOptionB()), HotQuestionBank::getOptionB, bo.getOptionB()); + lqw.eq(StringUtils.isNotBlank(bo.getOptionC()), HotQuestionBank::getOptionC, bo.getOptionC()); + lqw.eq(StringUtils.isNotBlank(bo.getOptionD()), HotQuestionBank::getOptionD, bo.getOptionD()); + lqw.eq(StringUtils.isNotBlank(bo.getOptionE()), HotQuestionBank::getOptionE, bo.getOptionE()); + lqw.eq(StringUtils.isNotBlank(bo.getOptionF()), HotQuestionBank::getOptionF, bo.getOptionF()); + lqw.eq(StringUtils.isNotBlank(bo.getCorrectAnswers()), HotQuestionBank::getCorrectAnswers, bo.getCorrectAnswers()); + lqw.eq(bo.getScore() != null, HotQuestionBank::getScore, bo.getScore()); + lqw.eq(bo.getDifficulty() != null, HotQuestionBank::getDifficulty, bo.getDifficulty()); + lqw.eq(StringUtils.isNotBlank(bo.getCaseDesc()), HotQuestionBank::getCaseDesc, bo.getCaseDesc()); + lqw.eq(bo.getStatus() != null, HotQuestionBank::getStatus, bo.getStatus()); + lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, + HotQuestionBank::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); + return lqw; + } + + /** + * 新增题库 + * + * @param bo 题库 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotQuestionBankBo bo) { + HotQuestionBank add = MapstructUtils.convert(bo, HotQuestionBank.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改题库 + * + * @param bo 题库 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotQuestionBankBo bo) { + HotQuestionBank update = MapstructUtils.convert(bo, HotQuestionBank.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotQuestionBank entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除题库信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/riskLevelConfig/controller/HotRiskLevelConfigController.java b/src/main/java/com/hotwj/platform/config/riskLevelConfig/controller/HotRiskLevelConfigController.java new file mode 100644 index 0000000..dd1a728 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/riskLevelConfig/controller/HotRiskLevelConfigController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.config.riskLevelConfig.controller; + +import com.hotwj.platform.config.riskLevelConfig.domain.bo.HotRiskLevelConfigBo; +import com.hotwj.platform.config.riskLevelConfig.domain.vo.HotRiskLevelConfigVo; +import com.hotwj.platform.config.riskLevelConfig.service.IHotRiskLevelConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 风险等级划分 + * + * @author shihongwei + * @date 2026-01-04 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/riskLevelConfig") +@Tag(name = "风险等级划分", description = "风险等级划分管理") +public class HotRiskLevelConfigController extends BaseController { + + private final IHotRiskLevelConfigService hotRiskLevelConfigService; + + /** + * 查询风险等级划分列表 + */ + //@SaCheckPermission("config:riskLevelConfig:list") + @GetMapping("/list") + @Operation(summary = "分页查询风险等级划分列表") + public TableDataInfo list(HotRiskLevelConfigBo bo, PageQuery pageQuery) { + return hotRiskLevelConfigService.queryPageList(bo, pageQuery); + } + + /** + * 导出风险等级划分列表 + */ + //@SaCheckPermission("config:riskLevelConfig:export") + @Log(title = "风险等级划分", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出风险等级划分列表") + public void export(HotRiskLevelConfigBo bo, HttpServletResponse response) { + List list = hotRiskLevelConfigService.queryList(bo); + ExcelUtil.exportExcel(list, "风险等级划分", HotRiskLevelConfigVo.class, response); + } + + /** + * 获取风险等级划分详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:riskLevelConfig:query") + @GetMapping("/{id}") + @Operation(summary = "获取风险等级划分详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotRiskLevelConfigService.queryById(id)); + } + + /** + * 新增风险等级划分 + */ + //@SaCheckPermission("config:riskLevelConfig:add") + @Log(title = "风险等级划分", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增风险等级划分") + public R add(@Validated(AddGroup.class) @RequestBody HotRiskLevelConfigBo bo) { + return toAjax(hotRiskLevelConfigService.insertByBo(bo)); + } + + /** + * 修改风险等级划分 + */ + //@SaCheckPermission("config:riskLevelConfig:edit") + @Log(title = "风险等级划分", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改风险等级划分") + public R edit(@Validated(EditGroup.class) @RequestBody HotRiskLevelConfigBo bo) { + return toAjax(hotRiskLevelConfigService.updateByBo(bo)); + } + + /** + * 删除风险等级划分 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:riskLevelConfig:remove") + @Log(title = "风险等级划分", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除风险等级划分") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotRiskLevelConfigService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/riskLevelConfig/domain/HotRiskLevelConfig.java b/src/main/java/com/hotwj/platform/config/riskLevelConfig/domain/HotRiskLevelConfig.java new file mode 100644 index 0000000..036c97a --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/riskLevelConfig/domain/HotRiskLevelConfig.java @@ -0,0 +1,59 @@ +package com.hotwj.platform.config.riskLevelConfig.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 风险等级划分对象 hot_risk_level_config + * + * @author shihongwei + * @date 2026-01-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_risk_level_config") +public class HotRiskLevelConfig extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 取值 + */ + private Long levelValue; + + /** + * 说明 + */ + private String levelName; + + /** + * 描述 + */ + private String description; + + /** + * 类型: 1=可能性, 2=严重程度 + */ + private Long type; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/riskLevelConfig/domain/bo/HotRiskLevelConfigBo.java b/src/main/java/com/hotwj/platform/config/riskLevelConfig/domain/bo/HotRiskLevelConfigBo.java new file mode 100644 index 0000000..2443cc8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/riskLevelConfig/domain/bo/HotRiskLevelConfigBo.java @@ -0,0 +1,61 @@ +package com.hotwj.platform.config.riskLevelConfig.domain.bo; + +import com.hotwj.platform.config.riskLevelConfig.domain.HotRiskLevelConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 风险等级划分业务对象 hot_risk_level_config + * + * @author shihongwei + * @date 2026-01-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotRiskLevelConfig.class, reverseConvertGenerate = false) +public class HotRiskLevelConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 取值 + */ + @NotNull(message = "取值不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long levelValue; + + /** + * 说明 + */ + @NotBlank(message = "说明不能为空", groups = {AddGroup.class, EditGroup.class}) + private String levelName; + + /** + * 描述 + */ + @NotBlank(message = "描述不能为空", groups = {AddGroup.class, EditGroup.class}) + private String description; + + /** + * 类型: 1=可能性, 2=严重程度 + */ + @NotNull(message = "类型: 1=可能性, 2=严重程度不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long type; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/riskLevelConfig/domain/vo/HotRiskLevelConfigVo.java b/src/main/java/com/hotwj/platform/config/riskLevelConfig/domain/vo/HotRiskLevelConfigVo.java new file mode 100644 index 0000000..e3157ac --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/riskLevelConfig/domain/vo/HotRiskLevelConfigVo.java @@ -0,0 +1,64 @@ +package com.hotwj.platform.config.riskLevelConfig.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.riskLevelConfig.domain.HotRiskLevelConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 风险等级划分视图对象 hot_risk_level_config + * + * @author shihongwei + * @date 2026-01-04 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotRiskLevelConfig.class) +public class HotRiskLevelConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 取值 + */ + @ExcelProperty(value = "取值") + private Long levelValue; + + /** + * 说明 + */ + @ExcelProperty(value = "说明") + private String levelName; + + /** + * 描述 + */ + @ExcelProperty(value = "描述") + private String description; + + /** + * 类型: 1=可能性, 2=严重程度 + */ + @ExcelProperty(value = "类型: 1=可能性, 2=严重程度") + private Long type; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/riskLevelConfig/mapper/HotRiskLevelConfigMapper.java b/src/main/java/com/hotwj/platform/config/riskLevelConfig/mapper/HotRiskLevelConfigMapper.java new file mode 100644 index 0000000..6e387cb --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/riskLevelConfig/mapper/HotRiskLevelConfigMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.riskLevelConfig.mapper; + +import com.hotwj.platform.config.riskLevelConfig.domain.HotRiskLevelConfig; +import com.hotwj.platform.config.riskLevelConfig.domain.vo.HotRiskLevelConfigVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 风险等级划分Mapper接口 + * + * @author shihongwei + * @date 2026-01-04 + */ +@Mapper +public interface HotRiskLevelConfigMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/riskLevelConfig/service/IHotRiskLevelConfigService.java b/src/main/java/com/hotwj/platform/config/riskLevelConfig/service/IHotRiskLevelConfigService.java new file mode 100644 index 0000000..97115b4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/riskLevelConfig/service/IHotRiskLevelConfigService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.riskLevelConfig.service; + +import com.hotwj.platform.config.riskLevelConfig.domain.bo.HotRiskLevelConfigBo; +import com.hotwj.platform.config.riskLevelConfig.domain.vo.HotRiskLevelConfigVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 风险等级划分Service接口 + * + * @author shihongwei + * @date 2026-01-04 + */ +public interface IHotRiskLevelConfigService { + + /** + * 查询风险等级划分 + * + * @param id 主键 + * @return 风险等级划分 + */ + HotRiskLevelConfigVo queryById(Long id); + + /** + * 分页查询风险等级划分列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 风险等级划分分页列表 + */ + TableDataInfo queryPageList(HotRiskLevelConfigBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的风险等级划分列表 + * + * @param bo 查询条件 + * @return 风险等级划分列表 + */ + List queryList(HotRiskLevelConfigBo bo); + + /** + * 新增风险等级划分 + * + * @param bo 风险等级划分 + * @return 是否新增成功 + */ + Boolean insertByBo(HotRiskLevelConfigBo bo); + + /** + * 修改风险等级划分 + * + * @param bo 风险等级划分 + * @return 是否修改成功 + */ + Boolean updateByBo(HotRiskLevelConfigBo bo); + + /** + * 校验并批量删除风险等级划分信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/riskLevelConfig/service/impl/HotRiskLevelConfigServiceImpl.java b/src/main/java/com/hotwj/platform/config/riskLevelConfig/service/impl/HotRiskLevelConfigServiceImpl.java new file mode 100644 index 0000000..8856ebb --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/riskLevelConfig/service/impl/HotRiskLevelConfigServiceImpl.java @@ -0,0 +1,136 @@ +package com.hotwj.platform.config.riskLevelConfig.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.riskLevelConfig.domain.HotRiskLevelConfig; +import com.hotwj.platform.config.riskLevelConfig.domain.bo.HotRiskLevelConfigBo; +import com.hotwj.platform.config.riskLevelConfig.domain.vo.HotRiskLevelConfigVo; +import com.hotwj.platform.config.riskLevelConfig.mapper.HotRiskLevelConfigMapper; +import com.hotwj.platform.config.riskLevelConfig.service.IHotRiskLevelConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 风险等级划分Service业务层处理 + * + * @author shihongwei + * @date 2026-01-04 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotRiskLevelConfigServiceImpl implements IHotRiskLevelConfigService { + + private final HotRiskLevelConfigMapper baseMapper; + + /** + * 查询风险等级划分 + * + * @param id 主键 + * @return 风险等级划分 + */ + @Override + public HotRiskLevelConfigVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询风险等级划分列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 风险等级划分分页列表 + */ + @Override + public TableDataInfo queryPageList(HotRiskLevelConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的风险等级划分列表 + * + * @param bo 查询条件 + * @return 风险等级划分列表 + */ + @Override + public List queryList(HotRiskLevelConfigBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotRiskLevelConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotRiskLevelConfig::getLevelValue); + lqw.eq(bo.getLevelValue() != null, HotRiskLevelConfig::getLevelValue, bo.getLevelValue()); + lqw.like(StringUtils.isNotBlank(bo.getLevelName()), HotRiskLevelConfig::getLevelName, bo.getLevelName()); + lqw.eq(StringUtils.isNotBlank(bo.getDescription()), HotRiskLevelConfig::getDescription, bo.getDescription()); + lqw.eq(bo.getType() != null, HotRiskLevelConfig::getType, bo.getType()); + lqw.eq(bo.getIsDeleted() != null, HotRiskLevelConfig::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增风险等级划分 + * + * @param bo 风险等级划分 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotRiskLevelConfigBo bo) { + HotRiskLevelConfig add = MapstructUtils.convert(bo, HotRiskLevelConfig.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改风险等级划分 + * + * @param bo 风险等级划分 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotRiskLevelConfigBo bo) { + HotRiskLevelConfig update = MapstructUtils.convert(bo, HotRiskLevelConfig.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotRiskLevelConfig entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除风险等级划分信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/riskScoreLevel/controller/HotRiskScoreLevelController.java b/src/main/java/com/hotwj/platform/config/riskScoreLevel/controller/HotRiskScoreLevelController.java new file mode 100644 index 0000000..44d81e3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/riskScoreLevel/controller/HotRiskScoreLevelController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.config.riskScoreLevel.controller; + +import com.hotwj.platform.config.riskScoreLevel.domain.bo.HotRiskScoreLevelBo; +import com.hotwj.platform.config.riskScoreLevel.domain.vo.HotRiskScoreLevelVo; +import com.hotwj.platform.config.riskScoreLevel.service.IHotRiskScoreLevelService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 风险等级划分 + * + * @author shihongwei + * @date 2026-01-04 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/riskScoreLevel") +@Tag(name = "风险等级划分", description = "风险等级划分管理") +public class HotRiskScoreLevelController extends BaseController { + + private final IHotRiskScoreLevelService hotRiskScoreLevelService; + + /** + * 查询风险等级划分列表 + */ + //@SaCheckPermission("config:riskScoreLevel:list") + @GetMapping("/list") + @Operation(summary = "分页查询风险等级划分列表") + public TableDataInfo list(HotRiskScoreLevelBo bo, PageQuery pageQuery) { + return hotRiskScoreLevelService.queryPageList(bo, pageQuery); + } + + /** + * 导出风险等级划分列表 + */ + //@SaCheckPermission("config:riskScoreLevel:export") + @Log(title = "风险等级划分", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出风险等级划分列表") + public void export(HotRiskScoreLevelBo bo, HttpServletResponse response) { + List list = hotRiskScoreLevelService.queryList(bo); + ExcelUtil.exportExcel(list, "风险等级划分", HotRiskScoreLevelVo.class, response); + } + + /** + * 获取风险等级划分详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:riskScoreLevel:query") + @GetMapping("/{id}") + @Operation(summary = "获取风险等级划分详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotRiskScoreLevelService.queryById(id)); + } + + /** + * 新增风险等级划分 + */ + //@SaCheckPermission("config:riskScoreLevel:add") + @Log(title = "风险等级划分", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增风险等级划分") + public R add(@Validated(AddGroup.class) @RequestBody HotRiskScoreLevelBo bo) { + return toAjax(hotRiskScoreLevelService.insertByBo(bo)); + } + + /** + * 修改风险等级划分 + */ + //@SaCheckPermission("config:riskScoreLevel:edit") + @Log(title = "风险等级划分", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改风险等级划分") + public R edit(@Validated(EditGroup.class) @RequestBody HotRiskScoreLevelBo bo) { + return toAjax(hotRiskScoreLevelService.updateByBo(bo)); + } + + /** + * 删除风险等级划分 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:riskScoreLevel:remove") + @Log(title = "风险等级划分", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除风险等级划分") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotRiskScoreLevelService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/riskScoreLevel/domain/HotRiskScoreLevel.java b/src/main/java/com/hotwj/platform/config/riskScoreLevel/domain/HotRiskScoreLevel.java new file mode 100644 index 0000000..b0bb9c1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/riskScoreLevel/domain/HotRiskScoreLevel.java @@ -0,0 +1,52 @@ +package com.hotwj.platform.config.riskScoreLevel.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 风险等级划分对象 hot_risk_score_level + * + * @author shihongwei + * @date 2026-01-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_risk_score_level") +public class HotRiskScoreLevel extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 风险最小值 + */ + private Long minScore; + + /** + * 风险最大值 + */ + private Long maxScore; + + /** + * 评价分级 + */ + private String levelName; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/riskScoreLevel/domain/bo/HotRiskScoreLevelBo.java b/src/main/java/com/hotwj/platform/config/riskScoreLevel/domain/bo/HotRiskScoreLevelBo.java new file mode 100644 index 0000000..b48fcb8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/riskScoreLevel/domain/bo/HotRiskScoreLevelBo.java @@ -0,0 +1,54 @@ +package com.hotwj.platform.config.riskScoreLevel.domain.bo; + +import com.hotwj.platform.config.riskScoreLevel.domain.HotRiskScoreLevel; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 风险等级划分业务对象 hot_risk_score_level + * + * @author shihongwei + * @date 2026-01-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotRiskScoreLevel.class, reverseConvertGenerate = false) +public class HotRiskScoreLevelBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 风险最小值 + */ + @NotNull(message = "风险最小值不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long minScore; + + /** + * 风险最大值 + */ + @NotNull(message = "风险最大值不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long maxScore; + + /** + * 评价分级 + */ + @NotBlank(message = "评价分级不能为空", groups = { AddGroup.class, EditGroup.class }) + private String levelName; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/riskScoreLevel/domain/vo/HotRiskScoreLevelVo.java b/src/main/java/com/hotwj/platform/config/riskScoreLevel/domain/vo/HotRiskScoreLevelVo.java new file mode 100644 index 0000000..75efa3e --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/riskScoreLevel/domain/vo/HotRiskScoreLevelVo.java @@ -0,0 +1,62 @@ +package com.hotwj.platform.config.riskScoreLevel.domain.vo; + +import com.hotwj.platform.config.riskScoreLevel.domain.HotRiskScoreLevel; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 风险等级划分视图对象 hot_risk_score_level + * + * @author shihongwei + * @date 2026-01-04 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotRiskScoreLevel.class) +public class HotRiskScoreLevelVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 风险最小值 + */ + @ExcelProperty(value = "风险最小值") + private Long minScore; + + /** + * 风险最大值 + */ + @ExcelProperty(value = "风险最大值") + private Long maxScore; + + /** + * 评价分级 + */ + @ExcelProperty(value = "评价分级") + private String levelName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/riskScoreLevel/mapper/HotRiskScoreLevelMapper.java b/src/main/java/com/hotwj/platform/config/riskScoreLevel/mapper/HotRiskScoreLevelMapper.java new file mode 100644 index 0000000..10a252e --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/riskScoreLevel/mapper/HotRiskScoreLevelMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.riskScoreLevel.mapper; + +import com.hotwj.platform.config.riskScoreLevel.domain.HotRiskScoreLevel; +import com.hotwj.platform.config.riskScoreLevel.domain.vo.HotRiskScoreLevelVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 风险等级划分Mapper接口 + * + * @author shihongwei + * @date 2026-01-04 + */ + @Mapper +public interface HotRiskScoreLevelMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/riskScoreLevel/service/IHotRiskScoreLevelService.java b/src/main/java/com/hotwj/platform/config/riskScoreLevel/service/IHotRiskScoreLevelService.java new file mode 100644 index 0000000..518b922 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/riskScoreLevel/service/IHotRiskScoreLevelService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.riskScoreLevel.service; + +import com.hotwj.platform.config.riskScoreLevel.domain.vo.HotRiskScoreLevelVo; +import com.hotwj.platform.config.riskScoreLevel.domain.bo.HotRiskScoreLevelBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 风险等级划分Service接口 + * + * @author shihongwei + * @date 2026-01-04 + */ +public interface IHotRiskScoreLevelService { + + /** + * 查询风险等级划分 + * + * @param id 主键 + * @return 风险等级划分 + */ + HotRiskScoreLevelVo queryById(Long id); + + /** + * 分页查询风险等级划分列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 风险等级划分分页列表 + */ + TableDataInfo queryPageList(HotRiskScoreLevelBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的风险等级划分列表 + * + * @param bo 查询条件 + * @return 风险等级划分列表 + */ + List queryList(HotRiskScoreLevelBo bo); + + /** + * 新增风险等级划分 + * + * @param bo 风险等级划分 + * @return 是否新增成功 + */ + Boolean insertByBo(HotRiskScoreLevelBo bo); + + /** + * 修改风险等级划分 + * + * @param bo 风险等级划分 + * @return 是否修改成功 + */ + Boolean updateByBo(HotRiskScoreLevelBo bo); + + /** + * 校验并批量删除风险等级划分信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/riskScoreLevel/service/impl/HotRiskScoreLevelServiceImpl.java b/src/main/java/com/hotwj/platform/config/riskScoreLevel/service/impl/HotRiskScoreLevelServiceImpl.java new file mode 100644 index 0000000..e52476d --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/riskScoreLevel/service/impl/HotRiskScoreLevelServiceImpl.java @@ -0,0 +1,135 @@ +package com.hotwj.platform.config.riskScoreLevel.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.hotwj.platform.config.riskScoreLevel.domain.bo.HotRiskScoreLevelBo; +import com.hotwj.platform.config.riskScoreLevel.domain.vo.HotRiskScoreLevelVo; +import com.hotwj.platform.config.riskScoreLevel.domain.HotRiskScoreLevel; +import com.hotwj.platform.config.riskScoreLevel.mapper.HotRiskScoreLevelMapper; +import com.hotwj.platform.config.riskScoreLevel.service.IHotRiskScoreLevelService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 风险等级划分Service业务层处理 + * + * @author shihongwei + * @date 2026-01-04 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotRiskScoreLevelServiceImpl implements IHotRiskScoreLevelService { + + private final HotRiskScoreLevelMapper baseMapper; + + /** + * 查询风险等级划分 + * + * @param id 主键 + * @return 风险等级划分 + */ + @Override + public HotRiskScoreLevelVo queryById(Long id){ + return baseMapper.selectVoById(id); + } + + /** + * 分页查询风险等级划分列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 风险等级划分分页列表 + */ + @Override + public TableDataInfo queryPageList(HotRiskScoreLevelBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的风险等级划分列表 + * + * @param bo 查询条件 + * @return 风险等级划分列表 + */ + @Override + public List queryList(HotRiskScoreLevelBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotRiskScoreLevelBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotRiskScoreLevel::getId); + lqw.eq(bo.getMinScore() != null, HotRiskScoreLevel::getMinScore, bo.getMinScore()); + lqw.eq(bo.getMaxScore() != null, HotRiskScoreLevel::getMaxScore, bo.getMaxScore()); + lqw.like(StringUtils.isNotBlank(bo.getLevelName()), HotRiskScoreLevel::getLevelName, bo.getLevelName()); + lqw.eq(bo.getIsDeleted() != null, HotRiskScoreLevel::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增风险等级划分 + * + * @param bo 风险等级划分 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotRiskScoreLevelBo bo) { + HotRiskScoreLevel add = MapstructUtils.convert(bo, HotRiskScoreLevel.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改风险等级划分 + * + * @param bo 风险等级划分 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotRiskScoreLevelBo bo) { + HotRiskScoreLevel update = MapstructUtils.convert(bo, HotRiskScoreLevel.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotRiskScoreLevel entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除风险等级划分信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/controller/HotSignAuditPermissionConfigController.java b/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/controller/HotSignAuditPermissionConfigController.java new file mode 100644 index 0000000..8bbdeeb --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/controller/HotSignAuditPermissionConfigController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.config.signAuditPermissionConfig.controller; + +import com.hotwj.platform.config.signAuditPermissionConfig.domain.bo.HotSignAuditPermissionConfigBo; +import com.hotwj.platform.config.signAuditPermissionConfig.domain.vo.HotSignAuditPermissionConfigVo; +import com.hotwj.platform.config.signAuditPermissionConfig.service.IHotSignAuditPermissionConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 签字审核权限配置 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/signAuditPermissionConfig") +@Tag(name = "签字审核权限配置", description = "签字审核权限配置管理") +public class HotSignAuditPermissionConfigController extends BaseController { + + private final IHotSignAuditPermissionConfigService hotSignAuditPermissionConfigService; + + /** + * 查询签字审核权限配置列表 + */ + //@SaCheckPermission("config:signAuditPermissionConfig:list") + @GetMapping("/list") + @Operation(summary = "分页查询签字审核权限配置列表") + public TableDataInfo list(HotSignAuditPermissionConfigBo bo, PageQuery pageQuery) { + return hotSignAuditPermissionConfigService.queryPageList(bo, pageQuery); + } + + /** + * 导出签字审核权限配置列表 + */ + //@SaCheckPermission("config:signAuditPermissionConfig:export") + @Log(title = "签字审核权限配置", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出签字审核权限配置列表") + public void export(HotSignAuditPermissionConfigBo bo, HttpServletResponse response) { + List list = hotSignAuditPermissionConfigService.queryList(bo); + ExcelUtil.exportExcel(list, "签字审核权限配置", HotSignAuditPermissionConfigVo.class, response); + } + + /** + * 获取签字审核权限配置详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:signAuditPermissionConfig:query") + @GetMapping("/{id}") + @Operation(summary = "获取签字审核权限配置详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotSignAuditPermissionConfigService.queryById(id)); + } + + /** + * 新增签字审核权限配置 + */ + //@SaCheckPermission("config:signAuditPermissionConfig:add") + @Log(title = "签字审核权限配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增签字审核权限配置") + public R add(@Validated(AddGroup.class) @RequestBody HotSignAuditPermissionConfigBo bo) { + return toAjax(hotSignAuditPermissionConfigService.insertByBo(bo)); + } + + /** + * 修改签字审核权限配置 + */ + //@SaCheckPermission("config:signAuditPermissionConfig:edit") + @Log(title = "签字审核权限配置", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改签字审核权限配置") + public R edit(@Validated(EditGroup.class) @RequestBody HotSignAuditPermissionConfigBo bo) { + return toAjax(hotSignAuditPermissionConfigService.updateByBo(bo)); + } + + /** + * 删除签字审核权限配置 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:signAuditPermissionConfig:remove") + @Log(title = "签字审核权限配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除签字审核权限配置") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotSignAuditPermissionConfigService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/domain/HotSignAuditPermissionConfig.java b/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/domain/HotSignAuditPermissionConfig.java new file mode 100644 index 0000000..e9d0f9e --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/domain/HotSignAuditPermissionConfig.java @@ -0,0 +1,64 @@ +package com.hotwj.platform.config.signAuditPermissionConfig.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 签字审核权限配置对象 hot_sign_audit_permission_config + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_sign_audit_permission_config") +public class HotSignAuditPermissionConfig extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 业务类型 1=车辆三检出车 2=车辆三检行车 3=车辆三检收车 4=隐患排查 5=隐患治理评估 6=隐患治理治理 7=隐患治理复查 + */ + private Long bizType; + + /** + * 审核人ID + */ + private Long auditorId; + + /** + * 审核人姓名 + */ + private String auditorName; + + /** + * 手机号 + */ + private String phone; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/domain/bo/HotSignAuditPermissionConfigBo.java b/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/domain/bo/HotSignAuditPermissionConfigBo.java new file mode 100644 index 0000000..90cc8d8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/domain/bo/HotSignAuditPermissionConfigBo.java @@ -0,0 +1,64 @@ +package com.hotwj.platform.config.signAuditPermissionConfig.domain.bo; + +import com.hotwj.platform.config.signAuditPermissionConfig.domain.HotSignAuditPermissionConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 签字审核权限配置业务对象 hot_sign_audit_permission_config + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotSignAuditPermissionConfig.class, reverseConvertGenerate = false) +public class HotSignAuditPermissionConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 业务类型 1=车辆三检出车 2=车辆三检行车 3=车辆三检收车 4=隐患排查 5=隐患治理评估 6=隐患治理治理 7=隐患治理复查 + */ + @NotNull(message = "业务类型 1=车辆三检出车 2=车辆三检行车 3=车辆三检收车 4=隐患排查 5=隐患治理评估 6=隐患治理治理 7=隐患治理复查不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long bizType; + + /** + * 审核人ID + */ + @NotNull(message = "审核人ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long auditorId; + + /** + * 审核人姓名 + */ + private String auditorName; + + /** + * 手机号 + */ + private String phone; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/domain/vo/HotSignAuditPermissionConfigVo.java b/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/domain/vo/HotSignAuditPermissionConfigVo.java new file mode 100644 index 0000000..2f4310f --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/domain/vo/HotSignAuditPermissionConfigVo.java @@ -0,0 +1,73 @@ +package com.hotwj.platform.config.signAuditPermissionConfig.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.signAuditPermissionConfig.domain.HotSignAuditPermissionConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 签字审核权限配置视图对象 hot_sign_audit_permission_config + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotSignAuditPermissionConfig.class) +public class HotSignAuditPermissionConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 业务类型 1=车辆三检出车 2=车辆三检行车 3=车辆三检收车 4=隐患排查 5=隐患治理评估 6=隐患治理治理 7=隐患治理复查 + */ + @ExcelProperty(value = "业务类型 1=车辆三检出车 2=车辆三检行车 3=车辆三检收车 4=隐患排查 5=隐患治理评估 6=隐患治理治理 7=隐患治理复查") + private Long bizType; + + /** + * 审核人ID + */ + @ExcelProperty(value = "审核人ID") + private Long auditorId; + + /** + * 审核人姓名 + */ + @Translation(type = TransConstant.SAFETY_MANAGER_ID_TO_NAME, mapper = "auditorId") + @ExcelProperty(value = "审核人姓名") + private String auditorName; + + /** + * 手机号 + */ + @ExcelProperty(value = "手机号") + private String phone; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/mapper/HotSignAuditPermissionConfigMapper.java b/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/mapper/HotSignAuditPermissionConfigMapper.java new file mode 100644 index 0000000..b137263 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/mapper/HotSignAuditPermissionConfigMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.signAuditPermissionConfig.mapper; + +import com.hotwj.platform.config.signAuditPermissionConfig.domain.HotSignAuditPermissionConfig; +import com.hotwj.platform.config.signAuditPermissionConfig.domain.vo.HotSignAuditPermissionConfigVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 签字审核权限配置Mapper接口 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Mapper +public interface HotSignAuditPermissionConfigMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/service/IHotSignAuditPermissionConfigService.java b/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/service/IHotSignAuditPermissionConfigService.java new file mode 100644 index 0000000..ab7536b --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/service/IHotSignAuditPermissionConfigService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.signAuditPermissionConfig.service; + +import com.hotwj.platform.config.signAuditPermissionConfig.domain.bo.HotSignAuditPermissionConfigBo; +import com.hotwj.platform.config.signAuditPermissionConfig.domain.vo.HotSignAuditPermissionConfigVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 签字审核权限配置Service接口 + * + * @author shihongwei + * @date 2025-12-29 + */ +public interface IHotSignAuditPermissionConfigService { + + /** + * 查询签字审核权限配置 + * + * @param id 主键 + * @return 签字审核权限配置 + */ + HotSignAuditPermissionConfigVo queryById(Long id); + + /** + * 分页查询签字审核权限配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 签字审核权限配置分页列表 + */ + TableDataInfo queryPageList(HotSignAuditPermissionConfigBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的签字审核权限配置列表 + * + * @param bo 查询条件 + * @return 签字审核权限配置列表 + */ + List queryList(HotSignAuditPermissionConfigBo bo); + + /** + * 新增签字审核权限配置 + * + * @param bo 签字审核权限配置 + * @return 是否新增成功 + */ + Boolean insertByBo(HotSignAuditPermissionConfigBo bo); + + /** + * 修改签字审核权限配置 + * + * @param bo 签字审核权限配置 + * @return 是否修改成功 + */ + Boolean updateByBo(HotSignAuditPermissionConfigBo bo); + + /** + * 校验并批量删除签字审核权限配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/service/impl/HotSignAuditPermissionConfigServiceImpl.java b/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/service/impl/HotSignAuditPermissionConfigServiceImpl.java new file mode 100644 index 0000000..44a8f4a --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/signAuditPermissionConfig/service/impl/HotSignAuditPermissionConfigServiceImpl.java @@ -0,0 +1,137 @@ +package com.hotwj.platform.config.signAuditPermissionConfig.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.signAuditPermissionConfig.domain.HotSignAuditPermissionConfig; +import com.hotwj.platform.config.signAuditPermissionConfig.domain.bo.HotSignAuditPermissionConfigBo; +import com.hotwj.platform.config.signAuditPermissionConfig.domain.vo.HotSignAuditPermissionConfigVo; +import com.hotwj.platform.config.signAuditPermissionConfig.mapper.HotSignAuditPermissionConfigMapper; +import com.hotwj.platform.config.signAuditPermissionConfig.service.IHotSignAuditPermissionConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 签字审核权限配置Service业务层处理 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotSignAuditPermissionConfigServiceImpl implements IHotSignAuditPermissionConfigService { + + private final HotSignAuditPermissionConfigMapper baseMapper; + + /** + * 查询签字审核权限配置 + * + * @param id 主键 + * @return 签字审核权限配置 + */ + @Override + public HotSignAuditPermissionConfigVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询签字审核权限配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 签字审核权限配置分页列表 + */ + @Override + public TableDataInfo queryPageList(HotSignAuditPermissionConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的签字审核权限配置列表 + * + * @param bo 查询条件 + * @return 签字审核权限配置列表 + */ + @Override + public List queryList(HotSignAuditPermissionConfigBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotSignAuditPermissionConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotSignAuditPermissionConfig::getId); + lqw.eq(bo.getCompanyId() != null, HotSignAuditPermissionConfig::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getBizType() != null, HotSignAuditPermissionConfig::getBizType, bo.getBizType()); + lqw.eq(bo.getAuditorId() != null, HotSignAuditPermissionConfig::getAuditorId, bo.getAuditorId()); + lqw.like(StringUtils.isNotBlank(bo.getAuditorName()), HotSignAuditPermissionConfig::getAuditorName, bo.getAuditorName()); + lqw.eq(StringUtils.isNotBlank(bo.getPhone()), HotSignAuditPermissionConfig::getPhone, bo.getPhone()); + lqw.eq(HotSignAuditPermissionConfig::getIsDeleted, 0L); + return lqw; + } + + /** + * 新增签字审核权限配置 + * + * @param bo 签字审核权限配置 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotSignAuditPermissionConfigBo bo) { + HotSignAuditPermissionConfig add = MapstructUtils.convert(bo, HotSignAuditPermissionConfig.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改签字审核权限配置 + * + * @param bo 签字审核权限配置 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotSignAuditPermissionConfigBo bo) { + HotSignAuditPermissionConfig update = MapstructUtils.convert(bo, HotSignAuditPermissionConfig.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotSignAuditPermissionConfig entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除签字审核权限配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/squareInfo/controller/HotSquareInfoController.java b/src/main/java/com/hotwj/platform/config/squareInfo/controller/HotSquareInfoController.java new file mode 100644 index 0000000..398da6c --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/squareInfo/controller/HotSquareInfoController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.config.squareInfo.controller; + +import com.hotwj.platform.config.squareInfo.domain.bo.HotSquareInfoBo; +import com.hotwj.platform.config.squareInfo.domain.vo.HotSquareInfoVo; +import com.hotwj.platform.config.squareInfo.service.IHotSquareInfoService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 广场信息 + * + * @author shihongwei + * @date 2026-01-29 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/squareInfo") +@Tag(name = "广场信息", description = "广场信息管理") +public class HotSquareInfoController extends BaseController { + + private final IHotSquareInfoService hotSquareInfoService; + + /** + * 查询广场信息列表 + */ + //@SaCheckPermission("config:squareInfo:list") + @GetMapping("/list") + @Operation(summary = "分页查询广场信息列表") + public TableDataInfo list(HotSquareInfoBo bo, PageQuery pageQuery) { + return hotSquareInfoService.queryPageList(bo, pageQuery); + } + + /** + * 导出广场信息列表 + */ + //@SaCheckPermission("config:squareInfo:export") + @Log(title = "广场信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出广场信息列表") + public void export(HotSquareInfoBo bo, HttpServletResponse response) { + List list = hotSquareInfoService.queryList(bo); + ExcelUtil.exportExcel(list, "广场信息", HotSquareInfoVo.class, response); + } + + /** + * 获取广场信息详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:squareInfo:query") + @GetMapping("/{id}") + @Operation(summary = "获取广场信息详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotSquareInfoService.queryById(id)); + } + + /** + * 新增广场信息 + */ + //@SaCheckPermission("config:squareInfo:add") + @Log(title = "广场信息", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增广场信息") + public R add(@Validated(AddGroup.class) @RequestBody HotSquareInfoBo bo) { + return toAjax(hotSquareInfoService.insertByBo(bo)); + } + + /** + * 修改广场信息 + */ + //@SaCheckPermission("config:squareInfo:edit") + @Log(title = "广场信息", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改广场信息") + public R edit(@Validated(EditGroup.class) @RequestBody HotSquareInfoBo bo) { + return toAjax(hotSquareInfoService.updateByBo(bo)); + } + + /** + * 删除广场信息 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:squareInfo:remove") + @Log(title = "广场信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除广场信息") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotSquareInfoService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/squareInfo/domain/HotSquareInfo.java b/src/main/java/com/hotwj/platform/config/squareInfo/domain/HotSquareInfo.java new file mode 100644 index 0000000..d315c4d --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/squareInfo/domain/HotSquareInfo.java @@ -0,0 +1,109 @@ +package com.hotwj.platform.config.squareInfo.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 广场信息对象 hot_square_info + * + * @author shihongwei + * @date 2026-01-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_square_info") +public class HotSquareInfo extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + private Long companyId; + + /** + * 标题 + */ + private String title; + + /** + * 内容 + */ + private String content; + + /** + * 照片OSS ID(多张逗号分隔) + */ + private String imageOssIds; + + /** + * 联系人(提交内容) + */ + private String contactName; + + /** + * 联系手机号(提交内容) + */ + private String contactPhone; + + /** + * 发布人联系电话 + */ + private String publisherPhone; + + /** + * 发布人类型:1=管理员 2=驾驶员 3=押运员 + */ + private Long publisherType; + + /** + * 审核状态:0=审核中 1=已发布 2=已拒绝 + */ + private Long auditStatus; + + /** + * 审核结果 1=发布 2=拒绝 + */ + private Long auditResult; + + /** + * 拒绝原因 + */ + private String auditRemark; + + /** + * 头像 + */ + private String avatar; + + /** + * 创建者姓名(发布人员) + */ + private String createByName; + + /** + * 更新者姓名 + */ + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/squareInfo/domain/bo/HotSquareInfoBo.java b/src/main/java/com/hotwj/platform/config/squareInfo/domain/bo/HotSquareInfoBo.java new file mode 100644 index 0000000..f015790 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/squareInfo/domain/bo/HotSquareInfoBo.java @@ -0,0 +1,110 @@ +package com.hotwj.platform.config.squareInfo.domain.bo; + +import com.hotwj.platform.config.squareInfo.domain.HotSquareInfo; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 广场信息业务对象 hot_square_info + * + * @author shihongwei + * @date 2026-01-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotSquareInfo.class, reverseConvertGenerate = false) +public class HotSquareInfoBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + private Long companyId; + + /** + * 标题 + */ + @NotBlank(message = "标题不能为空", groups = {AddGroup.class, EditGroup.class}) + private String title; + + /** + * 内容 + */ + @NotBlank(message = "内容不能为空", groups = {AddGroup.class, EditGroup.class}) + private String content; + + /** + * 照片OSS ID(多张逗号分隔) + */ + private String imageOssIds; + + /** + * 联系人(提交内容) + */ + @NotBlank(message = "联系人(提交内容)不能为空", groups = {AddGroup.class, EditGroup.class}) + private String contactName; + + /** + * 联系手机号(提交内容) + */ + @NotBlank(message = "联系手机号(提交内容)不能为空", groups = {AddGroup.class, EditGroup.class}) + private String contactPhone; + + /** + * 发布人联系电话 + */ + private String publisherPhone; + + /** + * 发布人类型:1=管理员 2=驾驶员 3=押运员 + */ + private Long publisherType; + + /** + * 审核状态:0=审核中 1=已发布 2=已拒绝 + */ + private Long auditStatus; + + /** + * 审核结果 1=发布 2=拒绝 + */ + private Long auditResult; + + /** + * 拒绝原因 + */ + private String auditRemark; + + /** + * 头像 + */ + private String avatar; + + /** + * 创建者姓名(发布人员) + */ + private String createByName; + + /** + * 更新者姓名 + */ + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/squareInfo/domain/vo/HotSquareInfoVo.java b/src/main/java/com/hotwj/platform/config/squareInfo/domain/vo/HotSquareInfoVo.java new file mode 100644 index 0000000..72bd041 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/squareInfo/domain/vo/HotSquareInfoVo.java @@ -0,0 +1,134 @@ +package com.hotwj.platform.config.squareInfo.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.squareInfo.domain.HotSquareInfo; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 广场信息视图对象 hot_square_info + * + * @author shihongwei + * @date 2026-01-29 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotSquareInfo.class) +public class HotSquareInfoVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "由=应用层保证必填") + private Long companyId; + + /** + * 标题 + */ + @ExcelProperty(value = "标题") + private String title; + + /** + * 内容 + */ + @ExcelProperty(value = "内容") + private String content; + + /** + * 照片OSS ID(多张逗号分隔) + */ + @ExcelProperty(value = "照片OSS ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "多=张逗号分隔") + private String imageOssIds; + + /** + * 联系人(提交内容) + */ + @ExcelProperty(value = "联系人", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "提=交内容") + private String contactName; + + /** + * 联系手机号(提交内容) + */ + @ExcelProperty(value = "联系手机号", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "提=交内容") + private String contactPhone; + + /** + * 发布人联系电话 + */ + @ExcelProperty(value = "发布人联系电话") + private String publisherPhone; + + /** + * 发布人类型:1=管理员 2=驾驶员 3=押运员 + */ + @ExcelProperty(value = "发布人类型:1=管理员 2=驾驶员 3=押运员") + private Long publisherType; + + /** + * 审核状态:0=审核中 1=已发布 2=已拒绝 + */ + @ExcelProperty(value = "审核状态:0=审核中 1=已发布 2=已拒绝") + private Long auditStatus; + + /** + * 审核结果 1=发布 2=拒绝 + */ + @ExcelProperty(value = "审核结果 1=发布 2=拒绝") + private Long auditResult; + + /** + * 拒绝原因 + */ + @ExcelProperty(value = "拒绝原因") + private String auditRemark; + + /** + * 头像 + */ + @ExcelProperty(value = "头像") + private String avatar; + + /** + * 创建者姓名(发布人员) + */ + @ExcelProperty(value = "创建者姓名", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "发=布人员") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + private Date createTime; + private Date updateTime; + +} diff --git a/src/main/java/com/hotwj/platform/config/squareInfo/mapper/HotSquareInfoMapper.java b/src/main/java/com/hotwj/platform/config/squareInfo/mapper/HotSquareInfoMapper.java new file mode 100644 index 0000000..e2ab734 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/squareInfo/mapper/HotSquareInfoMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.squareInfo.mapper; + +import com.hotwj.platform.config.squareInfo.domain.HotSquareInfo; +import com.hotwj.platform.config.squareInfo.domain.vo.HotSquareInfoVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 广场信息Mapper接口 + * + * @author shihongwei + * @date 2026-01-29 + */ +@Mapper +public interface HotSquareInfoMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/squareInfo/service/IHotSquareInfoService.java b/src/main/java/com/hotwj/platform/config/squareInfo/service/IHotSquareInfoService.java new file mode 100644 index 0000000..812ae29 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/squareInfo/service/IHotSquareInfoService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.squareInfo.service; + +import com.hotwj.platform.config.squareInfo.domain.bo.HotSquareInfoBo; +import com.hotwj.platform.config.squareInfo.domain.vo.HotSquareInfoVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 广场信息Service接口 + * + * @author shihongwei + * @date 2026-01-29 + */ +public interface IHotSquareInfoService { + + /** + * 查询广场信息 + * + * @param id 主键 + * @return 广场信息 + */ + HotSquareInfoVo queryById(Long id); + + /** + * 分页查询广场信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 广场信息分页列表 + */ + TableDataInfo queryPageList(HotSquareInfoBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的广场信息列表 + * + * @param bo 查询条件 + * @return 广场信息列表 + */ + List queryList(HotSquareInfoBo bo); + + /** + * 新增广场信息 + * + * @param bo 广场信息 + * @return 是否新增成功 + */ + Boolean insertByBo(HotSquareInfoBo bo); + + /** + * 修改广场信息 + * + * @param bo 广场信息 + * @return 是否修改成功 + */ + Boolean updateByBo(HotSquareInfoBo bo); + + /** + * 校验并批量删除广场信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/squareInfo/service/impl/HotSquareInfoServiceImpl.java b/src/main/java/com/hotwj/platform/config/squareInfo/service/impl/HotSquareInfoServiceImpl.java new file mode 100644 index 0000000..76e1b40 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/squareInfo/service/impl/HotSquareInfoServiceImpl.java @@ -0,0 +1,255 @@ +package com.hotwj.platform.config.squareInfo.service.impl; + +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.squareInfo.domain.HotSquareInfo; +import com.hotwj.platform.config.squareInfo.domain.bo.HotSquareInfoBo; +import com.hotwj.platform.config.squareInfo.domain.vo.HotSquareInfoVo; +import com.hotwj.platform.config.squareInfo.mapper.HotSquareInfoMapper; +import com.hotwj.platform.config.squareInfo.service.IHotSquareInfoService; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 广场信息Service业务层处理 + * + * @author shihongwei + * @date 2026-01-29 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotSquareInfoServiceImpl implements IHotSquareInfoService { + + private final HotSquareInfoMapper baseMapper; + private final IHotSystemNotificationService notificationService; + private final HotCompanySafetyManagerMapper companySafetyManagerMapper; + private final HotDriverMapper driverMapper; + + /** + * 查询广场信息 + * + * @param id 主键 + * @return 广场信息 + */ + @Override + public HotSquareInfoVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询广场信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 广场信息分页列表 + */ + @Override + public TableDataInfo queryPageList(HotSquareInfoBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的广场信息列表 + * + * @param bo 查询条件 + * @return 广场信息列表 + */ + @Override + public List queryList(HotSquareInfoBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotSquareInfoBo bo) { + Map params = bo.getParams(); + Object beginCreateTime = params.get("beginCreateTime"); + Object endCreateTime = params.get("endCreateTime"); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotSquareInfo::getId); + lqw.eq(bo.getCompanyId() != null, HotSquareInfo::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getTitle()), HotSquareInfo::getTitle, bo.getTitle()); + lqw.like(StringUtils.isNotBlank(bo.getContent()), HotSquareInfo::getContent, bo.getContent()); + lqw.eq(StringUtils.isNotBlank(bo.getImageOssIds()), HotSquareInfo::getImageOssIds, bo.getImageOssIds()); + lqw.like(StringUtils.isNotBlank(bo.getContactName()), HotSquareInfo::getContactName, bo.getContactName()); + lqw.eq(StringUtils.isNotBlank(bo.getContactPhone()), HotSquareInfo::getContactPhone, bo.getContactPhone()); + lqw.eq(StringUtils.isNotBlank(bo.getPublisherPhone()), HotSquareInfo::getPublisherPhone, bo.getPublisherPhone()); + lqw.eq(bo.getPublisherType() != null, HotSquareInfo::getPublisherType, bo.getPublisherType()); + lqw.eq(bo.getAuditStatus() != null, HotSquareInfo::getAuditStatus, bo.getAuditStatus()); + lqw.eq(bo.getAuditResult() != null, HotSquareInfo::getAuditResult, bo.getAuditResult()); + lqw.eq(StringUtils.isNotBlank(bo.getAuditRemark()), HotSquareInfo::getAuditRemark, bo.getAuditRemark()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotSquareInfo::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotSquareInfo::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotSquareInfo::getIsDeleted, bo.getIsDeleted()); + lqw.eq(bo.getCreateBy() != null, HotSquareInfo::getCreateBy, bo.getCreateBy()); + lqw.ge(beginCreateTime != null, HotSquareInfo::getCreateTime, beginCreateTime); + if (endCreateTime != null) { + Date endDate = endCreateTime instanceof Date ? (Date) endCreateTime : DateUtil.parse(endCreateTime.toString()); + lqw.lt(HotSquareInfo::getCreateTime, DateUtil.offsetDay(endDate, 1)); + } + return lqw; + } + + /** + * 新增广场信息 + * + * @param bo 广场信息 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotSquareInfoBo bo) { + HotSquareInfo add = MapstructUtils.convert(bo, HotSquareInfo.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改广场信息 + * + * @param bo 广场信息 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotSquareInfoBo bo) { + HotSquareInfo old = null; + if (bo.getId() != null) { + old = baseMapper.selectById(bo.getId()); + } + HotSquareInfo update = MapstructUtils.convert(bo, HotSquareInfo.class); + validEntityBeforeSave(update); + boolean updated = baseMapper.updateById(update) > 0; + if (updated) { + sendAuditNotificationIfNeeded(old, bo); + } + return updated; + } + + private void sendAuditNotificationIfNeeded(HotSquareInfo old, HotSquareInfoBo bo) { + if (bo == null || bo.getId() == null || bo.getAuditStatus() == null) { + return; + } + Long newAuditStatus = bo.getAuditStatus(); + if (!Long.valueOf(1L).equals(newAuditStatus) && !Long.valueOf(2L).equals(newAuditStatus)) { + return; + } + Long oldAuditStatus = old == null ? null : old.getAuditStatus(); + if (newAuditStatus.equals(oldAuditStatus)) { + return; + } + + String receiverType; + String receiverId = null; + Long publisherType = bo.getPublisherType() != null ? bo.getPublisherType() : (old == null ? null : old.getPublisherType()); + String publisherPhone = StringUtils.isNotBlank(bo.getPublisherPhone()) ? bo.getPublisherPhone() : (old == null ? null : old.getPublisherPhone()); + Long companyId = bo.getCompanyId() != null ? bo.getCompanyId() : (old == null ? null : old.getCompanyId()); + + if (Long.valueOf(1L).equals(publisherType)) { + receiverType = "管理员"; + if (StringUtils.isNotBlank(publisherPhone)) { + HotCompanySafetyManager manager = companySafetyManagerMapper.selectOne( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getPhone, publisherPhone) + .eq(companyId != null, HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + .last("limit 1") + ); + if (manager != null && manager.getId() != null) { + receiverId = String.valueOf(manager.getId()); + } + } + } else { + receiverType = "驾驶员"; + if (StringUtils.isNotBlank(publisherPhone)) { + HotDriver driver = driverMapper.selectOne( + Wrappers.lambdaQuery() + .eq(HotDriver::getPhone, publisherPhone) + .eq(companyId != null, HotDriver::getCompanyId, companyId) + .eq(HotDriver::getIsDeleted, 0L) + .last("limit 1") + ); + if (driver != null && StringUtils.isNotBlank(driver.getId())) { + receiverId = driver.getId(); + } + } + } + + if (StringUtils.isBlank(receiverId)) { + log.warn("广场信息审核通知发送失败,未匹配到接收人 squareInfoId={} publisherType={} publisherPhone={} companyId={}", + bo.getId(), publisherType, publisherPhone, companyId); + return; + } + + String title = StringUtils.isNotBlank(bo.getTitle()) ? bo.getTitle() : (old == null ? "广场信息" : StringUtils.blankToDefault(old.getTitle(), "广场信息")); + String content; + if (Long.valueOf(1L).equals(newAuditStatus)) { + content = "您发布的广场信息《" + title + "》已审核通过并发布。"; + } else { + String remark = StringUtils.isNotBlank(bo.getAuditRemark()) ? bo.getAuditRemark() : (old == null ? null : old.getAuditRemark()); + content = StringUtils.isNotBlank(remark) + ? "您发布的广场信息《" + title + "》审核未通过,原因:" + remark + "。" + : "您发布的广场信息《" + title + "》审核未通过。"; + } + + HotSystemNotificationGroupBo notifyBo = new HotSystemNotificationGroupBo(); + notifyBo.setLevel("普通"); + notifyBo.setContent(content); + notifyBo.setSourceType("广场信息"); + notifyBo.setSenderType("SYSTEM"); + notifyBo.setReceiverType(receiverType); + notifyBo.setReceiverId(receiverId); + notifyBo.setIsDeleted(0L); + try { + notificationService.insertByBo(notifyBo); + } catch (Exception e) { + log.warn("广场信息审核通知发送异常 squareInfoId={} receiverType={} receiverId={}", bo.getId(), receiverType, receiverId, e); + } + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotSquareInfo entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除广场信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/systemAgreement/controller/HotSystemAgreementController.java b/src/main/java/com/hotwj/platform/config/systemAgreement/controller/HotSystemAgreementController.java new file mode 100644 index 0000000..53d6e91 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/systemAgreement/controller/HotSystemAgreementController.java @@ -0,0 +1,118 @@ +package com.hotwj.platform.config.systemAgreement.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import com.hotwj.platform.config.systemAgreement.domain.bo.HotSystemAgreementBo; +import com.hotwj.platform.config.systemAgreement.domain.vo.HotSystemAgreementVo; +import com.hotwj.platform.config.systemAgreement.service.IHotSystemAgreementService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 用户隐私政策 + * + * @author shihongwei + * @date 2026-01-08 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/systemAgreement") +@Tag(name = "用户隐私政策", description = "用户隐私政策管理") +public class HotSystemAgreementController extends BaseController { + + private final IHotSystemAgreementService hotSystemAgreementService; + + /** + * 查询用户隐私政策列表 + */ + @SaIgnore + @GetMapping("/list") + @Operation(summary = "分页查询用户隐私政策列表") + public TableDataInfo list(HotSystemAgreementBo bo, PageQuery pageQuery) { + return hotSystemAgreementService.queryPageList(bo, pageQuery); + } + + /** + * 导出用户隐私政策列表 + */ + //@SaCheckPermission("config:systemAgreement:export") + @Log(title = "用户隐私政策", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出用户隐私政策列表") + public void export(HotSystemAgreementBo bo, HttpServletResponse response) { + List list = hotSystemAgreementService.queryList(bo); + ExcelUtil.exportExcel(list, "用户隐私政策", HotSystemAgreementVo.class, response); + } + + /** + * 获取用户隐私政策详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:systemAgreement:query") + @GetMapping("/{id}") + @Operation(summary = "获取用户隐私政策详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotSystemAgreementService.queryById(id)); + } + + /** + * 新增用户隐私政策 + */ + //@SaCheckPermission("config:systemAgreement:add") + @Log(title = "用户隐私政策", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增用户隐私政策") + public R add(@Validated(AddGroup.class) @RequestBody HotSystemAgreementBo bo) { + return toAjax(hotSystemAgreementService.insertByBo(bo)); + } + + /** + * 修改用户隐私政策 + */ + //@SaCheckPermission("config:systemAgreement:edit") + @Log(title = "用户隐私政策", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改用户隐私政策") + public R edit(@Validated(EditGroup.class) @RequestBody HotSystemAgreementBo bo) { + return toAjax(hotSystemAgreementService.updateByBo(bo)); + } + + /** + * 删除用户隐私政策 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:systemAgreement:remove") + @Log(title = "用户隐私政策", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除用户隐私政策") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotSystemAgreementService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/systemAgreement/domain/HotSystemAgreement.java b/src/main/java/com/hotwj/platform/config/systemAgreement/domain/HotSystemAgreement.java new file mode 100644 index 0000000..39e2546 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/systemAgreement/domain/HotSystemAgreement.java @@ -0,0 +1,49 @@ +package com.hotwj.platform.config.systemAgreement.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 用户隐私政策对象 hot_system_agreement + * + * @author shihongwei + * @date 2026-01-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_system_agreement") +public class HotSystemAgreement extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 协议名称 + */ + private String agreementName; + + /** + * 制度内容(富文本,附件优先展示) + */ + private String content; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/systemAgreement/domain/bo/HotSystemAgreementBo.java b/src/main/java/com/hotwj/platform/config/systemAgreement/domain/bo/HotSystemAgreementBo.java new file mode 100644 index 0000000..0cf803b --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/systemAgreement/domain/bo/HotSystemAgreementBo.java @@ -0,0 +1,48 @@ +package com.hotwj.platform.config.systemAgreement.domain.bo; + +import com.hotwj.platform.config.systemAgreement.domain.HotSystemAgreement; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 用户隐私政策业务对象 hot_system_agreement + * + * @author shihongwei + * @date 2026-01-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotSystemAgreement.class, reverseConvertGenerate = false) +public class HotSystemAgreementBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 协议名称 + */ + @NotBlank(message = "协议名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String agreementName; + + /** + * 制度内容(富文本,附件优先展示) + */ + private String content; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/systemAgreement/domain/vo/HotSystemAgreementVo.java b/src/main/java/com/hotwj/platform/config/systemAgreement/domain/vo/HotSystemAgreementVo.java new file mode 100644 index 0000000..fc6dce5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/systemAgreement/domain/vo/HotSystemAgreementVo.java @@ -0,0 +1,55 @@ +package com.hotwj.platform.config.systemAgreement.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.systemAgreement.domain.HotSystemAgreement; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 用户隐私政策视图对象 hot_system_agreement + * + * @author shihongwei + * @date 2026-01-08 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotSystemAgreement.class) +public class HotSystemAgreementVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 协议名称 + */ + @ExcelProperty(value = "协议名称") + private String agreementName; + + /** + * 制度内容(富文本,附件优先展示) + */ + @ExcelProperty(value = "制度内容", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "富=文本,附件优先展示") + private String content; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/systemAgreement/mapper/HotSystemAgreementMapper.java b/src/main/java/com/hotwj/platform/config/systemAgreement/mapper/HotSystemAgreementMapper.java new file mode 100644 index 0000000..5f0e4df --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/systemAgreement/mapper/HotSystemAgreementMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.systemAgreement.mapper; + +import com.hotwj.platform.config.systemAgreement.domain.HotSystemAgreement; +import com.hotwj.platform.config.systemAgreement.domain.vo.HotSystemAgreementVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 用户隐私政策Mapper接口 + * + * @author shihongwei + * @date 2026-01-08 + */ +@Mapper +public interface HotSystemAgreementMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/systemAgreement/service/IHotSystemAgreementService.java b/src/main/java/com/hotwj/platform/config/systemAgreement/service/IHotSystemAgreementService.java new file mode 100644 index 0000000..7b6bce9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/systemAgreement/service/IHotSystemAgreementService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.systemAgreement.service; + +import com.hotwj.platform.config.systemAgreement.domain.bo.HotSystemAgreementBo; +import com.hotwj.platform.config.systemAgreement.domain.vo.HotSystemAgreementVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 用户隐私政策Service接口 + * + * @author shihongwei + * @date 2026-01-08 + */ +public interface IHotSystemAgreementService { + + /** + * 查询用户隐私政策 + * + * @param id 主键 + * @return 用户隐私政策 + */ + HotSystemAgreementVo queryById(Long id); + + /** + * 分页查询用户隐私政策列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 用户隐私政策分页列表 + */ + TableDataInfo queryPageList(HotSystemAgreementBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的用户隐私政策列表 + * + * @param bo 查询条件 + * @return 用户隐私政策列表 + */ + List queryList(HotSystemAgreementBo bo); + + /** + * 新增用户隐私政策 + * + * @param bo 用户隐私政策 + * @return 是否新增成功 + */ + Boolean insertByBo(HotSystemAgreementBo bo); + + /** + * 修改用户隐私政策 + * + * @param bo 用户隐私政策 + * @return 是否修改成功 + */ + Boolean updateByBo(HotSystemAgreementBo bo); + + /** + * 校验并批量删除用户隐私政策信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/systemAgreement/service/impl/HotSystemAgreementServiceImpl.java b/src/main/java/com/hotwj/platform/config/systemAgreement/service/impl/HotSystemAgreementServiceImpl.java new file mode 100644 index 0000000..f7ea08d --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/systemAgreement/service/impl/HotSystemAgreementServiceImpl.java @@ -0,0 +1,134 @@ +package com.hotwj.platform.config.systemAgreement.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.systemAgreement.domain.HotSystemAgreement; +import com.hotwj.platform.config.systemAgreement.domain.bo.HotSystemAgreementBo; +import com.hotwj.platform.config.systemAgreement.domain.vo.HotSystemAgreementVo; +import com.hotwj.platform.config.systemAgreement.mapper.HotSystemAgreementMapper; +import com.hotwj.platform.config.systemAgreement.service.IHotSystemAgreementService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 用户隐私政策Service业务层处理 + * + * @author shihongwei + * @date 2026-01-08 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotSystemAgreementServiceImpl implements IHotSystemAgreementService { + + private final HotSystemAgreementMapper baseMapper; + + /** + * 查询用户隐私政策 + * + * @param id 主键 + * @return 用户隐私政策 + */ + @Override + public HotSystemAgreementVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询用户隐私政策列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 用户隐私政策分页列表 + */ + @Override + public TableDataInfo queryPageList(HotSystemAgreementBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的用户隐私政策列表 + * + * @param bo 查询条件 + * @return 用户隐私政策列表 + */ + @Override + public List queryList(HotSystemAgreementBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotSystemAgreementBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotSystemAgreement::getId); + lqw.like(StringUtils.isNotBlank(bo.getAgreementName()), HotSystemAgreement::getAgreementName, bo.getAgreementName()); + lqw.eq(StringUtils.isNotBlank(bo.getContent()), HotSystemAgreement::getContent, bo.getContent()); + lqw.eq(bo.getIsDeleted() != null, HotSystemAgreement::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增用户隐私政策 + * + * @param bo 用户隐私政策 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotSystemAgreementBo bo) { + HotSystemAgreement add = MapstructUtils.convert(bo, HotSystemAgreement.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改用户隐私政策 + * + * @param bo 用户隐私政策 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotSystemAgreementBo bo) { + HotSystemAgreement update = MapstructUtils.convert(bo, HotSystemAgreement.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotSystemAgreement entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除用户隐私政策信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/systemTemplate/controller/HotSystemTemplateController.java b/src/main/java/com/hotwj/platform/config/systemTemplate/controller/HotSystemTemplateController.java new file mode 100644 index 0000000..06f2194 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/systemTemplate/controller/HotSystemTemplateController.java @@ -0,0 +1,125 @@ +package com.hotwj.platform.config.systemTemplate.controller; + +import com.hotwj.platform.config.systemTemplate.domain.bo.HotSystemTemplateBo; +import com.hotwj.platform.config.systemTemplate.domain.vo.HotSystemTemplateVo; +import com.hotwj.platform.config.systemTemplate.service.IHotSystemTemplateService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 体系模板 + * + * @author shihongwei + * @date 2026-02-14 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/systemTemplate") +@Tag(name = "体系模板", description = "体系模板管理") +public class HotSystemTemplateController extends BaseController { + + private final IHotSystemTemplateService hotSystemTemplateService; + + /** + * 查询体系模板列表 + */ + //@SaCheckPermission("config:systemTemplate:list") + @GetMapping("/list") + @Operation(summary = "分页查询体系模板列表") + public TableDataInfo list(HotSystemTemplateBo bo, PageQuery pageQuery) { + return hotSystemTemplateService.queryPageList(bo, pageQuery); + } + + /** + * 导出体系模板列表 + */ + //@SaCheckPermission("config:systemTemplate:export") + @Log(title = "体系模板", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出体系模板列表") + public void export(HotSystemTemplateBo bo, HttpServletResponse response) { + List list = hotSystemTemplateService.queryList(bo); + ExcelUtil.exportExcel(list, "体系模板", HotSystemTemplateVo.class, response); + } + + /** + * 获取体系模板详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:systemTemplate:query") + @GetMapping("/{id}") + @Operation(summary = "获取体系模板详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotSystemTemplateService.queryById(id)); + } + + /** + * 新增体系模板 + */ + //@SaCheckPermission("config:systemTemplate:add") + @Log(title = "体系模板", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增体系模板") + public R add(@Validated(AddGroup.class) @RequestBody HotSystemTemplateBo bo) { + return toAjax(hotSystemTemplateService.insertByBo(bo)); + } + + /** + * 修改体系模板 + */ + //@SaCheckPermission("config:systemTemplate:edit") + @Log(title = "体系模板", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改体系模板") + public R edit(@Validated(EditGroup.class) @RequestBody HotSystemTemplateBo bo) { + return toAjax(hotSystemTemplateService.updateByBo(bo)); + } + + /** + * 删除体系模板 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:systemTemplate:remove") + @Log(title = "体系模板", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除体系模板") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotSystemTemplateService.deleteWithValidByIds(List.of(ids), true)); + } + + //@SaCheckPermission("config:systemTemplate:add") + @PostMapping("/sync") + @Operation(summary = "同步总部体系模板到企业") + public R sync(@NotNull @RequestParam("companyId") Long companyId, + @NotNull @RequestParam("type") Long type) { + return toAjax(hotSystemTemplateService.syncFromHeadquarters(companyId, type)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/systemTemplate/domain/HotSystemTemplate.java b/src/main/java/com/hotwj/platform/config/systemTemplate/domain/HotSystemTemplate.java new file mode 100644 index 0000000..5c810e0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/systemTemplate/domain/HotSystemTemplate.java @@ -0,0 +1,59 @@ +package com.hotwj.platform.config.systemTemplate.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 体系模板对象 hot_system_template + * + * @author shihongwei + * @date 2026-02-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_system_template") +public class HotSystemTemplate extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 上级ID + */ + private Long parentId; + + /** + * 名称(应用层校验必填) + */ + private String name; + + /** + * 体系类型(应用层枚举校验) + */ + private Long type; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/systemTemplate/domain/bo/HotSystemTemplateBo.java b/src/main/java/com/hotwj/platform/config/systemTemplate/domain/bo/HotSystemTemplateBo.java new file mode 100644 index 0000000..88214df --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/systemTemplate/domain/bo/HotSystemTemplateBo.java @@ -0,0 +1,54 @@ +package com.hotwj.platform.config.systemTemplate.domain.bo; + +import com.hotwj.platform.config.systemTemplate.domain.HotSystemTemplate; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 体系模板业务对象 hot_system_template + * + * @author shihongwei + * @date 2026-02-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotSystemTemplate.class, reverseConvertGenerate = false) +public class HotSystemTemplateBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 上级ID + */ + private Long parentId; + + /** + * 名称(应用层校验必填) + */ + private String name; + + /** + * 体系类型(应用层枚举校验) + */ + private Long type; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/systemTemplate/domain/vo/HotSystemTemplateVo.java b/src/main/java/com/hotwj/platform/config/systemTemplate/domain/vo/HotSystemTemplateVo.java new file mode 100644 index 0000000..95b042c --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/systemTemplate/domain/vo/HotSystemTemplateVo.java @@ -0,0 +1,81 @@ +package com.hotwj.platform.config.systemTemplate.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.systemTemplate.domain.HotSystemTemplate; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 体系模板视图对象 hot_system_template + * + * @author shihongwei + * @date 2026-02-14 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotSystemTemplate.class) +public class HotSystemTemplateVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 上级ID + */ + @ExcelProperty(value = "上级ID") + private Long parentId; + + /** + * 名称(应用层校验必填) + */ + @ExcelProperty(value = "名称", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层校验必填") + private String name; + + /** + * 体系类型(应用层枚举校验) + */ + @ExcelProperty(value = "体系类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层枚举校验") + private Long type; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/systemTemplate/mapper/HotSystemTemplateMapper.java b/src/main/java/com/hotwj/platform/config/systemTemplate/mapper/HotSystemTemplateMapper.java new file mode 100644 index 0000000..1ee9640 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/systemTemplate/mapper/HotSystemTemplateMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.systemTemplate.mapper; + +import com.hotwj.platform.config.systemTemplate.domain.HotSystemTemplate; +import com.hotwj.platform.config.systemTemplate.domain.vo.HotSystemTemplateVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 体系模板Mapper接口 + * + * @author shihongwei + * @date 2026-02-14 + */ +@Mapper +public interface HotSystemTemplateMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/systemTemplate/service/IHotSystemTemplateService.java b/src/main/java/com/hotwj/platform/config/systemTemplate/service/IHotSystemTemplateService.java new file mode 100644 index 0000000..0fcb24c --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/systemTemplate/service/IHotSystemTemplateService.java @@ -0,0 +1,77 @@ +package com.hotwj.platform.config.systemTemplate.service; + +import com.hotwj.platform.config.systemTemplate.domain.bo.HotSystemTemplateBo; +import com.hotwj.platform.config.systemTemplate.domain.vo.HotSystemTemplateVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 体系模板Service接口 + * + * @author shihongwei + * @date 2026-02-14 + */ +public interface IHotSystemTemplateService { + + /** + * 查询体系模板 + * + * @param id 主键 + * @return 体系模板 + */ + HotSystemTemplateVo queryById(Long id); + + /** + * 分页查询体系模板列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 体系模板分页列表 + */ + TableDataInfo queryPageList(HotSystemTemplateBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的体系模板列表 + * + * @param bo 查询条件 + * @return 体系模板列表 + */ + List queryList(HotSystemTemplateBo bo); + + /** + * 新增体系模板 + * + * @param bo 体系模板 + * @return 是否新增成功 + */ + Boolean insertByBo(HotSystemTemplateBo bo); + + /** + * 修改体系模板 + * + * @param bo 体系模板 + * @return 是否修改成功 + */ + Boolean updateByBo(HotSystemTemplateBo bo); + + /** + * 校验并批量删除体系模板信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 同步总部体系模板到指定企业 + * + * @param companyId 目标企业ID + * @param type 体系类型 + * @return 是否成功 + */ + Boolean syncFromHeadquarters(Long companyId, Long type); +} diff --git a/src/main/java/com/hotwj/platform/config/systemTemplate/service/impl/HotSystemTemplateServiceImpl.java b/src/main/java/com/hotwj/platform/config/systemTemplate/service/impl/HotSystemTemplateServiceImpl.java new file mode 100644 index 0000000..d9a8237 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/systemTemplate/service/impl/HotSystemTemplateServiceImpl.java @@ -0,0 +1,202 @@ +package com.hotwj.platform.config.systemTemplate.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.systemTemplate.domain.HotSystemTemplate; +import com.hotwj.platform.config.systemTemplate.domain.bo.HotSystemTemplateBo; +import com.hotwj.platform.config.systemTemplate.domain.vo.HotSystemTemplateVo; +import com.hotwj.platform.config.systemTemplate.mapper.HotSystemTemplateMapper; +import com.hotwj.platform.config.systemTemplate.service.IHotSystemTemplateService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.stream.Collectors; + +/** + * 体系模板Service业务层处理 + * + * @author shihongwei + * @date 2026-02-14 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotSystemTemplateServiceImpl implements IHotSystemTemplateService { + + private final HotSystemTemplateMapper baseMapper; + + /** + * 查询体系模板 + * + * @param id 主键 + * @return 体系模板 + */ + @Override + public HotSystemTemplateVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询体系模板列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 体系模板分页列表 + */ + @Override + public TableDataInfo queryPageList(HotSystemTemplateBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的体系模板列表 + * + * @param bo 查询条件 + * @return 体系模板列表 + */ + @Override + public List queryList(HotSystemTemplateBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotSystemTemplateBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotSystemTemplate::getId); + lqw.eq(bo.getCompanyId() != null, HotSystemTemplate::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getParentId() != null, HotSystemTemplate::getParentId, bo.getParentId()); + lqw.like(StringUtils.isNotBlank(bo.getName()), HotSystemTemplate::getName, bo.getName()); + lqw.eq(bo.getType() != null, HotSystemTemplate::getType, bo.getType()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotSystemTemplate::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotSystemTemplate::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotSystemTemplate::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增体系模板 + * + * @param bo 体系模板 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotSystemTemplateBo bo) { + HotSystemTemplate add = MapstructUtils.convert(bo, HotSystemTemplate.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改体系模板 + * + * @param bo 体系模板 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotSystemTemplateBo bo) { + HotSystemTemplate update = MapstructUtils.convert(bo, HotSystemTemplate.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotSystemTemplate entity) { + if (entity.getParentId() == null) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(HotSystemTemplate::getCompanyId, entity.getCompanyId()); + lqw.eq(HotSystemTemplate::getType, entity.getType()); + lqw.eq(HotSystemTemplate::getIsDeleted, 0L); + lqw.ne(entity.getId() != null, HotSystemTemplate::getId, entity.getId()); + Long count = baseMapper.selectCount(lqw); + if (count != null && count > 0) { + throw new ServiceException("同公司同体系类型的根节点只能存在一个"); + } + } + } + + /** + * 校验并批量删除体系模板信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + public Boolean syncFromHeadquarters(Long companyId, Long type) { + Long hqCompanyId = 1L; + LambdaQueryWrapper hqWrapper = Wrappers.lambdaQuery(); + hqWrapper.eq(HotSystemTemplate::getCompanyId, hqCompanyId); + hqWrapper.eq(HotSystemTemplate::getType, type); + hqWrapper.eq(HotSystemTemplate::getIsDeleted, 0L); + List hqList = baseMapper.selectList(hqWrapper); + if (hqList.isEmpty()) { + return true; + } + LambdaQueryWrapper tgtWrapper = Wrappers.lambdaQuery(); + tgtWrapper.eq(HotSystemTemplate::getCompanyId, companyId); + tgtWrapper.eq(HotSystemTemplate::getType, type); + tgtWrapper.eq(HotSystemTemplate::getIsDeleted, 0L); + List tgtList = baseMapper.selectList(tgtWrapper); + for (HotSystemTemplate t : tgtList) { + t.setIsDeleted(1L); + baseMapper.updateById(t); + } + Map> children = hqList.stream().collect(Collectors.groupingBy(HotSystemTemplate::getParentId)); + Map idMap = new HashMap<>(); + List roots = hqList.stream() + .filter(e -> e.getParentId() == null || Long.valueOf(0L).equals(e.getParentId())) + .collect(Collectors.toList()); + for (HotSystemTemplate r : roots) { + copyNode(r, companyId, type, idMap, children); + } + return true; + } + + private void copyNode(HotSystemTemplate src, Long companyId, Long type, + Map idMap, Map> children) { + Long parentTargetId = src.getParentId() == null ? null : idMap.get(src.getParentId()); + HotSystemTemplate add = new HotSystemTemplate(); + add.setCompanyId(companyId); + add.setParentId(parentTargetId); + add.setName(src.getName()); + add.setType(type); + add.setIsDeleted(0L); + baseMapper.insert(add); + idMap.put(src.getId(), add.getId()); + List childList = children.get(src.getId()); + if (childList != null) { + for (HotSystemTemplate c : childList) { + copyNode(c, companyId, type, idMap, children); + } + } + } + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleBrandModel/controller/HotVehicleBrandModelController.java b/src/main/java/com/hotwj/platform/config/vehicleBrandModel/controller/HotVehicleBrandModelController.java new file mode 100644 index 0000000..2bda56c --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleBrandModel/controller/HotVehicleBrandModelController.java @@ -0,0 +1,390 @@ +package com.hotwj.platform.config.vehicleBrandModel.controller; + +import com.hotwj.platform.config.vehicleBrandModel.domain.bo.HotVehicleBrandModelBo; +import com.hotwj.platform.config.vehicleBrandModel.domain.vo.HotVehicleBrandModelVo; +import com.hotwj.platform.config.vehicleBrandModel.service.IHotVehicleBrandModelService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 车辆品牌型号库 + * + * @author shihongwei + * @date 2026-02-24 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/vehicleBrandModel") +@Tag(name = "车辆品牌型号库", description = "车辆品牌型号库管理") +public class HotVehicleBrandModelController extends BaseController { + + private final IHotVehicleBrandModelService hotVehicleBrandModelService; + private final IHotVehicleService hotVehicleService; + /** + * 查询车辆品牌型号库列表 + */ + //@SaCheckPermission("config:vehicleBrandModel:list") + @GetMapping("/list") + @Operation(summary = "分页查询车辆品牌型号库列表") + public TableDataInfo list(HotVehicleBrandModelBo bo, PageQuery pageQuery) { + return hotVehicleBrandModelService.queryPageList(bo, pageQuery); + } + + /** + * 从车辆库中提取品牌型号建议(分页) + * 规则: + * 1) 返回不存在于品牌型号库的 vehicleModel,或品牌型号库中该 vehicleModel 的数据缺陷>0 + * 2) 同一 vehicleModel 若有多条车辆数据,选择数据缺陷最少的一条;如缺陷数相同,选择录入时间更早的一条 + * 3) 支持按车辆型号模糊查询 + */ + //@SaCheckPermission("config:vehicleBrandModel:list") + @GetMapping("/suggestFromVehicles") + @Operation(summary = "从车辆库提取品牌型号建议(分页)") + public TableDataInfo suggestFromVehicles( + @RequestParam(required = false) String brandModel, + PageQuery pageQuery + ) { + List vehicles = hotVehicleService.queryList(new HotVehicleBo()); + if (vehicles == null) vehicles = Collections.emptyList(); + // 模糊过滤 + final String q = brandModel; + vehicles = vehicles.stream() + .filter(v -> v.getBrandModel() != null && !v.getBrandModel().isBlank()) + .filter(v -> q == null || q.isBlank() || v.getBrandModel().toLowerCase().contains(q.toLowerCase())) + .collect(Collectors.toList()); + + // 现有品牌型号库(用于排除已完善的型号,按品牌型号+车辆型号双条件) + List existing = hotVehicleBrandModelService.queryList(new HotVehicleBrandModelBo()); + Map existingByKey = existing.stream() + .filter(e -> e.getBrandModel() != null && !e.getBrandModel().isBlank()) + .collect(Collectors.toMap(e -> compositeKey(e.getBrandModel(), e.getVehicleModel()), e -> e, (a, b) -> a)); + + Set goodKeys = existing.stream() + .filter(e -> e.getBrandModel() != null && !e.getBrandModel().isBlank()) + .filter(e -> e.getDataMissingCount() == null || e.getDataMissingCount() == 0L) + .map(e -> compositeKey(e.getBrandModel(), e.getVehicleModel())) + .collect(Collectors.toSet()); + + Set defectiveKeys = existing.stream() + .filter(e -> e.getBrandModel() != null && !e.getBrandModel().isBlank()) + .filter(e -> e.getDataMissingCount() != null && e.getDataMissingCount() > 0L) + .map(e -> compositeKey(e.getBrandModel(), e.getVehicleModel())) + .collect(Collectors.toSet()); + + // 按品牌型号+车辆型号分组 + Map> byKey = vehicles.stream() + .collect(Collectors.groupingBy(v -> compositeKey(v.getBrandModel(), v.getVehicleModel()))); + + List result = new ArrayList<>(); + for (Map.Entry> entry : byKey.entrySet()) { + String key = entry.getKey(); + boolean include = !goodKeys.contains(key) || defectiveKeys.contains(key); + if (!include) continue; + + // 选择缺陷最少,若相同取创建时间更早 + HotVehicleVo best = entry.getValue().stream() + .sorted((a, b) -> { + int da = countDefects(a); + int db = countDefects(b); + if (da != db) return Integer.compare(da, db); + String ia = a.getId(); + String ib = b.getId(); + if (ia == null && ib == null) return 0; + if (ia == null) return 1; + if (ib == null) return -1; + return ia.compareTo(ib); + }) + .findFirst().orElse(null); + if (best == null) continue; + + HotVehicleBrandModelVo vo = new HotVehicleBrandModelVo(); + vo.setVehicleType(best.getVehicleType()); + vo.setBrandModel(best.getBrandModel()); + vo.setBodyColor(best.getColor()); + vo.setPlateColor(best.getPlateColor()); + vo.setFuelType(best.getFuelType()); + vo.setManufacturerName(best.getManufacturerName()); + vo.setEngineDisplacement(best.getEngineDisplacementMl()); + vo.setEnginePowerKw(best.getEnginePowerKw()); + vo.setMotorPowerKw(best.getMotorPowerKw()); + vo.setDriveMotorModel(null); + vo.setWheelbaseMm(best.getWheelbaseMm()); + vo.setTireCount(best.getTireCount()); + vo.setTireSpec(best.getTireSpec()); + vo.setAxleCount(best.getAxleCount()); + vo.setVehicleModel(best.getVehicleModel()); + vo.setCargoLengthMm(best.getCargoInnerLengthMm()); + vo.setCargoWidthMm(best.getCargoInnerWidthMm()); + vo.setCargoHeightMm(best.getCargoInnerHeightMm()); + vo.setCargoVolume(best.getCargoInnerVolumeM3()); + vo.setTransmission(best.getTransmission()); + vo.setRetarder(best.getRetarder()); + vo.setEmissionStandard(best.getEmissionStandard()); + vo.setBatteryType(best.getBatteryType()); + vo.setInsuredSeatCount(best.getInsuredSeatCount()); + vo.setServiceBrakeType(best.getServiceBrakeType()); + vo.setFrontBrakeType(best.getFrontBrakeType()); + vo.setRearBrakeType(best.getRearBrakeType()); + vo.setAbsSystem(best.getHasAbs()); + vo.setParkingBrakeType(null); + vo.setAirConditioning(best.getHasAirConditioning()); + vo.setChassisModel(best.getChassisModel()); + vo.setDomesticFlag(best.getDomesticMark()); + vo.setDataMissingCount((long) countDefects(best)); + vo.setId(Long.valueOf(best.getId())); + result.add(vo); + } + + // 排序:缺陷少优先,其次创建时间更早(通过existing记录或不考虑) + result.sort((a, b) -> { + long da = a.getDataMissingCount() != null ? a.getDataMissingCount() : 0L; + long db = b.getDataMissingCount() != null ? b.getDataMissingCount() : 0L; + int cmp = Long.compare(da, db); + if (cmp != 0) return cmp; + // 如有现成库的 createTime,可比较;否则保持当前顺序 + return 0; + }); + + // 分页 + int pageNum = Optional.ofNullable(pageQuery.getPageNum()).orElse(PageQuery.DEFAULT_PAGE_NUM); + int pageSize = Optional.ofNullable(pageQuery.getPageSize()).orElse(PageQuery.DEFAULT_PAGE_SIZE); + if (pageNum <= 0) pageNum = PageQuery.DEFAULT_PAGE_NUM; + int total = result.size(); + int fromIndex = Math.min((pageNum - 1) * pageSize, total); + int toIndex = Math.min(fromIndex + pageSize, total); + List rows = result.subList(fromIndex, toIndex); + return new TableDataInfo<>(rows, total); + } + + private String compositeKey(String brandModel, String vehicleModel) { + String bm = brandModel == null ? "" : brandModel.trim(); + String vm = vehicleModel == null ? "" : vehicleModel.trim(); + return bm + "##" + vm; + } + + /** + * 同步品牌型号库(根据车辆ID自动补全并新增) + * 仅需传入车辆主键ID,其他信息由系统自动从车辆信息中补全 + */ + //@SaCheckPermission("config:vehicleBrandModel:add") + @PostMapping("/suggestFromVehicles/sync") + @Operation(summary = "批量根据车辆ID同步品牌型号到品牌型号库") + public R syncFromVehicles(@NotEmpty(message = "车辆ID集合不能为空") String vehicleIds) { + if (vehicleIds == null || vehicleIds.isBlank()) { + return R.fail("车辆ID集合不能为空"); + } + List idList = Arrays.stream(vehicleIds.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .map(Long::valueOf) + .collect(Collectors.toList()); + List existing = hotVehicleBrandModelService.queryList(new HotVehicleBrandModelBo()); + Set existingKeys = existing.stream() + .filter(e -> e.getBrandModel() != null && !e.getBrandModel().isBlank()) + .map(e -> compositeKey(e.getBrandModel(), e.getVehicleModel())) + .collect(Collectors.toSet()); + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + for (Long vehicleId : idList) { + try { + HotVehicleVo v = hotVehicleService.queryById(vehicleId); + if (v == null) { + failureNum++; + failureMsg.append("
").append(failureNum).append("、车辆ID ").append(vehicleId).append(" 不存在"); + continue; + } + String key = compositeKey(v.getBrandModel(), v.getVehicleModel()); + if (existingKeys.contains(key)) { + failureNum++; + failureMsg.append("
").append(failureNum).append("、品牌型号/车辆型号组合已存在:").append(key); + continue; + } + HotVehicleBrandModelBo bo = new HotVehicleBrandModelBo(); +// bo.setVehicleType(v.getVehicleType()); + bo.setBrandModel(v.getBrandModel()); + bo.setBodyColor(v.getColor()); + bo.setPlateColor(v.getPlateColor()); + bo.setFuelType(v.getFuelType()); + bo.setManufacturerName(v.getManufacturerName()); + bo.setEngineDisplacement(v.getEngineDisplacementMl()); + bo.setEnginePowerKw(v.getEnginePowerKw()); + bo.setMotorPowerKw(v.getMotorPowerKw()); + bo.setWheelbaseMm(v.getWheelbaseMm()); + bo.setTireCount(v.getTireCount()); + bo.setTireSpec(v.getTireSpec()); + bo.setAxleCount(v.getAxleCount()); + bo.setVehicleModel(v.getVehicleModel()); + bo.setCargoLengthMm(v.getCargoInnerLengthMm()); + bo.setCargoWidthMm(v.getCargoInnerWidthMm()); + bo.setCargoHeightMm(v.getCargoInnerHeightMm()); + bo.setCargoVolume(v.getCargoInnerVolumeM3()); + bo.setTransmission(v.getTransmission()); + bo.setRetarder(v.getRetarder()); + bo.setEmissionStandard(v.getEmissionStandard()); + bo.setBatteryType(v.getBatteryType()); + bo.setInsuredSeatCount(v.getInsuredSeatCount()); + bo.setServiceBrakeType(v.getServiceBrakeType()); + bo.setFrontBrakeType(v.getFrontBrakeType()); + bo.setRearBrakeType(v.getRearBrakeType()); + bo.setAbsSystem(v.getHasAbs()); + bo.setAirConditioning(v.getHasAirConditioning()); + bo.setChassisModel(v.getChassisModel()); + bo.setDomesticFlag(v.getDomesticMark()); + bo.setDataMissingCount((long) countDefects(v)); + boolean ok = hotVehicleBrandModelService.insertByBo(bo); + if (ok) { + existingKeys.add(key); + successNum++; + successMsg.append("
").append(successNum).append("、同步成功:").append(key); + } else { + failureNum++; + failureMsg.append("
").append(failureNum).append("、同步失败:").append(key); + } + } catch (Exception e) { + failureNum++; + failureMsg.append("
").append(failureNum).append("、车辆ID ").append(vehicleId).append(" 同步异常:").append(e.getMessage()); + } + } + if (failureNum > 0) { + failureMsg.insert(0, "部分同步失败!共 " + failureNum + " 条,如下:"); + } + if (successNum > 0) { + successMsg.insert(0, "同步成功共 " + successNum + " 条,详情:"); + } + return R.ok(successMsg.toString() + failureMsg.toString()); + } + + private int countDefects(HotVehicleVo v) { + int cnt = 0; + cnt += isBlank(v.getVehicleType()); + cnt += isBlank(v.getBrandModel()); + cnt += isBlank(v.getColor()); + cnt += isBlank(v.getPlateColor()); + cnt += isBlank(v.getFuelType()); + cnt += isBlank(v.getManufacturerName()); + cnt += isNull(v.getEngineDisplacementMl()); + cnt += isNull(v.getEnginePowerKw()); + cnt += isNull(v.getMotorPowerKw()); + cnt += isNull(v.getWheelbaseMm()); + cnt += isNull(v.getTireCount()); + cnt += isBlank(v.getTireSpec()); + cnt += isNull(v.getAxleCount()); + cnt += isBlank(v.getVehicleModel()); + cnt += isNull(v.getCargoInnerLengthMm()); + cnt += isNull(v.getCargoInnerWidthMm()); + cnt += isNull(v.getCargoInnerHeightMm()); + cnt += isNull(v.getCargoInnerVolumeM3()); + cnt += isBlank(v.getTransmission()); + cnt += isBlank(v.getRetarder()); + cnt += isBlank(v.getEmissionStandard()); + cnt += isBlank(v.getBatteryType()); + cnt += isNull(v.getInsuredSeatCount()); + cnt += isBlank(v.getServiceBrakeType()); + cnt += isBlank(v.getFrontBrakeType()); + cnt += isBlank(v.getRearBrakeType()); + cnt += (v.getHasAbs() == null ? 1 : 0); + cnt += (v.getHasAirConditioning() == null ? 1 : 0); + cnt += isBlank(v.getChassisModel()); + cnt += (v.getDomesticMark() == null ? 1 : 0); + return cnt; + } + + private int isBlank(String s) { + return (s == null || s.isBlank()) ? 1 : 0; + } + + private int isNull(Object o) { + return (o == null) ? 1 : 0; + } + + /** + * 导出车辆品牌型号库列表 + */ + //@SaCheckPermission("config:vehicleBrandModel:export") + @Log(title = "车辆品牌型号库", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆品牌型号库列表") + public void export(HotVehicleBrandModelBo bo, HttpServletResponse response) { + List list = hotVehicleBrandModelService.queryList(bo); + ExcelUtil.exportExcel(list, "车辆品牌型号库", HotVehicleBrandModelVo.class, response); + } + + /** + * 获取车辆品牌型号库详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:vehicleBrandModel:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆品牌型号库详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotVehicleBrandModelService.queryById(id)); + } + + /** + * 新增车辆品牌型号库 + */ + //@SaCheckPermission("config:vehicleBrandModel:add") + @Log(title = "车辆品牌型号库", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆品牌型号库") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleBrandModelBo bo) { + return toAjax(hotVehicleBrandModelService.insertByBo(bo)); + } + + /** + * 修改车辆品牌型号库 + */ + //@SaCheckPermission("config:vehicleBrandModel:edit") + @Log(title = "车辆品牌型号库", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆品牌型号库") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleBrandModelBo bo) { + return toAjax(hotVehicleBrandModelService.updateByBo(bo)); + } + + /** + * 删除车辆品牌型号库 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:vehicleBrandModel:remove") + @Log(title = "车辆品牌型号库", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆品牌型号库") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotVehicleBrandModelService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleBrandModel/domain/HotVehicleBrandModel.java b/src/main/java/com/hotwj/platform/config/vehicleBrandModel/domain/HotVehicleBrandModel.java new file mode 100644 index 0000000..d0b5664 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleBrandModel/domain/HotVehicleBrandModel.java @@ -0,0 +1,205 @@ +package com.hotwj.platform.config.vehicleBrandModel.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 车辆品牌型号库对象 hot_vehicle_brand_model + * + * @author shihongwei + * @date 2026-02-24 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_brand_model") +public class HotVehicleBrandModel extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 类型(字典) + */ + private String vehicleType; + + /** + * 品牌型号 + */ + private String brandModel; + + /** + * 车身颜色 + */ + private String bodyColor; + + /** + * 车牌颜色 + */ + private String plateColor; + + /** + * 燃料种类 + */ + private String fuelType; + + /** + * 制造厂名称 + */ + private String manufacturerName; + + /** + * 发动机排量(ml) + */ + private Long engineDisplacement; + + /** + * 发动机功率(KW) + */ + private Long enginePowerKw; + + /** + * 电机功率(KW) + */ + private Long motorPowerKw; + + /** + * 驱动电机型号 + */ + private String driveMotorModel; + + /** + * 轴距(mm) + */ + private Long wheelbaseMm; + + /** + * 轮胎数 + */ + private Long tireCount; + + /** + * 轮胎规格 + */ + private String tireSpec; + + /** + * 轴数 + */ + private Long axleCount; + + /** + * 车辆型号 + */ + private String vehicleModel; + + /** + * 货箱内部长(mm) + */ + private Long cargoLengthMm; + + /** + * 货箱内部宽(mm) + */ + private Long cargoWidthMm; + + /** + * 货箱内部高(mm) + */ + private Long cargoHeightMm; + + /** + * 货箱内部容积(立方米) + */ + private BigDecimal cargoVolume; + + /** + * 变速器 + */ + private String transmission; + + /** + * 缓速器 + */ + private String retarder; + + /** + * 排放标准 + */ + private String emissionStandard; + + /** + * 电池类型 + */ + private String batteryType; + + /** + * 承保座数 + */ + private Long insuredSeatCount; + + /** + * 行驶制动方式 + */ + private String serviceBrakeType; + + /** + * 前轮制动器形式 + */ + private String frontBrakeType; + + /** + * 制动防抱死系统 + */ + private String absSystem; + + /** + * 后轮制动器形式 + */ + private String rearBrakeType; + + /** + * 驻车制动方式 + */ + private String parkingBrakeType; + + /** + * 空调系统 + */ + private String airConditioning; + + /** + * 底盘型号 + */ + private String chassisModel; + + /** + * 国产标记 + */ + private String domesticFlag; + + /** + * 数据缺失统计 + */ + private Long dataMissingCount; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleBrandModel/domain/bo/HotVehicleBrandModelBo.java b/src/main/java/com/hotwj/platform/config/vehicleBrandModel/domain/bo/HotVehicleBrandModelBo.java new file mode 100644 index 0000000..3acb2de --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleBrandModel/domain/bo/HotVehicleBrandModelBo.java @@ -0,0 +1,206 @@ +package com.hotwj.platform.config.vehicleBrandModel.domain.bo; + +import com.hotwj.platform.config.vehicleBrandModel.domain.HotVehicleBrandModel; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.math.BigDecimal; + +/** + * 车辆品牌型号库业务对象 hot_vehicle_brand_model + * + * @author shihongwei + * @date 2026-02-24 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleBrandModel.class, reverseConvertGenerate = false) +public class HotVehicleBrandModelBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 类型(字典) + */ + @NotBlank(message = "类型(字典)不能为空", groups = {AddGroup.class, EditGroup.class}) + private String vehicleType; + + /** + * 品牌型号 + */ + @NotBlank(message = "品牌型号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String brandModel; + + /** + * 车身颜色 + */ + private String bodyColor; + + /** + * 车牌颜色 + */ + private String plateColor; + + /** + * 燃料种类 + */ + private String fuelType; + + /** + * 制造厂名称 + */ + private String manufacturerName; + + /** + * 发动机排量(ml) + */ + private Long engineDisplacement; + + /** + * 发动机功率(KW) + */ + private Long enginePowerKw; + + /** + * 电机功率(KW) + */ + private Long motorPowerKw; + + /** + * 驱动电机型号 + */ + private String driveMotorModel; + + /** + * 轴距(mm) + */ + private Long wheelbaseMm; + + /** + * 轮胎数 + */ + private Long tireCount; + + /** + * 轮胎规格 + */ + private String tireSpec; + + /** + * 轴数 + */ + private Long axleCount; + + /** + * 车辆型号 + */ + private String vehicleModel; + + /** + * 货箱内部长(mm) + */ + private Long cargoLengthMm; + + /** + * 货箱内部宽(mm) + */ + private Long cargoWidthMm; + + /** + * 货箱内部高(mm) + */ + private Long cargoHeightMm; + + /** + * 货箱内部容积(立方米) + */ + private BigDecimal cargoVolume; + + /** + * 变速器 + */ + private String transmission; + + /** + * 缓速器 + */ + private String retarder; + + /** + * 排放标准 + */ + private String emissionStandard; + + /** + * 电池类型 + */ + private String batteryType; + + /** + * 承保座数 + */ + private Long insuredSeatCount; + + /** + * 行驶制动方式 + */ + private String serviceBrakeType; + + /** + * 前轮制动器形式 + */ + private String frontBrakeType; + + /** + * 制动防抱死系统 + */ + private String absSystem; + + /** + * 后轮制动器形式 + */ + private String rearBrakeType; + + /** + * 驻车制动方式 + */ + private String parkingBrakeType; + + /** + * 空调系统 + */ + private String airConditioning; + + /** + * 底盘型号 + */ + private String chassisModel; + + /** + * 国产标记 + */ + private String domesticFlag; + + /** + * 数据缺失统计 + */ + private Long dataMissingCount; + + /** + * 0=正常, 1=已删除 + */ + @NotNull(message = "0=正常, 1=已删除不能为空", groups = {EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleBrandModel/domain/vo/HotVehicleBrandModelVo.java b/src/main/java/com/hotwj/platform/config/vehicleBrandModel/domain/vo/HotVehicleBrandModelVo.java new file mode 100644 index 0000000..3e8fd8c --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleBrandModel/domain/vo/HotVehicleBrandModelVo.java @@ -0,0 +1,239 @@ +package com.hotwj.platform.config.vehicleBrandModel.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.vehicleBrandModel.domain.HotVehicleBrandModel; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + + +/** + * 车辆品牌型号库视图对象 hot_vehicle_brand_model + * + * @author shihongwei + * @date 2026-02-24 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleBrandModel.class) +public class HotVehicleBrandModelVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 类型(字典) + */ + @ExcelProperty(value = "类型(字典)") + private String vehicleType; + + /** + * 品牌型号 + */ + @ExcelProperty(value = "品牌型号") + private String brandModel; + + /** + * 车身颜色 + */ + @ExcelProperty(value = "车身颜色") + private String bodyColor; + + /** + * 车牌颜色 + */ + @ExcelProperty(value = "车牌颜色") + private String plateColor; + + /** + * 燃料种类 + */ + @ExcelProperty(value = "燃料种类") + private String fuelType; + + /** + * 制造厂名称 + */ + @ExcelProperty(value = "制造厂名称") + private String manufacturerName; + + /** + * 发动机排量(ml) + */ + @ExcelProperty(value = "发动机排量(ml)") + private Long engineDisplacement; + + /** + * 发动机功率(KW) + */ + @ExcelProperty(value = "发动机功率(KW)") + private Long enginePowerKw; + + /** + * 电机功率(KW) + */ + @ExcelProperty(value = "电机功率(KW)") + private Long motorPowerKw; + + /** + * 驱动电机型号 + */ + @ExcelProperty(value = "驱动电机型号") + private String driveMotorModel; + + /** + * 轴距(mm) + */ + @ExcelProperty(value = "轴距(mm)") + private Long wheelbaseMm; + + /** + * 轮胎数 + */ + @ExcelProperty(value = "轮胎数") + private Long tireCount; + + /** + * 轮胎规格 + */ + @ExcelProperty(value = "轮胎规格") + private String tireSpec; + + /** + * 轴数 + */ + @ExcelProperty(value = "轴数") + private Long axleCount; + + /** + * 车辆型号 + */ + @ExcelProperty(value = "车辆型号") + private String vehicleModel; + + /** + * 货箱内部长(mm) + */ + @ExcelProperty(value = "货箱内部长(mm)") + private Long cargoLengthMm; + + /** + * 货箱内部宽(mm) + */ + @ExcelProperty(value = "货箱内部宽(mm)") + private Long cargoWidthMm; + + /** + * 货箱内部高(mm) + */ + @ExcelProperty(value = "货箱内部高(mm)") + private Long cargoHeightMm; + + /** + * 货箱内部容积(立方米) + */ + @ExcelProperty(value = "货箱内部容积(立方米)") + private BigDecimal cargoVolume; + + /** + * 变速器 + */ + @ExcelProperty(value = "变速器") + private String transmission; + + /** + * 缓速器 + */ + @ExcelProperty(value = "缓速器") + private String retarder; + + /** + * 排放标准 + */ + @ExcelProperty(value = "排放标准") + private String emissionStandard; + + /** + * 电池类型 + */ + @ExcelProperty(value = "电池类型") + private String batteryType; + + /** + * 承保座数 + */ + @ExcelProperty(value = "承保座数") + private Long insuredSeatCount; + + /** + * 行驶制动方式 + */ + @ExcelProperty(value = "行驶制动方式") + private String serviceBrakeType; + + /** + * 前轮制动器形式 + */ + @ExcelProperty(value = "前轮制动器形式") + private String frontBrakeType; + + /** + * 制动防抱死系统 + */ + @ExcelProperty(value = "制动防抱死系统") + private String absSystem; + + /** + * 后轮制动器形式 + */ + @ExcelProperty(value = "后轮制动器形式") + private String rearBrakeType; + + /** + * 驻车制动方式 + */ + @ExcelProperty(value = "驻车制动方式") + private String parkingBrakeType; + + /** + * 空调系统 + */ + @ExcelProperty(value = "空调系统") + private String airConditioning; + + /** + * 底盘型号 + */ + @ExcelProperty(value = "底盘型号") + private String chassisModel; + + /** + * 国产标记 + */ + @ExcelProperty(value = "国产标记") + private String domesticFlag; + + /** + * 数据缺失统计 + */ + @ExcelProperty(value = "数据缺失统计") + private Long dataMissingCount; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleBrandModel/mapper/HotVehicleBrandModelMapper.java b/src/main/java/com/hotwj/platform/config/vehicleBrandModel/mapper/HotVehicleBrandModelMapper.java new file mode 100644 index 0000000..2033eee --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleBrandModel/mapper/HotVehicleBrandModelMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.vehicleBrandModel.mapper; + +import com.hotwj.platform.config.vehicleBrandModel.domain.HotVehicleBrandModel; +import com.hotwj.platform.config.vehicleBrandModel.domain.vo.HotVehicleBrandModelVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆品牌型号库Mapper接口 + * + * @author shihongwei + * @date 2026-02-24 + */ +@Mapper +public interface HotVehicleBrandModelMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleBrandModel/service/IHotVehicleBrandModelService.java b/src/main/java/com/hotwj/platform/config/vehicleBrandModel/service/IHotVehicleBrandModelService.java new file mode 100644 index 0000000..8683da4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleBrandModel/service/IHotVehicleBrandModelService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.vehicleBrandModel.service; + +import com.hotwj.platform.config.vehicleBrandModel.domain.bo.HotVehicleBrandModelBo; +import com.hotwj.platform.config.vehicleBrandModel.domain.vo.HotVehicleBrandModelVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆品牌型号库Service接口 + * + * @author shihongwei + * @date 2026-02-24 + */ +public interface IHotVehicleBrandModelService { + + /** + * 查询车辆品牌型号库 + * + * @param id 主键 + * @return 车辆品牌型号库 + */ + HotVehicleBrandModelVo queryById(Long id); + + /** + * 分页查询车辆品牌型号库列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆品牌型号库分页列表 + */ + TableDataInfo queryPageList(HotVehicleBrandModelBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆品牌型号库列表 + * + * @param bo 查询条件 + * @return 车辆品牌型号库列表 + */ + List queryList(HotVehicleBrandModelBo bo); + + /** + * 新增车辆品牌型号库 + * + * @param bo 车辆品牌型号库 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleBrandModelBo bo); + + /** + * 修改车辆品牌型号库 + * + * @param bo 车辆品牌型号库 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleBrandModelBo bo); + + /** + * 校验并批量删除车辆品牌型号库信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleBrandModel/service/impl/HotVehicleBrandModelServiceImpl.java b/src/main/java/com/hotwj/platform/config/vehicleBrandModel/service/impl/HotVehicleBrandModelServiceImpl.java new file mode 100644 index 0000000..401089a --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleBrandModel/service/impl/HotVehicleBrandModelServiceImpl.java @@ -0,0 +1,164 @@ +package com.hotwj.platform.config.vehicleBrandModel.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.vehicleBrandModel.domain.HotVehicleBrandModel; +import com.hotwj.platform.config.vehicleBrandModel.domain.bo.HotVehicleBrandModelBo; +import com.hotwj.platform.config.vehicleBrandModel.domain.vo.HotVehicleBrandModelVo; +import com.hotwj.platform.config.vehicleBrandModel.mapper.HotVehicleBrandModelMapper; +import com.hotwj.platform.config.vehicleBrandModel.service.IHotVehicleBrandModelService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 车辆品牌型号库Service业务层处理 + * + * @author shihongwei + * @date 2026-02-24 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleBrandModelServiceImpl implements IHotVehicleBrandModelService { + + private final HotVehicleBrandModelMapper baseMapper; + + /** + * 查询车辆品牌型号库 + * + * @param id 主键 + * @return 车辆品牌型号库 + */ + @Override + public HotVehicleBrandModelVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆品牌型号库列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆品牌型号库分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleBrandModelBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆品牌型号库列表 + * + * @param bo 查询条件 + * @return 车辆品牌型号库列表 + */ + @Override + public List queryList(HotVehicleBrandModelBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleBrandModelBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotVehicleBrandModel::getId); + lqw.eq(StringUtils.isNotBlank(bo.getVehicleType()), HotVehicleBrandModel::getVehicleType, bo.getVehicleType()); + lqw.eq(StringUtils.isNotBlank(bo.getBrandModel()), HotVehicleBrandModel::getBrandModel, bo.getBrandModel()); + lqw.eq(StringUtils.isNotBlank(bo.getBodyColor()), HotVehicleBrandModel::getBodyColor, bo.getBodyColor()); + lqw.eq(StringUtils.isNotBlank(bo.getPlateColor()), HotVehicleBrandModel::getPlateColor, bo.getPlateColor()); + lqw.eq(StringUtils.isNotBlank(bo.getFuelType()), HotVehicleBrandModel::getFuelType, bo.getFuelType()); + lqw.like(StringUtils.isNotBlank(bo.getManufacturerName()), HotVehicleBrandModel::getManufacturerName, bo.getManufacturerName()); + lqw.eq(bo.getEngineDisplacement() != null, HotVehicleBrandModel::getEngineDisplacement, bo.getEngineDisplacement()); + lqw.eq(bo.getEnginePowerKw() != null, HotVehicleBrandModel::getEnginePowerKw, bo.getEnginePowerKw()); + lqw.eq(bo.getMotorPowerKw() != null, HotVehicleBrandModel::getMotorPowerKw, bo.getMotorPowerKw()); + lqw.eq(StringUtils.isNotBlank(bo.getDriveMotorModel()), HotVehicleBrandModel::getDriveMotorModel, bo.getDriveMotorModel()); + lqw.eq(bo.getWheelbaseMm() != null, HotVehicleBrandModel::getWheelbaseMm, bo.getWheelbaseMm()); + lqw.eq(bo.getTireCount() != null, HotVehicleBrandModel::getTireCount, bo.getTireCount()); + lqw.eq(StringUtils.isNotBlank(bo.getTireSpec()), HotVehicleBrandModel::getTireSpec, bo.getTireSpec()); + lqw.eq(bo.getAxleCount() != null, HotVehicleBrandModel::getAxleCount, bo.getAxleCount()); + lqw.like(StringUtils.isNotBlank(bo.getVehicleModel()), HotVehicleBrandModel::getVehicleModel, bo.getVehicleModel()); + lqw.eq(bo.getCargoLengthMm() != null, HotVehicleBrandModel::getCargoLengthMm, bo.getCargoLengthMm()); + lqw.eq(bo.getCargoWidthMm() != null, HotVehicleBrandModel::getCargoWidthMm, bo.getCargoWidthMm()); + lqw.eq(bo.getCargoHeightMm() != null, HotVehicleBrandModel::getCargoHeightMm, bo.getCargoHeightMm()); + lqw.eq(bo.getCargoVolume() != null, HotVehicleBrandModel::getCargoVolume, bo.getCargoVolume()); + lqw.eq(StringUtils.isNotBlank(bo.getTransmission()), HotVehicleBrandModel::getTransmission, bo.getTransmission()); + lqw.eq(StringUtils.isNotBlank(bo.getRetarder()), HotVehicleBrandModel::getRetarder, bo.getRetarder()); + lqw.eq(StringUtils.isNotBlank(bo.getEmissionStandard()), HotVehicleBrandModel::getEmissionStandard, bo.getEmissionStandard()); + lqw.eq(StringUtils.isNotBlank(bo.getBatteryType()), HotVehicleBrandModel::getBatteryType, bo.getBatteryType()); + lqw.eq(bo.getInsuredSeatCount() != null, HotVehicleBrandModel::getInsuredSeatCount, bo.getInsuredSeatCount()); + lqw.eq(StringUtils.isNotBlank(bo.getServiceBrakeType()), HotVehicleBrandModel::getServiceBrakeType, bo.getServiceBrakeType()); + lqw.eq(StringUtils.isNotBlank(bo.getFrontBrakeType()), HotVehicleBrandModel::getFrontBrakeType, bo.getFrontBrakeType()); + lqw.eq(StringUtils.isNotBlank(bo.getAbsSystem()), HotVehicleBrandModel::getAbsSystem, bo.getAbsSystem()); + lqw.eq(StringUtils.isNotBlank(bo.getRearBrakeType()), HotVehicleBrandModel::getRearBrakeType, bo.getRearBrakeType()); + lqw.eq(StringUtils.isNotBlank(bo.getParkingBrakeType()), HotVehicleBrandModel::getParkingBrakeType, bo.getParkingBrakeType()); + lqw.eq(StringUtils.isNotBlank(bo.getAirConditioning()), HotVehicleBrandModel::getAirConditioning, bo.getAirConditioning()); + lqw.eq(StringUtils.isNotBlank(bo.getChassisModel()), HotVehicleBrandModel::getChassisModel, bo.getChassisModel()); + lqw.eq(StringUtils.isNotBlank(bo.getDomesticFlag()), HotVehicleBrandModel::getDomesticFlag, bo.getDomesticFlag()); + lqw.eq(bo.getDataMissingCount() != null, HotVehicleBrandModel::getDataMissingCount, bo.getDataMissingCount()); + return lqw; + } + + /** + * 新增车辆品牌型号库 + * + * @param bo 车辆品牌型号库 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleBrandModelBo bo) { + HotVehicleBrandModel add = MapstructUtils.convert(bo, HotVehicleBrandModel.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改车辆品牌型号库 + * + * @param bo 车辆品牌型号库 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleBrandModelBo bo) { + HotVehicleBrandModel update = MapstructUtils.convert(bo, HotVehicleBrandModel.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleBrandModel entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除车辆品牌型号库信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/controller/HotVehicleInspectionConfigController.java b/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/controller/HotVehicleInspectionConfigController.java new file mode 100644 index 0000000..b9fc788 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/controller/HotVehicleInspectionConfigController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.config.vehicleInspectionConfig.controller; + +import com.hotwj.platform.config.vehicleInspectionConfig.domain.bo.HotVehicleInspectionConfigBo; +import com.hotwj.platform.config.vehicleInspectionConfig.domain.vo.HotVehicleInspectionConfigVo; +import com.hotwj.platform.config.vehicleInspectionConfig.service.IHotVehicleInspectionConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 车辆年审检查配置 + * + * @author shihongwei + * @date 2025-12-19 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/vehicleInspectionConfig") +@Tag(name = "车辆年审检查配置", description = "车辆年审检查配置增删改查与导出") +public class HotVehicleInspectionConfigController extends BaseController { + + private final IHotVehicleInspectionConfigService hotVehicleInspectionConfigService; + + /** + * 查询车辆年审检查配置列表 + */ + //@SaCheckPermission("config:vehicleInspectionConfig:list") + @GetMapping("/list") + @Operation(summary = "查询车辆年审检查配置列表") + public TableDataInfo list(HotVehicleInspectionConfigBo bo, PageQuery pageQuery) { + return hotVehicleInspectionConfigService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆年审检查配置列表 + */ + //@SaCheckPermission("config:vehicleInspectionConfig:export") + @Log(title = "车辆年审检查配置", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆年审检查配置列表") + public void export(HotVehicleInspectionConfigBo bo, HttpServletResponse response) { + List list = hotVehicleInspectionConfigService.queryList(bo); + ExcelUtil.exportExcel(list, "车辆年审检查配置", HotVehicleInspectionConfigVo.class, response); + } + + /** + * 获取车辆年审检查配置详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:vehicleInspectionConfig:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆年审检查配置详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotVehicleInspectionConfigService.queryById(id)); + } + + /** + * 新增车辆年审检查配置 + */ + //@SaCheckPermission("config:vehicleInspectionConfig:add") + @Log(title = "车辆年审检查配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆年审检查配置") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleInspectionConfigBo bo) { + return toAjax(hotVehicleInspectionConfigService.insertByBo(bo)); + } + + /** + * 修改车辆年审检查配置 + */ + //@SaCheckPermission("config:vehicleInspectionConfig:edit") + @Log(title = "车辆年审检查配置", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆年审检查配置") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleInspectionConfigBo bo) { + return toAjax(hotVehicleInspectionConfigService.updateByBo(bo)); + } + + /** + * 删除车辆年审检查配置 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:vehicleInspectionConfig:remove") + @Log(title = "车辆年审检查配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆年审检查配置") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotVehicleInspectionConfigService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/domain/HotVehicleInspectionConfig.java b/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/domain/HotVehicleInspectionConfig.java new file mode 100644 index 0000000..457b419 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/domain/HotVehicleInspectionConfig.java @@ -0,0 +1,59 @@ +package com.hotwj.platform.config.vehicleInspectionConfig.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 车辆年审检查配置对象 hot_vehicle_inspection_config + * + * @author shihongwei + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_inspection_config") +public class HotVehicleInspectionConfig extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 间隔年数 + */ + private Long intervalYears; + + /** + * 检测次数 + */ + private Long inspectionTimes; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 是否启用:1=启用,0=停用 + */ + private Long isEnabled; + + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/domain/bo/HotVehicleInspectionConfigBo.java b/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/domain/bo/HotVehicleInspectionConfigBo.java new file mode 100644 index 0000000..85c0605 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/domain/bo/HotVehicleInspectionConfigBo.java @@ -0,0 +1,59 @@ +package com.hotwj.platform.config.vehicleInspectionConfig.domain.bo; + +import com.hotwj.platform.config.vehicleInspectionConfig.domain.HotVehicleInspectionConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 车辆年审检查配置业务对象 hot_vehicle_inspection_config + * + * @author shihongwei + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleInspectionConfig.class, reverseConvertGenerate = false) +public class HotVehicleInspectionConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 间隔年数 + */ + @NotNull(message = "间隔年数不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long intervalYears; + + /** + * 检测次数 + */ + @NotNull(message = "检测次数不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long inspectionTimes; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + /** + * 是否启用:1=启用,0=停用 + */ + private Long isEnabled; + + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/domain/vo/HotVehicleInspectionConfigVo.java b/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/domain/vo/HotVehicleInspectionConfigVo.java new file mode 100644 index 0000000..a97dc93 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/domain/vo/HotVehicleInspectionConfigVo.java @@ -0,0 +1,64 @@ +package com.hotwj.platform.config.vehicleInspectionConfig.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.vehicleInspectionConfig.domain.HotVehicleInspectionConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 车辆年审检查配置视图对象 hot_vehicle_inspection_config + * + * @author shihongwei + * @date 2025-12-19 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleInspectionConfig.class) +public class HotVehicleInspectionConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 间隔年数 + */ + @ExcelProperty(value = "间隔年数") + private Long intervalYears; + + /** + * 检测次数 + */ + @ExcelProperty(value = "检测次数") + private Long inspectionTimes; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 是否启用:1=启用,0=停用 + */ + @ExcelProperty(value = "是否启用:1=启用,0=停用") + private Long isEnabled; + + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/mapper/HotVehicleInspectionConfigMapper.java b/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/mapper/HotVehicleInspectionConfigMapper.java new file mode 100644 index 0000000..cae3029 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/mapper/HotVehicleInspectionConfigMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.vehicleInspectionConfig.mapper; + +import com.hotwj.platform.config.vehicleInspectionConfig.domain.HotVehicleInspectionConfig; +import com.hotwj.platform.config.vehicleInspectionConfig.domain.vo.HotVehicleInspectionConfigVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆年审检查配置Mapper接口 + * + * @author shihongwei + * @date 2025-12-19 + */ +@Mapper +public interface HotVehicleInspectionConfigMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/service/IHotVehicleInspectionConfigService.java b/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/service/IHotVehicleInspectionConfigService.java new file mode 100644 index 0000000..741df9c --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/service/IHotVehicleInspectionConfigService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.vehicleInspectionConfig.service; + +import com.hotwj.platform.config.vehicleInspectionConfig.domain.bo.HotVehicleInspectionConfigBo; +import com.hotwj.platform.config.vehicleInspectionConfig.domain.vo.HotVehicleInspectionConfigVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆年审检查配置Service接口 + * + * @author shihongwei + * @date 2025-12-19 + */ +public interface IHotVehicleInspectionConfigService { + + /** + * 查询车辆年审检查配置 + * + * @param id 主键 + * @return 车辆年审检查配置 + */ + HotVehicleInspectionConfigVo queryById(Long id); + + /** + * 分页查询车辆年审检查配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆年审检查配置分页列表 + */ + TableDataInfo queryPageList(HotVehicleInspectionConfigBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆年审检查配置列表 + * + * @param bo 查询条件 + * @return 车辆年审检查配置列表 + */ + List queryList(HotVehicleInspectionConfigBo bo); + + /** + * 新增车辆年审检查配置 + * + * @param bo 车辆年审检查配置 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleInspectionConfigBo bo); + + /** + * 修改车辆年审检查配置 + * + * @param bo 车辆年审检查配置 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleInspectionConfigBo bo); + + /** + * 校验并批量删除车辆年审检查配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/service/impl/HotVehicleInspectionConfigServiceImpl.java b/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/service/impl/HotVehicleInspectionConfigServiceImpl.java new file mode 100644 index 0000000..9f1429a --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleInspectionConfig/service/impl/HotVehicleInspectionConfigServiceImpl.java @@ -0,0 +1,135 @@ +package com.hotwj.platform.config.vehicleInspectionConfig.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.vehicleInspectionConfig.domain.HotVehicleInspectionConfig; +import com.hotwj.platform.config.vehicleInspectionConfig.domain.bo.HotVehicleInspectionConfigBo; +import com.hotwj.platform.config.vehicleInspectionConfig.domain.vo.HotVehicleInspectionConfigVo; +import com.hotwj.platform.config.vehicleInspectionConfig.mapper.HotVehicleInspectionConfigMapper; +import com.hotwj.platform.config.vehicleInspectionConfig.service.IHotVehicleInspectionConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 车辆年审检查配置Service业务层处理 + * + * @author shihongwei + * @date 2025-12-19 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleInspectionConfigServiceImpl implements IHotVehicleInspectionConfigService { + + private final HotVehicleInspectionConfigMapper baseMapper; + + /** + * 查询车辆年审检查配置 + * + * @param id 主键 + * @return 车辆年审检查配置 + */ + @Override + public HotVehicleInspectionConfigVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆年审检查配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆年审检查配置分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleInspectionConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆年审检查配置列表 + * + * @param bo 查询条件 + * @return 车辆年审检查配置列表 + */ + @Override + public List queryList(HotVehicleInspectionConfigBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleInspectionConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotVehicleInspectionConfig::getId); + lqw.eq(bo.getCompanyId() != null, HotVehicleInspectionConfig::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getIntervalYears() != null, HotVehicleInspectionConfig::getIntervalYears, bo.getIntervalYears()); + lqw.eq(bo.getInspectionTimes() != null, HotVehicleInspectionConfig::getInspectionTimes, bo.getInspectionTimes()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleInspectionConfig::getIsDeleted, bo.getIsDeleted()); + lqw.eq(bo.getIsEnabled() != null, HotVehicleInspectionConfig::getIsEnabled, bo.getIsEnabled()); + return lqw; + } + + /** + * 新增车辆年审检查配置 + * + * @param bo 车辆年审检查配置 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleInspectionConfigBo bo) { + HotVehicleInspectionConfig add = MapstructUtils.convert(bo, HotVehicleInspectionConfig.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改车辆年审检查配置 + * + * @param bo 车辆年审检查配置 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleInspectionConfigBo bo) { + HotVehicleInspectionConfig update = MapstructUtils.convert(bo, HotVehicleInspectionConfig.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleInspectionConfig entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除车辆年审检查配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/controller/HotVehicleSecondaryMaintenanceConfigController.java b/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/controller/HotVehicleSecondaryMaintenanceConfigController.java new file mode 100644 index 0000000..9debeca --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/controller/HotVehicleSecondaryMaintenanceConfigController.java @@ -0,0 +1,350 @@ +package com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.controller; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.bo.HotVehicleSecondaryMaintenanceConfigBo; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.vo.HotVehicleSecondaryMaintenanceConfigVo; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.service.IHotVehicleSecondaryMaintenanceConfigService; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.vo.HotVehicleMaintenanceVo; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.service.IHotVehicleMaintenanceService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleStatusBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import com.hotwj.platform.resourceManagement.vehicleManagement.utils.VehicleUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.utils.DateUtils; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +/** + * 车辆二级维护配置 + * + * @author shihongwei + * @date 2025-12-19 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/vehicleSecondaryMaintenanceConfig") +@Tag(name = "车辆二级维护配置", description = "车辆二级维护配置增删改查与导出") +public class HotVehicleSecondaryMaintenanceConfigController extends BaseController { + + private final IHotVehicleSecondaryMaintenanceConfigService hotVehicleSecondaryMaintenanceConfigService; + private final IHotVehicleService hotVehicleService; + private final IHotVehicleMaintenanceService hotVehicleMaintenanceService; + + private static LocalDate toLocalDate(Date date) { + if (date == null) { + return null; + } + return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } + + + /** + * 查询车辆二级维护配置列表 + */ + //@SaCheckPermission("config:vehicleSecondaryMaintenanceConfig:list") + @GetMapping("/list") + @Operation(summary = "查询车辆二级维护配置列表") + public TableDataInfo list(HotVehicleSecondaryMaintenanceConfigBo bo, PageQuery pageQuery) { + return hotVehicleSecondaryMaintenanceConfigService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆二级维护状态列表 + */ + //@SaCheckPermission("config:vehicleSecondaryMaintenanceConfig:export") + @Log(title = "车辆二级维护状态", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆二级维护状态列表") + public void export(HotVehicleStatusBo bo, HttpServletResponse response) { + Long companyId = bo != null ? bo.getCompanyId() : null; + if (companyId == null) { + ExcelUtil.exportExcel(new ArrayList<>(), "车辆二级维护", VehicleSecondaryMaintenanceExportVo.class, response); + return; + } + + HotVehicleSecondaryMaintenanceConfigBo configBo = new HotVehicleSecondaryMaintenanceConfigBo(); + configBo.setCompanyId(companyId); + configBo.setIsEnabled(1L); + configBo.setIsDeleted(0L); + List configs = hotVehicleSecondaryMaintenanceConfigService.queryList(configBo); + configs = configs == null ? new ArrayList<>() : configs.stream() + .filter(c -> c != null && (c.getIsEnabled() == null || c.getIsEnabled() == 1L)) + .sorted(Comparator.comparing(c -> c.getIntervalYears() == null ? Long.MAX_VALUE : c.getIntervalYears())) + .toList(); + + List rules = new ArrayList<>(); + for (HotVehicleSecondaryMaintenanceConfigVo c : configs) { + if (c.getIntervalYears() == null || c.getInspectionTimes() == null) { + continue; + } + rules.add(new VehicleUtils.AnnualInspectionRule(c.getIntervalYears().intValue(), c.getInspectionTimes().intValue())); + } + + if (bo == null) { + bo = new HotVehicleStatusBo(); + } + bo.setCompanyId(companyId); + PageQuery pageQuery = new PageQuery(Integer.MAX_VALUE, PageQuery.DEFAULT_PAGE_NUM); + TableDataInfo vehiclePage = hotVehicleService.queryPageListByStatus(bo, pageQuery); + List vehicles = vehiclePage.getRows(); + if (vehicles == null || vehicles.isEmpty()) { + ExcelUtil.exportExcel(new ArrayList<>(), "车辆二级维护", VehicleSecondaryMaintenanceExportVo.class, response); + return; + } + + List rows = buildSecondaryMaintenanceExportRows(vehicles, rules); + ExcelUtil.exportExcel(rows, "车辆二级维护", VehicleSecondaryMaintenanceExportVo.class, response); + } + + /** + * 获取车辆二级维护配置详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:vehicleSecondaryMaintenanceConfig:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆二级维护配置详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotVehicleSecondaryMaintenanceConfigService.queryById(id)); + } + + /** + * 新增车辆二级维护配置 + */ + //@SaCheckPermission("config:vehicleSecondaryMaintenanceConfig:add") + @Log(title = "车辆二级维护配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆二级维护配置") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleSecondaryMaintenanceConfigBo bo) { + return toAjax(hotVehicleSecondaryMaintenanceConfigService.insertByBo(bo)); + } + + /** + * 修改车辆二级维护配置 + */ + //@SaCheckPermission("config:vehicleSecondaryMaintenanceConfig:edit") + @Log(title = "车辆二级维护配置", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆二级维护配置") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleSecondaryMaintenanceConfigBo bo) { + return toAjax(hotVehicleSecondaryMaintenanceConfigService.updateByBo(bo)); + } + + /** + * 删除车辆二级维护配置 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:vehicleSecondaryMaintenanceConfig:remove") + @Log(title = "车辆二级维护配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆二级维护配置") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotVehicleSecondaryMaintenanceConfigService.deleteWithValidByIds(List.of(ids), true)); + } + + private static Date toDate(LocalDate date) { + if (date == null) { + return null; + } + return Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant()); + } + + private List buildSecondaryMaintenanceExportRows(List vehicles, List rules) { + List rows = new ArrayList<>(); + for (HotVehicleVo v : vehicles) { + if (v == null) { + continue; + } + VehicleSecondaryMaintenanceExportVo row = new VehicleSecondaryMaintenanceExportVo(); + row.setPlateNumber(v.getPlateNumber()); + + Date issueDate = v.getCertificateIssueDate(); + row.setIssueDate(issueDate); + + HotVehicleMaintenanceVo lastMaintenance = v.getId() != null ? hotVehicleMaintenanceService.queryLatestByVehicleId(v.getId()) : null; + Date lastMaintenanceTime = null; + if (lastMaintenance != null) { + lastMaintenanceTime = lastMaintenance.getFinishTime() != null ? lastMaintenance.getFinishTime() : lastMaintenance.getMaintainDate(); + } + row.setLastMaintenanceTime(lastMaintenanceTime != null ? DateUtils.formatDate(lastMaintenanceTime) : "未上传信息"); + + LocalDate registrationDate = toLocalDate(issueDate); + LocalDate lastMaintenanceDate = toLocalDate(lastMaintenanceTime); + + VehicleUtils.InspectionResult result = VehicleUtils.calculateMaintenanceStatus(rules, registrationDate, lastMaintenanceDate); + if (result == null) { + rows.add(row); + continue; + } + + int times = result.getInspectionTimes(); + row.setMaintenanceTimesText("每年维护(" + times + "次)"); + row.setDueDate(toDate(result.getNextInspectionDate())); + row.setWarningLevelText(result.getWarningLevelDesc()); + + if (result.getWarningLevel() == 6) { + row.setMaintenanceTip("无需办理"); + } else if (result.getOverdueDays() > 0) { + row.setMaintenanceTip("已到期(" + result.getOverdueDays() + "天)"); + } else if (result.getRemainingDays() > 0) { + row.setMaintenanceTip("剩余(" + result.getRemainingDays() + "天)到期"); + } else { + row.setMaintenanceTip("已到期(0天)"); + } + + rows.add(row); + } + return rows; + } + + @GetMapping("/maintenanceStatus/list") + @Operation(summary = "车辆二级维护状态列表") + public TableDataInfo maintenanceStatusList(HotVehicleStatusBo bo, PageQuery pageQuery) { + Long companyId = bo != null ? bo.getCompanyId() : null; + if (companyId == null) { + return new TableDataInfo<>(new ArrayList<>(), 0); + } + + HotVehicleSecondaryMaintenanceConfigBo configBo = new HotVehicleSecondaryMaintenanceConfigBo(); + configBo.setCompanyId(companyId); + configBo.setIsEnabled(1L); + configBo.setIsDeleted(0L); + List configs = hotVehicleSecondaryMaintenanceConfigService.queryList(configBo); + configs = configs == null ? new ArrayList<>() : configs.stream() + .filter(c -> c != null && (c.getIsEnabled() == null || c.getIsEnabled() == 1L)) + .sorted(Comparator.comparing(c -> c.getIntervalYears() == null ? Long.MAX_VALUE : c.getIntervalYears())) + .toList(); + + List rules = new ArrayList<>(); + for (HotVehicleSecondaryMaintenanceConfigVo c : configs) { + if (c.getIntervalYears() == null || c.getInspectionTimes() == null) { + continue; + } + rules.add(new VehicleUtils.AnnualInspectionRule(c.getIntervalYears().intValue(), c.getInspectionTimes().intValue())); + } + + TableDataInfo vehiclePage = hotVehicleService.queryPageListByStatus(bo, pageQuery); + List vehicles = vehiclePage.getRows(); + if (vehicles == null || vehicles.isEmpty()) { + return new TableDataInfo<>(new ArrayList<>(), vehiclePage.getTotal()); + } + + List rows = new ArrayList<>(); + for (HotVehicleVo v : vehicles) { + if (v == null) { + continue; + } + VehicleSecondaryMaintenanceStatusVo row = new VehicleSecondaryMaintenanceStatusVo(); + row.setVehicleId(v.getId()); + row.setPlateNumber(v.getPlateNumber()); + + Date issueDate = v.getCertificateIssueDate(); + row.setIssueDate(issueDate); + + HotVehicleMaintenanceVo lastMaintenance = v.getId() != null ? hotVehicleMaintenanceService.queryLatestByVehicleId(v.getId()) : null; + Date lastMaintenanceTime = null; + if (lastMaintenance != null) { + lastMaintenanceTime = lastMaintenance.getFinishTime() != null ? lastMaintenance.getFinishTime() : lastMaintenance.getMaintainDate(); + } + row.setLastMaintenanceTime(lastMaintenanceTime); + + LocalDate registrationDate = toLocalDate(issueDate); + LocalDate lastMaintenanceDate = toLocalDate(lastMaintenanceTime); + + VehicleUtils.InspectionResult result = VehicleUtils.calculateMaintenanceStatus(rules, registrationDate, lastMaintenanceDate); + + if (result != null) { + int times = result.getInspectionTimes(); + row.setMaintenanceTimesText("每年维护(" + times + "次)"); + + LocalDate dueLocalDate = result.getNextInspectionDate(); + row.setDueDate(toDate(dueLocalDate)); + row.setWarningLevel(result.getWarningLevel()); + row.setWarningLevelText(result.getWarningLevelDesc()); + + if (result.getWarningLevel() == 6) { + row.setMaintenanceTip("无需办理"); + } else if (result.getOverdueDays() > 0) { + row.setMaintenanceTip("已到期(" + result.getOverdueDays() + "天)"); + } else if (result.getRemainingDays() > 0) { + row.setMaintenanceTip("剩余(" + result.getRemainingDays() + "天)到期"); + } else { + row.setMaintenanceTip("已到期(0天)"); + } + } + + rows.add(row); + } + + return new TableDataInfo<>(rows, vehiclePage.getTotal()); + } + + @Data + public static class VehicleSecondaryMaintenanceStatusVo { + private String vehicleId; + private String plateNumber; + private String maintenanceTimesText; + private String maintenanceTip; + private Date lastMaintenanceTime; + private Date issueDate; + private Date dueDate; + private Integer warningLevel; + private String warningLevelText; + } + + @Data + @ExcelIgnoreUnannotated + public static class VehicleSecondaryMaintenanceExportVo { + @ExcelProperty(value = "车牌号", index = 0) + private String plateNumber; + + @ExcelProperty(value = "每年维护次数", index = 1) + private String maintenanceTimesText; + + @ExcelProperty(value = "维护提示", index = 2) + private String maintenanceTip; + + @ExcelProperty(value = "最近维护时间", index = 3) + private String lastMaintenanceTime; + + @ExcelProperty(value = "行驶证发证日期", index = 4) + private Date issueDate; + + @ExcelProperty(value = "到期日期", index = 5) + private Date dueDate; + + @ExcelProperty(value = "提示等级", index = 6) + private String warningLevelText; + } +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/domain/HotVehicleSecondaryMaintenanceConfig.java b/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/domain/HotVehicleSecondaryMaintenanceConfig.java new file mode 100644 index 0000000..876de79 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/domain/HotVehicleSecondaryMaintenanceConfig.java @@ -0,0 +1,59 @@ +package com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 车辆二级维护配置对象 hot_vehicle_secondary_maintenance_config + * + * @author shihongwei + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_secondary_maintenance_config") +public class HotVehicleSecondaryMaintenanceConfig extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 间隔年数 + */ + private Long intervalYears; + + /** + * 检测次数 + */ + private Long inspectionTimes; + + /** + * 是否启用 + */ + private Long isEnabled; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/domain/bo/HotVehicleSecondaryMaintenanceConfigBo.java b/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/domain/bo/HotVehicleSecondaryMaintenanceConfigBo.java new file mode 100644 index 0000000..68e3105 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/domain/bo/HotVehicleSecondaryMaintenanceConfigBo.java @@ -0,0 +1,59 @@ +package com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.bo; + +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.HotVehicleSecondaryMaintenanceConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 车辆二级维护配置业务对象 hot_vehicle_secondary_maintenance_config + * + * @author shihongwei + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleSecondaryMaintenanceConfig.class, reverseConvertGenerate = false) +public class HotVehicleSecondaryMaintenanceConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 间隔年数 + */ + @NotNull(message = "间隔年数不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long intervalYears; + + /** + * 检测次数 + */ + @NotNull(message = "检测次数不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long inspectionTimes; + + /** + * 是否启用 + */ + private Long isEnabled; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/domain/vo/HotVehicleSecondaryMaintenanceConfigVo.java b/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/domain/vo/HotVehicleSecondaryMaintenanceConfigVo.java new file mode 100644 index 0000000..e7d026f --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/domain/vo/HotVehicleSecondaryMaintenanceConfigVo.java @@ -0,0 +1,64 @@ +package com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.HotVehicleSecondaryMaintenanceConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 车辆二级维护配置视图对象 hot_vehicle_secondary_maintenance_config + * + * @author shihongwei + * @date 2025-12-19 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleSecondaryMaintenanceConfig.class) +public class HotVehicleSecondaryMaintenanceConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 间隔年数 + */ + @ExcelProperty(value = "间隔年数") + private Long intervalYears; + + /** + * 检测次数 + */ + @ExcelProperty(value = "检测次数") + private Long inspectionTimes; + + /** + * 是否启用 + */ + @ExcelProperty(value = "是否启用") + private Long isEnabled; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/mapper/HotVehicleSecondaryMaintenanceConfigMapper.java b/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/mapper/HotVehicleSecondaryMaintenanceConfigMapper.java new file mode 100644 index 0000000..bfbb0f4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/mapper/HotVehicleSecondaryMaintenanceConfigMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.mapper; + +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.HotVehicleSecondaryMaintenanceConfig; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.vo.HotVehicleSecondaryMaintenanceConfigVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆二级维护配置Mapper接口 + * + * @author shihongwei + * @date 2025-12-19 + */ +@Mapper +public interface HotVehicleSecondaryMaintenanceConfigMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/service/IHotVehicleSecondaryMaintenanceConfigService.java b/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/service/IHotVehicleSecondaryMaintenanceConfigService.java new file mode 100644 index 0000000..b3601ac --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/service/IHotVehicleSecondaryMaintenanceConfigService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.service; + +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.bo.HotVehicleSecondaryMaintenanceConfigBo; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.vo.HotVehicleSecondaryMaintenanceConfigVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆二级维护配置Service接口 + * + * @author shihongwei + * @date 2025-12-19 + */ +public interface IHotVehicleSecondaryMaintenanceConfigService { + + /** + * 查询车辆二级维护配置 + * + * @param id 主键 + * @return 车辆二级维护配置 + */ + HotVehicleSecondaryMaintenanceConfigVo queryById(Long id); + + /** + * 分页查询车辆二级维护配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆二级维护配置分页列表 + */ + TableDataInfo queryPageList(HotVehicleSecondaryMaintenanceConfigBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆二级维护配置列表 + * + * @param bo 查询条件 + * @return 车辆二级维护配置列表 + */ + List queryList(HotVehicleSecondaryMaintenanceConfigBo bo); + + /** + * 新增车辆二级维护配置 + * + * @param bo 车辆二级维护配置 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleSecondaryMaintenanceConfigBo bo); + + /** + * 修改车辆二级维护配置 + * + * @param bo 车辆二级维护配置 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleSecondaryMaintenanceConfigBo bo); + + /** + * 校验并批量删除车辆二级维护配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/service/impl/HotVehicleSecondaryMaintenanceConfigServiceImpl.java b/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/service/impl/HotVehicleSecondaryMaintenanceConfigServiceImpl.java new file mode 100644 index 0000000..d1061c3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleSecondaryMaintenanceConfig/service/impl/HotVehicleSecondaryMaintenanceConfigServiceImpl.java @@ -0,0 +1,135 @@ +package com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.HotVehicleSecondaryMaintenanceConfig; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.bo.HotVehicleSecondaryMaintenanceConfigBo; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.vo.HotVehicleSecondaryMaintenanceConfigVo; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.mapper.HotVehicleSecondaryMaintenanceConfigMapper; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.service.IHotVehicleSecondaryMaintenanceConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 车辆二级维护配置Service业务层处理 + * + * @author shihongwei + * @date 2025-12-19 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleSecondaryMaintenanceConfigServiceImpl implements IHotVehicleSecondaryMaintenanceConfigService { + + private final HotVehicleSecondaryMaintenanceConfigMapper baseMapper; + + /** + * 查询车辆二级维护配置 + * + * @param id 主键 + * @return 车辆二级维护配置 + */ + @Override + public HotVehicleSecondaryMaintenanceConfigVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆二级维护配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆二级维护配置分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleSecondaryMaintenanceConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆二级维护配置列表 + * + * @param bo 查询条件 + * @return 车辆二级维护配置列表 + */ + @Override + public List queryList(HotVehicleSecondaryMaintenanceConfigBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleSecondaryMaintenanceConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotVehicleSecondaryMaintenanceConfig::getId); + lqw.eq(bo.getCompanyId() != null, HotVehicleSecondaryMaintenanceConfig::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getIntervalYears() != null, HotVehicleSecondaryMaintenanceConfig::getIntervalYears, bo.getIntervalYears()); + lqw.eq(bo.getInspectionTimes() != null, HotVehicleSecondaryMaintenanceConfig::getInspectionTimes, bo.getInspectionTimes()); + lqw.eq(bo.getIsEnabled() != null, HotVehicleSecondaryMaintenanceConfig::getIsEnabled, bo.getIsEnabled()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleSecondaryMaintenanceConfig::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增车辆二级维护配置 + * + * @param bo 车辆二级维护配置 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleSecondaryMaintenanceConfigBo bo) { + HotVehicleSecondaryMaintenanceConfig add = MapstructUtils.convert(bo, HotVehicleSecondaryMaintenanceConfig.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改车辆二级维护配置 + * + * @param bo 车辆二级维护配置 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleSecondaryMaintenanceConfigBo bo) { + HotVehicleSecondaryMaintenanceConfig update = MapstructUtils.convert(bo, HotVehicleSecondaryMaintenanceConfig.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleSecondaryMaintenanceConfig entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除车辆二级维护配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/callback/VehicleThreeInspectCallback.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/callback/VehicleThreeInspectCallback.java new file mode 100644 index 0000000..c53fe0c --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/callback/VehicleThreeInspectCallback.java @@ -0,0 +1,106 @@ +package com.hotwj.platform.config.vehicleThreeInspect.callback; + +import com.hotwj.platform.config.vehicleThreeInspect.domain.HotVehicleThreeInspect; +import com.hotwj.platform.config.vehicleThreeInspect.mapper.HotVehicleThreeInspectMapper; +import com.hotwj.platform.flow.callback.IFlowCallback; +import com.hotwj.platform.flow.service.ISysFlowService; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.HotHiddenDanger; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.bo.HotHiddenDangerBo; +import com.hotwj.platform.securityManagement.hiddenDanger.mapper.HotHiddenDangerMapper; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.Date; + +/** + * 车辆三检流程回调 + * + * @author shihongwei + */ +@Slf4j +@Component +public class VehicleThreeInspectCallback implements IFlowCallback { + + private final HotVehicleThreeInspectMapper inspectMapper; + private final HotHiddenDangerMapper hiddenDangerMapper; + private final ISysFlowService flowService; + + public VehicleThreeInspectCallback(HotVehicleThreeInspectMapper inspectMapper, + HotHiddenDangerMapper hiddenDangerMapper, + @Lazy ISysFlowService flowService) { + this.inspectMapper = inspectMapper; + this.hiddenDangerMapper = hiddenDangerMapper; + this.flowService = flowService; + } + + @Override + public String getFlowCode() { + return "VEHICLE_THREE_INSPECT"; + } + + @Override + public void onProcessEnd(String instanceId, String businessId, String flowCode, boolean success) { + log.info("车辆三检流程结束回调: businessId={}, success={}", businessId, success); + Long id = Long.parseLong(businessId); + // 去掉try catch,防止事务无法正常回滚 + HotVehicleThreeInspect inspect = inspectMapper.selectById(id); + if (inspect != null) { + if (success) { + inspect.setFlowStatus("APPROVED"); + inspect.setAuditResult("审核通过"); + } else { + inspect.setFlowStatus("REJECTED"); + // 审核驳回,如果有隐患信息,自动开启隐患治理流程 + createHiddenDangerFlow(inspect); + } + inspectMapper.updateById(inspect); + } + } + + /** + * 创建隐患治理流程 + */ + private void createHiddenDangerFlow(HotVehicleThreeInspect inspect) { + if (inspect.getHasHiddenDanger() != null && inspect.getHasHiddenDanger() == 1L) { + log.info("车辆三检存在隐患,自动创建隐患治理流程,业务ID: {}", inspect.getId()); + + HotHiddenDangerBo hiddenDangerBo = new HotHiddenDangerBo(); + hiddenDangerBo.setCompanyId(inspect.getCompanyId()); + hiddenDangerBo.setCheckTime(new Date()); + hiddenDangerBo.setCheckerId(String.valueOf(inspect.getAuditorId())); + hiddenDangerBo.setCheckerName(inspect.getAuditorName()); + hiddenDangerBo.setDescription(inspect.getAuditResult()); + hiddenDangerBo.setEvidenceUrls(inspect.getAttachImgUrl()); + hiddenDangerBo.setDangerNo("YH-" + System.currentTimeMillis() / 1000); + hiddenDangerBo.setSourceName("车辆"); + hiddenDangerBo.setSourceId(String.valueOf(inspect.getId())); + hiddenDangerBo.setSourceType(2L); + hiddenDangerBo.setDataSource(2L); + hiddenDangerBo.setDangerName("车辆三检治理"); + hiddenDangerBo.setStatus(0L); + + HotHiddenDanger danger = MapstructUtils.convert(hiddenDangerBo, HotHiddenDanger.class); + hiddenDangerMapper.insert(danger); + + // 启动隐患治理流程 + // 发起人为审核人(发现隐患的人) + String initiator = String.valueOf(inspect.getAuditorId()); + // 默认整改人也先设置为审核人,或者留空等待指派 + String rectifierId = String.valueOf(inspect.getEvaluatorId()); + + String instanceId = flowService.startFlow( + "HIDDEN_DANGER_GOVERNANCE", + String.valueOf(danger.getId()), + inspect.getCompanyId(), + initiator, + rectifierId + ); + danger.setInstanceId(instanceId); + danger.setEvaluatorId(inspect.getEvaluatorId()); + danger.setFlowStatus("SUBMITTED"); + hiddenDangerMapper.updateById(danger); + } + } +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/controller/HotVehicleThreeInspectController.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/controller/HotVehicleThreeInspectController.java new file mode 100644 index 0000000..90a1528 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/controller/HotVehicleThreeInspectController.java @@ -0,0 +1,161 @@ +package com.hotwj.platform.config.vehicleThreeInspect.controller; + +import com.hotwj.platform.config.vehicleThreeInspect.domain.bo.HotVehicleThreeInspectBo; +import com.hotwj.platform.config.vehicleThreeInspect.domain.vo.HotVehicleThreeInspectVo; +import com.hotwj.platform.config.vehicleThreeInspect.service.IHotVehicleThreeInspectPrintService; +import com.hotwj.platform.config.vehicleThreeInspect.service.IHotVehicleThreeInspectService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 车辆三检 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/vehicleThreeInspect") +@Tag(name = "车辆三检", description = "车辆三检管理") +public class HotVehicleThreeInspectController extends BaseController { + + private final IHotVehicleThreeInspectService hotVehicleThreeInspectService; + private final IHotVehicleThreeInspectPrintService printService; + + /** + * 查询车辆三检列表 + */ + //@SaCheckPermission("securityManagement:vehicleThreeInspect:list") + @GetMapping("/list") + @Operation(summary = "分页查询车辆三检列表") + public TableDataInfo list(HotVehicleThreeInspectBo bo, PageQuery pageQuery) { + return hotVehicleThreeInspectService.queryPageList(bo, pageQuery); + } + + /** + * 查询可进行三检的车辆列表 + */ + @GetMapping("/availableVehicles") + @Operation(summary = "查询可进行三检的车辆列表") + public R> queryAvailableVehicles(@RequestParam Long companyId) { + return R.ok(hotVehicleThreeInspectService.queryAvailableVehicles(companyId)); + } + + /** + * 导出车辆三检列表 + */ + //@SaCheckPermission("securityManagement:vehicleThreeInspect:export") + @Log(title = "车辆三检", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆三检列表") + public void export(HotVehicleThreeInspectBo bo, HttpServletResponse response) { + List list = hotVehicleThreeInspectService.queryList(bo); + ExcelUtil.exportExcel(list, "车辆三检", HotVehicleThreeInspectVo.class, response); + } + + /** + * 导出车辆三检PDF + */ + //@SaCheckPermission("securityManagement:vehicleThreeInspect:print") + @Log(title = "车辆三检打印", businessType = BusinessType.EXPORT) + @PostMapping("/exportPdf") + @Operation(summary = "导出车辆三检PDF") + public void exportPdf(HotVehicleThreeInspectBo bo, HttpServletResponse response) { + printService.exportPdf(bo, response); + } + + //@SaCheckPermission("securityManagement:vehicleThreeInspect:print") + @Log(title = "车辆三检表单打印", businessType = BusinessType.EXPORT) + @PostMapping("/exportFormPdf") + @Operation(summary = "导出车辆三检检查表单PDF") + public void exportFormPdf(@RequestParam Long inspectId, HttpServletResponse response) { + printService.exportFormPdf(inspectId, response); + } + + /** + * 获取车辆三检详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:vehicleThreeInspect:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆三检详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotVehicleThreeInspectService.queryById(id)); + } + + /** + * 新增车辆三检 + */ + //@SaCheckPermission("securityManagement:vehicleThreeInspect:add") + @Log(title = "车辆三检", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆三检") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleThreeInspectBo bo) { + return toAjax(hotVehicleThreeInspectService.insertByBo(bo)); + } + + /** + * 修改车辆三检 + */ + //@SaCheckPermission("securityManagement:vehicleThreeInspect:edit") + @Log(title = "车辆三检", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆三检") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleThreeInspectBo bo) { + return toAjax(hotVehicleThreeInspectService.updateByBo(bo)); + } + + /** + * 审核车辆三检 + */ + //@SaCheckPermission("securityManagement:vehicleThreeInspect:edit") + @Log(title = "车辆三检", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/audit") + @Operation(summary = "审核车辆三检") + public R audit(@RequestBody HotVehicleThreeInspectBo bo) { + hotVehicleThreeInspectService.audit(bo); + return R.ok(); + } + + /** + * 删除车辆三检 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:vehicleThreeInspect:remove") + @Log(title = "车辆三检", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆三检") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotVehicleThreeInspectService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/HotVehicleThreeInspect.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/HotVehicleThreeInspect.java new file mode 100644 index 0000000..edc637e --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/HotVehicleThreeInspect.java @@ -0,0 +1,138 @@ +package com.hotwj.platform.config.vehicleThreeInspect.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 车辆三检对象 hot_vehicle_three_inspect + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_three_inspect") +public class HotVehicleThreeInspect extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆ID + */ + private Long vehicleId; + + /** + * 当前驾驶员ID + */ + private String driverId; + + /** + * 当前驾驶员姓名 + */ + private String driverName; + + /** + * 检查阶段 1=出车 2=行车 3=收车 + */ + private Long inspectPhase; + + /** + * 检查id + */ + private Long inspectId; + + /** + * 检查时间 + */ + private Date inspectTime; + + /** + * 审核人ID + */ + private Long auditorId; + + /** + * 审核人姓名 + */ + private String auditorName; + + /** + * 检查项目结果JSON + */ + private String inspectResultJson; + + /** + * 检查附件图片url + */ + private String attachImgUrl; + + /** + * 检查员签名图片url + */ + private String inspectorSignImgUrl; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 是否误报 + */ + private Long isFalseReport; + + /** + * 是否存在隐患 0=否, 1=是 + */ + private Long hasHiddenDanger; + + /** + * 审核时间 + */ + private Date auditTime; + + /** + * 审核结论 + */ + private String auditResult; + + /** + * 审核人员签名图片url + */ + private String auditorSignImgUrl; + + /** + * 流程实例ID + */ + private String instanceId; + + /** + * 流程状态 + */ + private String flowStatus; + + /** + * 评估人id + */ + private Long evaluatorId; +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/bo/HotVehicleThreeInspectBo.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/bo/HotVehicleThreeInspectBo.java new file mode 100644 index 0000000..5a05ec5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/bo/HotVehicleThreeInspectBo.java @@ -0,0 +1,140 @@ +package com.hotwj.platform.config.vehicleThreeInspect.domain.bo; + +import com.hotwj.platform.config.vehicleThreeInspect.domain.HotVehicleThreeInspect; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 车辆三检业务对象 hot_vehicle_three_inspect + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleThreeInspect.class, reverseConvertGenerate = false) +public class HotVehicleThreeInspectBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 车辆ID + */ + @NotNull(message = "车辆ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long vehicleId; + + /** + * 当前驾驶员ID + */ + @NotNull(message = "当前驾驶员ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private String driverId; + + /** + * 当前驾驶员姓名 + */ + private String driverName; + + /** + * 检查阶段 1=出车 2=行车 3=收车 + */ + @NotNull(message = "检查阶段 1=出车 2=行车 3=收车不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long inspectPhase; + + /** + * 检查id + */ + private Long inspectId; + + /** + * 检查时间 + */ + @NotNull(message = "检查时间不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date inspectTime; + + /** + * 审核人ID + */ + @NotNull(message = "审核人ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long auditorId; + + /** + * 审核人姓名 + */ + private String auditorName; + + /** + * 检查项目结果JSON + */ + @NotBlank(message = "检查项目结果JSON不能为空", groups = {AddGroup.class, EditGroup.class}) + private String inspectResultJson; + + /** + * 检查附件图片url + */ + @NotBlank(message = "检查附件图片url不能为空", groups = {AddGroup.class, EditGroup.class}) + private String attachImgUrl; + + /** + * 检查员签名图片url + */ + @NotBlank(message = "检查员签名图片url不能为空", groups = {AddGroup.class, EditGroup.class}) + private String inspectorSignImgUrl; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + /** + * 是否误报 + */ + private Long isFalseReport; + + /** + * 是否存在隐患 0=否, 1=是 + */ + private Long hasHiddenDanger; + + private Long evaluatorId; + + /** + * 审核时间 + */ + private Date auditTime; + + /** + * 审核结论 + */ + private String auditResult; + + /** + * 审核人员签名图片url + */ + private String auditorSignImgUrl; + + /** + * 流程任务ID + */ + private String taskId; + + private String ids; +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/vo/HotVehicleThreeInspectPrintVo.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/vo/HotVehicleThreeInspectPrintVo.java new file mode 100644 index 0000000..a9e6c55 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/vo/HotVehicleThreeInspectPrintVo.java @@ -0,0 +1,62 @@ +package com.hotwj.platform.config.vehicleThreeInspect.domain.vo; + +import lombok.Data; + +/** + * 车辆三检打印VO + */ +@Data +public class HotVehicleThreeInspectPrintVo { + + /** + * 检查id + */ + private Long inspectId; + + /** + * 车牌号 + */ + private String plateNumber; + + // --- 出车前 Phase 1 --- + /** + * 出车前-检查时间 + */ + private String startPhaseTime; + /** + * 出车前-状态 + */ + private String startPhaseStatus; + /** + * 出车前-隐患 + */ + private String startPhaseHiddenDanger; + + // --- 行车中 Phase 2 --- + /** + * 行车中-检查时间 + */ + private String runningPhaseTime; + /** + * 行车中-状态 + */ + private String runningPhaseStatus; + /** + * 行车中-隐患 + */ + private String runningPhaseHiddenDanger; + + // --- 收车后 Phase 3 --- + /** + * 收车后-检查时间 + */ + private String endPhaseTime; + /** + * 收车后-状态 + */ + private String endPhaseStatus; + /** + * 收车后-隐患 + */ + private String endPhaseHiddenDanger; +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/vo/HotVehicleThreeInspectVo.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/vo/HotVehicleThreeInspectVo.java new file mode 100644 index 0000000..00a3f9f --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/domain/vo/HotVehicleThreeInspectVo.java @@ -0,0 +1,160 @@ +package com.hotwj.platform.config.vehicleThreeInspect.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.vehicleThreeInspect.domain.HotVehicleThreeInspect; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 车辆三检视图对象 hot_vehicle_three_inspect + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleThreeInspect.class) +public class HotVehicleThreeInspectVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 车辆ID + */ + @ExcelProperty(value = "车辆ID") + private Long vehicleId; + + /** + * 当前驾驶员ID + */ + @ExcelProperty(value = "当前驾驶员ID") + private String driverId; + + /** + * 当前驾驶员姓名 + */ + @ExcelProperty(value = "当前驾驶员姓名") + private String driverName; + + /** + * 检查阶段 1=出车 2=行车 3=收车 + */ + @ExcelProperty(value = "检查阶段 1=出车 2=行车 3=收车") + private Long inspectPhase; + + /** + * 检查id + */ + private Long inspectId; + + /** + * 检查时间 + */ + @ExcelProperty(value = "检查时间") + private Date inspectTime; + + /** + * 审核人ID + */ + @ExcelProperty(value = "审核人ID") + private Long auditorId; + + /** + * 审核人姓名 + */ + @ExcelProperty(value = "审核人姓名") + private String auditorName; + + /** + * 检查项目结果JSON + */ + @ExcelProperty(value = "检查项目结果JSON") + private String inspectResultJson; + + /** + * 检查附件图片url + */ + @ExcelProperty(value = "检查附件图片url") + private String attachImgUrl; + + /** + * 检查员签名图片url + */ + @ExcelProperty(value = "检查员签名图片url") + private String inspectorSignImgUrl; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 是否误报 + */ + @ExcelProperty(value = "是否误报") + private Long isFalseReport; + + /** + * 是否存在隐患 0=否, 1=是 + */ + @ExcelProperty(value = "是否存在隐患 0=否, 1=是") + private Long hasHiddenDanger; + + /** + * 审核时间 + */ + @ExcelProperty(value = "审核时间") + private Date auditTime; + + /** + * 审核结论 + */ + @ExcelProperty(value = "审核结论") + private String auditResult; + + /** + * 审核人员签名图片url + */ + @ExcelProperty(value = "审核人员签名图片url") + private String auditorSignImgUrl; + + /** + * 流程实例ID + */ + @ExcelProperty(value = "流程实例ID") + private String instanceId; + + /** + * 流程状态 + */ + @ExcelProperty(value = "流程状态") + private String flowStatus; + + /** + * 流程任务ID + */ + @ExcelProperty(value = "流程任务ID") + private String taskId; + + private Long evaluatorId; +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/mapper/HotVehicleThreeInspectMapper.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/mapper/HotVehicleThreeInspectMapper.java new file mode 100644 index 0000000..207910c --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/mapper/HotVehicleThreeInspectMapper.java @@ -0,0 +1,30 @@ +package com.hotwj.platform.config.vehicleThreeInspect.mapper; + +import com.hotwj.platform.config.vehicleThreeInspect.domain.HotVehicleThreeInspect; +import com.hotwj.platform.config.vehicleThreeInspect.domain.bo.HotVehicleThreeInspectBo; +import com.hotwj.platform.config.vehicleThreeInspect.domain.vo.HotVehicleThreeInspectPrintVo; +import com.hotwj.platform.config.vehicleThreeInspect.domain.vo.HotVehicleThreeInspectVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +import java.util.List; + +/** + * 车辆三检Mapper接口 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Mapper +public interface HotVehicleThreeInspectMapper extends BaseMapperPlus { + + /** + * 查询车辆三检打印列表 + * + * @param bo 查询条件 + * @return 车辆三检打印列表 + */ + List selectPrintList(@Param("bo") HotVehicleThreeInspectBo bo); + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/IHotVehicleThreeInspectPrintService.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/IHotVehicleThreeInspectPrintService.java new file mode 100644 index 0000000..d91321e --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/IHotVehicleThreeInspectPrintService.java @@ -0,0 +1,19 @@ +package com.hotwj.platform.config.vehicleThreeInspect.service; + +import com.hotwj.platform.config.vehicleThreeInspect.domain.bo.HotVehicleThreeInspectBo; +import jakarta.servlet.http.HttpServletResponse; + +/** + * 车辆三检打印服务接口 + */ +public interface IHotVehicleThreeInspectPrintService { + /** + * 导出PDF + * + * @param bo 查询参数 + * @param response 响应对象 + */ + void exportPdf(HotVehicleThreeInspectBo bo, HttpServletResponse response); + + void exportFormPdf(Long inspectId, HttpServletResponse response); +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/IHotVehicleThreeInspectService.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/IHotVehicleThreeInspectService.java new file mode 100644 index 0000000..4d731cf --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/IHotVehicleThreeInspectService.java @@ -0,0 +1,84 @@ +package com.hotwj.platform.config.vehicleThreeInspect.service; + +import com.hotwj.platform.config.vehicleThreeInspect.domain.bo.HotVehicleThreeInspectBo; +import com.hotwj.platform.config.vehicleThreeInspect.domain.vo.HotVehicleThreeInspectVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆三检Service接口 + * + * @author shihongwei + * @date 2025-12-29 + */ +public interface IHotVehicleThreeInspectService { + + /** + * 查询可进行三检的车辆列表(无历史记录或上一轮已完结) + * + * @param companyId 公司ID + * @return 车辆列表 + */ + List queryAvailableVehicles(Long companyId); + + /** + * 查询车辆三检 + * + * @param id 主键 + * @return 车辆三检 + */ + HotVehicleThreeInspectVo queryById(Long id); + + /** + * 分页查询车辆三检列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆三检分页列表 + */ + TableDataInfo queryPageList(HotVehicleThreeInspectBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆三检列表 + * + * @param bo 查询条件 + * @return 车辆三检列表 + */ + List queryList(HotVehicleThreeInspectBo bo); + + /** + * 新增车辆三检 + * + * @param bo 车辆三检 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleThreeInspectBo bo); + + /** + * 修改车辆三检 + * + * @param bo 车辆三检 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleThreeInspectBo bo); + + /** + * 校验并批量删除车辆三检信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 审核车辆三检 + * + * @param bo 业务对象 + */ + void audit(HotVehicleThreeInspectBo bo); +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/impl/HotVehicleThreeInspectPrintServiceImpl.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/impl/HotVehicleThreeInspectPrintServiceImpl.java new file mode 100644 index 0000000..904809e --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/impl/HotVehicleThreeInspectPrintServiceImpl.java @@ -0,0 +1,482 @@ +package com.hotwj.platform.config.vehicleThreeInspect.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.fasterxml.jackson.core.type.TypeReference; +import com.hotwj.platform.common.utils.PdfWatermarkUtil; +import com.hotwj.platform.config.vehicleThreeInspect.domain.HotVehicleThreeInspect; +import com.hotwj.platform.config.vehicleThreeInspect.domain.bo.HotVehicleThreeInspectBo; +import com.hotwj.platform.config.vehicleThreeInspect.domain.vo.HotVehicleThreeInspectPrintVo; +import com.hotwj.platform.config.vehicleThreeInspect.mapper.HotVehicleThreeInspectMapper; +import com.hotwj.platform.config.vehicleThreeInspect.service.IHotVehicleThreeInspectPrintService; +import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.HotVehicleThreeInspectConfig; +import com.hotwj.platform.config.vehicleThreeInspectConfig.mapper.HotVehicleThreeInspectConfigMapper; +import com.hotwj.platform.reportStatistics.integration.GotenbergClient; +import com.hotwj.platform.reportStatistics.template.VelocityTemplateRenderer; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle; +import com.hotwj.platform.resourceManagement.vehicleManagement.mapper.HotVehicleMapper; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.velocity.VelocityContext; +import org.dromara.common.core.domain.dto.OssDTO; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.file.FileUtils; +import org.dromara.common.json.utils.JsonUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.FileWriter; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.*; + +@Slf4j +@Service +@RequiredArgsConstructor +public class HotVehicleThreeInspectPrintServiceImpl implements IHotVehicleThreeInspectPrintService { + + private final HotVehicleThreeInspectMapper threeInspectMapper; + private final HotVehicleMapper vehicleMapper; + private final HotVehicleThreeInspectConfigMapper threeInspectConfigMapper; + private final GotenbergClient gotenbergClient; + private final VelocityTemplateRenderer velocityTemplateRenderer; + private final OssService ossService; + private final ISysCompanyService sysCompanyService; + + @Value("${dromara.app.name:HOT交通安全管理清单平台}") + private String appName; + + @Override + public void exportPdf(HotVehicleThreeInspectBo bo, HttpServletResponse response) { + // Use custom XML query to get flattened VO list + List vos = threeInspectMapper.selectPrintList(bo); + if (CollUtil.isEmpty(vos)) { + throw new ServiceException("数据不存在"); + } + + // Generate PDF + VelocityContext context = new VelocityContext(); + context.put("list", vos); + context.put("appName", appName); + context.put("printTime", DateUtil.now()); + + String bodyHtml = velocityTemplateRenderer.render("templates/vehicle/vehicleThreeInspectRecord.html.vm", context); + + // Merge into landscape layout + VelocityContext layoutContext = new VelocityContext(); + layoutContext.put("title", appName + "车辆三检记录表"); + List bodies = new ArrayList<>(); + bodies.add(bodyHtml); + layoutContext.put("bodies", bodies); + + String finalHtml = velocityTemplateRenderer.render("templates/layout/printLayoutLandscape.html.vm", layoutContext); + + Long companyId = bo.getCompanyId(); + if (companyId == null && bo.getVehicleId() != null) { + HotVehicle vehicle = vehicleMapper.selectById(String.valueOf(bo.getVehicleId())); + if (vehicle != null) { + companyId = vehicle.getCompanyId(); + } + } + + try { + byte[] pdfBytes = convertAndWatermarkLandscape(finalHtml, companyId); + + response.setContentType("application/pdf"); + String fileName = "车辆三检记录表.pdf"; + FileUtils.setAttachmentResponseHeader(response, fileName); + response.getOutputStream().write(pdfBytes); + } catch (Exception e) { + log.error("Export PDF failed", e); + throw new ServiceException("导出PDF失败"); + } + } + + @Override + public void exportFormPdf(Long inspectId, HttpServletResponse response) { + if (inspectId == null) { + throw new ServiceException("inspectId不能为空"); + } + + // 1. 查询该车辆、该日期的所有三检记录 (出车前、行车中、收车后) + // inspectId 是三检任务的唯一标识,关联不同阶段的记录 + List inspectList = threeInspectMapper.selectList( + Wrappers.lambdaQuery(HotVehicleThreeInspect.class) + .eq(HotVehicleThreeInspect::getInspectId, inspectId) + ); + + if (CollUtil.isEmpty(inspectList)) { + throw new ServiceException("检查记录不存在"); + } + + // 取第一条记录作为基础信息来源 + HotVehicleThreeInspect originInspect = inspectList.get(0); + + // 2. 准备基础数据 + Long companyId = originInspect.getCompanyId(); + HotVehicle vehicle = null; + if (originInspect.getVehicleId() != null) { + vehicle = vehicleMapper.selectById(String.valueOf(originInspect.getVehicleId())); + if (companyId == null && vehicle != null) { + companyId = vehicle.getCompanyId(); + } + } + SysCompanyVo company = companyId == null ? null : sysCompanyService.queryById(companyId); + + // 4. 分组处理各阶段数据 + // Map + Map> phaseDataMap = new HashMap<>(); + + // 预加载配置 (一次性加载该公司的所有配置,或者分阶段加载) + // 这里简化逻辑:获取所有涉及的 keys,统一加载配置 + List allConfigs = threeInspectConfigMapper.selectList( + Wrappers.lambdaQuery(HotVehicleThreeInspectConfig.class) + .eq(HotVehicleThreeInspectConfig::getCompanyId, companyId) + .eq(HotVehicleThreeInspectConfig::getIsDeleted, 0) + ); + Map configMap = new HashMap<>(); + if (allConfigs != null) { + for (HotVehicleThreeInspectConfig cfg : allConfigs) { + configMap.put(cfg.getId(), cfg); + } + } + + // 遍历记录处理 + HotVehicleThreeInspect phase1 = null; + HotVehicleThreeInspect phase2 = null; + HotVehicleThreeInspect phase3 = null; + + // 对 inspectList 按创建时间排序(虽然数据库查出来可能是乱序,我们希望取最新的) + // 实际上数据库一般是按插入顺序,但为了保险,可以在代码里比较 inspectTime 或 createTime + // 这里直接遍历,如果有重复阶段,取 inspectTime 或 id 最大的 + + // 先按阶段分组并取最新 + Map latestInspectMap = new HashMap<>(); + for (HotVehicleThreeInspect inspect : inspectList) { + Long phase = inspect.getInspectPhase(); + if (phase == null) continue; + + HotVehicleThreeInspect existing = latestInspectMap.get(phase); + if (existing == null) { + latestInspectMap.put(phase, inspect); + } else { + // 比较逻辑:优先取 id 大的(通常意味着后创建),或者 inspectTime 晚的 + // 这里简单用 id 比较 + if (inspect.getId() > existing.getId()) { + latestInspectMap.put(phase, inspect); + } + } + } + + // 仅处理最新的记录 + for (Map.Entry entry : latestInspectMap.entrySet()) { + Long phase = entry.getKey(); + HotVehicleThreeInspect inspect = entry.getValue(); + + if (phase == 1L) phase1 = inspect; + else if (phase == 2L) phase2 = inspect; + else if (phase == 3L) phase3 = inspect; + + Map data = new HashMap<>(); + data.put("inspect", inspect); + + // 解析检查项 + LinkedHashMap resultMap = parseInspectResult(inspect.getInspectResultJson()); + List> items = new ArrayList<>(); + int index = 1; + for (Map.Entry entry1 : resultMap.entrySet()) { + String key = entry1.getKey(); + if (StringUtils.isBlank(key)) continue; + + Long configId = parseLong(key); + HotVehicleThreeInspectConfig cfg = configId == null ? null : configMap.get(configId); + // 过滤掉不属于当前阶段的配置(理论上 json 里存的应该就是对应的,但双重校验更安全) + if (cfg != null && !cfg.getInspectType().equals(phase)) { + // 严格模式下可以 continue,但为了兼容性,如果配置存在就显示 + } + + String title = cfg != null ? cfg.getTitle() : key; + Map itemRow = new HashMap<>(); + itemRow.put("index", index++); + itemRow.put("title", StringUtils.blankToDefault(title, "")); + itemRow.put("result", StringUtils.blankToDefault(entry1.getValue(), "")); + items.add(itemRow); + } + data.put("items", items); + + // 图片 + data.put("attachImages", resolveImages(inspect.getAttachImgUrl())); + + // 签名 + data.put("inspectorSignUrl", resolveMaybeOssUrl(inspect.getInspectorSignImgUrl())); + data.put("auditorSignUrl", resolveMaybeOssUrl(inspect.getAuditorSignImgUrl())); + + // 其它字段 + data.put("inspectTime", inspect.getInspectTime() == null ? "" : DateUtil.formatDateTime(inspect.getInspectTime())); + data.put("inspectorName", StringUtils.blankToDefault(inspect.getDriverName(), "")); + data.put("auditResult", StringUtils.blankToDefault(inspect.getAuditResult(), "")); + data.put("auditorName", StringUtils.blankToDefault(inspect.getAuditorName(), "")); + data.put("auditTime", inspect.getAuditTime() == null ? "" : DateUtil.formatDateTime(inspect.getAuditTime())); + data.put("hasHiddenDanger", resolveYesNo(inspect.getHasHiddenDanger())); + data.put("isFalseReport", resolveYesNo(inspect.getIsFalseReport())); + + phaseDataMap.put(phase, data); + } + + VelocityContext ctx = new VelocityContext(); + ctx.put("printTime", DateUtil.now()); + ctx.put("companyName", company != null ? StringUtils.blankToDefault(company.getCompanyName(), "") : ""); + ctx.put("plateNumber", vehicle != null ? StringUtils.blankToDefault(vehicle.getPlateNumber(), "") : ""); + + ctx.put("phase1", phaseDataMap.get(1L)); + ctx.put("phase2", phaseDataMap.get(2L)); + ctx.put("phase3", phaseDataMap.get(3L)); + + // 总体审核信息(通常取最后阶段或合并显示?根据模板需求,如果模板是分栏显示,则各显示各的) + // 如果模板底部只有一个审核区,通常取 phase 1 的审核信息或者最后一次的? + // 根据截图,三个表单是顺序排列的,每个都有自己的签名区。 + // 所以模板里应该循环或者按顺序 block 显示。 + + String bodyHtml = velocityTemplateRenderer.render("templates/vehicle/vehicleThreeInspectForm.html.vm", ctx); + VelocityContext layoutContext = new VelocityContext(); + layoutContext.put("title", "车辆三检检查表单"); + List bodies = new ArrayList<>(); + bodies.add(bodyHtml); + layoutContext.put("bodies", bodies); + String finalHtml = velocityTemplateRenderer.render("templates/layout/printLayout.html.vm", layoutContext); + try { + byte[] pdfBytes = convertAndWatermark(finalHtml, companyId); + response.setContentType("application/pdf"); + String fileName = "车辆三检检查表单.pdf"; + FileUtils.setAttachmentResponseHeader(response, fileName); + response.getOutputStream().write(pdfBytes); + } catch (Exception e) { + log.error("Export form PDF failed", e); + throw new ServiceException("导出PDF失败"); + } + } + + private byte[] convertAndWatermarkLandscape(String htmlContent, Long companyId) throws Exception { + File temp = Files.createTempFile("three-inspect-print-land-", ".html").toFile(); + try { + try (FileWriter fw = new FileWriter(temp, StandardCharsets.UTF_8)) { + fw.write(htmlContent); + } + + String footer = """ +
+ 平台信息依实为准,信息使用自判 + / +
+ """; + + byte[] pdf = gotenbergClient.convertHtmlToPdf(temp, footer, true); + byte[] watermarked = PdfWatermarkUtil.addWatermarkLandscape(pdf, appName); + byte[] sealBytes = loadCompanySeal(companyId); + if (sealBytes == null) { + return watermarked; + } + return PdfWatermarkUtil.addSealBottomRight(watermarked, sealBytes); + } finally { + if (temp.exists()) { + if (!temp.delete()) { + log.warn("Failed to delete temp file: {}", temp.getAbsolutePath()); + } + } + } + } + + private byte[] convertAndWatermark(String htmlContent, Long companyId) throws Exception { + File temp = Files.createTempFile("three-inspect-print-", ".html").toFile(); + try { + try (FileWriter fw = new FileWriter(temp, StandardCharsets.UTF_8)) { + fw.write(htmlContent); + } + String footer = """ +
+ 平台信息依实为准,信息使用自判 + / +
+ """; + byte[] pdf = gotenbergClient.convertHtmlToPdf(temp, footer); + byte[] watermarked = PdfWatermarkUtil.addWatermark(pdf, appName); + byte[] sealBytes = loadCompanySeal(companyId); + if (sealBytes == null) { + return watermarked; + } + return PdfWatermarkUtil.addSealBottomRight(watermarked, sealBytes); + } finally { + if (temp.exists()) { + if (!temp.delete()) { + log.warn("Failed to delete temp file: {}", temp.getAbsolutePath()); + } + } + } + } + + private LinkedHashMap parseInspectResult(String json) { + if (StringUtils.isBlank(json)) { + return new LinkedHashMap<>(); + } + try { + Map map = JsonUtils.parseObject(json, new TypeReference>() { + }); + if (map == null) { + return new LinkedHashMap<>(); + } + return new LinkedHashMap<>(map); + } catch (Exception e) { + return new LinkedHashMap<>(); + } + } + + private Map loadConfigs(Long companyId, Long inspectPhase, List keys) { + if (CollUtil.isEmpty(keys)) { + return new HashMap<>(); + } + List ids = new ArrayList<>(); + for (String key : keys) { + Long id = parseLong(key); + if (id != null) { + ids.add(id); + } + } + if (ids.isEmpty()) { + return new HashMap<>(); + } + List list = threeInspectConfigMapper.selectList( + Wrappers.lambdaQuery(HotVehicleThreeInspectConfig.class) + .eq(companyId != null, HotVehicleThreeInspectConfig::getCompanyId, companyId) + .eq(inspectPhase != null, HotVehicleThreeInspectConfig::getInspectType, inspectPhase) + .in(HotVehicleThreeInspectConfig::getId, ids) + ); + Map map = new HashMap<>(); + for (HotVehicleThreeInspectConfig cfg : list) { + if (cfg != null && cfg.getId() != null) { + map.put(cfg.getId(), cfg); + } + } + return map; + } + + private Long parseLong(String value) { + if (StringUtils.isBlank(value)) { + return null; + } + try { + return Long.parseLong(value.trim()); + } catch (Exception e) { + return null; + } + } + + private String resolvePhaseLabel(Long phase) { + if (phase == null) { + return "出车"; + } + if (phase == 2L) { + return "行车中"; + } + if (phase == 3L) { + return "收车后"; + } + return "出车前"; + } + + private String resolveYesNo(Long value) { + if (value == null) { + return ""; + } + return value == 1L ? "是" : "否"; + } + + private List resolveImages(String value) { + List result = new ArrayList<>(); + if (StringUtils.isBlank(value)) { + return result; + } + String urls = value; + if (!(value.contains("http://") || value.contains("https://"))) { + urls = ossService.selectUrlByIds(value); + } + if (StringUtils.isBlank(urls)) { + return result; + } + for (String s : urls.split(",")) { + String t = s == null ? "" : s.trim(); + if (StringUtils.isNotBlank(t)) { + result.add(t); + } + } + return result; + } + + private String resolveMaybeOssUrl(String value) { + if (StringUtils.isBlank(value)) { + return null; + } + String first = value.split(",")[0].trim(); + if (StringUtils.isBlank(first)) { + return null; + } + if (StringUtils.ishttp(first)) { + return first; + } + if (first.matches("^\\d+$")) { + String urls = ossService.selectUrlByIds(first); + if (StringUtils.isBlank(urls)) { + return null; + } + return urls.split(",")[0].trim(); + } + return first; + } + + private byte[] loadCompanySeal(Long companyId) { + if (companyId == null) { + return null; + } + SysCompanyVo company = sysCompanyService.queryById(companyId); + if (company == null) { + return null; + } + String seal = company.getSealUrl(); + if (StringUtils.isBlank(seal)) { + return null; + } + String trimmed = seal.trim(); + if (trimmed.matches("^\\d+(,\\d+)*$")) { + List list = ossService.selectByIds(trimmed); + if (list == null || list.isEmpty()) { + return null; + } + String url = list.get(0).getUrl(); + return downloadBytes(url); + } else { + return downloadBytes(trimmed); + } + } + + private byte[] downloadBytes(String url) { + if (StringUtils.isBlank(url)) { + return null; + } + try { + URL u = new URL(url); + try (java.io.InputStream is = u.openStream()) { + return is.readAllBytes(); + } + } catch (Exception e) { + log.error("Failed to download seal image from url: {}", url, e); + return null; + } + } +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/impl/HotVehicleThreeInspectServiceImpl.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/impl/HotVehicleThreeInspectServiceImpl.java new file mode 100644 index 0000000..dd3c2c3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/service/impl/HotVehicleThreeInspectServiceImpl.java @@ -0,0 +1,399 @@ +package com.hotwj.platform.config.vehicleThreeInspect.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.common.helper.DriverLoginContextHelper; +import com.hotwj.platform.config.vehicleThreeInspect.domain.HotVehicleThreeInspect; +import com.hotwj.platform.config.vehicleThreeInspect.domain.bo.HotVehicleThreeInspectBo; +import com.hotwj.platform.config.vehicleThreeInspect.domain.vo.HotVehicleThreeInspectVo; +import com.hotwj.platform.config.vehicleThreeInspect.mapper.HotVehicleThreeInspectMapper; +import com.hotwj.platform.config.vehicleThreeInspect.service.IHotVehicleThreeInspectService; +import com.hotwj.platform.flow.service.ISysFlowService; +import com.hotwj.platform.integration.ocr.SignatureVerifyService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.mapper.HotVehicleMapper; +import com.hotwj.platform.securityManagement.hiddenDanger.mapper.HotHiddenDangerMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 车辆三检Service业务层处理 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleThreeInspectServiceImpl implements IHotVehicleThreeInspectService { + + private final HotVehicleThreeInspectMapper baseMapper; + private final HotVehicleMapper hotVehicleMapper; + private final HotHiddenDangerMapper hotHiddenDangerMapper; + private final ISysFlowService flowService; + private final SignatureVerifyService signatureVerifyService; + + /** + * 查询可进行三检的车辆列表(无历史记录或上一轮已完结) + * + * @param companyId 公司ID + * @return 车辆列表 + */ + @Override + public List queryAvailableVehicles(Long companyId) { + return hotVehicleMapper.selectAvailableForThreeInspect(companyId); + } + + /** + * 查询车辆三检 + * + * @param id 主键 + * @return 车辆三检 + */ + @Override + public HotVehicleThreeInspectVo queryById(Long id) { + HotVehicleThreeInspectVo vo = baseMapper.selectVoById(id); + if (vo != null && StringUtils.isNotBlank(vo.getInstanceId())) { + String userId = LoginHelper.getBusinessUserId(); + vo.setTaskId(flowService.getTaskId(vo.getInstanceId(), userId)); + } + return vo; + } + + /** + * 分页查询车辆三检列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆三检分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleThreeInspectBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + // 填充taskId + String businessUserId = LoginHelper.getBusinessUserId(); + for (HotVehicleThreeInspectVo vo : result.getRecords()) { + if (StringUtils.isNotBlank(vo.getInstanceId())) { + String taskId = flowService.getTaskId(vo.getInstanceId(), businessUserId); + vo.setTaskId(taskId); + } + } + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆三检列表 + * + * @param bo 查询条件 + * @return 车辆三检列表 + */ + @Override + public List queryList(HotVehicleThreeInspectBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleThreeInspectBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotVehicleThreeInspect::getCreateTime); + Object beginTime = params == null ? null : params.get("beginTime"); + Object endTime = params == null ? null : params.get("endTime"); + if (beginTime != null && endTime != null) { + lqw.apply( + "EXISTS (SELECT 1 FROM hot_vehicle_three_inspect t2 " + + "WHERE t2.inspect_id = hot_vehicle_three_inspect.inspect_id " + + "AND t2.inspect_time BETWEEN {0} AND {1})", + beginTime, + endTime + ); + } else if (beginTime != null) { + lqw.apply( + "EXISTS (SELECT 1 FROM hot_vehicle_three_inspect t2 " + + "WHERE t2.inspect_id = hot_vehicle_three_inspect.inspect_id " + + "AND t2.inspect_time >= {0})", + beginTime + ); + } else if (endTime != null) { + lqw.apply( + "EXISTS (SELECT 1 FROM hot_vehicle_three_inspect t2 " + + "WHERE t2.inspect_id = hot_vehicle_three_inspect.inspect_id " + + "AND t2.inspect_time <= {0})", + endTime + ); + } + lqw.eq(bo.getCompanyId() != null, HotVehicleThreeInspect::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getVehicleId() != null, HotVehicleThreeInspect::getVehicleId, bo.getVehicleId()); + lqw.eq(bo.getDriverId() != null, HotVehicleThreeInspect::getDriverId, bo.getDriverId()); + lqw.like(StringUtils.isNotBlank(bo.getDriverName()), HotVehicleThreeInspect::getDriverName, bo.getDriverName()); + lqw.eq(bo.getInspectPhase() != null, HotVehicleThreeInspect::getInspectPhase, bo.getInspectPhase()); + lqw.eq(bo.getInspectTime() != null, HotVehicleThreeInspect::getInspectTime, bo.getInspectTime()); + lqw.eq(bo.getAuditorId() != null, HotVehicleThreeInspect::getAuditorId, bo.getAuditorId()); + lqw.like(StringUtils.isNotBlank(bo.getAuditorName()), HotVehicleThreeInspect::getAuditorName, bo.getAuditorName()); + lqw.eq(StringUtils.isNotBlank(bo.getInspectResultJson()), HotVehicleThreeInspect::getInspectResultJson, bo.getInspectResultJson()); + lqw.eq(StringUtils.isNotBlank(bo.getAttachImgUrl()), HotVehicleThreeInspect::getAttachImgUrl, bo.getAttachImgUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getInspectorSignImgUrl()), HotVehicleThreeInspect::getInspectorSignImgUrl, bo.getInspectorSignImgUrl()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleThreeInspect::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增车辆三检 + * + * @param bo 车辆三检 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleThreeInspectBo bo) { + HotVehicleThreeInspect add = MapstructUtils.convert(bo, HotVehicleThreeInspect.class); + validEntityBeforeSave(add); + + if (DriverLoginContextHelper.isDriverPort() && StringUtils.isNotBlank(bo.getInspectorSignImgUrl())) { + validateInspectorSignatureByPhase(bo); + } + + // 校验该车辆三检流程逻辑 + if (add.getVehicleId() != null) { + // 获取该车辆最近的一条三检记录 + List list = baseMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotVehicleThreeInspect::getVehicleId, add.getVehicleId()) + .orderByDesc(HotVehicleThreeInspect::getCreateTime) + .last("LIMIT 1") + ); + + Long currentPhase = add.getInspectPhase(); + + if (list != null && !list.isEmpty()) { + HotVehicleThreeInspect last = list.get(0); + Long lastPhase = last.getInspectPhase(); + // 1. 如果上一条是收车检查(第3阶段),则本次必须是新一轮的出车检查(第1阶段) + if (lastPhase == 3) { + if (currentPhase != 1) { + throw new ServiceException("上一轮检查已完成(已收车),请开始新的出车检查(第一阶段)"); + } + } + // 2. 如果上一条是出车检查(第1阶段) + else if (lastPhase == 1) { + if (currentPhase == 1) { + throw new ServiceException("当前已有出车检查记录,请进行行车检查或收车检查"); + } + // 可以是行车检查(2) 或 收车检查(3) + // 如果是收车检查(3),需要判断时间间隔 + if (currentPhase == 3) { + // 检查出车时间与当前时间间隔 + // 如果 last.getInspectTime() 为空,使用 createTime + java.util.Date lastTime = last.getInspectTime() != null ? last.getInspectTime() : last.getCreateTime(); + long diff = System.currentTimeMillis() - lastTime.getTime(); + long hours = diff / (60 * 60 * 1000); + if (hours > 4) { + throw new ServiceException("距离出车检查已超过4小时,必须先进行行车检查(第二阶段),才能进行收车检查"); + } + } + } + // 3. 如果上一条是行车检查(第2阶段) + else if (lastPhase == 2) { + if (currentPhase == 1) { + throw new ServiceException("当前轮次尚未完成收车检查,不能开始新一轮出车检查"); + } + if (currentPhase == 2) { + // 允许连续多次行车检查? 需求未明确禁止,通常行车检查可以有多次,但这里假设按顺序走 + // 如果严格按1-2-3走,这里可能需要限制。根据需求描述 "三次检查依次是...",倾向于 1->2->3 + // 但需求也说 "收车检查也不用等到行车检查审核完结",意味着 1->2 后必须接 3 + throw new ServiceException("当前已完成行车检查,请进行收车检查(第三阶段)"); + } + // 必须是收车检查(3) + } + } else { + // 没有历史记录,必须从第一阶段开始 + if (currentPhase != 1) { + throw new ServiceException("新的一轮检查必须从出车检查(第一阶段)开始"); + } + } + } + + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + + // 开启车辆三检的流程 + String initiator = String.valueOf(LoginHelper.getBusinessUserId()); + + // 下一步审核人 + String approverId = bo.getAuditorId() != null ? String.valueOf(bo.getAuditorId()) : null; + + if (StringUtils.isBlank(approverId)) { + throw new ServiceException("请选择审核人"); + } + + String instanceId = flowService.startFlow( + "VEHICLE_THREE_INSPECT", + String.valueOf(add.getId()), + add.getCompanyId(), + initiator, + approverId + ); + + // 更新流程状态 + add.setInstanceId(instanceId); + add.setFlowStatus("SUBMITTED"); + baseMapper.updateById(add); + } + return flag; + } + + private void validateInspectorSignatureByPhase(HotVehicleThreeInspectBo bo) { + Long inspectPhase = bo.getInspectPhase(); + if (inspectPhase == null) { + throw new ServiceException("检查阶段不能为空"); + } + if (StringUtils.isBlank(bo.getDriverName())) { + throw new ServiceException("检查员姓名不能为空"); + } + if (inspectPhase == 1L || inspectPhase == 2L || inspectPhase == 3L) { + Long ossId = parseSignatureOssId(bo.getInspectorSignImgUrl()); + signatureVerifyService.validateSelfSignature(ossId, bo.getDriverName()); + return; + } + throw new ServiceException("检查阶段错误"); + } + + private Long parseSignatureOssId(String signImgUrl) { + String first = signImgUrl.split(",")[0].trim(); + if (!first.matches("^\\d+$")) { + throw new ServiceException("签名文件格式错误"); + } + return Long.parseLong(first); + } + + /** + * 修改车辆三检 + * + * @param bo 车辆三检 + * @return 是否修改成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(HotVehicleThreeInspectBo bo) { + HotVehicleThreeInspect update = MapstructUtils.convert(bo, HotVehicleThreeInspect.class); + validEntityBeforeSave(update); + boolean flag = baseMapper.updateById(update) > 0; + return flag; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleThreeInspect entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除车辆三检信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + + } + List list = baseMapper.selectList( + Wrappers.lambdaQuery() + .in(HotVehicleThreeInspect::getId, ids) + ); + if (list != null && !list.isEmpty()) { + java.util.Set instanceIds = new java.util.HashSet<>(); + for (HotVehicleThreeInspect e : list) { + if (org.dromara.common.core.utils.StringUtils.isNotBlank(e.getInstanceId())) { + instanceIds.add(e.getInstanceId()); + } + } + if (!instanceIds.isEmpty()) { + flowService.deleteTodosByInstanceIds(instanceIds); + } + } + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void audit(HotVehicleThreeInspectBo bo) { + // 1. 校验参数 + if (bo.getTaskId() == null) { + throw new ServiceException("流程任务ID不能为空"); + } + if (bo.getId() == null) { + throw new ServiceException("三检记录ID不能为空"); + } + HotVehicleThreeInspect current = baseMapper.selectById(bo.getId()); + if (current == null) { + throw new ServiceException("三检记录不存在"); + } + validateAuditTimeNotBeforeInspectTime(bo, current); + if (StringUtils.isNotBlank(bo.getAuditorSignImgUrl())) { + String auditorName = resolveAuditorName(bo); + signatureVerifyService.validateSelfSignature(parseSignatureOssId(bo.getAuditorSignImgUrl()), auditorName); + } + // 2. 更新业务数据 + HotVehicleThreeInspect update = MapstructUtils.convert(bo, HotVehicleThreeInspect.class); + boolean flag = baseMapper.updateById(update) > 0; + + if (flag) { + // 只有审核人员签名了,才完成流程任务 + if (StringUtils.isNotBlank(bo.getAuditorSignImgUrl())) { + // 4. 完成流程任务 + // 如果有隐患,我们认为流程是"驳回"的(需要整改) + boolean auditPass = bo.getHasHiddenDanger() != null && bo.getHasHiddenDanger() == 0L; + flowService.audit( + bo.getTaskId(), + auditPass, + bo.getAuditResult(), + LoginHelper.getBusinessUserId() + ); + } + } + + } + + private void validateAuditTimeNotBeforeInspectTime(HotVehicleThreeInspectBo bo, HotVehicleThreeInspect current) { + java.util.Date auditTime = bo.getAuditTime(); + if (auditTime == null) { + return; + } + java.util.Date inspectTime = bo.getInspectTime() != null ? bo.getInspectTime() : current.getInspectTime(); + if (inspectTime != null && auditTime.before(inspectTime)) { + throw new ServiceException("审核日期不能早于检查日期"); + } + } + + private String resolveAuditorName(HotVehicleThreeInspectBo bo) { + if (StringUtils.isNotBlank(bo.getAuditorName())) { + return bo.getAuditorName().trim(); + } + if (bo.getId() != null) { + HotVehicleThreeInspect current = baseMapper.selectById(bo.getId()); + if (current != null && StringUtils.isNotBlank(current.getAuditorName())) { + return current.getAuditorName().trim(); + } + } + throw new ServiceException("审核人姓名不能为空"); + } +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/strategy/VehicleThreeInspectStrategy.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/strategy/VehicleThreeInspectStrategy.java new file mode 100644 index 0000000..d64ff0d --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspect/strategy/VehicleThreeInspectStrategy.java @@ -0,0 +1,82 @@ +package com.hotwj.platform.config.vehicleThreeInspect.strategy; + +import com.hotwj.platform.config.vehicleThreeInspect.domain.HotVehicleThreeInspect; +import com.hotwj.platform.config.vehicleThreeInspect.mapper.HotVehicleThreeInspectMapper; +import com.hotwj.platform.flow.domain.SysFlowInstance; +import com.hotwj.platform.flow.domain.dto.FlowNextDto; +import com.hotwj.platform.flow.strategy.IFlowStrategy; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle; +import com.hotwj.platform.resourceManagement.vehicleManagement.mapper.HotVehicleMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +/** + * 车辆三检流程策略 + * + * @author shihongwei + * @date 2026-01-25 + */ +@Component +@RequiredArgsConstructor +public class VehicleThreeInspectStrategy implements IFlowStrategy { + + private final HotVehicleThreeInspectMapper inspectMapper; + private final HotVehicleMapper vehicleMapper; + + @Override + public String getFlowCode() { + return "VEHICLE_THREE_INSPECT"; + } + + @Override + public String getFlowName() { + return "车辆三检"; + } + + @Override + public FlowNextDto next(SysFlowInstance instance, String currentNodeCode) { + // 简单流程:只有一级审批,审批通过即结束 + // 如果需要多级审批,可以在这里根据 currentNodeCode 判断返回下一级节点 + // 这里返回 null 表示没有下一节点,流程结束 + return null; + } + + @Override + public FlowNextDto getInitialNode(String businessId) { + String nodeName = "车辆三检审批"; + try { + Long id = Long.valueOf(businessId); + HotVehicleThreeInspect inspect = inspectMapper.selectById(id); + if (inspect != null) { + // 获取车牌号 + String plateNumber = ""; + HotVehicle vehicle = vehicleMapper.selectById(inspect.getVehicleId()); + if (vehicle != null) { + plateNumber = vehicle.getPlateNumber(); + } + + // 获取检查阶段 + String phaseName = getPhaseName(inspect.getInspectPhase()); + + nodeName = String.format("(%s) (%s) 审核", plateNumber, phaseName); + } + } catch (Exception e) { + // 忽略异常,使用默认名称 + } + return new FlowNextDto("NODE_INSPECT_AUDIT", nodeName, null); + } + + private String getPhaseName(Long phase) { + if (phase == null) { + return "未知阶段"; + } + if (phase == 1L) { + return "出车前检查"; + } else if (phase == 2L) { + return "行车中检查"; + } else if (phase == 3L) { + return "收车后检查"; + } + return "未知阶段"; + } +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/controller/HotVehicleThreeInspectConfigController.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/controller/HotVehicleThreeInspectConfigController.java new file mode 100644 index 0000000..e5e0dce --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/controller/HotVehicleThreeInspectConfigController.java @@ -0,0 +1,163 @@ +package com.hotwj.platform.config.vehicleThreeInspectConfig.controller; + +import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.bo.HotVehicleThreeInspectConfigBo; +import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.vo.HotVehicleThreeInspectConfigVo; +import com.hotwj.platform.config.vehicleThreeInspectConfig.service.IHotVehicleThreeInspectConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 车辆三检配置 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/vehicleThreeInspectConfig") +@Tag(name = "车辆三检配置", description = "车辆三检配置管理") +public class HotVehicleThreeInspectConfigController extends BaseController { + + private final IHotVehicleThreeInspectConfigService hotVehicleThreeInspectConfigService; + + /** + * 查询车辆三检配置列表 + */ + //@SaCheckPermission("config:vehicleThreeInspectConfig:list") + @GetMapping("/list") + @Operation(summary = "分页查询车辆三检配置列表") + public TableDataInfo list(HotVehicleThreeInspectConfigBo bo, PageQuery pageQuery) { + return hotVehicleThreeInspectConfigService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆三检配置列表 + */ + //@SaCheckPermission("config:vehicleThreeInspectConfig:export") + @Log(title = "车辆三检配置", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆三检配置列表") + public void export(HotVehicleThreeInspectConfigBo bo, HttpServletResponse response) { + List list = hotVehicleThreeInspectConfigService.queryList(bo); + ExcelUtil.exportExcel(list, "车辆三检配置", HotVehicleThreeInspectConfigVo.class, response); + } + + /** + * 获取车辆三检配置详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:vehicleThreeInspectConfig:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆三检配置详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotVehicleThreeInspectConfigService.queryById(id)); + } + + /** + * 新增车辆三检配置 + */ + //@SaCheckPermission("config:vehicleThreeInspectConfig:add") + @Log(title = "车辆三检配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆三检配置") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleThreeInspectConfigBo bo) { + return toAjax(hotVehicleThreeInspectConfigService.insertByBo(bo)); + } + + /** + * 修改车辆三检配置 + */ + //@SaCheckPermission("config:vehicleThreeInspectConfig:edit") + @Log(title = "车辆三检配置", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆三检配置") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleThreeInspectConfigBo bo) { + return toAjax(hotVehicleThreeInspectConfigService.updateByBo(bo)); + } + + /** + * 删除车辆三检配置 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:vehicleThreeInspectConfig:remove") + @Log(title = "车辆三检配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆三检配置") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotVehicleThreeInspectConfigService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 采用默认配置 + */ + //@SaCheckPermission("config:vehicleThreeInspectConfig:add") + @Log(title = "车辆三检配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/useDefault") + @Operation(summary = "采用默认配置") + public R useDefault() { + return toAjax(hotVehicleThreeInspectConfigService.useDefault()); + } + + /** + * 一键清空 + */ + //@SaCheckPermission("config:vehicleThreeInspectConfig:remove") + @Log(title = "车辆三检配置", businessType = BusinessType.DELETE) + @RepeatSubmit() + @PostMapping("/clearAll") + @Operation(summary = "一键清空") + public R clearAll() { + return toAjax(hotVehicleThreeInspectConfigService.clearAll()); + } + + /** + * 根据公司ID与ID串查询车辆三检配置(包含已删除数据) + */ + //@SaCheckPermission("config:vehicleThreeInspectConfig:list") + @GetMapping("/listByIds") + @Operation(summary = "根据公司ID与ID串查询车辆三检配置列表") + public R> listByIds( + @RequestParam Long companyId, + @RequestParam String ids + ) { + if (companyId == null) { + return R.ok(java.util.List.of()); + } + java.util.List idList = java.util.Arrays.stream(ids.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .map(Long::valueOf) + .distinct() + .toList(); + return R.ok(hotVehicleThreeInspectConfigService.queryByCompanyIdAndIds(companyId, idList)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/domain/HotVehicleThreeInspectConfig.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/domain/HotVehicleThreeInspectConfig.java new file mode 100644 index 0000000..627c9f6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/domain/HotVehicleThreeInspectConfig.java @@ -0,0 +1,74 @@ +package com.hotwj.platform.config.vehicleThreeInspectConfig.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 车辆三检配置对象 hot_vehicle_three_inspect_config + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_three_inspect_config") +public class HotVehicleThreeInspectConfig extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 检查类型 1=行车前 2=行车中 3=收车后 + */ + private Long inspectType; + + /** + * 检查项目标题 + */ + private String title; + + /** + * 无问题 + */ + private String normalText; + + /** + * 有问题 + */ + private String abnormalText; + + /** + * 三检项目代码 + */ + private String code; + + /** + * 是否启用 0=否 1=是 + */ + private Long isEnabled; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + private Long isChecked; +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/domain/bo/HotVehicleThreeInspectConfigBo.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/domain/bo/HotVehicleThreeInspectConfigBo.java new file mode 100644 index 0000000..d868325 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/domain/bo/HotVehicleThreeInspectConfigBo.java @@ -0,0 +1,78 @@ +package com.hotwj.platform.config.vehicleThreeInspectConfig.domain.bo; + +import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.HotVehicleThreeInspectConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 车辆三检配置业务对象 hot_vehicle_three_inspect_config + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleThreeInspectConfig.class, reverseConvertGenerate = false) +public class HotVehicleThreeInspectConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 检查类型 1=行车前 2=行车中 3=收车后 + */ + @NotNull(message = "检查类型 1=行车前 2=行车中 3=收车后不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long inspectType; + + /** + * 检查项目标题 + */ + @NotBlank(message = "检查项目标题不能为空", groups = {AddGroup.class, EditGroup.class}) + private String title; + + /** + * 无问题 + */ + @NotBlank(message = "无问题不能为空", groups = {AddGroup.class, EditGroup.class}) + private String normalText; + + /** + * 有问题 + */ + @NotBlank(message = "有问题不能为空", groups = {AddGroup.class, EditGroup.class}) + private String abnormalText; + + /** + * 三检项目代码 + */ + @NotBlank(message = "三检项目代码不能为空", groups = {AddGroup.class, EditGroup.class}) + private String code; + + /** + * 是否启用 0=否 1=是 + */ + private Long isEnabled; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + private Long isChecked; +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/domain/vo/HotVehicleThreeInspectConfigVo.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/domain/vo/HotVehicleThreeInspectConfigVo.java new file mode 100644 index 0000000..7833306 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/domain/vo/HotVehicleThreeInspectConfigVo.java @@ -0,0 +1,82 @@ +package com.hotwj.platform.config.vehicleThreeInspectConfig.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.HotVehicleThreeInspectConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 车辆三检配置视图对象 hot_vehicle_three_inspect_config + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleThreeInspectConfig.class) +public class HotVehicleThreeInspectConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 检查类型 1=行车前 2=行车中 3=收车后 + */ + @ExcelProperty(value = "检查类型 1=行车前 2=行车中 3=收车后") + private Long inspectType; + + /** + * 检查项目标题 + */ + @ExcelProperty(value = "检查项目标题") + private String title; + + /** + * 无问题 + */ + @ExcelProperty(value = "无问题") + private String normalText; + + /** + * 有问题 + */ + @ExcelProperty(value = "有问题") + private String abnormalText; + + /** + * 三检项目代码 + */ + @ExcelProperty(value = "三检项目代码") + private String code; + + /** + * 是否启用 0=否 1=是 + */ + @ExcelProperty(value = "是否启用 0=否 1=是") + private Long isEnabled; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + private Long isChecked; +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/mapper/HotVehicleThreeInspectConfigMapper.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/mapper/HotVehicleThreeInspectConfigMapper.java new file mode 100644 index 0000000..1ccfb52 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/mapper/HotVehicleThreeInspectConfigMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.vehicleThreeInspectConfig.mapper; + +import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.HotVehicleThreeInspectConfig; +import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.vo.HotVehicleThreeInspectConfigVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆三检配置Mapper接口 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Mapper +public interface HotVehicleThreeInspectConfigMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/service/IHotVehicleThreeInspectConfigService.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/service/IHotVehicleThreeInspectConfigService.java new file mode 100644 index 0000000..4513ce7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/service/IHotVehicleThreeInspectConfigService.java @@ -0,0 +1,92 @@ +package com.hotwj.platform.config.vehicleThreeInspectConfig.service; + +import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.bo.HotVehicleThreeInspectConfigBo; +import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.vo.HotVehicleThreeInspectConfigVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆三检配置Service接口 + * + * @author shihongwei + * @date 2025-12-29 + */ +public interface IHotVehicleThreeInspectConfigService { + + /** + * 查询车辆三检配置 + * + * @param id 主键 + * @return 车辆三检配置 + */ + HotVehicleThreeInspectConfigVo queryById(Long id); + + /** + * 分页查询车辆三检配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆三检配置分页列表 + */ + TableDataInfo queryPageList(HotVehicleThreeInspectConfigBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆三检配置列表 + * + * @param bo 查询条件 + * @return 车辆三检配置列表 + */ + List queryList(HotVehicleThreeInspectConfigBo bo); + + /** + * 根据公司ID与ID列表查询(包含已删除的数据) + * + * @param companyId 公司ID + * @param ids 主键ID列表 + * @return 车辆三检配置列表 + */ + List queryByCompanyIdAndIds(Long companyId, java.util.List ids); + + + /** + * 新增车辆三检配置 + * + * @param bo 车辆三检配置 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleThreeInspectConfigBo bo); + + /** + * 修改车辆三检配置 + * + * @param bo 车辆三检配置 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleThreeInspectConfigBo bo); + + /** + * 校验并批量删除车辆三检配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 采用默认配置 + * + * @return 结果 + */ + Boolean useDefault(); + + /** + * 清空当前公司所有配置 + * + * @return 是否成功 + */ + Boolean clearAll(); +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/service/impl/HotVehicleThreeInspectConfigServiceImpl.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/service/impl/HotVehicleThreeInspectConfigServiceImpl.java new file mode 100644 index 0000000..a2a00bf --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectConfig/service/impl/HotVehicleThreeInspectConfigServiceImpl.java @@ -0,0 +1,259 @@ +package com.hotwj.platform.config.vehicleThreeInspectConfig.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.HotVehicleThreeInspectConfig; +import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.bo.HotVehicleThreeInspectConfigBo; +import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.vo.HotVehicleThreeInspectConfigVo; +import com.hotwj.platform.config.vehicleThreeInspectConfig.mapper.HotVehicleThreeInspectConfigMapper; +import com.hotwj.platform.config.vehicleThreeInspectConfig.service.IHotVehicleThreeInspectConfigService; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 车辆三检配置Service业务层处理 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleThreeInspectConfigServiceImpl implements IHotVehicleThreeInspectConfigService { + + private final HotVehicleThreeInspectConfigMapper baseMapper; + private final ISysCompanyService sysCompanyService; + + /** + * 查询车辆三检配置 + * + * @param id 主键 + * @return 车辆三检配置 + */ + @Override + public HotVehicleThreeInspectConfigVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean useDefault() { + Long companyId = LoginHelper.getCompanyId(); + if (companyId == null) { + throw new ServiceException("当前登录用户无公司信息"); + } + + SysCompanyVo company = sysCompanyService.queryById(companyId); + if (company == null) { + throw new ServiceException("公司不存在"); + } + + // 校验当前公司是否已有配置 + Long count = baseMapper.selectCount( + Wrappers.lambdaQuery(HotVehicleThreeInspectConfig.class) + .eq(HotVehicleThreeInspectConfig::getCompanyId, companyId) + ); + if (count != null && count > 0) { + throw new ServiceException("当前公司已存在车辆三检配置,请先清除已存在数据"); + } + + // 获取总部配置 (companyId = 1) + List defaultConfigs = baseMapper.selectList( + Wrappers.lambdaQuery(HotVehicleThreeInspectConfig.class) + .eq(HotVehicleThreeInspectConfig::getCompanyId, 1L) + ); + + if (CollUtil.isEmpty(defaultConfigs)) { + throw new ServiceException("默认配置不存在,请联系管理员"); + } + + List newConfigs = new ArrayList<>(); + for (HotVehicleThreeInspectConfig cfg : defaultConfigs) { + HotVehicleThreeInspectConfig newCfg = new HotVehicleThreeInspectConfig(); + // 复制属性,排除ID等 + newCfg.setCompanyId(companyId); + newCfg.setInspectType(cfg.getInspectType()); + newCfg.setTitle(cfg.getTitle()); + newCfg.setNormalText(cfg.getNormalText()); + newCfg.setAbnormalText(cfg.getAbnormalText()); + newCfg.setCode(cfg.getCode()); + newCfg.setIsEnabled(cfg.getIsEnabled()); + newCfg.setIsChecked(cfg.getIsChecked()); + newCfg.setIsDeleted(0L); + newConfigs.add(newCfg); + } + + return baseMapper.insertBatch(newConfigs); + } + + + /** + * 分页查询车辆三检配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆三检配置分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleThreeInspectConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆三检配置列表 + * + * @param bo 查询条件 + * @return 车辆三检配置列表 + */ + @Override + public List queryList(HotVehicleThreeInspectConfigBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + @Override + public List queryByCompanyIdAndIds(Long companyId, java.util.List ids) { + if (companyId == null || ids == null || ids.isEmpty()) { + return java.util.Collections.emptyList(); + } + LambdaQueryWrapper lqw = Wrappers.lambdaQuery() + .eq(HotVehicleThreeInspectConfig::getCompanyId, companyId) + .in(HotVehicleThreeInspectConfig::getId, ids); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleThreeInspectConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotVehicleThreeInspectConfig::getId); + lqw.eq(bo.getCompanyId() != null, HotVehicleThreeInspectConfig::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getInspectType() != null, HotVehicleThreeInspectConfig::getInspectType, bo.getInspectType()); + lqw.eq(StringUtils.isNotBlank(bo.getTitle()), HotVehicleThreeInspectConfig::getTitle, bo.getTitle()); + lqw.eq(StringUtils.isNotBlank(bo.getNormalText()), HotVehicleThreeInspectConfig::getNormalText, bo.getNormalText()); + lqw.eq(StringUtils.isNotBlank(bo.getAbnormalText()), HotVehicleThreeInspectConfig::getAbnormalText, bo.getAbnormalText()); + lqw.eq(StringUtils.isNotBlank(bo.getCode()), HotVehicleThreeInspectConfig::getCode, bo.getCode()); + lqw.eq(bo.getIsEnabled() != null, HotVehicleThreeInspectConfig::getIsEnabled, bo.getIsEnabled()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleThreeInspectConfig::getIsDeleted, bo.getIsDeleted()); + lqw.eq(bo.getIsChecked() != null, HotVehicleThreeInspectConfig::getIsChecked, bo.getIsChecked()); + return lqw; + } + + /** + * 新增车辆三检配置 + * + * @param bo 车辆三检配置 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleThreeInspectConfigBo bo) { + HotVehicleThreeInspectConfig add = MapstructUtils.convert(bo, HotVehicleThreeInspectConfig.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改车辆三检配置 + * + * @param bo 车辆三检配置 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleThreeInspectConfigBo bo) { + HotVehicleThreeInspectConfig update = MapstructUtils.convert(bo, HotVehicleThreeInspectConfig.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleThreeInspectConfig entity) { + if (entity.getCompanyId() == null || StringUtils.isBlank(entity.getCode())) { + return; + } + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(HotVehicleThreeInspectConfig::getCompanyId, entity.getCompanyId()); + lqw.eq(HotVehicleThreeInspectConfig::getCode, entity.getCode()); + lqw.eq(HotVehicleThreeInspectConfig::getInspectType, entity.getInspectType()); + lqw.eq(HotVehicleThreeInspectConfig::getIsDeleted, 0L); + if (entity.getId() != null) { + lqw.ne(HotVehicleThreeInspectConfig::getId, entity.getId()); + } + Long count = baseMapper.selectCount(lqw); + if (count != null && count > 0) { + throw new ServiceException("同一公司下三检项目代码已存在,请修改后重试"); + } + + // 校验标题是否重复 + if (StringUtils.isNotBlank(entity.getTitle())) { + // 去除首尾空格 + String title = entity.getTitle().trim(); + entity.setTitle(title); + + LambdaQueryWrapper lqwTitle = Wrappers.lambdaQuery(); + lqwTitle.eq(HotVehicleThreeInspectConfig::getCompanyId, entity.getCompanyId()); + lqwTitle.eq(HotVehicleThreeInspectConfig::getTitle, title); + lqwTitle.eq(HotVehicleThreeInspectConfig::getInspectType, entity.getInspectType()); + lqwTitle.eq(HotVehicleThreeInspectConfig::getIsDeleted, 0L); + if (entity.getId() != null) { + lqwTitle.ne(HotVehicleThreeInspectConfig::getId, entity.getId()); + } + Long countTitle = baseMapper.selectCount(lqwTitle); + if (countTitle != null && countTitle > 0) { + throw new ServiceException("同一公司下三检项目名称已存在,请修改后重试"); + } + } + } + + /** + * 校验并批量删除车辆三检配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean clearAll() { + Long companyId = LoginHelper.getCompanyId(); + if (companyId == null) { + throw new ServiceException("当前登录用户无公司信息"); + } + // 逻辑删除当前公司所有配置 + return baseMapper.delete( + Wrappers.lambdaQuery(HotVehicleThreeInspectConfig.class) + .eq(HotVehicleThreeInspectConfig::getCompanyId, companyId) + ) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/controller/HotVehicleThreeInspectItemController.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/controller/HotVehicleThreeInspectItemController.java new file mode 100644 index 0000000..94666ad --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/controller/HotVehicleThreeInspectItemController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.config.vehicleThreeInspectItem.controller; + +import com.hotwj.platform.config.vehicleThreeInspectItem.domain.bo.HotVehicleThreeInspectItemBo; +import com.hotwj.platform.config.vehicleThreeInspectItem.domain.vo.HotVehicleThreeInspectItemVo; +import com.hotwj.platform.config.vehicleThreeInspectItem.service.IHotVehicleThreeInspectItemService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 车辆三检检查项 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/vehicleThreeInspectItem") +@Tag(name = "车辆三检检查项", description = "车辆三检检查项管理") +public class HotVehicleThreeInspectItemController extends BaseController { + + private final IHotVehicleThreeInspectItemService hotVehicleThreeInspectItemService; + + /** + * 查询车辆三检检查项列表 + */ + //@SaCheckPermission("config:vehicleThreeInspectItem:list") + @GetMapping("/list") + @Operation(summary = "分页查询车辆三检检查项列表") + public TableDataInfo list(HotVehicleThreeInspectItemBo bo, PageQuery pageQuery) { + return hotVehicleThreeInspectItemService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆三检检查项列表 + */ + //@SaCheckPermission("config:vehicleThreeInspectItem:export") + @Log(title = "车辆三检检查项", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆三检检查项列表") + public void export(HotVehicleThreeInspectItemBo bo, HttpServletResponse response) { + List list = hotVehicleThreeInspectItemService.queryList(bo); + ExcelUtil.exportExcel(list, "车辆三检检查项", HotVehicleThreeInspectItemVo.class, response); + } + + /** + * 获取车辆三检检查项详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:vehicleThreeInspectItem:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆三检检查项详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotVehicleThreeInspectItemService.queryById(id)); + } + + /** + * 新增车辆三检检查项 + */ + //@SaCheckPermission("config:vehicleThreeInspectItem:add") + @Log(title = "车辆三检检查项", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆三检检查项") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleThreeInspectItemBo bo) { + return toAjax(hotVehicleThreeInspectItemService.insertByBo(bo)); + } + + /** + * 修改车辆三检检查项 + */ + //@SaCheckPermission("config:vehicleThreeInspectItem:edit") + @Log(title = "车辆三检检查项", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆三检检查项") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleThreeInspectItemBo bo) { + return toAjax(hotVehicleThreeInspectItemService.updateByBo(bo)); + } + + /** + * 删除车辆三检检查项 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:vehicleThreeInspectItem:remove") + @Log(title = "车辆三检检查项", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆三检检查项") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotVehicleThreeInspectItemService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/domain/HotVehicleThreeInspectItem.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/domain/HotVehicleThreeInspectItem.java new file mode 100644 index 0000000..9c9dfd7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/domain/HotVehicleThreeInspectItem.java @@ -0,0 +1,59 @@ +package com.hotwj.platform.config.vehicleThreeInspectItem.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 车辆三检检查项对象 hot_vehicle_three_inspect_item + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_three_inspect_item") +public class HotVehicleThreeInspectItem extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 检查项ID + */ + private Long inspectId; + + /** + * 检查类型 1=行车前 2=行车中 3=收车后 + */ + private Long inspectType; + + /** + * 排序序号,数字越小越靠前 + */ + private Long sortNo; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/domain/bo/HotVehicleThreeInspectItemBo.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/domain/bo/HotVehicleThreeInspectItemBo.java new file mode 100644 index 0000000..dcf3627 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/domain/bo/HotVehicleThreeInspectItemBo.java @@ -0,0 +1,60 @@ +package com.hotwj.platform.config.vehicleThreeInspectItem.domain.bo; + +import com.hotwj.platform.config.vehicleThreeInspectItem.domain.HotVehicleThreeInspectItem; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 车辆三检检查项业务对象 hot_vehicle_three_inspect_item + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleThreeInspectItem.class, reverseConvertGenerate = false) +public class HotVehicleThreeInspectItemBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ +// @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 检查项ID + */ + @NotNull(message = "检查项ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long inspectId; + + /** + * 检查类型 1=行车前 2=行车中 3=收车后 + */ + @NotNull(message = "检查类型 1=行车前 2=行车中 3=收车后不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long inspectType; + + /** + * 排序序号,数字越小越靠前 + */ + @NotNull(message = "排序序号,数字越小越靠前不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long sortNo; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/domain/vo/HotVehicleThreeInspectItemVo.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/domain/vo/HotVehicleThreeInspectItemVo.java new file mode 100644 index 0000000..1e9bf02 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/domain/vo/HotVehicleThreeInspectItemVo.java @@ -0,0 +1,64 @@ +package com.hotwj.platform.config.vehicleThreeInspectItem.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.vehicleThreeInspectItem.domain.HotVehicleThreeInspectItem; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 车辆三检检查项视图对象 hot_vehicle_three_inspect_item + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleThreeInspectItem.class) +public class HotVehicleThreeInspectItemVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 检查项ID + */ + @ExcelProperty(value = "检查项ID") + private Long inspectId; + + /** + * 检查类型 1=行车前 2=行车中 3=收车后 + */ + @ExcelProperty(value = "检查类型 1=行车前 2=行车中 3=收车后") + private Long inspectType; + + /** + * 排序序号,数字越小越靠前 + */ + @ExcelProperty(value = "排序序号,数字越小越靠前") + private Long sortNo; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/mapper/HotVehicleThreeInspectItemMapper.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/mapper/HotVehicleThreeInspectItemMapper.java new file mode 100644 index 0000000..d782c5e --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/mapper/HotVehicleThreeInspectItemMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.vehicleThreeInspectItem.mapper; + +import com.hotwj.platform.config.vehicleThreeInspectItem.domain.HotVehicleThreeInspectItem; +import com.hotwj.platform.config.vehicleThreeInspectItem.domain.vo.HotVehicleThreeInspectItemVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆三检检查项Mapper接口 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Mapper +public interface HotVehicleThreeInspectItemMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/service/IHotVehicleThreeInspectItemService.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/service/IHotVehicleThreeInspectItemService.java new file mode 100644 index 0000000..60e4658 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/service/IHotVehicleThreeInspectItemService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.vehicleThreeInspectItem.service; + +import com.hotwj.platform.config.vehicleThreeInspectItem.domain.bo.HotVehicleThreeInspectItemBo; +import com.hotwj.platform.config.vehicleThreeInspectItem.domain.vo.HotVehicleThreeInspectItemVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆三检检查项Service接口 + * + * @author shihongwei + * @date 2025-12-29 + */ +public interface IHotVehicleThreeInspectItemService { + + /** + * 查询车辆三检检查项 + * + * @param id 主键 + * @return 车辆三检检查项 + */ + HotVehicleThreeInspectItemVo queryById(Long id); + + /** + * 分页查询车辆三检检查项列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆三检检查项分页列表 + */ + TableDataInfo queryPageList(HotVehicleThreeInspectItemBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆三检检查项列表 + * + * @param bo 查询条件 + * @return 车辆三检检查项列表 + */ + List queryList(HotVehicleThreeInspectItemBo bo); + + /** + * 新增车辆三检检查项 + * + * @param bo 车辆三检检查项 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleThreeInspectItemBo bo); + + /** + * 修改车辆三检检查项 + * + * @param bo 车辆三检检查项 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleThreeInspectItemBo bo); + + /** + * 校验并批量删除车辆三检检查项信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/service/impl/HotVehicleThreeInspectItemServiceImpl.java b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/service/impl/HotVehicleThreeInspectItemServiceImpl.java new file mode 100644 index 0000000..ed6ae96 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleThreeInspectItem/service/impl/HotVehicleThreeInspectItemServiceImpl.java @@ -0,0 +1,135 @@ +package com.hotwj.platform.config.vehicleThreeInspectItem.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.vehicleThreeInspectItem.domain.HotVehicleThreeInspectItem; +import com.hotwj.platform.config.vehicleThreeInspectItem.domain.bo.HotVehicleThreeInspectItemBo; +import com.hotwj.platform.config.vehicleThreeInspectItem.domain.vo.HotVehicleThreeInspectItemVo; +import com.hotwj.platform.config.vehicleThreeInspectItem.mapper.HotVehicleThreeInspectItemMapper; +import com.hotwj.platform.config.vehicleThreeInspectItem.service.IHotVehicleThreeInspectItemService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 车辆三检检查项Service业务层处理 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleThreeInspectItemServiceImpl implements IHotVehicleThreeInspectItemService { + + private final HotVehicleThreeInspectItemMapper baseMapper; + + /** + * 查询车辆三检检查项 + * + * @param id 主键 + * @return 车辆三检检查项 + */ + @Override + public HotVehicleThreeInspectItemVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆三检检查项列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆三检检查项分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleThreeInspectItemBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆三检检查项列表 + * + * @param bo 查询条件 + * @return 车辆三检检查项列表 + */ + @Override + public List queryList(HotVehicleThreeInspectItemBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleThreeInspectItemBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotVehicleThreeInspectItem::getSortNo); + lqw.eq(bo.getCompanyId() != null, HotVehicleThreeInspectItem::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getInspectId() != null, HotVehicleThreeInspectItem::getInspectId, bo.getInspectId()); + lqw.eq(bo.getInspectType() != null, HotVehicleThreeInspectItem::getInspectType, bo.getInspectType()); + lqw.eq(bo.getSortNo() != null, HotVehicleThreeInspectItem::getSortNo, bo.getSortNo()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleThreeInspectItem::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增车辆三检检查项 + * + * @param bo 车辆三检检查项 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleThreeInspectItemBo bo) { + HotVehicleThreeInspectItem add = MapstructUtils.convert(bo, HotVehicleThreeInspectItem.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改车辆三检检查项 + * + * @param bo 车辆三检检查项 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleThreeInspectItemBo bo) { + HotVehicleThreeInspectItem update = MapstructUtils.convert(bo, HotVehicleThreeInspectItem.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleThreeInspectItem entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除车辆三检检查项信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleType/controller/HotVehicleTypeController.java b/src/main/java/com/hotwj/platform/config/vehicleType/controller/HotVehicleTypeController.java new file mode 100644 index 0000000..2added6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleType/controller/HotVehicleTypeController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.config.vehicleType.controller; + +import com.hotwj.platform.config.vehicleType.domain.bo.HotVehicleTypeBo; +import com.hotwj.platform.config.vehicleType.domain.vo.HotVehicleTypeVo; +import com.hotwj.platform.config.vehicleType.service.IHotVehicleTypeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 车辆类型 + * + * @author shihongwei + * @date 2026-02-14 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/config/vehicleType") +@Tag(name = "车辆类型", description = "车辆类型管理") +public class HotVehicleTypeController extends BaseController { + + private final IHotVehicleTypeService hotVehicleTypeService; + + /** + * 查询车辆类型列表 + */ + //@SaCheckPermission("config:vehicleType:list") + @GetMapping("/list") + @Operation(summary = "分页查询车辆类型列表") + public TableDataInfo list(HotVehicleTypeBo bo, PageQuery pageQuery) { + return hotVehicleTypeService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆类型列表 + */ + //@SaCheckPermission("config:vehicleType:export") + @Log(title = "车辆类型", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆类型列表") + public void export(HotVehicleTypeBo bo, HttpServletResponse response) { + List list = hotVehicleTypeService.queryList(bo); + ExcelUtil.exportExcel(list, "车辆类型", HotVehicleTypeVo.class, response); + } + + /** + * 获取车辆类型详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("config:vehicleType:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆类型详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotVehicleTypeService.queryById(id)); + } + + /** + * 新增车辆类型 + */ + //@SaCheckPermission("config:vehicleType:add") + @Log(title = "车辆类型", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆类型") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleTypeBo bo) { + return toAjax(hotVehicleTypeService.insertByBo(bo)); + } + + /** + * 修改车辆类型 + */ + //@SaCheckPermission("config:vehicleType:edit") + @Log(title = "车辆类型", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆类型") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleTypeBo bo) { + return toAjax(hotVehicleTypeService.updateByBo(bo)); + } + + /** + * 删除车辆类型 + * + * @param ids 主键串 + */ + //@SaCheckPermission("config:vehicleType:remove") + @Log(title = "车辆类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆类型") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotVehicleTypeService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleType/domain/HotVehicleType.java b/src/main/java/com/hotwj/platform/config/vehicleType/domain/HotVehicleType.java new file mode 100644 index 0000000..94b1c4d --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleType/domain/HotVehicleType.java @@ -0,0 +1,59 @@ +package com.hotwj.platform.config.vehicleType.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 车辆类型对象 hot_vehicle_type + * + * @author shihongwei + * @date 2026-02-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_type") +public class HotVehicleType extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + private Long companyId; + + /** + * 名称 + */ + private String name; + + /** + * 代码 + */ + private String code; + + /** + * 种类 + */ + private String category; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleType/domain/bo/HotVehicleTypeBo.java b/src/main/java/com/hotwj/platform/config/vehicleType/domain/bo/HotVehicleTypeBo.java new file mode 100644 index 0000000..4cbf051 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleType/domain/bo/HotVehicleTypeBo.java @@ -0,0 +1,54 @@ +package com.hotwj.platform.config.vehicleType.domain.bo; + +import com.hotwj.platform.config.vehicleType.domain.HotVehicleType; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 车辆类型业务对象 hot_vehicle_type + * + * @author shihongwei + * @date 2026-02-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleType.class, reverseConvertGenerate = false) +public class HotVehicleTypeBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + private Long companyId; + + /** + * 名称 + */ + private String name; + + /** + * 代码 + */ + private String code; + + /** + * 种类 + */ + private String category; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleType/domain/vo/HotVehicleTypeVo.java b/src/main/java/com/hotwj/platform/config/vehicleType/domain/vo/HotVehicleTypeVo.java new file mode 100644 index 0000000..0a23509 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleType/domain/vo/HotVehicleTypeVo.java @@ -0,0 +1,79 @@ +package com.hotwj.platform.config.vehicleType.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.vehicleType.domain.HotVehicleType; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 车辆类型视图对象 hot_vehicle_type + * + * @author shihongwei + * @date 2026-02-14 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleType.class) +public class HotVehicleTypeVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "由=应用层保证必填") + private Long companyId; + + /** + * 名称 + */ + @ExcelProperty(value = "名称") + private String name; + + /** + * 代码 + */ + @ExcelProperty(value = "代码") + private String code; + + /** + * 种类 + */ + @ExcelProperty(value = "种类") + private String category; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleType/mapper/HotVehicleTypeMapper.java b/src/main/java/com/hotwj/platform/config/vehicleType/mapper/HotVehicleTypeMapper.java new file mode 100644 index 0000000..89533db --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleType/mapper/HotVehicleTypeMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.config.vehicleType.mapper; + +import com.hotwj.platform.config.vehicleType.domain.HotVehicleType; +import com.hotwj.platform.config.vehicleType.domain.vo.HotVehicleTypeVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆类型Mapper接口 + * + * @author shihongwei + * @date 2026-02-14 + */ +@Mapper +public interface HotVehicleTypeMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleType/service/IHotVehicleTypeService.java b/src/main/java/com/hotwj/platform/config/vehicleType/service/IHotVehicleTypeService.java new file mode 100644 index 0000000..c32ae5b --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleType/service/IHotVehicleTypeService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.config.vehicleType.service; + +import com.hotwj.platform.config.vehicleType.domain.bo.HotVehicleTypeBo; +import com.hotwj.platform.config.vehicleType.domain.vo.HotVehicleTypeVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆类型Service接口 + * + * @author shihongwei + * @date 2026-02-14 + */ +public interface IHotVehicleTypeService { + + /** + * 查询车辆类型 + * + * @param id 主键 + * @return 车辆类型 + */ + HotVehicleTypeVo queryById(Long id); + + /** + * 分页查询车辆类型列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆类型分页列表 + */ + TableDataInfo queryPageList(HotVehicleTypeBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆类型列表 + * + * @param bo 查询条件 + * @return 车辆类型列表 + */ + List queryList(HotVehicleTypeBo bo); + + /** + * 新增车辆类型 + * + * @param bo 车辆类型 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleTypeBo bo); + + /** + * 修改车辆类型 + * + * @param bo 车辆类型 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleTypeBo bo); + + /** + * 校验并批量删除车辆类型信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/config/vehicleType/service/impl/HotVehicleTypeServiceImpl.java b/src/main/java/com/hotwj/platform/config/vehicleType/service/impl/HotVehicleTypeServiceImpl.java new file mode 100644 index 0000000..7bcfa27 --- /dev/null +++ b/src/main/java/com/hotwj/platform/config/vehicleType/service/impl/HotVehicleTypeServiceImpl.java @@ -0,0 +1,156 @@ +package com.hotwj.platform.config.vehicleType.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.vehicleType.domain.HotVehicleType; +import com.hotwj.platform.config.vehicleType.domain.bo.HotVehicleTypeBo; +import com.hotwj.platform.config.vehicleType.domain.vo.HotVehicleTypeVo; +import com.hotwj.platform.config.vehicleType.mapper.HotVehicleTypeMapper; +import com.hotwj.platform.config.vehicleType.service.IHotVehicleTypeService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 车辆类型Service业务层处理 + * + * @author shihongwei + * @date 2026-02-14 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleTypeServiceImpl implements IHotVehicleTypeService { + + private final HotVehicleTypeMapper baseMapper; + + /** + * 查询车辆类型 + * + * @param id 主键 + * @return 车辆类型 + */ + @Override + public HotVehicleTypeVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆类型列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆类型分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleTypeBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆类型列表 + * + * @param bo 查询条件 + * @return 车辆类型列表 + */ + @Override + public List queryList(HotVehicleTypeBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleTypeBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotVehicleType::getId); + lqw.eq(bo.getCompanyId() != null, HotVehicleType::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getName()), HotVehicleType::getName, bo.getName()); + lqw.eq(StringUtils.isNotBlank(bo.getCode()), HotVehicleType::getCode, bo.getCode()); + lqw.eq(StringUtils.isNotBlank(bo.getCategory()), HotVehicleType::getCategory, bo.getCategory()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotVehicleType::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotVehicleType::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleType::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增车辆类型 + * + * @param bo 车辆类型 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleTypeBo bo) { + HotVehicleType add = MapstructUtils.convert(bo, HotVehicleType.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改车辆类型 + * + * @param bo 车辆类型 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleTypeBo bo) { + HotVehicleType update = MapstructUtils.convert(bo, HotVehicleType.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleType entity) { + // 校验名称唯一 + if (StringUtils.isNotBlank(entity.getName())) { + boolean exists = baseMapper.exists(Wrappers.lambdaQuery() + .eq(HotVehicleType::getName, entity.getName()) + .ne(entity.getId() != null, HotVehicleType::getId, entity.getId())); + if (exists) { + throw new ServiceException("车辆类型名称已存在"); + } + } + // 校验代码唯一 + if (StringUtils.isNotBlank(entity.getCode())) { + boolean exists = baseMapper.exists(Wrappers.lambdaQuery() + .eq(HotVehicleType::getCode, entity.getCode()) + .ne(entity.getId() != null, HotVehicleType::getId, entity.getId())); + if (exists) { + throw new ServiceException("车辆类型代码已存在"); + } + } + } + + /** + * 校验并批量删除车辆类型信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/controller/HotDriverController.java b/src/main/java/com/hotwj/platform/driverManagement/driver/controller/HotDriverController.java new file mode 100644 index 0000000..9520b9a --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/controller/HotDriverController.java @@ -0,0 +1,279 @@ +package com.hotwj.platform.driverManagement.driver.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.hutool.core.date.DateUtil; +import com.hotwj.platform.driverManagement.driver.domain.bo.DriverRegisterBo; +import com.hotwj.platform.driverManagement.driver.domain.bo.HotDriverBo; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverImportVo; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverLedgerExportVo; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.*; + +/** + * 驾驶员基本信息 + * + * @author shihongwei + * @date 2025-12-19 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/driverManagement/driver") +public class HotDriverController extends BaseController { + + private final IHotDriverService hotDriverService; + private final IHotVehicleService hotVehicleService; + + /** + * 导入驾驶员 + */ + //@SaCheckPermission("driverManagement:driver:import") + @Log(title = "驾驶员基本信息", businessType = BusinessType.IMPORT) + @RepeatSubmit() + @PostMapping("/importData") + @Operation(summary = "导入驾驶员信息") + public R importData(@RequestPart("file") MultipartFile file) throws Exception { + List list = ExcelUtil.importExcel(file.getInputStream(), HotDriverImportVo.class); + Long companyId = LoginHelper.getLoginUser().getCompanyId(); + return R.ok(hotDriverService.importData(list, companyId)); + } + + /** + * 查询驾驶员基本信息列表 + */ + //@SaCheckPermission("driverManagement:driver:list") + @GetMapping("/list") + public TableDataInfo list(HotDriverBo bo, PageQuery pageQuery) { + return hotDriverService.queryPageList(bo, pageQuery); + } + + /** + * 通过邀请码注册驾驶员 + */ + @SaIgnore + @Log(title = "驾驶员注册", businessType = BusinessType.INSERT) + @PostMapping("/register") + public R register(@Validated @RequestBody DriverRegisterBo bo) { + return toAjax(hotDriverService.registerByInviteCode(bo)); + } + + //@SaCheckPermission("driverManagement:driver:list") + @GetMapping("/stat") + public R> stat(@RequestParam(required = false) Long companyId) { + return R.ok(hotDriverService.queryDriverCategoryStat(companyId)); + } + + /** + * 导出驾驶员基本信息列表 + */ + //@SaCheckPermission("driverManagement:driver:export") + @Log(title = "驾驶员基本信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HotDriverBo bo, HttpServletResponse response) { + List list = hotDriverService.queryList(bo); + ExcelUtil.exportExcel(list, "驾驶员基本信息", HotDriverVo.class, response); + } + + /** + * 下载导入模板 + */ + @SaIgnore + @GetMapping("/importTemplate") + @Operation(summary = "下载驾驶员导入模板") + public void importTemplate(HttpServletResponse response) { + ExcelUtil.exportExcel(new ArrayList<>(), "驾驶员导入模板", HotDriverImportVo.class, response); + } + + /** + * 按导入模板导出驾驶员信息 + */ + //@SaCheckPermission("driverManagement:driver:export") + @Log(title = "驾驶员导出(导入模板列)", businessType = BusinessType.EXPORT) + @PostMapping("/exportTemplateData") + @Operation(summary = "按导入模板导出驾驶员信息") + public void exportTemplateData(HotDriverBo bo, HttpServletResponse response) { + List drivers = hotDriverService.queryList(bo); + + HotVehicleBo vehicleBo = new HotVehicleBo(); + vehicleBo.setCompanyId(bo.getCompanyId()); + List vehicles = hotVehicleService.queryList(vehicleBo); + Map plateMap = new HashMap<>(); + for (HotVehicleVo v : vehicles) { + plateMap.put(v.getPlateNumber(), v); + } + + List exportList = new ArrayList<>(); + for (HotDriverVo d : drivers) { + HotDriverImportVo vo = new HotDriverImportVo(); + vo.setName(d.getName()); + vo.setPhone(d.getPhone()); + vo.setPlateNumber(d.getPlateNumber()); + vo.setIdCardNo(d.getIdCardNo()); + vo.setIdCardExpireDate(resolveExpireDateText(d.getIdCardExpireDate(), d.getIdCardLongTerm())); + vo.setDriverLicenseFirstIssueDate(d.getDriverLicenseFirstIssueDate()); + vo.setDriverLicenseVehicleType(d.getDriverLicenseVehicleType()); + vo.setDriverLicenseExpireDate(resolveExpireDateText(d.getDriverLicenseExpireDate(), d.getDriverLicenseLongTerm())); + vo.setDriverLicenseOrg(d.getDriverLicenseOrg()); + vo.setGender(d.getGender()); + vo.setNation(d.getNation()); + vo.setQualificationType(d.getQualificationType()); + vo.setQualificationNo(d.getQualificationNo()); + vo.setQualificationFirstIssueDate(d.getQualificationFirstIssueDate()); + vo.setQualificationValidEndDate(d.getQualificationValidEndDate()); + vo.setQualificationValidStartDate(d.getQualificationValidStartDate()); + vo.setQualificationOrg(d.getQualificationOrg()); + vo.setBirthDate(d.getBirthDate()); + vo.setHouseholdAddress(d.getHouseholdAddress()); + vo.setCurrentAddress(d.getCurrentAddress()); + vo.setEducation(d.getEducation()); + vo.setPoliticalStatus(d.getPoliticalStatus()); + vo.setMaritalStatus(d.getMaritalStatus()); + vo.setHealthStatus(d.getHealthStatus()); + vo.setEntryDate(d.getEntryDate()); + vo.setPersonType(d.getPersonType()); + exportList.add(vo); + } + ExcelUtil.exportExcel(exportList, "驾驶员导入模板列导出", HotDriverImportVo.class, response); + } + + private String resolveExpireDateText(Date expireDate, Long longTermFlag) { + if (Long.valueOf(1L).equals(longTermFlag)) { + return "长期"; + } + if (expireDate == null) { + return null; + } + return DateUtil.formatDate(expireDate); + } + + /** + * 驾驶员台账导出 + */ + //@SaCheckPermission("driverManagement:driver:export") + @Log(title = "驾驶员台账导出", businessType = BusinessType.EXPORT) + @PostMapping("/ledgerExport") + @Operation(summary = "驾驶员台账导出") + public void ledgerExport(HotDriverBo bo, HttpServletResponse response) { + List drivers = hotDriverService.queryList(bo); + + HotVehicleBo vehicleBo = new HotVehicleBo(); + vehicleBo.setCompanyId(bo.getCompanyId()); + List vehicles = hotVehicleService.queryList(vehicleBo); + Map plateMap = new HashMap<>(); + for (HotVehicleVo v : vehicles) { + plateMap.put(v.getPlateNumber(), v); + } + + List exportList = new ArrayList<>(); + for (HotDriverVo d : drivers) { + HotDriverLedgerExportVo vo = new HotDriverLedgerExportVo(); + vo.setName(d.getName()); + vo.setPhone(d.getPhone()); + vo.setIdCardNo(d.getIdCardNo()); + vo.setEntryDate(d.getEntryDate()); + vo.setCurrentAddress(d.getCurrentAddress()); + HotVehicleVo vehicle = plateMap.get(d.getPlateNumber()); + if (vehicle != null) { + vo.setVehicleArchiveNo(vehicle.getArchiveNo()); + vo.setPlateNumber(vehicle.getPlateNumber()); + } else { + vo.setPlateNumber(d.getPlateNumber()); + } + vo.setDriverLicenseNo(d.getDriverLicenseNo()); + vo.setDriverLicenseArchiveNo(d.getDriverLicenseArchiveNo()); + vo.setDriverLicenseVehicleType(d.getDriverLicenseVehicleType()); + vo.setDriverLicenseFirstIssueDate(d.getDriverLicenseFirstIssueDate()); + vo.setDriverLicenseExpireDate(d.getDriverLicenseExpireDate()); + vo.setQualificationNo(d.getQualificationNo()); + vo.setQualificationType(d.getQualificationType()); + vo.setQualificationFirstIssueDate(d.getQualificationFirstIssueDate()); + vo.setQualificationValidEndDate(d.getQualificationValidEndDate()); + exportList.add(vo); + } + ExcelUtil.exportExcel(exportList, "驾驶员台账信息", HotDriverLedgerExportVo.class, response); + } + + /** + * 获取驾驶员基本信息详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("driverManagement:driver:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable String id) { + return R.ok(hotDriverService.queryById(id)); + } + + /** + * 新增驾驶员基本信息 + */ + //@SaCheckPermission("driverManagement:driver:add") + @Log(title = "驾驶员基本信息", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody HotDriverBo bo) { + return toAjax(hotDriverService.insertByBo(bo)); + } + + /** + * 修改驾驶员基本信息 + */ + //@SaCheckPermission("driverManagement:driver:edit") + @Log(title = "驾驶员基本信息", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody HotDriverBo bo) { + return toAjax(hotDriverService.updateByBo(bo)); + } + + /** + * 删除驾驶员基本信息 + * + * @param ids 主键串 + */ + //@SaCheckPermission("driverManagement:driver:remove") + @Log(title = "驾驶员基本信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotDriverService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 审核驾驶员 + * + * @param id 驾驶员ID + * @param auditStatus 审核状态 + */ + //@SaCheckPermission("driverManagement:driver:edit") + @Log(title = "驾驶员审核", businessType = BusinessType.UPDATE) + @PutMapping("/audit/{id}/{auditStatus}") + public R audit(@PathVariable String id, @PathVariable Integer auditStatus) { + return toAjax(hotDriverService.auditDriver(id, auditStatus)); + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/domain/HotDriver.java b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/HotDriver.java new file mode 100644 index 0000000..5941269 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/HotDriver.java @@ -0,0 +1,312 @@ +package com.hotwj.platform.driverManagement.driver.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 驾驶员基本信息对象 hot_driver + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_driver") +public class HotDriver extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private String id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆Id + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS) + private String vehicleId; + + /** + * 车牌号 + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS) + private String plateNumber; + + /** + * 入职日期 + */ + private Date entryDate; + + /** + * 姓名 + */ + private String name; + + /** + * 人员类型(字典/下拉) + */ + private String personType; + + /** + * 驾驶年限(年) + */ + private Long drivingYears; + + /** + * 手机号码 + */ + private String phone; + + /** + * 性别 + */ + private String gender; + + /** + * 民族 + */ + private String nation; + + /** + * 参加工作时间 + */ + private Date startWorkDate; + + /** + * 户籍地址 + */ + private String householdAddress; + + /** + * 籍贯 + */ + private String nativePlace; + + /** + * 现居地 + */ + private String currentAddress; + + /** + * 是否党员:1是 0否 + */ + private Long isPartyMember; + + /** + * 政治面貌 + */ + private String politicalStatus; + + /** + * 婚姻状态:1已婚 0未婚 + */ + private Long maritalStatus; + + /** + * 学历(字典) + */ + private String education; + + /** + * 健康状态/体检结论(字典) + */ + private String healthStatus; + + /** + * 出生日期 + */ + private Date birthDate; + + /** + * 身份证号 + */ + private String idCardNo; + + /** + * 身份证到期日期 + */ + private Date idCardExpireDate; + + /** + * 身份证长期有效:1是 0否 + */ + private Long idCardLongTerm; + + /** + * 身份证正面URL + */ + private String idcardFrontUrl; + + /** + * 身份证反面URL + */ + private String idcardBackUrl; + + /** + * 寸照URL + */ + private String portraitUrl; + + /** + * 电子签名URL + */ + private String signatureUrl; + + /** + * 诚信承诺到期日期 + */ + private Date integrityPromiseExpireDate; + + /** + * 诚信考核等级(字典) + */ + private String integrityLevel; + + /** + * 诚信承诺书附件URL + */ + private String integrityPromiseAttachmentUrl; + + /** + * 人事意见 + */ + private String hrOpinion; + + /** + * 部门意见 + */ + private String deptOpinion; + + /** + * 公司领导意见 + */ + private String leaderOpinion; + + /** + * 处分或表彰说明 + */ + private String rewardOrPunishment; + + /** + * 签名 + */ + private String signature; + + /** + * 状态:1=正常 0=禁用 + */ + private Long status; + + /** + * 审核状态:0=待审核 1=审核通过 2=审核驳回 + */ + private Integer auditStatus; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 驾驶证初次领证日期 + */ + private Date driverLicenseFirstIssueDate; + + /** + * 驾驶证到期时间 + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS) + private Date driverLicenseExpireDate; + + /** + * 驾驶证是否长期 0否 1是 + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS) + private Long driverLicenseLongTerm; + + /** + * 准驾车型 + */ + private String driverLicenseVehicleType; + + /** + * 驾驶证号 + */ + private String driverLicenseNo; + + /** + * 档案号 + */ + private String driverLicenseArchiveNo; + + /** + * 驾驶证发证机关 + */ + private String driverLicenseOrg; + + /** + * 驾驶证主页图片地址 + */ + private String driverLicenseMainImg; + + /** + * 驾驶证副页图片地址 + */ + private String driverLicenseViceImg; + + /** + * 驾驶证其他附页图片地址 + */ + private String driverLicenseOtherImg; + + /** + * 从业资格证类别 + */ + private String qualificationType; + + /** + * 从业资格证号 + */ + private String qualificationNo; + + /** + * 从业资格证初次申领日期 + */ + private Date qualificationFirstIssueDate; + + /** + * 从业资格证有效起始日期 + */ + private Date qualificationValidStartDate; + + /** + * 从业资格证有效终止日期 + */ + private Date qualificationValidEndDate; + + /** + * 从业资格证发证机关 + */ + private String qualificationOrg; + + /** + * 从业资格证图片地址 + */ + private String qualificationImg; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/DriverRegisterBo.java b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/DriverRegisterBo.java new file mode 100644 index 0000000..df61ee6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/DriverRegisterBo.java @@ -0,0 +1,41 @@ +package com.hotwj.platform.driverManagement.driver.domain.bo; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +/** + * 驾驶员邀请码注册业务对象 + */ +@Data +public class DriverRegisterBo { + + /** + * 手机号码 + */ + @NotBlank(message = "手机号码不能为空") + private String phone; + + /** + * 密码 + */ + @NotBlank(message = "密码不能为空") + @Size(min = 6, message = "密码长度不能低于6位") + private String password; + + /** + * 企业ID + */ + @NotNull(message = "企业ID不能为空") + private Long companyId; + + @NotNull(message = "人员类型不能为空") + private String personType; + + /** + * 邀请码 + */ + @NotBlank(message = "邀请码不能为空") + private String inviteCode; +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/HotDriverBo.java b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/HotDriverBo.java new file mode 100644 index 0000000..3e975ac --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/bo/HotDriverBo.java @@ -0,0 +1,355 @@ +package com.hotwj.platform.driverManagement.driver.domain.bo; + +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.driverManagement.driverFamilyMember.domain.bo.HotDriverFamilyMemberBo; +import com.hotwj.platform.driverManagement.driverHealthReport.domain.bo.HotDriverHealthReportBo; +import com.hotwj.platform.driverManagement.driverLaborContract.domain.bo.HotDriverLaborContractBo; +import com.hotwj.platform.driverManagement.driverWorkExperience.domain.bo.HotDriverWorkExperienceBo; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; +import java.util.List; + +/** + * 驾驶员基本信息业务对象 hot_driver + * + * @author shihongwei + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotDriver.class, reverseConvertGenerate = false) +public class HotDriverBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private String id; + + private List ids; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆Id + */ + private String vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 入职日期 + */ +// @NotNull(message = "入职日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date entryDate; + + /** + * 姓名 + */ + @NotBlank(message = "姓名不能为空", groups = {AddGroup.class, EditGroup.class}) + private String name; + + /** + * 人员类型(字典/下拉) + */ + private String personType; + + /** + * 驾驶年限(年) + */ + private Long drivingYears; + + /** + * 手机号码 + */ + @NotBlank(message = "手机号码不能为空", groups = {AddGroup.class, EditGroup.class}) + private String phone; + + /** + * 性别 + */ + @NotBlank(message = "性别不能为空", groups = {AddGroup.class, EditGroup.class}) + private String gender; + + /** + * 民族 + */ + @NotBlank(message = "民族不能为空", groups = {AddGroup.class, EditGroup.class}) + private String nation; + + /** + * 参加工作时间 + */ + private Date startWorkDate; + + /** + * 户籍地址 + */ + private String householdAddress; + + /** + * 籍贯 + */ + private String nativePlace; + + /** + * 现居地 + */ + private String currentAddress; + + /** + * 是否党员:1是 0否 + */ +// @NotNull(message = "是否党员:1是 0否不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isPartyMember; + + /** + * 政治面貌 + */ + private String politicalStatus; + + /** + * 婚姻状态:1已婚 0未婚 + */ +// @NotNull(message = "婚姻状态:1已婚 0未婚不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long maritalStatus; + + /** + * 学历(字典) + */ + private String education; + + /** + * 健康状态/体检结论(字典) + */ + private String healthStatus; + + /** + * 出生日期 + */ + private Date birthDate; + + /** + * 身份证号 + */ + @NotBlank(message = "身份证号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String idCardNo; + + /** + * 身份证到期日期 + */ + private Date idCardExpireDate; + + /** + * 身份证长期有效:1是 0否 + */ + private Long idCardLongTerm; + + /** + * 身份证正面URL + */ + private String idcardFrontUrl; + + /** + * 身份证反面URL + */ + private String idcardBackUrl; + + /** + * 寸照URL + */ + private String portraitUrl; + + /** + * 电子签名URL + */ + private String signatureUrl; + + /** + * 诚信承诺到期日期 + */ + private Date integrityPromiseExpireDate; + + /** + * 诚信考核等级(字典) + */ + private String integrityLevel; + + /** + * 诚信承诺书附件URL + */ + private String integrityPromiseAttachmentUrl; + + /** + * 人事意见 + */ + private String hrOpinion; + + /** + * 部门意见 + */ + private String deptOpinion; + + /** + * 公司领导意见 + */ + private String leaderOpinion; + + /** + * 处分或表彰说明 + */ + private String rewardOrPunishment; + + /** + * 签名 + */ + private String signature; + + /** + * 状态:1=正常 0=禁用 + */ + private Long status; + + /** + * 审核状态:0=待审核 1=审核通过 2=审核驳回 + */ + private Integer auditStatus; + + /** + * 邀请码(非数据库字段) + */ + private String inviteCode; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + /** + * 驾驶证初次领证日期 + */ +// @NotNull(message = "驾驶证初次领证日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date driverLicenseFirstIssueDate; + + /** + * 驾驶证到期时间 + */ +// @NotNull(message = "驾驶证到期时间不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date driverLicenseExpireDate; + + /** + * 驾驶证是否长期 0否 1是 + */ +// @NotNull(message = "驾驶证是否长期 0否 1是不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long driverLicenseLongTerm; + + /** + * 准驾车型 + */ +// @NotBlank(message = "准驾车型不能为空", groups = {AddGroup.class, EditGroup.class}) + private String driverLicenseVehicleType; + + /** + * 驾驶证号 + */ + private String driverLicenseNo; + + /** + * 档案号 + */ + private String driverLicenseArchiveNo; + + /** + * 驾驶证发证机关 + */ + private String driverLicenseOrg; + + /** + * 驾驶证主页图片地址 + */ + private String driverLicenseMainImg; + + /** + * 驾驶证副页图片地址 + */ + private String driverLicenseViceImg; + + /** + * 驾驶证其他附页图片地址 + */ + private String driverLicenseOtherImg; + + /** + * 从业资格证类别 + */ +// @NotBlank(message = "从业资格证类别不能为空", groups = {AddGroup.class, EditGroup.class}) + private String qualificationType; + + /** + * 从业资格证号 + */ +// @NotBlank(message = "从业资格证号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String qualificationNo; + + /** + * 从业资格证初次申领日期 + */ + private Date qualificationFirstIssueDate; + + /** + * 从业资格证有效起始日期 + */ + private Date qualificationValidStartDate; + + /** + * 从业资格证有效终止日期 + */ +// @NotNull(message = "从业资格证有效终止日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date qualificationValidEndDate; + + /** + * 从业资格证发证机关 + */ + private String qualificationOrg; + + /** + * 从业资格证图片地址 + */ + private String qualificationImg; + + + // 子表数据 + /** + * 驾驶员家庭成员列表 + */ + private List driverFamilyMemberList; + + /** + * 驾驶员工作经历列表 + */ + private List driverWorkExperienceList; + + /** + * 驾驶员劳动合同列表 + */ + private List driverLaborContractList; + + /** + * 驾驶员健康报告列表 + */ + private List driverHealthReportList; +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/domain/vo/HotDriverCategoryStatVo.java b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/vo/HotDriverCategoryStatVo.java new file mode 100644 index 0000000..05f9da8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/vo/HotDriverCategoryStatVo.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.driverManagement.driver.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +@Data +public class HotDriverCategoryStatVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private String personType; + + private Long count; +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/domain/vo/HotDriverImportVo.java b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/vo/HotDriverImportVo.java new file mode 100644 index 0000000..cc88f43 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/vo/HotDriverImportVo.java @@ -0,0 +1,112 @@ +package com.hotwj.platform.driverManagement.driver.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +@Data +@AutoMapper(target = com.hotwj.platform.driverManagement.driver.domain.HotDriver.class) +public class HotDriverImportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty("姓名") + private String name; + + @ExcelProperty("手机号") + private String phone; + + @ExcelProperty("驾驶车牌号") + private String plateNumber; + + @ExcelProperty("身份证号") + private String idCardNo; + + @ExcelProperty("身份证到期时间") + private String idCardExpireDate; + + @ExcelProperty("驾驶证初次领证日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date driverLicenseFirstIssueDate; + + @ExcelProperty("准驾车型") + private String driverLicenseVehicleType; + + @ExcelProperty("驾驶证到期时间") + private String driverLicenseExpireDate; + + @ExcelProperty("发证机关") + private String driverLicenseOrg; + + @ExcelProperty(value = "性别", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_user_sex") + private String gender; + + @ExcelProperty(value = "民族", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "nation") + private String nation; + + @ExcelProperty(value = "从业资格证类别", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_category_qualification_certificate") + private String qualificationType; + + @ExcelProperty("从业资格证号") + private String qualificationNo; + + @ExcelProperty("从业资格证初领日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date qualificationFirstIssueDate; + + @ExcelProperty("从业资格证到期时间") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date qualificationValidEndDate; + + @ExcelProperty("从业资格证有效起始日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date qualificationValidStartDate; + + @ExcelProperty("从业资格证发证机关") + private String qualificationOrg; + + @ExcelProperty("出生日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date birthDate; + + @ExcelProperty("户籍地址") + private String householdAddress; + + @ExcelProperty("现居住地址") + private String currentAddress; + + @ExcelProperty(value = "学历", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_academic") + private String education; + + @ExcelProperty(value = "政治面貌", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_political_status") + private String politicalStatus; + + @ExcelProperty(value = "婚姻状况", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1=已婚,0=未婚") + private Long maritalStatus; + + @ExcelProperty(value = "健康状况", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_health") + private String healthStatus; + + @ExcelProperty("入职时间") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date entryDate; + + @ExcelProperty(value = "人员类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_person_type") + private String personType; +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/domain/vo/HotDriverLedgerExportVo.java b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/vo/HotDriverLedgerExportVo.java new file mode 100644 index 0000000..88403e9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/vo/HotDriverLedgerExportVo.java @@ -0,0 +1,71 @@ +package com.hotwj.platform.driverManagement.driver.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 驾驶员台账导出字段 + */ +@Data +@ExcelIgnoreUnannotated +public class HotDriverLedgerExportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty(value = "驾驶员姓名", index = 0) + private String name; + + @ExcelProperty(value = "手机号", index = 1) + private String phone; + + @ExcelProperty(value = "身份证号", index = 2) + private String idCardNo; + + @ExcelProperty(value = "入职时间", index = 3) + private Date entryDate; + + @ExcelProperty(value = "现居地址", index = 4) + private String currentAddress; + + @ExcelProperty(value = "车辆档案号", index = 5) + private String vehicleArchiveNo; + + @ExcelProperty(value = "车牌号", index = 6) + private String plateNumber; + + @ExcelProperty(value = "驾驶证号码", index = 7) + private String driverLicenseNo; + + @ExcelProperty(value = "驾驶证档案号", index = 8) + private String driverLicenseArchiveNo; + + @ExcelProperty(value = "准驾车型", index = 9) + private String driverLicenseVehicleType; + + @ExcelProperty(value = "驾驶证初领日期", index = 10) + private Date driverLicenseFirstIssueDate; + + @ExcelProperty(value = "驾驶证有效期", index = 11) + private Date driverLicenseExpireDate; + + @ExcelProperty(value = "从业证资格号", index = 12) + private String qualificationNo; + + @ExcelProperty(value = "从业资格类型", index = 13, converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_category_qualification_certificate") + private String qualificationType; + + @ExcelProperty(value = "从业证初领日期", index = 14) + private Date qualificationFirstIssueDate; + + @ExcelProperty(value = "从业证到期日期", index = 15) + private Date qualificationValidEndDate; +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/domain/vo/HotDriverVo.java b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/vo/HotDriverVo.java new file mode 100644 index 0000000..272810b --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/domain/vo/HotDriverVo.java @@ -0,0 +1,381 @@ +package com.hotwj.platform.driverManagement.driver.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 驾驶员基本信息视图对象 hot_driver + * + * @author shihongwei + * @date 2025-12-19 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotDriver.class) +public class HotDriverVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private String id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 车辆Id + */ + private String vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 入职日期 + */ + @ExcelProperty(value = "入职日期") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date entryDate; + + /** + * 姓名 + */ + @ExcelProperty(value = "姓名") + private String name; + + /** + * 人员类型(字典/下拉) + */ + @ExcelProperty(value = "人员类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_person_type") + private String personType; + + /** + * 驾驶年限(年) + */ + @ExcelProperty(value = "驾驶年限", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "年=") + private Long drivingYears; + + /** + * 手机号码 + */ + @ExcelProperty(value = "手机号码") + private String phone; + + /** + * 性别 + */ + @ExcelProperty(value = "性别", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_user_sex") + private String gender; + + /** + * 民族 + */ + @ExcelProperty(value = "民族", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "nation") + private String nation; + + /** + * 参加工作时间 + */ + @ExcelProperty(value = "参加工作时间") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date startWorkDate; + + /** + * 户籍地址 + */ + @ExcelProperty(value = "户籍地址") + private String householdAddress; + + /** + * 籍贯 + */ + @ExcelProperty(value = "籍贯") + private String nativePlace; + + /** + * 现居地 + */ + @ExcelProperty(value = "现居地") + private String currentAddress; + + /** + * 是否党员:1是 0否 + */ + private Long isPartyMember; + + /** + * 政治面貌 + */ + @ExcelProperty(value = "政治面貌", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_political_status") + private String politicalStatus; + + /** + * 婚姻状态:1已婚 0未婚 + */ + @ExcelProperty(value = "婚姻状态:1已婚 0未婚") + private Long maritalStatus; + + /** + * 学历(字典) + */ + @ExcelProperty(value = "学历", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_academic") + private String education; + + /** + * 健康状态/体检结论(字典) + */ + @ExcelProperty(value = "健康状态/体检结论", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_health") + private String healthStatus; + + /** + * 出生日期 + */ + @ExcelProperty(value = "出生日期") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date birthDate; + + /** + * 身份证号 + */ + @ExcelProperty(value = "身份证号") + private String idCardNo; + + /** + * 身份证到期日期 + */ + @ExcelProperty(value = "身份证到期日期") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date idCardExpireDate; + + /** + * 身份证长期有效:1是 0否 + */ + @ExcelProperty(value = "身份证长期有效:1是 0否") + private Long idCardLongTerm; + + /** + * 身份证正面URL + */ + @ExcelProperty(value = "身份证正面URL") + private String idcardFrontUrl; + + /** + * 身份证反面URL + */ + @ExcelProperty(value = "身份证反面URL") + private String idcardBackUrl; + + /** + * 寸照URL + */ + @ExcelProperty(value = "寸照URL") + private String portraitUrl; + + /** + * 电子签名URL + */ + @ExcelProperty(value = "电子签名URL") + private String signatureUrl; + + /** + * 诚信承诺到期日期 + */ + @ExcelProperty(value = "诚信承诺到期日期") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date integrityPromiseExpireDate; + + /** + * 诚信考核等级(字典) + */ + @ExcelProperty(value = "诚信考核等级", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "字=典") + private String integrityLevel; + + /** + * 诚信承诺书附件URL + */ + @ExcelProperty(value = "诚信承诺书附件URL") + private String integrityPromiseAttachmentUrl; + + /** + * 人事意见 + */ + @ExcelProperty(value = "人事意见") + private String hrOpinion; + + /** + * 部门意见 + */ + @ExcelProperty(value = "部门意见") + private String deptOpinion; + + /** + * 公司领导意见 + */ + @ExcelProperty(value = "公司领导意见") + private String leaderOpinion; + + /** + * 处分或表彰说明 + */ + @ExcelProperty(value = "处分或表彰说明") + private String rewardOrPunishment; + + /** + * 签名 + */ + @ExcelProperty(value = "签名") + private String signature; + + /** + * 状态:1=正常 0=禁用 + */ + @ExcelProperty(value = "状态:1=正常 0=禁用") + private Long status; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 驾驶证初次领证日期 + */ + @ExcelProperty(value = "驾驶证初次领证日期") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date driverLicenseFirstIssueDate; + + /** + * 驾驶证到期时间 + */ + @ExcelProperty(value = "驾驶证到期时间") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date driverLicenseExpireDate; + + /** + * 驾驶证是否长期 0否 1是 + */ + @ExcelProperty(value = "驾驶证是否长期 0否 1是") + private Long driverLicenseLongTerm; + + /** + * 准驾车型 + */ + @ExcelProperty(value = "准驾车型") + private String driverLicenseVehicleType; + + /** + * 驾驶证号 + */ + @ExcelProperty(value = "驾驶证号") + private String driverLicenseNo; + + /** + * 档案号 + */ + @ExcelProperty(value = "档案号") + private String driverLicenseArchiveNo; + + /** + * 驾驶证发证机关 + */ + @ExcelProperty(value = "驾驶证发证机关") + private String driverLicenseOrg; + + /** + * 驾驶证主页图片地址 + */ + @ExcelProperty(value = "驾驶证主页图片地址") + private String driverLicenseMainImg; + + /** + * 驾驶证副页图片地址 + */ + @ExcelProperty(value = "驾驶证副页图片地址") + private String driverLicenseViceImg; + + /** + * 驾驶证其他附页图片地址 + */ + @ExcelProperty(value = "驾驶证其他附页图片地址") + private String driverLicenseOtherImg; + + /** + * 从业资格证类别 + */ + @ExcelProperty(value = "从业资格证类别", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_category_qualification_certificate") + private String qualificationType; + + /** + * 从业资格证号 + */ + @ExcelProperty(value = "从业资格证号") + private String qualificationNo; + + /** + * 从业资格证初次申领日期 + */ + @ExcelProperty(value = "从业资格证初次申领日期") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date qualificationFirstIssueDate; + + /** + * 从业资格证有效起始日期 + */ + @ExcelProperty(value = "从业资格证有效起始日期") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date qualificationValidStartDate; + + /** + * 从业资格证有效终止日期 + */ + @ExcelProperty(value = "从业资格证有效终止日期") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date qualificationValidEndDate; + + /** + * 从业资格证发证机关 + */ + @ExcelProperty(value = "从业资格证发证机关") + private String qualificationOrg; + + /** + * 从业资格证图片地址 + */ + @ExcelProperty(value = "从业资格证图片地址") + private String qualificationImg; + + /** + * 审核状态:0=待审核 1=审核通过 2=审核驳回 + */ + private Integer auditStatus; +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/mapper/HotDriverMapper.java b/src/main/java/com/hotwj/platform/driverManagement/driver/mapper/HotDriverMapper.java new file mode 100644 index 0000000..03095dc --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/mapper/HotDriverMapper.java @@ -0,0 +1,21 @@ +package com.hotwj.platform.driverManagement.driver.mapper; + +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverCategoryStatVo; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +import java.util.List; + +/** + * 驾驶员基本信息Mapper接口 + * + * @author shihongwei + * @date 2025-12-19 + */ + @Mapper +public interface HotDriverMapper extends BaseMapperPlus { + + List selectDriverCategoryStat(@org.apache.ibatis.annotations.Param("companyId") Long companyId); +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/service/IHotDriverService.java b/src/main/java/com/hotwj/platform/driverManagement/driver/service/IHotDriverService.java new file mode 100644 index 0000000..e2f149a --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/service/IHotDriverService.java @@ -0,0 +1,113 @@ +package com.hotwj.platform.driverManagement.driver.service; + +import com.hotwj.platform.driverManagement.driver.domain.bo.DriverRegisterBo; +import com.hotwj.platform.driverManagement.driver.domain.bo.HotDriverBo; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverImportVo; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 驾驶员基本信息Service接口 + * + * @author shihongwei + * @date 2025-12-19 + */ +public interface IHotDriverService { + + /** + * 查询驾驶员基本信息 + * + * @param id 主键 + * @return 驾驶员基本信息 + */ + HotDriverVo queryById(String id); + + /** + * 分页查询驾驶员基本信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员基本信息分页列表 + */ + TableDataInfo queryPageList(HotDriverBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的驾驶员基本信息列表 + * + * @param bo 查询条件 + * @return 驾驶员基本信息列表 + */ + List queryList(HotDriverBo bo); + + /** + * 新增驾驶员基本信息 + * + * @param bo 驾驶员基本信息 + * @return 是否新增成功 + */ + Boolean insertByBo(HotDriverBo bo); + + /** + * 修改驾驶员基本信息 + * + * @param bo 驾驶员基本信息 + * @return 是否修改成功 + */ + Boolean updateByBo(HotDriverBo bo); + + /** + * 审核驾驶员账号 + * + * @param driverId 驾驶员ID + * @param auditStatus 审核状态 + * @return 是否成功 + */ + Boolean auditDriver(String driverId, Integer auditStatus); + + /** + * 导入驾驶员数据 + * + * @param driverList 驾驶员数据列表 + * @param companyId 公司ID + * @return 导入结果 + */ + String importData(List driverList, Long companyId); + + /** + * 校验并批量删除驾驶员基本信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 统计驾驶员准驾车型数量 + * + * @param companyId 公司ID + * @return 统计结果 + */ + java.util.List queryDriverCategoryStat(Long companyId); + + /** + * 通过邀请码注册驾驶员 + * + * @param bo 注册信息 + * @return 注册是否成功 + */ + Boolean registerByInviteCode(DriverRegisterBo bo); + + /** + * 更新驾驶员存照 + * + * @param businessUserId + * @param ossId + * @return + */ + boolean updateUserFaceInitialOssId(String businessUserId, Long ossId); +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driver/service/impl/HotDriverServiceImpl.java b/src/main/java/com/hotwj/platform/driverManagement/driver/service/impl/HotDriverServiceImpl.java new file mode 100644 index 0000000..a5b6a28 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driver/service/impl/HotDriverServiceImpl.java @@ -0,0 +1,980 @@ +package com.hotwj.platform.driverManagement.driver.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.common.utils.PasswordUtils; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.driverManagement.driver.domain.bo.DriverRegisterBo; +import com.hotwj.platform.driverManagement.driver.domain.bo.HotDriverBo; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverImportVo; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.driverManagement.driverAnnualAssessment.domain.HotDriverAnnualAssessment; +import com.hotwj.platform.driverManagement.driverAnnualAssessment.mapper.HotDriverAnnualAssessmentMapper; +import com.hotwj.platform.driverManagement.driverFamilyMember.domain.HotDriverFamilyMember; +import com.hotwj.platform.driverManagement.driverFamilyMember.mapper.HotDriverFamilyMemberMapper; +import com.hotwj.platform.driverManagement.driverGroup.domain.HotDriverGroup; +import com.hotwj.platform.driverManagement.driverGroup.mapper.HotDriverGroupMapper; +import com.hotwj.platform.driverManagement.driverHealthReport.domain.HotDriverHealthReport; +import com.hotwj.platform.driverManagement.driverHealthReport.mapper.HotDriverHealthReportMapper; +import com.hotwj.platform.driverManagement.driverLaborContract.domain.HotDriverLaborContract; +import com.hotwj.platform.driverManagement.driverLaborContract.mapper.HotDriverLaborContractMapper; +import com.hotwj.platform.driverManagement.driverRewardPunishment.domain.HotDriverRewardPunishment; +import com.hotwj.platform.driverManagement.driverRewardPunishment.mapper.HotDriverRewardPunishmentMapper; +import com.hotwj.platform.driverManagement.driverSkillAssessment.domain.HotDriverSkillAssessment; +import com.hotwj.platform.driverManagement.driverSkillAssessment.mapper.HotDriverSkillAssessmentMapper; +import com.hotwj.platform.driverManagement.driverWorkExperience.domain.HotDriverWorkExperience; +import com.hotwj.platform.driverManagement.driverWorkExperience.mapper.HotDriverWorkExperienceMapper; +import com.hotwj.platform.driverManagement.inviteLog.domain.HotDriverInviteLog; +import com.hotwj.platform.driverManagement.inviteLog.mapper.HotDriverInviteLogMapper; +import com.hotwj.platform.noticeManagerment.noticeSignDocument.service.IHotNoticeSignDocumentService; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.company.domain.SysUserLoginPort; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle; +import com.hotwj.platform.resourceManagement.vehicleManagement.mapper.HotVehicleMapper; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.ValidatorUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.system.domain.bo.SysUserBo; +import org.dromara.system.domain.vo.SysUserVo; +import org.dromara.system.service.ISysUserService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 驾驶员基本信息Service业务层处理 + * + * @author shihongwei + * @date 2025-12-19 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotDriverServiceImpl implements IHotDriverService { + + private final HotDriverMapper baseMapper; + private final HotDriverFamilyMemberMapper driverFamilyMemberMapper; + private final HotDriverWorkExperienceMapper driverWorkExperienceMapper; + private final HotDriverLaborContractMapper driverLaborContractMapper; + private final HotDriverHealthReportMapper driverHealthReportMapper; + private final ISysCompanyService sysCompanyService; + private final HotDriverInviteLogMapper inviteLogMapper; + private final ISysUserService sysUserService; + private final ISysUserLoginPortService sysUserLoginPortService; + private final HotVehicleMapper vehicleMapper; + private final HotDriverMapper driverMapper; + private final HotCompanySafetyManagerMapper managerMapper; + private final HotDriverGroupMapper groupMapper; + private final IHotSystemNotificationService notificationService; + private final HotDriverRewardPunishmentMapper rewardPunishmentMapper; + private final HotDriverSkillAssessmentMapper skillAssessmentMapper; + private final HotDriverAnnualAssessmentMapper annualAssessmentMapper; + private final IHotNoticeSignDocumentService noticeSignDocumentService; + + /** + * 查询驾驶员基本信息 + * + * @param id 主键 + * @return 驾驶员基本信息 + */ + @Override + public HotDriverVo queryById(String id) { + return baseMapper.selectVoById(id); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean registerByInviteCode(DriverRegisterBo bo) { + // 1. 校验邀请码 + SysCompanyVo company = sysCompanyService.queryByInviteCode(bo.getInviteCode()); + if (company == null) { + throw new ServiceException("邀请码无效"); + } + if (!company.getId().equals(bo.getCompanyId())) { + throw new ServiceException("邀请码与企业不匹配"); + } + + // 2. 校验手机号是否已存在 + SysUserVo existingUser = sysUserService.selectUserByPhonenumber(bo.getPhone()); + if (existingUser != null) { + throw new ServiceException("手机号已存在"); + } + HotDriver existingDriver = baseMapper.selectOne(Wrappers.lambdaQuery().eq(HotDriver::getPhone, bo.getPhone())); + if (existingDriver != null) { + throw new ServiceException("驾驶员信息已存在"); + } + + // 3. 创建驾驶员信息 + HotDriver driver = new HotDriver(); + String driverId = IdWorker.get32UUID(); + driver.setId(driverId); + driver.setCompanyId(bo.getCompanyId()); + driver.setPhone(bo.getPhone()); + driver.setPersonType(bo.getPersonType()); + // 初始姓名暂存为手机号,待完善 +// driver.setName(bo.getPhone()); + driver.setAuditStatus(0); // 待审核 + driver.setStatus(1L); + driver.setCreateTime(new Date()); + // 其他必填项需后续完善 + int insert = baseMapper.insert(driver); + + if (insert > 0) { + // 4. 同步驾驶员信息到用户表 + syncDriverToSysUser(driver, bo.getPassword()); + // 5. 根据默认新进人员配置自动下发签订文件 + noticeSignDocumentService.issueDefaultForNewDriver(driver); + // 5. 记录邀请日志 + HotDriverInviteLog log = new HotDriverInviteLog(); + log.setInviteCode(bo.getInviteCode()); + log.setCompanyId(company.getId()); + log.setDriverId(driverId); + log.setDriverName(driver.getName()); + log.setDriverPhone(driver.getPhone()); + log.setCreateTime(new Date()); + inviteLogMapper.insert(log); + } + + return true; + } + + @Override + public boolean updateUserFaceInitialOssId(String businessUserId, Long ossId) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(HotDriver::getPortraitUrl, ossId) + .eq(HotDriver::getId, businessUserId)) > 0; + } + + /** + * 分页查询驾驶员基本信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员基本信息分页列表 + */ + @Override + public TableDataInfo queryPageList(HotDriverBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的驾驶员基本信息列表 + * + * @param bo 查询条件 + * @return 驾驶员基本信息列表 + */ + @Override + public List queryList(HotDriverBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotDriverBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotDriver::getCreateTime); + lqw.in(CollUtil.isNotEmpty(bo.getIds()), HotDriver::getId, bo.getIds()); + lqw.eq(bo.getCompanyId() != null, HotDriver::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getEntryDate() != null, HotDriver::getEntryDate, bo.getEntryDate()); + lqw.like(StringUtils.isNotBlank(bo.getName()), HotDriver::getName, bo.getName()); + lqw.eq(StringUtils.isNotBlank(bo.getPersonType()), HotDriver::getPersonType, bo.getPersonType()); + lqw.eq(bo.getDrivingYears() != null, HotDriver::getDrivingYears, bo.getDrivingYears()); + lqw.like(StringUtils.isNotBlank(bo.getPhone()), HotDriver::getPhone, bo.getPhone()); + lqw.eq(StringUtils.isNotBlank(bo.getGender()), HotDriver::getGender, bo.getGender()); + lqw.eq(StringUtils.isNotBlank(bo.getNation()), HotDriver::getNation, bo.getNation()); + lqw.eq(bo.getStartWorkDate() != null, HotDriver::getStartWorkDate, bo.getStartWorkDate()); + lqw.eq(StringUtils.isNotBlank(bo.getHouseholdAddress()), HotDriver::getHouseholdAddress, bo.getHouseholdAddress()); + lqw.eq(StringUtils.isNotBlank(bo.getNativePlace()), HotDriver::getNativePlace, bo.getNativePlace()); + lqw.eq(StringUtils.isNotBlank(bo.getCurrentAddress()), HotDriver::getCurrentAddress, bo.getCurrentAddress()); + lqw.eq(bo.getIsPartyMember() != null, HotDriver::getIsPartyMember, bo.getIsPartyMember()); + lqw.eq(bo.getMaritalStatus() != null, HotDriver::getMaritalStatus, bo.getMaritalStatus()); + lqw.eq(StringUtils.isNotBlank(bo.getEducation()), HotDriver::getEducation, bo.getEducation()); + lqw.eq(StringUtils.isNotBlank(bo.getHealthStatus()), HotDriver::getHealthStatus, bo.getHealthStatus()); + lqw.eq(bo.getBirthDate() != null, HotDriver::getBirthDate, bo.getBirthDate()); + lqw.like(StringUtils.isNotBlank(bo.getIdCardNo()), HotDriver::getIdCardNo, bo.getIdCardNo()); + lqw.eq(bo.getIdCardExpireDate() != null, HotDriver::getIdCardExpireDate, bo.getIdCardExpireDate()); + lqw.eq(bo.getIdCardLongTerm() != null, HotDriver::getIdCardLongTerm, bo.getIdCardLongTerm()); + lqw.eq(StringUtils.isNotBlank(bo.getIdcardFrontUrl()), HotDriver::getIdcardFrontUrl, bo.getIdcardFrontUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getIdcardBackUrl()), HotDriver::getIdcardBackUrl, bo.getIdcardBackUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getPortraitUrl()), HotDriver::getPortraitUrl, bo.getPortraitUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getSignatureUrl()), HotDriver::getSignatureUrl, bo.getSignatureUrl()); + lqw.eq(bo.getIntegrityPromiseExpireDate() != null, HotDriver::getIntegrityPromiseExpireDate, bo.getIntegrityPromiseExpireDate()); + lqw.eq(StringUtils.isNotBlank(bo.getIntegrityLevel()), HotDriver::getIntegrityLevel, bo.getIntegrityLevel()); + lqw.eq(StringUtils.isNotBlank(bo.getIntegrityPromiseAttachmentUrl()), HotDriver::getIntegrityPromiseAttachmentUrl, bo.getIntegrityPromiseAttachmentUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getHrOpinion()), HotDriver::getHrOpinion, bo.getHrOpinion()); + lqw.eq(StringUtils.isNotBlank(bo.getDeptOpinion()), HotDriver::getDeptOpinion, bo.getDeptOpinion()); + lqw.eq(StringUtils.isNotBlank(bo.getLeaderOpinion()), HotDriver::getLeaderOpinion, bo.getLeaderOpinion()); + lqw.eq(StringUtils.isNotBlank(bo.getRewardOrPunishment()), HotDriver::getRewardOrPunishment, bo.getRewardOrPunishment()); + lqw.eq(StringUtils.isNotBlank(bo.getSignature()), HotDriver::getSignature, bo.getSignature()); + lqw.eq(bo.getStatus() != null, HotDriver::getStatus, bo.getStatus()); + lqw.eq(bo.getAuditStatus() != null, HotDriver::getAuditStatus, bo.getAuditStatus()); + lqw.eq(bo.getIsDeleted() != null, HotDriver::getIsDeleted, bo.getIsDeleted()); + lqw.eq(bo.getDriverLicenseFirstIssueDate() != null, HotDriver::getDriverLicenseFirstIssueDate, bo.getDriverLicenseFirstIssueDate()); + lqw.eq(bo.getDriverLicenseExpireDate() != null, HotDriver::getDriverLicenseExpireDate, bo.getDriverLicenseExpireDate()); + lqw.eq(bo.getDriverLicenseLongTerm() != null, HotDriver::getDriverLicenseLongTerm, bo.getDriverLicenseLongTerm()); + lqw.eq(StringUtils.isNotBlank(bo.getDriverLicenseVehicleType()), HotDriver::getDriverLicenseVehicleType, bo.getDriverLicenseVehicleType()); + lqw.eq(StringUtils.isNotBlank(bo.getDriverLicenseNo()), HotDriver::getDriverLicenseNo, bo.getDriverLicenseNo()); + lqw.eq(StringUtils.isNotBlank(bo.getDriverLicenseArchiveNo()), HotDriver::getDriverLicenseArchiveNo, bo.getDriverLicenseArchiveNo()); + lqw.eq(StringUtils.isNotBlank(bo.getDriverLicenseOrg()), HotDriver::getDriverLicenseOrg, bo.getDriverLicenseOrg()); + lqw.eq(StringUtils.isNotBlank(bo.getDriverLicenseMainImg()), HotDriver::getDriverLicenseMainImg, bo.getDriverLicenseMainImg()); + lqw.eq(StringUtils.isNotBlank(bo.getDriverLicenseViceImg()), HotDriver::getDriverLicenseViceImg, bo.getDriverLicenseViceImg()); + lqw.eq(StringUtils.isNotBlank(bo.getDriverLicenseOtherImg()), HotDriver::getDriverLicenseOtherImg, bo.getDriverLicenseOtherImg()); + lqw.eq(StringUtils.isNotBlank(bo.getQualificationType()), HotDriver::getQualificationType, bo.getQualificationType()); + lqw.eq(StringUtils.isNotBlank(bo.getQualificationNo()), HotDriver::getQualificationNo, bo.getQualificationNo()); + lqw.eq(bo.getQualificationFirstIssueDate() != null, HotDriver::getQualificationFirstIssueDate, bo.getQualificationFirstIssueDate()); + lqw.eq(bo.getQualificationValidStartDate() != null, HotDriver::getQualificationValidStartDate, bo.getQualificationValidStartDate()); + lqw.eq(bo.getQualificationValidEndDate() != null, HotDriver::getQualificationValidEndDate, bo.getQualificationValidEndDate()); + lqw.eq(StringUtils.isNotBlank(bo.getQualificationOrg()), HotDriver::getQualificationOrg, bo.getQualificationOrg()); + lqw.eq(StringUtils.isNotBlank(bo.getQualificationImg()), HotDriver::getQualificationImg, bo.getQualificationImg()); + return lqw; + } + + /** + * 新增驾驶员基本信息 + * + * @param bo 驾驶员基本信息 + * @return 是否新增成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(HotDriverBo bo) { + // 校验手机号 + checkPhoneUnique(bo.getPhone(), null); + + HotDriver add = MapstructUtils.convert(bo, HotDriver.class); + String id = IdWorker.get32UUID(); + add.setId(id); + if (add.getAuditStatus() == null) { + add.setAuditStatus(0); + } + if (add.getStatus() == null) { + add.setStatus(1L); + } + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + + insertDriverFamilyMembers(bo, id); + insertDriverWorkExperiences(bo, id); + insertDriverLaborContracts(bo, id); + insertDriverHealthReports(bo, id); + + // 创建关联账号或同步信息 + // 无论是否有邀请码,都尝试同步/创建账号 + if (flag) { + syncDriverToSysUser(add, null); + noticeSignDocumentService.issueDefaultForNewDriver(add); + } + + return flag; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean auditDriver(String driverId, Integer auditStatus) { + HotDriver driver = baseMapper.selectById(driverId); + if (driver == null) { + throw new ServiceException("驾驶员不存在"); + } + Integer oldAuditStatus = driver.getAuditStatus(); + driver.setAuditStatus(auditStatus); + boolean flag = baseMapper.updateById(driver) > 0; + + if (flag) { + // 同步账号状态 + syncDriverToSysUser(driver, null); + + // 检查审核状态变更:变成未通过(0) + boolean isAuditRejected = (oldAuditStatus == null || oldAuditStatus != 0) && (auditStatus != null && auditStatus == 0); + if (isAuditRejected) { + handleDriverResignation(driverId, driver.getPhone(), driver.getCompanyId()); + } + } + + return flag; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public String importData(List driverList, Long companyId) { + if (CollUtil.isEmpty(driverList)) { + throw new ServiceException("导入数据不能为空!"); + } + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + Set batchIdCards = new HashSet<>(); + + for (HotDriverImportVo vo : driverList) { + try { + ValidatorUtils.validate(vo); + String idCardNo = vo.getIdCardNo(); + if (StringUtils.isBlank(idCardNo)) { + failureNum++; + failureMsg.append("
").append(failureNum).append("、缺少身份证号,不能导入"); + continue; + } + if (!batchIdCards.add(idCardNo)) { + failureNum++; + failureMsg.append("
").append(failureNum).append("、身份证号 ").append(idCardNo).append(" 在本次导入中重复,不能导入重复的人员信息"); + continue; + } + boolean exists = baseMapper.selectCount(new LambdaQueryWrapper() + .eq(HotDriver::getIdCardNo, idCardNo) + .eq(HotDriver::getCompanyId, companyId) + .eq(HotDriver::getIsDeleted, 0L)) > 0; + if (exists) { + failureNum++; + failureMsg.append("
").append(failureNum).append("、身份证号 ").append(idCardNo).append(" 已存在,不能导入重复的人员信息"); + continue; + } + + HotDriver driver = buildDriverFromImport(vo); + + // 校验手机号唯一性 + if (StringUtils.isNotBlank(driver.getPhone())) { + try { + checkPhoneUnique(driver.getPhone(), null); + } catch (ServiceException e) { + failureNum++; + failureMsg.append("
").append(failureNum).append("、手机号 ").append(driver.getPhone()).append(" 已存在,不能导入重复的人员信息"); + continue; + } + } + + String id = IdWorker.get32UUID(); + driver.setId(id); + driver.setCompanyId(companyId); + driver.setStatus(1L); + driver.setIsDeleted(0L); + driver.setCreateTime(new Date()); + driver.setAuditStatus(0); + validEntityBeforeSave(driver); + + baseMapper.insert(driver); + // 创建关联账号或同步信息 + syncDriverToSysUser(driver, null); + noticeSignDocumentService.issueDefaultForNewDriver(driver); + successNum++; + String nameOrCard = StringUtils.blankToDefault(driver.getName(), idCardNo); + successMsg.append("
").append(successNum).append("、驾驶员 ").append(nameOrCard).append(" 导入成功"); + } catch (ConstraintViolationException e) { + failureNum++; + String msg = e.getConstraintViolations().stream() + .map(ConstraintViolation::getMessage) + .collect(Collectors.joining(", ")); + failureMsg.append("
").append(failureNum).append("、身份证号 ") + .append(vo.getIdCardNo()).append(" 导入失败:").append(msg); + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、身份证号 " + vo.getIdCardNo() + " 导入失败:"; + failureMsg.append(msg).append(e.getMessage()); + log.error(msg, e); + } + } + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条校验错误,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + + private HotDriver buildDriverFromImport(HotDriverImportVo vo) { + HotDriver driver = new HotDriver(); + driver.setName(vo.getName()); + driver.setPhone(vo.getPhone()); + driver.setPlateNumber(vo.getPlateNumber()); + driver.setIdCardNo(vo.getIdCardNo()); + setIdCardExpire(driver, vo.getIdCardExpireDate()); + driver.setDriverLicenseFirstIssueDate(vo.getDriverLicenseFirstIssueDate()); + driver.setDriverLicenseVehicleType(vo.getDriverLicenseVehicleType()); + setDriverLicenseExpire(driver, vo.getDriverLicenseExpireDate()); + driver.setDriverLicenseOrg(vo.getDriverLicenseOrg()); + driver.setGender(vo.getGender()); + driver.setNation(vo.getNation()); + driver.setQualificationType(vo.getQualificationType()); + driver.setQualificationNo(vo.getQualificationNo()); + driver.setQualificationFirstIssueDate(vo.getQualificationFirstIssueDate()); + driver.setQualificationValidEndDate(vo.getQualificationValidEndDate()); + driver.setQualificationValidStartDate(vo.getQualificationValidStartDate()); + driver.setQualificationOrg(vo.getQualificationOrg()); + driver.setBirthDate(vo.getBirthDate()); + driver.setHouseholdAddress(vo.getHouseholdAddress()); + driver.setCurrentAddress(vo.getCurrentAddress()); + driver.setEducation(vo.getEducation()); + driver.setPoliticalStatus(vo.getPoliticalStatus()); + driver.setMaritalStatus(vo.getMaritalStatus()); + driver.setHealthStatus(vo.getHealthStatus()); + driver.setEntryDate(vo.getEntryDate()); + driver.setPersonType(vo.getPersonType()); + return driver; + } + + private void setIdCardExpire(HotDriver driver, String rawValue) { + String text = normalizeCellText(rawValue); + if (text == null) { + driver.setIdCardExpireDate(null); + driver.setIdCardLongTerm(null); + return; + } + if (isLongTerm(text)) { + driver.setIdCardExpireDate(null); + driver.setIdCardLongTerm(1L); + return; + } + driver.setIdCardExpireDate(parseDateText(text, "身份证到期时间")); + driver.setIdCardLongTerm(0L); + } + + private void setDriverLicenseExpire(HotDriver driver, String rawValue) { + String text = normalizeCellText(rawValue); + if (text == null) { + driver.setDriverLicenseExpireDate(null); + driver.setDriverLicenseLongTerm(null); + return; + } + if (isLongTerm(text)) { + driver.setDriverLicenseExpireDate(null); + driver.setDriverLicenseLongTerm(1L); + return; + } + driver.setDriverLicenseExpireDate(parseDateText(text, "驾驶证到期时间")); + driver.setDriverLicenseLongTerm(0L); + } + + private Date parseDateText(String text, String fieldName) { + try { + return DateUtil.parse(text).toJdkDate(); + } catch (Exception e) { + throw new ServiceException(fieldName + "格式错误,值:" + text + ",请填写yyyy-MM-dd或“长期”"); + } + } + + private boolean isLongTerm(String text) { + return "长期".equals(text) + || "长期有效".equals(text) + || "长期/有效".equals(text) + || "长期有效期".equals(text); + } + + private String normalizeCellText(String text) { + if (text == null) { + return null; + } + String value = text.trim(); + return value.isEmpty() ? null : value; + } + + /** + * 同步驾驶员信息到系统用户 + */ + private void syncDriverToSysUser(HotDriver driver, String registerPassword) { + if (driver == null || StringUtils.isBlank(driver.getPhone())) { + return; + } + + SysUserVo existingUser = sysUserService.selectUserByPhonenumber(driver.getPhone()); + Long userId = null; + + if (existingUser != null) { + // 用户已存在,更新信息 + SysUserBo userUpdate = new SysUserBo(); + userUpdate.setUserId(existingUser.getUserId()); + userUpdate.setUserName(driver.getName()); + userUpdate.setPhonenumber(driver.getPhone()); + // 用户始终不禁用,只禁用端口 + userUpdate.setStatus("0"); + userUpdate.setPortEnable(driver.getStatus() != null && driver.getStatus() == 1L); // 只要启用了就能登录 + // 确保绑定驾驶员端口 + userUpdate.setCompanyId(driver.getCompanyId()); + userUpdate.setLoginPort(Collections.singletonList(ISysUserLoginPortService.DRIVER_PORT)); + userUpdate.setRoleIds(new Long[]{ISysUserLoginPortService.DRIVER_ROLE_ID}); + // 确保设置子密码(如果需要)- updateUser可能不会处理子密码初始化,所以需要单独检查 + + // 此处使用 updateUser,旨在涵盖管理端的完整更新逻辑 + sysUserService.updateUser(userUpdate, false); + userId = existingUser.getUserId(); + } else { + // 用户不存在,创建新用户 + SysUserBo userBo = new SysUserBo(); + userBo.setUserName(driver.getName()); + userBo.setNickName(driver.getName()); + userBo.setPhonenumber(driver.getPhone()); + // 主密码保留默认 + userBo.setPassword(PasswordUtils.createDefaultPassword(driver.getPhone())); // 现在没用 + userBo.setStatus("0"); + userBo.setPortEnable(true); + userBo.setCompanyId(driver.getCompanyId()); + userBo.setLoginPort(Collections.singletonList(ISysUserLoginPortService.DRIVER_PORT)); + userBo.setRoleIds(new Long[]{ISysUserLoginPortService.DRIVER_ROLE_ID}); + sysUserService.insertUser(userBo, false); + log.info("驾驶员账号创建成功,驾驶员ID:{},用户账号:{}", driver.getId(), userBo.getUserName()); + userId = sysUserService.selectUserByPhonenumber(driver.getPhone()).getUserId(); + } + + // 显式确保驾驶员端口和子密码存在 + ensureDriverLoginPort(userId, driver.getCompanyId(), driver.getName(), driver.getPhone(), registerPassword); + } + + private void ensureDriverLoginPort(Long userId, Long companyId, String username, String phone, String registerPassword) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysUserLoginPort::getUserId, userId) + .eq(SysUserLoginPort::getCompanyId, companyId) + .eq(SysUserLoginPort::getLoginPort, ISysUserLoginPortService.DRIVER_PORT); + + SysUserLoginPort port = sysUserLoginPortService.getOne(wrapper); + // 子密码默认规则 + String defaultSubPwd = PasswordUtils.createDefaultPassword(phone); + String regSubPwd = PasswordUtils.createPassword(registerPassword); + if (port == null) { + port = new SysUserLoginPort(); + port.setUserId(userId); + port.setCompanyId(companyId); + port.setUsername(username); + port.setLoginPort(ISysUserLoginPortService.DRIVER_PORT); + port.setStatus(1); + if (StringUtils.isNotBlank(registerPassword)) { + port.setSubPassword(regSubPwd); + } else { + port.setSubPassword(defaultSubPwd); + } + sysUserLoginPortService.save(port); + } else { + // 如果已存在但无子密码,初始化 + if (StringUtils.isBlank(port.getSubPassword())) { + if (StringUtils.isNotBlank(registerPassword)) { + port.setSubPassword(regSubPwd); + } else { + port.setSubPassword(defaultSubPwd); + } + sysUserLoginPortService.updateById(port); + } + } + } + + /** + * 修改驾驶员基本信息 + * + * @param bo 驾驶员基本信息 + * @return 是否修改成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(HotDriverBo bo) { + // 校验手机号 + checkPhoneUnique(bo.getPhone(), bo.getId()); + + // 获取旧数据用于比对 + HotDriver oldDriver = baseMapper.selectById(bo.getId()); + HotDriver update = MapstructUtils.convert(bo, HotDriver.class); + validEntityBeforeSave(update); + boolean flag = baseMapper.updateById(update) > 0; + + String driverId = update.getId(); + Long companyId = update.getCompanyId(); + // 如果bo中没有companyId,尝试从旧数据获取,保证子表数据完整 + if (companyId == null && oldDriver != null) { + companyId = oldDriver.getCompanyId(); + } + + if (bo.getDriverFamilyMemberList() != null) { + LambdaQueryWrapper familyDelLqw = Wrappers.lambdaQuery(); + familyDelLqw.eq(HotDriverFamilyMember::getDriverId, driverId); + driverFamilyMemberMapper.delete(familyDelLqw); + + if (CollUtil.isNotEmpty(bo.getDriverFamilyMemberList())) { + List members = MapstructUtils.convert(bo.getDriverFamilyMemberList(), HotDriverFamilyMember.class); + for (HotDriverFamilyMember m : members) { + m.setId(IdWorker.getId()); + m.setDriverId(driverId); + m.setCompanyId(companyId); + m.setIsDeleted(0L); + driverFamilyMemberMapper.insert(m); + } + } + } + + if (bo.getDriverWorkExperienceList() != null) { + LambdaQueryWrapper workDelLqw = Wrappers.lambdaQuery(); + workDelLqw.eq(HotDriverWorkExperience::getDriverId, driverId); + driverWorkExperienceMapper.delete(workDelLqw); + + if (CollUtil.isNotEmpty(bo.getDriverWorkExperienceList())) { + List works = MapstructUtils.convert(bo.getDriverWorkExperienceList(), HotDriverWorkExperience.class); + for (HotDriverWorkExperience w : works) { + w.setId(IdWorker.getId()); + w.setDriverId(driverId); + w.setCompanyId(companyId); + w.setIsDeleted(0L); + driverWorkExperienceMapper.insert(w); + } + } + } + + if (bo.getDriverLaborContractList() != null) { + LambdaQueryWrapper laborDelLqw = Wrappers.lambdaQuery(); + laborDelLqw.eq(HotDriverLaborContract::getDriverId, driverId); + driverLaborContractMapper.delete(laborDelLqw); + + if (CollUtil.isNotEmpty(bo.getDriverLaborContractList())) { + List labors = MapstructUtils.convert(bo.getDriverLaborContractList(), HotDriverLaborContract.class); + for (HotDriverLaborContract c : labors) { + c.setId(IdWorker.getId()); + c.setDriverId(driverId); + c.setCompanyId(companyId); + c.setIsDeleted(0L); + driverLaborContractMapper.insert(c); + } + } + } + + if (bo.getDriverHealthReportList() != null) { + LambdaQueryWrapper healthDelLqw = Wrappers.lambdaQuery(); + healthDelLqw.eq(HotDriverHealthReport::getDriverId, driverId); + driverHealthReportMapper.delete(healthDelLqw); + + if (CollUtil.isNotEmpty(bo.getDriverHealthReportList())) { + List reports = MapstructUtils.convert(bo.getDriverHealthReportList(), HotDriverHealthReport.class); + for (HotDriverHealthReport r : reports) { + r.setId(IdWorker.getId()); + r.setDriverId(driverId); + r.setCompanyId(companyId); + r.setIsDeleted(0L); + driverHealthReportMapper.insert(r); + } + } + } + + if (flag) { + // 处理手机号变更:如果手机号改变,移除旧手机号对应的用户在当前企业的司机端权限 + if (oldDriver != null && StringUtils.isNotBlank(update.getPhone()) + && !update.getPhone().equals(oldDriver.getPhone())) { + removeDriverPortFromUser(oldDriver.getPhone(), oldDriver.getCompanyId()); + } + + // 处理车牌号变更:同步车辆信息的当前驾驶员 + String oldPlate = oldDriver != null ? oldDriver.getPlateNumber() : null; + String newPlate = update.getPlateNumber(); + if (!StringUtils.equals(oldPlate, newPlate)) { + // 1. 如果旧车牌存在,从旧车辆的当前驾驶员列表中移除当前司机 + if (StringUtils.isNotBlank(oldPlate)) { + HotVehicle oldVehicle = vehicleMapper.selectOne(new LambdaQueryWrapper() + .eq(HotVehicle::getPlateNumber, oldPlate) + .eq(HotVehicle::getCompanyId, update.getCompanyId())); + + if (oldVehicle != null && StringUtils.isNotBlank(oldVehicle.getCurrentDriver())) { + List driverList = new ArrayList<>(Arrays.asList(oldVehicle.getCurrentDriver().split(","))); + if (driverList.remove(driverId)) { + String newDriverStr = String.join(",", driverList); + // 如果结果为空字符串,设为null或空 + vehicleMapper.update(null, + new LambdaUpdateWrapper() + .set(HotVehicle::getCurrentDriver, StringUtils.isBlank(newDriverStr) ? null : newDriverStr) + .eq(HotVehicle::getId, oldVehicle.getId())); + } + } + } + + // 2. 如果新车牌存在,将当前司机追加到新车辆的当前驾驶员列表 + if (StringUtils.isNotBlank(newPlate)) { + HotVehicle newVehicle = vehicleMapper.selectOne(new LambdaQueryWrapper() + .eq(HotVehicle::getPlateNumber, newPlate) + .eq(HotVehicle::getCompanyId, update.getCompanyId())); + + if (newVehicle != null) { + String currentDriverStr = newVehicle.getCurrentDriver(); + List driverList = StringUtils.isBlank(currentDriverStr) + ? new ArrayList<>() + : new ArrayList<>(Arrays.asList(currentDriverStr.split(","))); + + // 避免重复添加 + if (!driverList.contains(driverId)) { + driverList.add(driverId); + String newDriverStr = String.join(",", driverList); + vehicleMapper.update(null, + new LambdaUpdateWrapper() + .set(HotVehicle::getCurrentDriver, newDriverStr) + .eq(HotVehicle::getId, newVehicle.getId())); + } + } + } + } + + // 同步最新的驾驶员信息到用户表(包含状态变更、基本信息变更) + HotDriver newDriver = baseMapper.selectById(driverId); + syncDriverToSysUser(newDriver, null); + Long oldStatus = oldDriver != null ? oldDriver.getStatus() : null; + Long newStatus = update.getStatus(); + boolean resigned = oldStatus != null && Long.valueOf(1L).equals(oldStatus) && newStatus != null && Long.valueOf(0L).equals(newStatus); + + // 检查审核状态变更:变成未通过(0) + Integer oldAuditStatus = oldDriver != null ? oldDriver.getAuditStatus() : null; + Integer newAuditStatus = update.getAuditStatus(); + boolean isAuditRejected = (oldAuditStatus == null || oldAuditStatus != 0) && (newAuditStatus != null && newAuditStatus == 0); + + if ((resigned || isAuditRejected) && companyId != null) { + String phone = StringUtils.isNotBlank(update.getPhone()) ? update.getPhone() : (oldDriver != null ? oldDriver.getPhone() : null); + handleDriverResignation(driverId, phone, companyId); + } + + if (resigned && companyId != null) { + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (CollUtil.isNotEmpty(managers)) { + List receiverIds = managers.stream().map(m -> String.valueOf(m.getId())).collect(Collectors.toList()); + String name = StringUtils.blankToDefault(update.getName(), "该驾驶员"); + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent("驾驶员【" + name + "】已经离职。"); + bos.setSourceType("驾驶员管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送驾驶员离职通知失败 companyId={} driverId={}", companyId, driverId, e); + } + } + } + } + + return flag; + } + + /** + * 移除用户在特定企业的司机端登录权限 + */ + private void removeDriverPortFromUser(String phone, Long companyId) { + if (StringUtils.isBlank(phone) || companyId == null) { + return; + } + SysUserVo user = sysUserService.selectUserByPhonenumber(phone); + if (user != null) { + sysUserLoginPortService.remove(new LambdaQueryWrapper() + .eq(SysUserLoginPort::getUserId, user.getUserId()) + .eq(SysUserLoginPort::getCompanyId, companyId) + .eq(SysUserLoginPort::getLoginPort, ISysUserLoginPortService.DRIVER_PORT)); + } + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotDriver entity) { + if (StringUtils.isBlank(entity.getIdCardNo())) { + return; + } + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(HotDriver::getIdCardNo, entity.getIdCardNo()) + .eq(HotDriver::getIsDeleted, 0L) + .ne(StringUtils.isNotBlank(entity.getId()), HotDriver::getId, entity.getId()) + .last("limit 1"); + HotDriver existsDriver = baseMapper.selectOne(wrapper); + if (existsDriver != null) { + if (existsDriver.getCompanyId() != null + && entity.getCompanyId() != null + && !Objects.equals(existsDriver.getCompanyId(), entity.getCompanyId())) { + throw new ServiceException("同一身份证号的驾驶员不能存在于不同企业"); + } + throw new ServiceException("身份证号已被录入"); + } + } + + /** + * 校验并批量删除驾驶员基本信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + if (CollUtil.isNotEmpty(ids)) { + List driverIds = ids.stream().map(String::valueOf).collect(Collectors.toList()); + + // 删除奖惩记录 + rewardPunishmentMapper.delete(Wrappers.lambdaQuery() + .in(HotDriverRewardPunishment::getDriverId, driverIds)); + + // 删除技能考核 + skillAssessmentMapper.delete(Wrappers.lambdaQuery() + .in(HotDriverSkillAssessment::getDriverId, driverIds)); + + // 删除年度考核 + annualAssessmentMapper.delete(Wrappers.lambdaQuery() + .in(HotDriverAnnualAssessment::getDriverId, driverIds)); + } + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + public java.util.List queryDriverCategoryStat(Long companyId) { + return driverMapper.selectDriverCategoryStat(companyId); + } + + + /** + * 校验手机号唯一性 + * + * @param phone 手机号 + * @param excludeDriverId 排除的驾驶员ID(用于编辑时) + */ + private void checkPhoneUnique(String phone, String excludeDriverId) { + if (StringUtils.isBlank(phone)) { + return; + } + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(HotDriver::getPhone, phone) + .eq(HotDriver::getIsDeleted, 0); // 仅校验未删除的 + + if (StringUtils.isNotBlank(excludeDriverId)) { + wrapper.ne(HotDriver::getId, excludeDriverId); + } + + long count = baseMapper.selectCount(wrapper); + if (count > 0) { + throw new ServiceException("该手机号已存在,请检查!"); + } + } + + /** + * 保存驾驶员家庭成员 + */ + private void insertDriverFamilyMembers(HotDriverBo bo, String driverId) { + if (CollUtil.isNotEmpty(bo.getDriverFamilyMemberList())) { + List members = MapstructUtils.convert(bo.getDriverFamilyMemberList(), HotDriverFamilyMember.class); + for (HotDriverFamilyMember m : members) { + m.setId(IdWorker.getId()); + m.setDriverId(driverId); + m.setCompanyId(bo.getCompanyId()); + m.setIsDeleted(0L); + driverFamilyMemberMapper.insert(m); + } + } + } + + private void insertDriverWorkExperiences(HotDriverBo bo, String driverId) { + if (CollUtil.isNotEmpty(bo.getDriverWorkExperienceList())) { + List works = MapstructUtils.convert(bo.getDriverWorkExperienceList(), HotDriverWorkExperience.class); + for (HotDriverWorkExperience w : works) { + w.setId(IdWorker.getId()); + w.setDriverId(driverId); + w.setCompanyId(bo.getCompanyId()); + w.setIsDeleted(0L); + driverWorkExperienceMapper.insert(w); + } + } + } + + private void insertDriverLaborContracts(HotDriverBo bo, String driverId) { + if (CollUtil.isNotEmpty(bo.getDriverLaborContractList())) { + List labors = MapstructUtils.convert(bo.getDriverLaborContractList(), HotDriverLaborContract.class); + for (HotDriverLaborContract c : labors) { + c.setId(IdWorker.getId()); + c.setDriverId(driverId); + c.setCompanyId(bo.getCompanyId()); + c.setIsDeleted(0L); + driverLaborContractMapper.insert(c); + } + } + } + + private void insertDriverHealthReports(HotDriverBo bo, String driverId) { + if (CollUtil.isNotEmpty(bo.getDriverHealthReportList())) { + List reports = MapstructUtils.convert(bo.getDriverHealthReportList(), HotDriverHealthReport.class); + for (HotDriverHealthReport r : reports) { + r.setId(IdWorker.getId()); + r.setDriverId(driverId); + r.setCompanyId(bo.getCompanyId()); + r.setIsDeleted(0L); + driverHealthReportMapper.insert(r); + } + } + } + + /** + * 处理驾驶员离职或审核未通过 + * 1. 人员分组也要将该驾驶员删除 + */ + private void handleDriverResignation(String driverId, String phone, Long companyId) { + if (StringUtils.isBlank(driverId) || companyId == null) { + return; + } + + // 2. 将该驾驶员从所有组中移除 + // 查找包含该驾驶员的所有组 + List groups = groupMapper.selectList( + new LambdaQueryWrapper() + .eq(HotDriverGroup::getCompanyId, companyId) + .like(HotDriverGroup::getMemberIds, driverId) + .eq(HotDriverGroup::getIsDeleted, 0L) + ); + + if (CollUtil.isNotEmpty(groups)) { + for (HotDriverGroup group : groups) { + String memberIds = group.getMemberIds(); + if (StringUtils.isBlank(memberIds)) { + continue; + } + + List memberList = new ArrayList<>(Arrays.asList(memberIds.split(","))); + // 确保移除的是当前驾驶员ID + boolean removed = false; + while (memberList.remove(driverId)) { + removed = true; + } + + if (removed) { + String newMemberIds = String.join(",", memberList); + // 只更新memberIds字段 + groupMapper.update(null, + new LambdaUpdateWrapper() + .set(HotDriverGroup::getMemberIds, newMemberIds) + .eq(HotDriverGroup::getId, group.getId()) + ); + } + } + } + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/controller/HotDriverAnnualAssessmentController.java b/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/controller/HotDriverAnnualAssessmentController.java new file mode 100644 index 0000000..acc3448 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/controller/HotDriverAnnualAssessmentController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.driverManagement.driverAnnualAssessment.controller; + +import com.hotwj.platform.driverManagement.driverAnnualAssessment.domain.bo.HotDriverAnnualAssessmentBo; +import com.hotwj.platform.driverManagement.driverAnnualAssessment.domain.vo.HotDriverAnnualAssessmentVo; +import com.hotwj.platform.driverManagement.driverAnnualAssessment.service.IHotDriverAnnualAssessmentService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 驾驶员年度考核 + * + * @author shihongwei + * @date 2025-12-16 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/driverManagement/driverAnnualAssessment") +@Tag(name = "驾驶员年度考核", description = "年度考核管理") +public class HotDriverAnnualAssessmentController extends BaseController { + + private final IHotDriverAnnualAssessmentService hotDriverAnnualAssessmentService; + + /** + * 查询驾驶员年度考核列表 + */ + //@SaCheckPermission("driverManagement:driverAnnualAssessment:list") + @GetMapping("/list") + @Operation(summary = "分页查询驾驶员年度考核列表") + public TableDataInfo list(HotDriverAnnualAssessmentBo bo, PageQuery pageQuery) { + return hotDriverAnnualAssessmentService.queryPageList(bo, pageQuery); + } + + /** + * 导出驾驶员年度考核列表 + */ + //@SaCheckPermission("driverManagement:driverAnnualAssessment:export") + @Log(title = "驾驶员年度考核", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出驾驶员年度考核列表") + public void export(HotDriverAnnualAssessmentBo bo, HttpServletResponse response) { + List list = hotDriverAnnualAssessmentService.queryList(bo); + ExcelUtil.exportExcel(list, "驾驶员年度考核", HotDriverAnnualAssessmentVo.class, response); + } + + /** + * 获取驾驶员年度考核详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("driverManagement:driverAnnualAssessment:query") + @GetMapping("/{id}") + @Operation(summary = "查询驾驶员年度考核详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotDriverAnnualAssessmentService.queryById(id)); + } + + /** + * 新增驾驶员年度考核 + */ + //@SaCheckPermission("driverManagement:driverAnnualAssessment:add") + @Log(title = "驾驶员年度考核", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增驾驶员年度考核") + public R add(@Validated(AddGroup.class) @RequestBody HotDriverAnnualAssessmentBo bo) { + return toAjax(hotDriverAnnualAssessmentService.insertByBo(bo)); + } + + /** + * 修改驾驶员年度考核 + */ + //@SaCheckPermission("driverManagement:driverAnnualAssessment:edit") + @Log(title = "驾驶员年度考核", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改驾驶员年度考核") + public R edit(@Validated(EditGroup.class) @RequestBody HotDriverAnnualAssessmentBo bo) { + return toAjax(hotDriverAnnualAssessmentService.updateByBo(bo)); + } + + /** + * 删除驾驶员年度考核 + * + * @param ids 主键串 + */ + //@SaCheckPermission("driverManagement:driverAnnualAssessment:remove") + @Log(title = "驾驶员年度考核", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除驾驶员年度考核") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotDriverAnnualAssessmentService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/domain/HotDriverAnnualAssessment.java b/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/domain/HotDriverAnnualAssessment.java new file mode 100644 index 0000000..d472ae1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/domain/HotDriverAnnualAssessment.java @@ -0,0 +1,80 @@ +package com.hotwj.platform.driverManagement.driverAnnualAssessment.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 驾驶员年度考核对象 hot_driver_annual_assessment + * + * @author shihongwei + * @date 2025-12-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_driver_annual_assessment") +public class HotDriverAnnualAssessment extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 驾驶员姓名 + */ + private String driverName; + + /** + * 考核内容 + */ + private String assessmentContent; + + /** + * 考核结果 + */ + private String assessmentResult; + + /** + * 考核时间 + */ + private Date assessmentTime; + + /** + * 考核人签名OSS文件ID + */ + private Long evaluatorSignatureId; + + /** + * 被考核人签名OSS文件ID + */ + private Long assessedSignatureId; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/domain/bo/HotDriverAnnualAssessmentBo.java b/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/domain/bo/HotDriverAnnualAssessmentBo.java new file mode 100644 index 0000000..efcb998 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/domain/bo/HotDriverAnnualAssessmentBo.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.driverManagement.driverAnnualAssessment.domain.bo; + +import com.hotwj.platform.driverManagement.driverAnnualAssessment.domain.HotDriverAnnualAssessment; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 驾驶员年度考核业务对象 hot_driver_annual_assessment + * + * @author shihongwei + * @date 2025-12-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotDriverAnnualAssessment.class, reverseConvertGenerate = false) +public class HotDriverAnnualAssessmentBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 驾驶员姓名 + */ + private String driverName; + + /** + * 考核内容 + */ + private String assessmentContent; + + /** + * 考核结果 + */ + private String assessmentResult; + + /** + * 考核时间 + */ + private Date assessmentTime; + + /** + * 考核人签名OSS文件ID + */ + private Long evaluatorSignatureId; + + /** + * 被考核人签名OSS文件ID + */ + private Long assessedSignatureId; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/domain/vo/HotDriverAnnualAssessmentVo.java b/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/domain/vo/HotDriverAnnualAssessmentVo.java new file mode 100644 index 0000000..98f4bf3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/domain/vo/HotDriverAnnualAssessmentVo.java @@ -0,0 +1,88 @@ +package com.hotwj.platform.driverManagement.driverAnnualAssessment.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.driverManagement.driverAnnualAssessment.domain.HotDriverAnnualAssessment; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 驾驶员年度考核视图对象 hot_driver_annual_assessment + * + * @author shihongwei + * @date 2025-12-16 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotDriverAnnualAssessment.class) +public class HotDriverAnnualAssessmentVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 驾驶员ID + */ + @ExcelProperty(value = "驾驶员ID") + private String driverId; + + /** + * 驾驶员姓名 + */ + private String driverName; + + /** + * 考核内容 + */ + @ExcelProperty(value = "考核内容") + private String assessmentContent; + + /** + * 考核结果 + */ + @ExcelProperty(value = "考核结果") + private String assessmentResult; + + /** + * 考核时间 + */ + @ExcelProperty(value = "考核时间") + private Date assessmentTime; + + /** + * 考核人签名OSS文件ID + */ + @ExcelProperty(value = "考核人签名OSS文件ID") + private Long evaluatorSignatureId; + + /** + * 被考核人签名OSS文件ID + */ + @ExcelProperty(value = "被考核人签名OSS文件ID") + private Long assessedSignatureId; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/mapper/HotDriverAnnualAssessmentMapper.java b/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/mapper/HotDriverAnnualAssessmentMapper.java new file mode 100644 index 0000000..f58e4d8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/mapper/HotDriverAnnualAssessmentMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.driverManagement.driverAnnualAssessment.mapper; + +import com.hotwj.platform.driverManagement.driverAnnualAssessment.domain.HotDriverAnnualAssessment; +import com.hotwj.platform.driverManagement.driverAnnualAssessment.domain.vo.HotDriverAnnualAssessmentVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 驾驶员年度考核Mapper接口 + * + * @author shihongwei + * @date 2025-12-16 + */ +@Mapper +public interface HotDriverAnnualAssessmentMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/service/IHotDriverAnnualAssessmentService.java b/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/service/IHotDriverAnnualAssessmentService.java new file mode 100644 index 0000000..a02c37e --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/service/IHotDriverAnnualAssessmentService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.driverManagement.driverAnnualAssessment.service; + +import com.hotwj.platform.driverManagement.driverAnnualAssessment.domain.bo.HotDriverAnnualAssessmentBo; +import com.hotwj.platform.driverManagement.driverAnnualAssessment.domain.vo.HotDriverAnnualAssessmentVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 驾驶员年度考核Service接口 + * + * @author shihongwei + * @date 2025-12-16 + */ +public interface IHotDriverAnnualAssessmentService { + + /** + * 查询驾驶员年度考核 + * + * @param id 主键 + * @return 驾驶员年度考核 + */ + HotDriverAnnualAssessmentVo queryById(Long id); + + /** + * 分页查询驾驶员年度考核列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员年度考核分页列表 + */ + TableDataInfo queryPageList(HotDriverAnnualAssessmentBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的驾驶员年度考核列表 + * + * @param bo 查询条件 + * @return 驾驶员年度考核列表 + */ + List queryList(HotDriverAnnualAssessmentBo bo); + + /** + * 新增驾驶员年度考核 + * + * @param bo 驾驶员年度考核 + * @return 是否新增成功 + */ + Boolean insertByBo(HotDriverAnnualAssessmentBo bo); + + /** + * 修改驾驶员年度考核 + * + * @param bo 驾驶员年度考核 + * @return 是否修改成功 + */ + Boolean updateByBo(HotDriverAnnualAssessmentBo bo); + + /** + * 校验并批量删除驾驶员年度考核信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/service/impl/HotDriverAnnualAssessmentServiceImpl.java b/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/service/impl/HotDriverAnnualAssessmentServiceImpl.java new file mode 100644 index 0000000..c94585b --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverAnnualAssessment/service/impl/HotDriverAnnualAssessmentServiceImpl.java @@ -0,0 +1,186 @@ +package com.hotwj.platform.driverManagement.driverAnnualAssessment.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.driverAnnualAssessment.domain.HotDriverAnnualAssessment; +import com.hotwj.platform.driverManagement.driverAnnualAssessment.domain.bo.HotDriverAnnualAssessmentBo; +import com.hotwj.platform.driverManagement.driverAnnualAssessment.domain.vo.HotDriverAnnualAssessmentVo; +import com.hotwj.platform.driverManagement.driverAnnualAssessment.mapper.HotDriverAnnualAssessmentMapper; +import com.hotwj.platform.driverManagement.driverAnnualAssessment.service.IHotDriverAnnualAssessmentService; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员年度考核Service业务层处理 + * + * @author shihongwei + * @date 2025-12-16 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotDriverAnnualAssessmentServiceImpl implements IHotDriverAnnualAssessmentService { + + private final HotDriverAnnualAssessmentMapper baseMapper; + private final IHotSystemNotificationService notificationService; + + /** + * 查询驾驶员年度考核 + * + * @param id 主键 + * @return 驾驶员年度考核 + */ + @Override + public HotDriverAnnualAssessmentVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询驾驶员年度考核列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员年度考核分页列表 + */ + @Override + public TableDataInfo queryPageList(HotDriverAnnualAssessmentBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的驾驶员年度考核列表 + * + * @param bo 查询条件 + * @return 驾驶员年度考核列表 + */ + @Override + public List queryList(HotDriverAnnualAssessmentBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotDriverAnnualAssessmentBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotDriverAnnualAssessment::getAssessmentTime); + lqw.eq(bo.getCompanyId() != null, HotDriverAnnualAssessment::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getDriverId() != null, HotDriverAnnualAssessment::getDriverId, bo.getDriverId()); + lqw.eq(StringUtils.isNotBlank(bo.getAssessmentContent()), HotDriverAnnualAssessment::getAssessmentContent, bo.getAssessmentContent()); + lqw.eq(StringUtils.isNotBlank(bo.getAssessmentResult()), HotDriverAnnualAssessment::getAssessmentResult, bo.getAssessmentResult()); + lqw.eq(bo.getAssessmentTime() != null, HotDriverAnnualAssessment::getAssessmentTime, bo.getAssessmentTime()); + lqw.eq(bo.getEvaluatorSignatureId() != null, HotDriverAnnualAssessment::getEvaluatorSignatureId, bo.getEvaluatorSignatureId()); + lqw.eq(bo.getAssessedSignatureId() != null, HotDriverAnnualAssessment::getAssessedSignatureId, bo.getAssessedSignatureId()); + lqw.eq(bo.getIsDeleted() != null, HotDriverAnnualAssessment::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增驾驶员年度考核 + * + * @param bo 驾驶员年度考核 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotDriverAnnualAssessmentBo bo) { + HotDriverAnnualAssessment add = MapstructUtils.convert(bo, HotDriverAnnualAssessment.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + if (StringUtils.isNotBlank(add.getDriverId())) { + String result = StringUtils.blankToDefault(add.getAssessmentResult(), "未知"); + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent("您的年度考核结果为" + result + "。"); + bos.setSourceType("驾驶员管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("驾驶员"); + bos.setReceiverIds(java.util.Collections.singletonList(add.getDriverId())); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送年度考核结果通知失败 driverId={} companyId={}", add.getDriverId(), add.getCompanyId(), e); + } + } + } + return flag; + } + + /** + * 修改驾驶员年度考核 + * + * @param bo 驾驶员年度考核 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotDriverAnnualAssessmentBo bo) { + HotDriverAnnualAssessment before = null; + if (bo.getId() != null) { + before = baseMapper.selectById(bo.getId()); + } + HotDriverAnnualAssessment update = MapstructUtils.convert(bo, HotDriverAnnualAssessment.class); + validEntityBeforeSave(update); + boolean ok = baseMapper.updateById(update) > 0; + if (ok) { + String driverId = org.dromara.common.core.utils.StringUtils.isNotBlank(update.getDriverId()) ? update.getDriverId() + : (before != null ? before.getDriverId() : null); + if (org.dromara.common.core.utils.StringUtils.isNotBlank(driverId)) { + String result = org.dromara.common.core.utils.StringUtils.blankToDefault( + update.getAssessmentResult() != null ? update.getAssessmentResult() : (before != null ? before.getAssessmentResult() : null), + "未知" + ); + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent("您的年度考核结果已更新为" + result + "。"); + bos.setSourceType("驾驶员管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("驾驶员"); + bos.setReceiverIds(java.util.Collections.singletonList(driverId)); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送年度考核更新通知失败 driverId={} companyId={}", driverId, update.getCompanyId(), e); + } + } + } + return ok; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotDriverAnnualAssessment entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除驾驶员年度考核信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/controller/HotDriverFamilyMemberController.java b/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/controller/HotDriverFamilyMemberController.java new file mode 100644 index 0000000..8538e16 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/controller/HotDriverFamilyMemberController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.driverManagement.driverFamilyMember.controller; + +import com.hotwj.platform.driverManagement.driverFamilyMember.domain.bo.HotDriverFamilyMemberBo; +import com.hotwj.platform.driverManagement.driverFamilyMember.domain.vo.HotDriverFamilyMemberVo; +import com.hotwj.platform.driverManagement.driverFamilyMember.service.IHotDriverFamilyMemberService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 驾驶员家庭成员 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/driverManagement/driverFamilyMember") +@Tag(name = "驾驶员家庭成员", description = "家庭成员管理") +public class HotDriverFamilyMemberController extends BaseController { + + private final IHotDriverFamilyMemberService hotDriverFamilyMemberService; + + /** + * 查询驾驶员家庭成员列表 + */ + //@SaCheckPermission("driverManagement:driver:list") + @GetMapping("/list") + @Operation(summary = "分页查询驾驶员家庭成员列表") + public TableDataInfo list(HotDriverFamilyMemberBo bo, PageQuery pageQuery) { + return hotDriverFamilyMemberService.queryPageList(bo, pageQuery); + } + + /** + * 导出驾驶员家庭成员列表 + */ + //@SaCheckPermission("driverManagement:driver:export") + @Log(title = "驾驶员家庭成员", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出驾驶员家庭成员列表") + public void export(HotDriverFamilyMemberBo bo, HttpServletResponse response) { + List list = hotDriverFamilyMemberService.queryList(bo); + ExcelUtil.exportExcel(list, "驾驶员家庭成员", HotDriverFamilyMemberVo.class, response); + } + + /** + * 获取驾驶员家庭成员详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("driverManagement:driver:query") + @GetMapping("/{id}") + @Operation(summary = "查询驾驶员家庭成员详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotDriverFamilyMemberService.queryById(id)); + } + + /** + * 新增驾驶员家庭成员 + */ + //@SaCheckPermission("driverManagement:driver:add") + @Log(title = "驾驶员家庭成员", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增驾驶员家庭成员") + public R add(@Validated(AddGroup.class) @RequestBody HotDriverFamilyMemberBo bo) { + return toAjax(hotDriverFamilyMemberService.insertByBo(bo)); + } + + /** + * 修改驾驶员家庭成员 + */ + //@SaCheckPermission("driverManagement:driver:edit") + @Log(title = "驾驶员家庭成员", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改驾驶员家庭成员") + public R edit(@Validated(EditGroup.class) @RequestBody HotDriverFamilyMemberBo bo) { + return toAjax(hotDriverFamilyMemberService.updateByBo(bo)); + } + + /** + * 删除驾驶员家庭成员 + * + * @param ids 主键串 + */ + //@SaCheckPermission("driverManagement:driver:remove") + @Log(title = "驾驶员家庭成员", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除驾驶员家庭成员") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotDriverFamilyMemberService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/domain/HotDriverFamilyMember.java b/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/domain/HotDriverFamilyMember.java new file mode 100644 index 0000000..edbe720 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/domain/HotDriverFamilyMember.java @@ -0,0 +1,70 @@ +package com.hotwj.platform.driverManagement.driverFamilyMember.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 驾驶员家庭成员对象 hot_driver_family_member + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_driver_family_member") +public class HotDriverFamilyMember extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 姓名 + */ + private String name; + + /** + * 与本人关系 + */ + private String relationship; + + /** + * 出生年月 + */ + private Date birthMonth; + + /** + * 联系电话 + */ + private String phone; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/domain/bo/HotDriverFamilyMemberBo.java b/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/domain/bo/HotDriverFamilyMemberBo.java new file mode 100644 index 0000000..a60efc2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/domain/bo/HotDriverFamilyMemberBo.java @@ -0,0 +1,66 @@ +package com.hotwj.platform.driverManagement.driverFamilyMember.domain.bo; + +import com.hotwj.platform.driverManagement.driverFamilyMember.domain.HotDriverFamilyMember; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 驾驶员家庭成员业务对象 hot_driver_family_member + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotDriverFamilyMember.class, reverseConvertGenerate = false) +public class HotDriverFamilyMemberBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 姓名 + */ + private String name; + + /** + * 与本人关系 + */ + private String relationship; + + /** + * 出生年月 + */ + private Date birthMonth; + + /** + * 联系电话 + */ + private String phone; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/domain/vo/HotDriverFamilyMemberVo.java b/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/domain/vo/HotDriverFamilyMemberVo.java new file mode 100644 index 0000000..127ee35 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/domain/vo/HotDriverFamilyMemberVo.java @@ -0,0 +1,77 @@ +package com.hotwj.platform.driverManagement.driverFamilyMember.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.driverManagement.driverFamilyMember.domain.HotDriverFamilyMember; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 驾驶员家庭成员视图对象 hot_driver_family_member + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotDriverFamilyMember.class) +public class HotDriverFamilyMemberVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 驾驶员ID + */ + @ExcelProperty(value = "驾驶员ID") + private String driverId; + + /** + * 姓名 + */ + @ExcelProperty(value = "姓名") + private String name; + + /** + * 与本人关系 + */ + @ExcelProperty(value = "与本人关系") + private String relationship; + + /** + * 出生年月 + */ + @ExcelProperty(value = "出生年月") + private Date birthMonth; + + /** + * 联系电话 + */ + @ExcelProperty(value = "联系电话") + private String phone; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/mapper/HotDriverFamilyMemberMapper.java b/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/mapper/HotDriverFamilyMemberMapper.java new file mode 100644 index 0000000..2a2c831 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/mapper/HotDriverFamilyMemberMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.driverManagement.driverFamilyMember.mapper; + +import com.hotwj.platform.driverManagement.driverFamilyMember.domain.HotDriverFamilyMember; +import com.hotwj.platform.driverManagement.driverFamilyMember.domain.vo.HotDriverFamilyMemberVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 驾驶员家庭成员Mapper接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Mapper +public interface HotDriverFamilyMemberMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/service/IHotDriverFamilyMemberService.java b/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/service/IHotDriverFamilyMemberService.java new file mode 100644 index 0000000..a484a65 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/service/IHotDriverFamilyMemberService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.driverManagement.driverFamilyMember.service; + +import com.hotwj.platform.driverManagement.driverFamilyMember.domain.bo.HotDriverFamilyMemberBo; +import com.hotwj.platform.driverManagement.driverFamilyMember.domain.vo.HotDriverFamilyMemberVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 驾驶员家庭成员Service接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +public interface IHotDriverFamilyMemberService { + + /** + * 查询驾驶员家庭成员 + * + * @param id 主键 + * @return 驾驶员家庭成员 + */ + HotDriverFamilyMemberVo queryById(Long id); + + /** + * 分页查询驾驶员家庭成员列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员家庭成员分页列表 + */ + TableDataInfo queryPageList(HotDriverFamilyMemberBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的驾驶员家庭成员列表 + * + * @param bo 查询条件 + * @return 驾驶员家庭成员列表 + */ + List queryList(HotDriverFamilyMemberBo bo); + + /** + * 新增驾驶员家庭成员 + * + * @param bo 驾驶员家庭成员 + * @return 是否新增成功 + */ + Boolean insertByBo(HotDriverFamilyMemberBo bo); + + /** + * 修改驾驶员家庭成员 + * + * @param bo 驾驶员家庭成员 + * @return 是否修改成功 + */ + Boolean updateByBo(HotDriverFamilyMemberBo bo); + + /** + * 校验并批量删除驾驶员家庭成员信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/service/impl/HotDriverFamilyMemberServiceImpl.java b/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/service/impl/HotDriverFamilyMemberServiceImpl.java new file mode 100644 index 0000000..0d49d9d --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverFamilyMember/service/impl/HotDriverFamilyMemberServiceImpl.java @@ -0,0 +1,138 @@ +package com.hotwj.platform.driverManagement.driverFamilyMember.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.driverFamilyMember.domain.HotDriverFamilyMember; +import com.hotwj.platform.driverManagement.driverFamilyMember.domain.bo.HotDriverFamilyMemberBo; +import com.hotwj.platform.driverManagement.driverFamilyMember.domain.vo.HotDriverFamilyMemberVo; +import com.hotwj.platform.driverManagement.driverFamilyMember.mapper.HotDriverFamilyMemberMapper; +import com.hotwj.platform.driverManagement.driverFamilyMember.service.IHotDriverFamilyMemberService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员家庭成员Service业务层处理 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotDriverFamilyMemberServiceImpl implements IHotDriverFamilyMemberService { + + private final HotDriverFamilyMemberMapper baseMapper; + + /** + * 查询驾驶员家庭成员 + * + * @param id 主键 + * @return 驾驶员家庭成员 + */ + @Override + public HotDriverFamilyMemberVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询驾驶员家庭成员列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员家庭成员分页列表 + */ + @Override + public TableDataInfo queryPageList(HotDriverFamilyMemberBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的驾驶员家庭成员列表 + * + * @param bo 查询条件 + * @return 驾驶员家庭成员列表 + */ + @Override + public List queryList(HotDriverFamilyMemberBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotDriverFamilyMemberBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotDriverFamilyMember::getId); + lqw.eq(bo.getCompanyId() != null, HotDriverFamilyMember::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getDriverId() != null, HotDriverFamilyMember::getDriverId, bo.getDriverId()); + lqw.like(StringUtils.isNotBlank(bo.getName()), HotDriverFamilyMember::getName, bo.getName()); + lqw.eq(StringUtils.isNotBlank(bo.getRelationship()), HotDriverFamilyMember::getRelationship, bo.getRelationship()); + lqw.eq(bo.getBirthMonth() != null, HotDriverFamilyMember::getBirthMonth, bo.getBirthMonth()); + lqw.eq(StringUtils.isNotBlank(bo.getPhone()), HotDriverFamilyMember::getPhone, bo.getPhone()); + lqw.eq(bo.getIsDeleted() != null, HotDriverFamilyMember::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增驾驶员家庭成员 + * + * @param bo 驾驶员家庭成员 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotDriverFamilyMemberBo bo) { + HotDriverFamilyMember add = MapstructUtils.convert(bo, HotDriverFamilyMember.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改驾驶员家庭成员 + * + * @param bo 驾驶员家庭成员 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotDriverFamilyMemberBo bo) { + HotDriverFamilyMember update = MapstructUtils.convert(bo, HotDriverFamilyMember.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotDriverFamilyMember entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除驾驶员家庭成员信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/controller/HotDriverFileSignController.java b/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/controller/HotDriverFileSignController.java new file mode 100644 index 0000000..f93e959 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/controller/HotDriverFileSignController.java @@ -0,0 +1,115 @@ +package com.hotwj.platform.driverManagement.driverFileSign.controller; + +import com.hotwj.platform.driverManagement.driverFileSign.domain.bo.HotDriverFileSignBo; +import com.hotwj.platform.driverManagement.driverFileSign.domain.vo.HotDriverFileSignVo; +import com.hotwj.platform.driverManagement.driverFileSign.service.IHotDriverFileSignService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 驾驶员文件签订 + * + * @author shihongwei + * @date 2025-12-22 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/driverManagement/driverFileSign") +@Tag(name = "驾驶员文件签订", description = "驾驶员文件签订管理") +public class HotDriverFileSignController extends BaseController { + + private final IHotDriverFileSignService hotDriverFileSignService; + + /** + * 查询驾驶员文件签订列表 + */ + //@SaCheckPermission("driverManagement:driverFileSign:list") + @GetMapping("/list") + @Operation(summary = "查询驾驶员文件签订列表") + public TableDataInfo list(HotDriverFileSignBo bo, PageQuery pageQuery) { + return hotDriverFileSignService.queryPageList(bo, pageQuery); + } + + /** + * 导出驾驶员文件签订列表 + */ + //@SaCheckPermission("driverManagement:driverFileSign:export") + @Log(title = "驾驶员文件签订", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出驾驶员文件签订列表") + public void export(HotDriverFileSignBo bo, HttpServletResponse response) { + List list = hotDriverFileSignService.queryList(bo); + ExcelUtil.exportExcel(list, "驾驶员文件签订", HotDriverFileSignVo.class, response); + } + + /** + * 获取驾驶员文件签订详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("driverManagement:driverFileSign:query") + @GetMapping("/{id}") + @Operation(summary = "获取驾驶员文件签订详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotDriverFileSignService.queryById(id)); + } + + /** + * 新增驾驶员文件签订 + */ + //@SaCheckPermission("driverManagement:driverFileSign:add") + @Log(title = "驾驶员文件签订", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增驾驶员文件签订") + public R add(@Validated(AddGroup.class) @RequestBody HotDriverFileSignBo bo) { + bo.setIsDeleted(0L); + return toAjax(hotDriverFileSignService.insertByBo(bo)); + } + + /** + * 修改驾驶员文件签订 + */ + //@SaCheckPermission("driverManagement:driverFileSign:edit") + @Log(title = "驾驶员文件签订", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改驾驶员文件签订") + public R edit(@Validated(EditGroup.class) @RequestBody HotDriverFileSignBo bo) { + return toAjax(hotDriverFileSignService.updateByBo(bo)); + } + + /** + * 删除驾驶员文件签订 + * + * @param ids 主键串 + */ + //@SaCheckPermission("driverManagement:driverFileSign:remove") + @Log(title = "驾驶员文件签订", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除驾驶员文件签订") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotDriverFileSignService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/domain/HotDriverFileSign.java b/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/domain/HotDriverFileSign.java new file mode 100644 index 0000000..6b18581 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/domain/HotDriverFileSign.java @@ -0,0 +1,80 @@ +package com.hotwj.platform.driverManagement.driverFileSign.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 驾驶员文件签订对象 hot_driver_file_sign + * + * @author shihongwei + * @date 2025-12-22 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_driver_file_sign") +public class HotDriverFileSign extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 文件名称 + */ + private String fileName; + + /** + * 签订文件图片url + */ + private String signFileImgUrl; + + /** + * 办理人员 + */ + private String handlerName; + + /** + * 签订时间 + */ + private Date signTime; + + /** + * 到期时间 + */ + private Date expireTime; + + /** + * 是否归档 0=否 1=是 + */ + private Long isArchived; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 公司ID + */ + private Long companyId; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/domain/bo/HotDriverFileSignBo.java b/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/domain/bo/HotDriverFileSignBo.java new file mode 100644 index 0000000..60f7627 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/domain/bo/HotDriverFileSignBo.java @@ -0,0 +1,86 @@ +package com.hotwj.platform.driverManagement.driverFileSign.domain.bo; + +import com.hotwj.platform.driverManagement.driverFileSign.domain.HotDriverFileSign; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 驾驶员文件签订业务对象 hot_driver_file_sign + * + * @author shihongwei + * @date 2025-12-22 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotDriverFileSign.class, reverseConvertGenerate = false) +public class HotDriverFileSignBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 驾驶员ID + */ + @NotNull(message = "驾驶员ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private String driverId; + + /** + * 文件名称 + */ + @NotBlank(message = "文件名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String fileName; + + /** + * 签订文件图片url + */ + @NotBlank(message = "签订文件图片url不能为空", groups = {AddGroup.class, EditGroup.class}) + private String signFileImgUrl; + + /** + * 办理人员 + */ + @NotBlank(message = "办理人员不能为空", groups = {AddGroup.class, EditGroup.class}) + private String handlerName; + + /** + * 签订时间 + */ + @NotNull(message = "签订时间不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date signTime; + + /** + * 到期时间 + */ + private Date expireTime; + + /** + * 是否归档 0=否 1=是 + */ + @NotNull(message = "是否归档 0=否 1=是不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isArchived; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + /** + * 公司ID + */ +// @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/domain/vo/HotDriverFileSignVo.java b/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/domain/vo/HotDriverFileSignVo.java new file mode 100644 index 0000000..377e546 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/domain/vo/HotDriverFileSignVo.java @@ -0,0 +1,96 @@ +package com.hotwj.platform.driverManagement.driverFileSign.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.driverManagement.driverFileSign.domain.HotDriverFileSign; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 驾驶员文件签订视图对象 hot_driver_file_sign + * + * @author shihongwei + * @date 2025-12-22 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotDriverFileSign.class) +public class HotDriverFileSignVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 驾驶员ID + */ + @ExcelProperty(value = "驾驶员ID") + private String driverId; + + /** + * 文件名称 + */ + @ExcelProperty(value = "文件名称") + private String fileName; + + /** + * 签订文件图片url + */ + @ExcelProperty(value = "签订文件图片url") + private String signFileImgUrl; + + /** + * 签订文件图片urlUrl + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "signFileImgUrl") + private String signFileImgUrlUrl; + /** + * 办理人员 + */ + @ExcelProperty(value = "办理人员") + private String handlerName; + + /** + * 签订时间 + */ + @ExcelProperty(value = "签订时间") + private Date signTime; + + /** + * 到期时间 + */ + @ExcelProperty(value = "到期时间") + private Date expireTime; + + /** + * 是否归档 0=否 1=是 + */ + @ExcelProperty(value = "是否归档 0=否 1=是") + private Long isArchived; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/mapper/HotDriverFileSignMapper.java b/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/mapper/HotDriverFileSignMapper.java new file mode 100644 index 0000000..5a23930 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/mapper/HotDriverFileSignMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.driverManagement.driverFileSign.mapper; + +import com.hotwj.platform.driverManagement.driverFileSign.domain.HotDriverFileSign; +import com.hotwj.platform.driverManagement.driverFileSign.domain.vo.HotDriverFileSignVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 驾驶员文件签订Mapper接口 + * + * @author shihongwei + * @date 2025-12-22 + */ +@Mapper +public interface HotDriverFileSignMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/service/IHotDriverFileSignService.java b/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/service/IHotDriverFileSignService.java new file mode 100644 index 0000000..43668b3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/service/IHotDriverFileSignService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.driverManagement.driverFileSign.service; + +import com.hotwj.platform.driverManagement.driverFileSign.domain.bo.HotDriverFileSignBo; +import com.hotwj.platform.driverManagement.driverFileSign.domain.vo.HotDriverFileSignVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 驾驶员文件签订Service接口 + * + * @author shihongwei + * @date 2025-12-22 + */ +public interface IHotDriverFileSignService { + + /** + * 查询驾驶员文件签订 + * + * @param id 主键 + * @return 驾驶员文件签订 + */ + HotDriverFileSignVo queryById(Long id); + + /** + * 分页查询驾驶员文件签订列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员文件签订分页列表 + */ + TableDataInfo queryPageList(HotDriverFileSignBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的驾驶员文件签订列表 + * + * @param bo 查询条件 + * @return 驾驶员文件签订列表 + */ + List queryList(HotDriverFileSignBo bo); + + /** + * 新增驾驶员文件签订 + * + * @param bo 驾驶员文件签订 + * @return 是否新增成功 + */ + Boolean insertByBo(HotDriverFileSignBo bo); + + /** + * 修改驾驶员文件签订 + * + * @param bo 驾驶员文件签订 + * @return 是否修改成功 + */ + Boolean updateByBo(HotDriverFileSignBo bo); + + /** + * 校验并批量删除驾驶员文件签订信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/service/impl/HotDriverFileSignServiceImpl.java b/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/service/impl/HotDriverFileSignServiceImpl.java new file mode 100644 index 0000000..d11a30a --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverFileSign/service/impl/HotDriverFileSignServiceImpl.java @@ -0,0 +1,140 @@ +package com.hotwj.platform.driverManagement.driverFileSign.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.driverFileSign.domain.HotDriverFileSign; +import com.hotwj.platform.driverManagement.driverFileSign.domain.bo.HotDriverFileSignBo; +import com.hotwj.platform.driverManagement.driverFileSign.domain.vo.HotDriverFileSignVo; +import com.hotwj.platform.driverManagement.driverFileSign.mapper.HotDriverFileSignMapper; +import com.hotwj.platform.driverManagement.driverFileSign.service.IHotDriverFileSignService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员文件签订Service业务层处理 + * + * @author shihongwei + * @date 2025-12-22 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotDriverFileSignServiceImpl implements IHotDriverFileSignService { + + private final HotDriverFileSignMapper baseMapper; + + /** + * 查询驾驶员文件签订 + * + * @param id 主键 + * @return 驾驶员文件签订 + */ + @Override + public HotDriverFileSignVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询驾驶员文件签订列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员文件签订分页列表 + */ + @Override + public TableDataInfo queryPageList(HotDriverFileSignBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的驾驶员文件签订列表 + * + * @param bo 查询条件 + * @return 驾驶员文件签订列表 + */ + @Override + public List queryList(HotDriverFileSignBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotDriverFileSignBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotDriverFileSign::getId); + lqw.eq(bo.getDriverId() != null, HotDriverFileSign::getDriverId, bo.getDriverId()); + lqw.like(StringUtils.isNotBlank(bo.getFileName()), HotDriverFileSign::getFileName, bo.getFileName()); + lqw.eq(StringUtils.isNotBlank(bo.getSignFileImgUrl()), HotDriverFileSign::getSignFileImgUrl, bo.getSignFileImgUrl()); + lqw.like(StringUtils.isNotBlank(bo.getHandlerName()), HotDriverFileSign::getHandlerName, bo.getHandlerName()); + lqw.eq(bo.getSignTime() != null, HotDriverFileSign::getSignTime, bo.getSignTime()); + lqw.eq(bo.getExpireTime() != null, HotDriverFileSign::getExpireTime, bo.getExpireTime()); + lqw.eq(bo.getIsArchived() != null, HotDriverFileSign::getIsArchived, bo.getIsArchived()); + lqw.eq(bo.getIsDeleted() != null, HotDriverFileSign::getIsDeleted, bo.getIsDeleted()); + lqw.eq(bo.getCompanyId() != null, HotDriverFileSign::getCompanyId, bo.getCompanyId()); + return lqw; + } + + /** + * 新增驾驶员文件签订 + * + * @param bo 驾驶员文件签订 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotDriverFileSignBo bo) { + HotDriverFileSign add = MapstructUtils.convert(bo, HotDriverFileSign.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改驾驶员文件签订 + * + * @param bo 驾驶员文件签订 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotDriverFileSignBo bo) { + HotDriverFileSign update = MapstructUtils.convert(bo, HotDriverFileSign.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotDriverFileSign entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除驾驶员文件签订信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverGroup/controller/HotDriverGroupController.java b/src/main/java/com/hotwj/platform/driverManagement/driverGroup/controller/HotDriverGroupController.java new file mode 100644 index 0000000..3e6ad04 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverGroup/controller/HotDriverGroupController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.driverManagement.driverGroup.controller; + +import com.hotwj.platform.driverManagement.driverGroup.domain.bo.HotDriverGroupBo; +import com.hotwj.platform.driverManagement.driverGroup.domain.vo.HotDriverGroupVo; +import com.hotwj.platform.driverManagement.driverGroup.service.IHotDriverGroupService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 驾驶员分组 + * + * @author shihongwei + * @date 2025-12-16 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/driverManagement/driverGroup") +@Tag(name = "驾驶员分组", description = "分组管理") +public class HotDriverGroupController extends BaseController { + + private final IHotDriverGroupService hotDriverGroupService; + + /** + * 查询驾驶员分组列表 + */ + //@SaCheckPermission("driverManagement:driverGroup:list") + @GetMapping("/list") + @Operation(summary = "分页查询驾驶员分组列表") + public TableDataInfo list(HotDriverGroupBo bo, PageQuery pageQuery) { + return hotDriverGroupService.queryPageList(bo, pageQuery); + } + + /** + * 导出驾驶员分组列表 + */ + //@SaCheckPermission("driverManagement:driverGroup:export") + @Log(title = "驾驶员分组", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出驾驶员分组列表") + public void export(HotDriverGroupBo bo, HttpServletResponse response) { + List list = hotDriverGroupService.queryList(bo); + ExcelUtil.exportExcel(list, "驾驶员分组", HotDriverGroupVo.class, response); + } + + /** + * 获取驾驶员分组详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("driverManagement:driverGroup:query") + @GetMapping("/{id}") + @Operation(summary = "查询驾驶员分组详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotDriverGroupService.queryById(id)); + } + + /** + * 新增驾驶员分组 + */ + //@SaCheckPermission("driverManagement:driverGroup:add") + @Log(title = "驾驶员分组", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增驾驶员分组") + public R add(@Validated(AddGroup.class) @RequestBody HotDriverGroupBo bo) { + return toAjax(hotDriverGroupService.insertByBo(bo)); + } + + /** + * 修改驾驶员分组 + */ + //@SaCheckPermission("driverManagement:driverGroup:edit") + @Log(title = "驾驶员分组", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改驾驶员分组") + public R edit(@Validated(EditGroup.class) @RequestBody HotDriverGroupBo bo) { + return toAjax(hotDriverGroupService.updateByBo(bo)); + } + + /** + * 删除驾驶员分组 + * + * @param ids 主键串 + */ + //@SaCheckPermission("driverManagement:driverGroup:remove") + @Log(title = "驾驶员分组", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除驾驶员分组") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotDriverGroupService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverGroup/domain/HotDriverGroup.java b/src/main/java/com/hotwj/platform/driverManagement/driverGroup/domain/HotDriverGroup.java new file mode 100644 index 0000000..7b0f929 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverGroup/domain/HotDriverGroup.java @@ -0,0 +1,69 @@ +package com.hotwj.platform.driverManagement.driverGroup.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 驾驶员分组对象 hot_driver_group + * + * @author shihongwei + * @date 2025-12-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_driver_group") +public class HotDriverGroup extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 组名 + */ + private String groupName; + + /** + * 组长驾驶员ID + */ + private Long leaderId; + + /** + * 组长姓名 + */ + private String leaderName; + + /** + * 组员驾驶员ID列表(逗号分隔) + */ + private String memberIds; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverGroup/domain/bo/HotDriverGroupBo.java b/src/main/java/com/hotwj/platform/driverManagement/driverGroup/domain/bo/HotDriverGroupBo.java new file mode 100644 index 0000000..9bd9a36 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverGroup/domain/bo/HotDriverGroupBo.java @@ -0,0 +1,64 @@ +package com.hotwj.platform.driverManagement.driverGroup.domain.bo; + +import com.hotwj.platform.driverManagement.driverGroup.domain.HotDriverGroup; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 驾驶员分组业务对象 hot_driver_group + * + * @author shihongwei + * @date 2025-12-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotDriverGroup.class, reverseConvertGenerate = false) +public class HotDriverGroupBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 组名 + */ + private String groupName; + + /** + * 组长驾驶员ID + */ + private Long leaderId; + + /** + * 组长姓名 + */ + private String leaderName; + + /** + * 组员驾驶员ID列表(逗号分隔) + */ + private String memberIds; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverGroup/domain/vo/HotDriverGroupVo.java b/src/main/java/com/hotwj/platform/driverManagement/driverGroup/domain/vo/HotDriverGroupVo.java new file mode 100644 index 0000000..49e0dc1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverGroup/domain/vo/HotDriverGroupVo.java @@ -0,0 +1,79 @@ +package com.hotwj.platform.driverManagement.driverGroup.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.driverManagement.driverGroup.domain.HotDriverGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 驾驶员分组视图对象 hot_driver_group + * + * @author shihongwei + * @date 2025-12-16 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotDriverGroup.class) +public class HotDriverGroupVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 组名 + */ + @ExcelProperty(value = "组名") + private String groupName; + + /** + * 组长驾驶员ID + */ + @ExcelProperty(value = "组长驾驶员ID") + private Long leaderId; + + /** + * 组长姓名 + */ + @ExcelProperty(value = "组长姓名") + private String leaderName; + + /** + * 组员驾驶员ID列表(逗号分隔) + */ + @ExcelProperty(value = "组员驾驶员ID列表", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逗=号分隔") + private String memberIds; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverGroup/mapper/HotDriverGroupMapper.java b/src/main/java/com/hotwj/platform/driverManagement/driverGroup/mapper/HotDriverGroupMapper.java new file mode 100644 index 0000000..415543a --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverGroup/mapper/HotDriverGroupMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.driverManagement.driverGroup.mapper; + +import com.hotwj.platform.driverManagement.driverGroup.domain.HotDriverGroup; +import com.hotwj.platform.driverManagement.driverGroup.domain.vo.HotDriverGroupVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 驾驶员分组Mapper接口 + * + * @author shihongwei + * @date 2025-12-16 + */ +@Mapper +public interface HotDriverGroupMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverGroup/service/IHotDriverGroupService.java b/src/main/java/com/hotwj/platform/driverManagement/driverGroup/service/IHotDriverGroupService.java new file mode 100644 index 0000000..649b630 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverGroup/service/IHotDriverGroupService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.driverManagement.driverGroup.service; + +import com.hotwj.platform.driverManagement.driverGroup.domain.bo.HotDriverGroupBo; +import com.hotwj.platform.driverManagement.driverGroup.domain.vo.HotDriverGroupVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 驾驶员分组Service接口 + * + * @author shihongwei + * @date 2025-12-16 + */ +public interface IHotDriverGroupService { + + /** + * 查询驾驶员分组 + * + * @param id 主键 + * @return 驾驶员分组 + */ + HotDriverGroupVo queryById(Long id); + + /** + * 分页查询驾驶员分组列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员分组分页列表 + */ + TableDataInfo queryPageList(HotDriverGroupBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的驾驶员分组列表 + * + * @param bo 查询条件 + * @return 驾驶员分组列表 + */ + List queryList(HotDriverGroupBo bo); + + /** + * 新增驾驶员分组 + * + * @param bo 驾驶员分组 + * @return 是否新增成功 + */ + Boolean insertByBo(HotDriverGroupBo bo); + + /** + * 修改驾驶员分组 + * + * @param bo 驾驶员分组 + * @return 是否修改成功 + */ + Boolean updateByBo(HotDriverGroupBo bo); + + /** + * 校验并批量删除驾驶员分组信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverGroup/service/impl/HotDriverGroupServiceImpl.java b/src/main/java/com/hotwj/platform/driverManagement/driverGroup/service/impl/HotDriverGroupServiceImpl.java new file mode 100644 index 0000000..c570321 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverGroup/service/impl/HotDriverGroupServiceImpl.java @@ -0,0 +1,137 @@ +package com.hotwj.platform.driverManagement.driverGroup.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.driverGroup.domain.HotDriverGroup; +import com.hotwj.platform.driverManagement.driverGroup.domain.bo.HotDriverGroupBo; +import com.hotwj.platform.driverManagement.driverGroup.domain.vo.HotDriverGroupVo; +import com.hotwj.platform.driverManagement.driverGroup.mapper.HotDriverGroupMapper; +import com.hotwj.platform.driverManagement.driverGroup.service.IHotDriverGroupService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员分组Service业务层处理 + * + * @author shihongwei + * @date 2025-12-16 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotDriverGroupServiceImpl implements IHotDriverGroupService { + + private final HotDriverGroupMapper baseMapper; + + /** + * 查询驾驶员分组 + * + * @param id 主键 + * @return 驾驶员分组 + */ + @Override + public HotDriverGroupVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询驾驶员分组列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员分组分页列表 + */ + @Override + public TableDataInfo queryPageList(HotDriverGroupBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的驾驶员分组列表 + * + * @param bo 查询条件 + * @return 驾驶员分组列表 + */ + @Override + public List queryList(HotDriverGroupBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotDriverGroupBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotDriverGroup::getId); + lqw.eq(bo.getCompanyId() != null, HotDriverGroup::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getGroupName()), HotDriverGroup::getGroupName, bo.getGroupName()); + lqw.eq(bo.getLeaderId() != null, HotDriverGroup::getLeaderId, bo.getLeaderId()); + lqw.like(StringUtils.isNotBlank(bo.getLeaderName()), HotDriverGroup::getLeaderName, bo.getLeaderName()); + lqw.eq(StringUtils.isNotBlank(bo.getMemberIds()), HotDriverGroup::getMemberIds, bo.getMemberIds()); + lqw.eq(bo.getIsDeleted() != null, HotDriverGroup::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增驾驶员分组 + * + * @param bo 驾驶员分组 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotDriverGroupBo bo) { + HotDriverGroup add = MapstructUtils.convert(bo, HotDriverGroup.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改驾驶员分组 + * + * @param bo 驾驶员分组 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotDriverGroupBo bo) { + HotDriverGroup update = MapstructUtils.convert(bo, HotDriverGroup.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotDriverGroup entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除驾驶员分组信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/controller/HotDriverHealthReportController.java b/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/controller/HotDriverHealthReportController.java new file mode 100644 index 0000000..afa11c0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/controller/HotDriverHealthReportController.java @@ -0,0 +1,127 @@ +package com.hotwj.platform.driverManagement.driverHealthReport.controller; + +import com.hotwj.platform.driverManagement.driverHealthReport.domain.bo.HotDriverHealthReportBo; +import com.hotwj.platform.driverManagement.driverHealthReport.domain.vo.HotDriverHealthReportVo; +import com.hotwj.platform.driverManagement.driverHealthReport.service.IHotDriverHealthReportService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 驾驶员体检报告 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/driverManagement/driverHealthReport") +@Tag(name = "驾驶员体检报告", description = "体检报告管理") +public class HotDriverHealthReportController extends BaseController { + + private final IHotDriverHealthReportService hotDriverHealthReportService; + + /** + * 查询驾驶员体检报告列表 + */ + //@SaCheckPermission("driverManagement:driver:list") + @GetMapping("/list") + @Operation(summary = "分页查询驾驶员体检报告列表") + public TableDataInfo list(HotDriverHealthReportBo bo, PageQuery pageQuery) { + return hotDriverHealthReportService.queryPageList(bo, pageQuery); + } + + /** + * 导出驾驶员体检报告列表 + */ + //@SaCheckPermission("driverManagement:driver:export") + @Log(title = "驾驶员体检报告", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出驾驶员体检报告列表") + public void export(HotDriverHealthReportBo bo, HttpServletResponse response) { + List list = hotDriverHealthReportService.queryList(bo); + ExcelUtil.exportExcel(list, "驾驶员体检报告", HotDriverHealthReportVo.class, response); + } + + /** + * 获取驾驶员体检报告详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("driverManagement:driver:query") + @GetMapping("/{id}") + @Operation(summary = "查询驾驶员体检报告详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotDriverHealthReportService.queryById(id)); + } + + /** + * 归档驾驶员体检报告 + * + * @param id 主键 + */ + //@SaCheckPermission("driverManagement:driverHealthReport:edit") + @Log(title = "驾驶员体检报告", businessType = BusinessType.UPDATE) + @PutMapping("/archive/{id}") + @Operation(summary = "归档驾驶员体检报告") + public R archive(@NotNull(message = "主键不能为空") @PathVariable Long id) { + return toAjax(hotDriverHealthReportService.archive(id)); + } + + /** + * 新增驾驶员体检报告 + */ + //@SaCheckPermission("driverManagement:driver:add") + @Log(title = "驾驶员体检报告", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增驾驶员体检报告") + public R add(@Validated(AddGroup.class) @RequestBody HotDriverHealthReportBo bo) { + return toAjax(hotDriverHealthReportService.insertByBo(bo)); + } + + /** + * 修改驾驶员体检报告 + */ + //@SaCheckPermission("driverManagement:driverHealthReport:edit") + @Log(title = "驾驶员体检报告", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改驾驶员体检报告") + public R edit(@Validated(EditGroup.class) @RequestBody HotDriverHealthReportBo bo) { + return toAjax(hotDriverHealthReportService.updateByBo(bo)); + } + + /** + * 删除驾驶员体检报告 + * + * @param ids 主键串 + */ + //@SaCheckPermission("driverManagement:driver:remove") + @Log(title = "驾驶员体检报告", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除驾驶员体检报告") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotDriverHealthReportService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/domain/HotDriverHealthReport.java b/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/domain/HotDriverHealthReport.java new file mode 100644 index 0000000..8bc4d1b --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/domain/HotDriverHealthReport.java @@ -0,0 +1,90 @@ +package com.hotwj.platform.driverManagement.driverHealthReport.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 驾驶员体检报告对象 hot_driver_health_report + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_driver_health_report") +public class HotDriverHealthReport extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 体检报告日期 + */ + private Date reportDate; + + /** + * 医院 + */ + private String hospital; + + /** + * 体检结果(字典) + */ + private String healthStatus; + + /** + * 记录人 + */ + private Long recorderId; + + /** + * 体检报告附件URL + */ + private String attachmentUrl; + + /** + * 预览图URL + */ + private String previewImageUrl; + + /** + * 备注/内部编号 + */ + private String remark; + + /** + * 是否归档 (0=未归档, 1=已归档) + */ + private Integer archiveStatus; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/domain/bo/HotDriverHealthReportBo.java b/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/domain/bo/HotDriverHealthReportBo.java new file mode 100644 index 0000000..f05cace --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/domain/bo/HotDriverHealthReportBo.java @@ -0,0 +1,84 @@ +package com.hotwj.platform.driverManagement.driverHealthReport.domain.bo; + +import com.hotwj.platform.driverManagement.driverHealthReport.domain.HotDriverHealthReport; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 驾驶员体检报告业务对象 hot_driver_health_report + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotDriverHealthReport.class, reverseConvertGenerate = false) +public class HotDriverHealthReportBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 体检报告日期 + */ + private Date reportDate; + + /** + * 医院 + */ + private String hospital; + + /** + * 体检结果(字典) + */ + private String healthStatus; + + /** + * 记录人 + */ + private Long recorderId; + + /** + * 体检报告附件URL + */ + private String attachmentUrl; + + /** + * 预览图URL + */ + private String previewImageUrl; + + /** + * 备注/内部编号 + */ + private String remark; + + /** + * 是否归档 (0=未归档, 1=已归档) + */ + private Integer archiveStatus; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/domain/vo/HotDriverHealthReportVo.java b/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/domain/vo/HotDriverHealthReportVo.java new file mode 100644 index 0000000..62e0615 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/domain/vo/HotDriverHealthReportVo.java @@ -0,0 +1,110 @@ +package com.hotwj.platform.driverManagement.driverHealthReport.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.driverManagement.driverHealthReport.domain.HotDriverHealthReport; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 驾驶员体检报告视图对象 hot_driver_health_report + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotDriverHealthReport.class) +public class HotDriverHealthReportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 驾驶员ID + */ + @ExcelProperty(value = "驾驶员ID") + private String driverId; + + /** + * 体检报告日期 + */ + @ExcelProperty(value = "体检报告日期") + private Date reportDate; + + /** + * 医院 + */ + @ExcelProperty(value = "医院") + private String hospital; + + /** + * 体检结果(字典) + */ + @ExcelProperty(value = "体检结果", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "字=典") + private String healthStatus; + + /** + * 记录人 + */ + @ExcelProperty(value = "记录人") + private Long recorderId; + + @Translation(type = TransConstant.SAFETY_MANAGER_ID_TO_NAME, mapper = "recorderId") + private String recorderName; + + /** + * 体检报告附件URL + */ + @ExcelProperty(value = "体检报告附件URL") + private String attachmentUrl; + + /** + * 预览图URL + */ + @ExcelProperty(value = "预览图URL") + private String previewImageUrl; + + /** + * 备注/内部编号 + */ + @ExcelProperty(value = "备注/内部编号") + private String remark; + + /** + * 是否归档 (0=未归档, 1=已归档) + */ + @ExcelProperty(value = "是否归档", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=未归档,1=已归档") + private Integer archiveStatus; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/mapper/HotDriverHealthReportMapper.java b/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/mapper/HotDriverHealthReportMapper.java new file mode 100644 index 0000000..9e90579 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/mapper/HotDriverHealthReportMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.driverManagement.driverHealthReport.mapper; + +import com.hotwj.platform.driverManagement.driverHealthReport.domain.HotDriverHealthReport; +import com.hotwj.platform.driverManagement.driverHealthReport.domain.vo.HotDriverHealthReportVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 驾驶员体检报告Mapper接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Mapper +public interface HotDriverHealthReportMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/service/IHotDriverHealthReportService.java b/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/service/IHotDriverHealthReportService.java new file mode 100644 index 0000000..a921e2b --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/service/IHotDriverHealthReportService.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.driverManagement.driverHealthReport.service; + +import com.hotwj.platform.driverManagement.driverHealthReport.domain.bo.HotDriverHealthReportBo; +import com.hotwj.platform.driverManagement.driverHealthReport.domain.vo.HotDriverHealthReportVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 驾驶员体检报告Service接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +public interface IHotDriverHealthReportService { + + /** + * 查询驾驶员体检报告 + * + * @param id 主键 + * @return 驾驶员体检报告 + */ + HotDriverHealthReportVo queryById(Long id); + + /** + * 分页查询驾驶员体检报告列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员体检报告分页列表 + */ + TableDataInfo queryPageList(HotDriverHealthReportBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的驾驶员体检报告列表 + * + * @param bo 查询条件 + * @return 驾驶员体检报告列表 + */ + List queryList(HotDriverHealthReportBo bo); + + /** + * 新增驾驶员体检报告 + * + * @param bo 驾驶员体检报告 + * @return 是否新增成功 + */ + Boolean insertByBo(HotDriverHealthReportBo bo); + + /** + * 修改驾驶员体检报告 + * + * @param bo 驾驶员体检报告 + * @return 是否修改成功 + */ + Boolean updateByBo(HotDriverHealthReportBo bo); + + /** + * 归档驾驶员体检报告 + * + * @param id 主键 + * @return 是否归档成功 + */ + Boolean archive(Long id); + + /** + * 校验并批量删除驾驶员体检报告信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/service/impl/HotDriverHealthReportServiceImpl.java b/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/service/impl/HotDriverHealthReportServiceImpl.java new file mode 100644 index 0000000..ab744d6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverHealthReport/service/impl/HotDriverHealthReportServiceImpl.java @@ -0,0 +1,170 @@ +package com.hotwj.platform.driverManagement.driverHealthReport.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.driverHealthReport.domain.HotDriverHealthReport; +import com.hotwj.platform.driverManagement.driverHealthReport.domain.bo.HotDriverHealthReportBo; +import com.hotwj.platform.driverManagement.driverHealthReport.domain.vo.HotDriverHealthReportVo; +import com.hotwj.platform.driverManagement.driverHealthReport.mapper.HotDriverHealthReportMapper; +import com.hotwj.platform.driverManagement.driverHealthReport.service.IHotDriverHealthReportService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员体检报告Service业务层处理 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotDriverHealthReportServiceImpl implements IHotDriverHealthReportService { + + private final HotDriverHealthReportMapper baseMapper; + + /** + * 查询驾驶员体检报告 + * + * @param id 主键 + * @return 驾驶员体检报告 + */ + @Override + public HotDriverHealthReportVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询驾驶员体检报告列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员体检报告分页列表 + */ + @Override + public TableDataInfo queryPageList(HotDriverHealthReportBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的驾驶员体检报告列表 + * + * @param bo 查询条件 + * @return 驾驶员体检报告列表 + */ + @Override + public List queryList(HotDriverHealthReportBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotDriverHealthReportBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotDriverHealthReport::getReportDate); + lqw.eq(bo.getCompanyId() != null, HotDriverHealthReport::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getDriverId() != null, HotDriverHealthReport::getDriverId, bo.getDriverId()); + lqw.eq(bo.getReportDate() != null, HotDriverHealthReport::getReportDate, bo.getReportDate()); + lqw.eq(StringUtils.isNotBlank(bo.getHospital()), HotDriverHealthReport::getHospital, bo.getHospital()); + lqw.eq(StringUtils.isNotBlank(bo.getHealthStatus()), HotDriverHealthReport::getHealthStatus, bo.getHealthStatus()); + lqw.eq(bo.getRecorderId() != null, HotDriverHealthReport::getRecorderId, bo.getRecorderId()); + lqw.eq(StringUtils.isNotBlank(bo.getAttachmentUrl()), HotDriverHealthReport::getAttachmentUrl, bo.getAttachmentUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getPreviewImageUrl()), HotDriverHealthReport::getPreviewImageUrl, bo.getPreviewImageUrl()); + lqw.eq(bo.getArchiveStatus() != null, HotDriverHealthReport::getArchiveStatus, bo.getArchiveStatus()); + lqw.eq(bo.getIsDeleted() != null, HotDriverHealthReport::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 归档驾驶员体检报告 + * + * @param id 主键 + * @return 是否归档成功 + */ + @Override + public Boolean archive(Long id) { + HotDriverHealthReport report = baseMapper.selectById(id); + if (report == null) { + throw new ServiceException("体检报告不存在"); + } + if (report.getArchiveStatus() != null && report.getArchiveStatus() == 1) { + throw new ServiceException("该体检报告已归档"); + } + report.setArchiveStatus(1); + return baseMapper.updateById(report) > 0; + } + + /** + * 新增驾驶员体检报告 + * + * @param bo 驾驶员体检报告 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotDriverHealthReportBo bo) { + HotDriverHealthReport add = MapstructUtils.convert(bo, HotDriverHealthReport.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改驾驶员体检报告 + * + * @param bo 驾驶员体检报告 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotDriverHealthReportBo bo) { + HotDriverHealthReport old = baseMapper.selectById(bo.getId()); + if (old != null && old.getArchiveStatus() != null && old.getArchiveStatus() == 1) { + throw new ServiceException("该体检报告已归档,不可修改"); + } + HotDriverHealthReport update = MapstructUtils.convert(bo, HotDriverHealthReport.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotDriverHealthReport entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除驾驶员体检报告信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + List list = baseMapper.selectBatchIds(ids); + for (HotDriverHealthReport report : list) { + if (report.getArchiveStatus() != null && report.getArchiveStatus() == 1) { + throw new ServiceException("包含已归档的体检报告,不可删除"); + } + } + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/controller/HotDriverLaborContractController.java b/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/controller/HotDriverLaborContractController.java new file mode 100644 index 0000000..149051e --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/controller/HotDriverLaborContractController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.driverManagement.driverLaborContract.controller; + +import com.hotwj.platform.driverManagement.driverLaborContract.domain.bo.HotDriverLaborContractBo; +import com.hotwj.platform.driverManagement.driverLaborContract.domain.vo.HotDriverLaborContractVo; +import com.hotwj.platform.driverManagement.driverLaborContract.service.IHotDriverLaborContractService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 驾驶员劳动合同 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/driverManagement/driverLaborContract") +@Tag(name = "驾驶员劳动合同", description = "劳动合同管理") +public class HotDriverLaborContractController extends BaseController { + + private final IHotDriverLaborContractService hotDriverLaborContractService; + + /** + * 查询驾驶员劳动合同列表 + */ + //@SaCheckPermission("driverManagement:driver:list") + @GetMapping("/list") + @Operation(summary = "分页查询驾驶员劳动合同列表") + public TableDataInfo list(HotDriverLaborContractBo bo, PageQuery pageQuery) { + return hotDriverLaborContractService.queryPageList(bo, pageQuery); + } + + /** + * 导出驾驶员劳动合同列表 + */ + //@SaCheckPermission("driverManagement:driver:export") + @Log(title = "驾驶员劳动合同", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出驾驶员劳动合同列表") + public void export(HotDriverLaborContractBo bo, HttpServletResponse response) { + List list = hotDriverLaborContractService.queryList(bo); + ExcelUtil.exportExcel(list, "驾驶员劳动合同", HotDriverLaborContractVo.class, response); + } + + /** + * 获取驾驶员劳动合同详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("driverManagement:driver:query") + @GetMapping("/{id}") + @Operation(summary = "查询驾驶员劳动合同详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotDriverLaborContractService.queryById(id)); + } + + /** + * 新增驾驶员劳动合同 + */ + //@SaCheckPermission("driverManagement:driver:add") + @Log(title = "驾驶员劳动合同", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增驾驶员劳动合同") + public R add(@Validated(AddGroup.class) @RequestBody HotDriverLaborContractBo bo) { + return toAjax(hotDriverLaborContractService.insertByBo(bo)); + } + + /** + * 修改驾驶员劳动合同 + */ + //@SaCheckPermission("driverManagement:driver:edit") + @Log(title = "驾驶员劳动合同", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改驾驶员劳动合同") + public R edit(@Validated(EditGroup.class) @RequestBody HotDriverLaborContractBo bo) { + return toAjax(hotDriverLaborContractService.updateByBo(bo)); + } + + /** + * 删除驾驶员劳动合同 + * + * @param ids 主键串 + */ + //@SaCheckPermission("driverManagement:driver:remove") + @Log(title = "驾驶员劳动合同", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除驾驶员劳动合同") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotDriverLaborContractService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/domain/HotDriverLaborContract.java b/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/domain/HotDriverLaborContract.java new file mode 100644 index 0000000..79952c6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/domain/HotDriverLaborContract.java @@ -0,0 +1,75 @@ +package com.hotwj.platform.driverManagement.driverLaborContract.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 驾驶员劳动合同对象 hot_driver_labor_contract + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_driver_labor_contract") +public class HotDriverLaborContract extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 合同开始日期 + */ + private Date effectiveDate; + + /** + * 合同结束日期 + */ + private Date expireDate; + + /** + * 合同图片或附件URL + */ + private String attachmentUrl; + + /** + * 备注/内部编号 + */ + private String remark; + + /** + * 是否归档:1是 0否 + */ + private Long isArchived; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/domain/bo/HotDriverLaborContractBo.java b/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/domain/bo/HotDriverLaborContractBo.java new file mode 100644 index 0000000..f4331ca --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/domain/bo/HotDriverLaborContractBo.java @@ -0,0 +1,71 @@ +package com.hotwj.platform.driverManagement.driverLaborContract.domain.bo; + +import com.hotwj.platform.driverManagement.driverLaborContract.domain.HotDriverLaborContract; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 驾驶员劳动合同业务对象 hot_driver_labor_contract + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotDriverLaborContract.class, reverseConvertGenerate = false) +public class HotDriverLaborContractBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 合同开始日期 + */ + private Date effectiveDate; + + /** + * 合同结束日期 + */ + private Date expireDate; + + /** + * 合同图片或附件URL + */ + private String attachmentUrl; + + /** + * 备注/内部编号 + */ + private String remark; + + /** + * 是否归档:1是 0否 + */ + private Long isArchived; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/domain/vo/HotDriverLaborContractVo.java b/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/domain/vo/HotDriverLaborContractVo.java new file mode 100644 index 0000000..0e5766a --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/domain/vo/HotDriverLaborContractVo.java @@ -0,0 +1,83 @@ +package com.hotwj.platform.driverManagement.driverLaborContract.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.driverManagement.driverLaborContract.domain.HotDriverLaborContract; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 驾驶员劳动合同视图对象 hot_driver_labor_contract + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotDriverLaborContract.class) +public class HotDriverLaborContractVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 驾驶员ID + */ + @ExcelProperty(value = "驾驶员ID") + private String driverId; + + /** + * 合同开始日期 + */ + @ExcelProperty(value = "合同开始日期") + private Date effectiveDate; + + /** + * 合同结束日期 + */ + @ExcelProperty(value = "合同结束日期") + private Date expireDate; + + /** + * 合同图片或附件URL + */ + @ExcelProperty(value = "合同图片或附件URL") + private String attachmentUrl; + + /** + * 备注/内部编号 + */ + @ExcelProperty(value = "备注/内部编号") + private String remark; + + /** + * 是否归档:1是 0否 + */ + @ExcelProperty(value = "是否归档:1是 0否") + private Long isArchived; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/mapper/HotDriverLaborContractMapper.java b/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/mapper/HotDriverLaborContractMapper.java new file mode 100644 index 0000000..9523b9b --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/mapper/HotDriverLaborContractMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.driverManagement.driverLaborContract.mapper; + +import com.hotwj.platform.driverManagement.driverLaborContract.domain.HotDriverLaborContract; +import com.hotwj.platform.driverManagement.driverLaborContract.domain.vo.HotDriverLaborContractVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 驾驶员劳动合同Mapper接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Mapper +public interface HotDriverLaborContractMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/service/IHotDriverLaborContractService.java b/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/service/IHotDriverLaborContractService.java new file mode 100644 index 0000000..fb310f5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/service/IHotDriverLaborContractService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.driverManagement.driverLaborContract.service; + +import com.hotwj.platform.driverManagement.driverLaborContract.domain.bo.HotDriverLaborContractBo; +import com.hotwj.platform.driverManagement.driverLaborContract.domain.vo.HotDriverLaborContractVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 驾驶员劳动合同Service接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +public interface IHotDriverLaborContractService { + + /** + * 查询驾驶员劳动合同 + * + * @param id 主键 + * @return 驾驶员劳动合同 + */ + HotDriverLaborContractVo queryById(Long id); + + /** + * 分页查询驾驶员劳动合同列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员劳动合同分页列表 + */ + TableDataInfo queryPageList(HotDriverLaborContractBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的驾驶员劳动合同列表 + * + * @param bo 查询条件 + * @return 驾驶员劳动合同列表 + */ + List queryList(HotDriverLaborContractBo bo); + + /** + * 新增驾驶员劳动合同 + * + * @param bo 驾驶员劳动合同 + * @return 是否新增成功 + */ + Boolean insertByBo(HotDriverLaborContractBo bo); + + /** + * 修改驾驶员劳动合同 + * + * @param bo 驾驶员劳动合同 + * @return 是否修改成功 + */ + Boolean updateByBo(HotDriverLaborContractBo bo); + + /** + * 校验并批量删除驾驶员劳动合同信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/service/impl/HotDriverLaborContractServiceImpl.java b/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/service/impl/HotDriverLaborContractServiceImpl.java new file mode 100644 index 0000000..e15e1ba --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverLaborContract/service/impl/HotDriverLaborContractServiceImpl.java @@ -0,0 +1,138 @@ +package com.hotwj.platform.driverManagement.driverLaborContract.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.driverLaborContract.domain.HotDriverLaborContract; +import com.hotwj.platform.driverManagement.driverLaborContract.domain.bo.HotDriverLaborContractBo; +import com.hotwj.platform.driverManagement.driverLaborContract.domain.vo.HotDriverLaborContractVo; +import com.hotwj.platform.driverManagement.driverLaborContract.mapper.HotDriverLaborContractMapper; +import com.hotwj.platform.driverManagement.driverLaborContract.service.IHotDriverLaborContractService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员劳动合同Service业务层处理 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotDriverLaborContractServiceImpl implements IHotDriverLaborContractService { + + private final HotDriverLaborContractMapper baseMapper; + + /** + * 查询驾驶员劳动合同 + * + * @param id 主键 + * @return 驾驶员劳动合同 + */ + @Override + public HotDriverLaborContractVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询驾驶员劳动合同列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员劳动合同分页列表 + */ + @Override + public TableDataInfo queryPageList(HotDriverLaborContractBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的驾驶员劳动合同列表 + * + * @param bo 查询条件 + * @return 驾驶员劳动合同列表 + */ + @Override + public List queryList(HotDriverLaborContractBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotDriverLaborContractBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotDriverLaborContract::getId); + lqw.eq(bo.getCompanyId() != null, HotDriverLaborContract::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getDriverId() != null, HotDriverLaborContract::getDriverId, bo.getDriverId()); + lqw.eq(bo.getEffectiveDate() != null, HotDriverLaborContract::getEffectiveDate, bo.getEffectiveDate()); + lqw.eq(bo.getExpireDate() != null, HotDriverLaborContract::getExpireDate, bo.getExpireDate()); + lqw.eq(StringUtils.isNotBlank(bo.getAttachmentUrl()), HotDriverLaborContract::getAttachmentUrl, bo.getAttachmentUrl()); + lqw.eq(bo.getIsArchived() != null, HotDriverLaborContract::getIsArchived, bo.getIsArchived()); + lqw.eq(bo.getIsDeleted() != null, HotDriverLaborContract::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增驾驶员劳动合同 + * + * @param bo 驾驶员劳动合同 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotDriverLaborContractBo bo) { + HotDriverLaborContract add = MapstructUtils.convert(bo, HotDriverLaborContract.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改驾驶员劳动合同 + * + * @param bo 驾驶员劳动合同 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotDriverLaborContractBo bo) { + HotDriverLaborContract update = MapstructUtils.convert(bo, HotDriverLaborContract.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotDriverLaborContract entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除驾驶员劳动合同信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/controller/HotDriverProfCertController.java b/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/controller/HotDriverProfCertController.java new file mode 100644 index 0000000..964727c --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/controller/HotDriverProfCertController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.driverManagement.driverProfCert.controller; + +import com.hotwj.platform.driverManagement.driverProfCert.domain.bo.HotDriverProfCertBo; +import com.hotwj.platform.driverManagement.driverProfCert.domain.vo.HotDriverProfCertVo; +import com.hotwj.platform.driverManagement.driverProfCert.service.IHotDriverProfCertService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 从业资格证 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/driverManagement/driverProfCert") +@Tag(name = "从业资格证", description = "从业资格证管理") +public class HotDriverProfCertController extends BaseController { + + private final IHotDriverProfCertService hotDriverProfCertService; + + /** + * 查询从业资格证列表 + */ + //@SaCheckPermission("driverManagement:driver:list") + @GetMapping("/list") + @Operation(summary = "分页查询从业资格证列表") + public TableDataInfo list(HotDriverProfCertBo bo, PageQuery pageQuery) { + return hotDriverProfCertService.queryPageList(bo, pageQuery); + } + + /** + * 导出从业资格证列表 + */ + //@SaCheckPermission("driverManagement:driver:export") + @Log(title = "从业资格证", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出从业资格证列表") + public void export(HotDriverProfCertBo bo, HttpServletResponse response) { + List list = hotDriverProfCertService.queryList(bo); + ExcelUtil.exportExcel(list, "从业资格证", HotDriverProfCertVo.class, response); + } + + /** + * 获取从业资格证详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("driverManagement:driver:query") + @GetMapping("/{id}") + @Operation(summary = "查询从业资格证详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotDriverProfCertService.queryById(id)); + } + + /** + * 新增从业资格证 + */ + //@SaCheckPermission("driverManagement:driver:add") + @Log(title = "从业资格证", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增从业资格证") + public R add(@Validated(AddGroup.class) @RequestBody HotDriverProfCertBo bo) { + return toAjax(hotDriverProfCertService.insertByBo(bo)); + } + + /** + * 修改从业资格证 + */ + //@SaCheckPermission("driverManagement:driver:edit") + @Log(title = "从业资格证", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改从业资格证") + public R edit(@Validated(EditGroup.class) @RequestBody HotDriverProfCertBo bo) { + return toAjax(hotDriverProfCertService.updateByBo(bo)); + } + + /** + * 删除从业资格证 + * + * @param ids 主键串 + */ + //@SaCheckPermission("driverManagement:driver:remove") + @Log(title = "从业资格证", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除从业资格证") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotDriverProfCertService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/domain/HotDriverProfCert.java b/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/domain/HotDriverProfCert.java new file mode 100644 index 0000000..a0dc6db --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/domain/HotDriverProfCert.java @@ -0,0 +1,95 @@ +package com.hotwj.platform.driverManagement.driverProfCert.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 从业资格证对象 hot_driver_prof_cert + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_driver_prof_cert") +public class HotDriverProfCert extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private Long driverId; + + /** + * 从业资格证类别 + */ + private String category; + + /** + * 资格证号 + */ + private String certNo; + + /** + * 初次领证日期 + */ + private Date firstIssueDate; + + /** + * 有效起始日期 + */ + private Date validFrom; + + /** + * 有效终止日期 + */ + private Date validTo; + + /** + * 有效期描述 + */ + private String validityTerm; + + /** + * 发证机关 + */ + private String issuingAuthority; + + /** + * 从业资格证附件URL + */ + private String attachmentUrl; + + /** + * 状态:1=正常 0=禁用 + */ + private Long status; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/domain/bo/HotDriverProfCertBo.java b/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/domain/bo/HotDriverProfCertBo.java new file mode 100644 index 0000000..da02e0b --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/domain/bo/HotDriverProfCertBo.java @@ -0,0 +1,91 @@ +package com.hotwj.platform.driverManagement.driverProfCert.domain.bo; + +import com.hotwj.platform.driverManagement.driverProfCert.domain.HotDriverProfCert; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 从业资格证业务对象 hot_driver_prof_cert + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotDriverProfCert.class, reverseConvertGenerate = false) +public class HotDriverProfCertBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private Long driverId; + + /** + * 从业资格证类别 + */ + private String category; + + /** + * 资格证号 + */ + private String certNo; + + /** + * 初次领证日期 + */ + private Date firstIssueDate; + + /** + * 有效起始日期 + */ + private Date validFrom; + + /** + * 有效终止日期 + */ + private Date validTo; + + /** + * 有效期描述 + */ + private String validityTerm; + + /** + * 发证机关 + */ + private String issuingAuthority; + + /** + * 从业资格证附件URL + */ + private String attachmentUrl; + + /** + * 状态:1=正常 0=禁用 + */ + private Long status; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/domain/vo/HotDriverProfCertVo.java b/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/domain/vo/HotDriverProfCertVo.java new file mode 100644 index 0000000..553d2d2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/domain/vo/HotDriverProfCertVo.java @@ -0,0 +1,107 @@ +package com.hotwj.platform.driverManagement.driverProfCert.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.driverManagement.driverProfCert.domain.HotDriverProfCert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 从业资格证视图对象 hot_driver_prof_cert + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotDriverProfCert.class) +public class HotDriverProfCertVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 驾驶员ID + */ + @ExcelProperty(value = "驾驶员ID") + private Long driverId; + + /** + * 从业资格证类别 + */ + @ExcelProperty(value = "从业资格证类别") + private String category; + + /** + * 资格证号 + */ + @ExcelProperty(value = "资格证号") + private String certNo; + + /** + * 初次领证日期 + */ + @ExcelProperty(value = "初次领证日期") + private Date firstIssueDate; + + /** + * 有效起始日期 + */ + @ExcelProperty(value = "有效起始日期") + private Date validFrom; + + /** + * 有效终止日期 + */ + @ExcelProperty(value = "有效终止日期") + private Date validTo; + + /** + * 有效期描述 + */ + @ExcelProperty(value = "有效期描述") + private String validityTerm; + + /** + * 发证机关 + */ + @ExcelProperty(value = "发证机关") + private String issuingAuthority; + + /** + * 从业资格证附件URL + */ + @ExcelProperty(value = "从业资格证附件URL") + private String attachmentUrl; + + /** + * 状态:1=正常 0=禁用 + */ + @ExcelProperty(value = "状态:1=正常 0=禁用") + private Long status; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/mapper/HotDriverProfCertMapper.java b/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/mapper/HotDriverProfCertMapper.java new file mode 100644 index 0000000..b984580 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/mapper/HotDriverProfCertMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.driverManagement.driverProfCert.mapper; + +import com.hotwj.platform.driverManagement.driverProfCert.domain.HotDriverProfCert; +import com.hotwj.platform.driverManagement.driverProfCert.domain.vo.HotDriverProfCertVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 从业资格证Mapper接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Mapper +public interface HotDriverProfCertMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/service/IHotDriverProfCertService.java b/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/service/IHotDriverProfCertService.java new file mode 100644 index 0000000..7157162 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/service/IHotDriverProfCertService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.driverManagement.driverProfCert.service; + +import com.hotwj.platform.driverManagement.driverProfCert.domain.bo.HotDriverProfCertBo; +import com.hotwj.platform.driverManagement.driverProfCert.domain.vo.HotDriverProfCertVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 从业资格证Service接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +public interface IHotDriverProfCertService { + + /** + * 查询从业资格证 + * + * @param id 主键 + * @return 从业资格证 + */ + HotDriverProfCertVo queryById(Long id); + + /** + * 分页查询从业资格证列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 从业资格证分页列表 + */ + TableDataInfo queryPageList(HotDriverProfCertBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的从业资格证列表 + * + * @param bo 查询条件 + * @return 从业资格证列表 + */ + List queryList(HotDriverProfCertBo bo); + + /** + * 新增从业资格证 + * + * @param bo 从业资格证 + * @return 是否新增成功 + */ + Boolean insertByBo(HotDriverProfCertBo bo); + + /** + * 修改从业资格证 + * + * @param bo 从业资格证 + * @return 是否修改成功 + */ + Boolean updateByBo(HotDriverProfCertBo bo); + + /** + * 校验并批量删除从业资格证信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/service/impl/HotDriverProfCertServiceImpl.java b/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/service/impl/HotDriverProfCertServiceImpl.java new file mode 100644 index 0000000..5a28c7f --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverProfCert/service/impl/HotDriverProfCertServiceImpl.java @@ -0,0 +1,143 @@ +package com.hotwj.platform.driverManagement.driverProfCert.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.driverProfCert.domain.HotDriverProfCert; +import com.hotwj.platform.driverManagement.driverProfCert.domain.bo.HotDriverProfCertBo; +import com.hotwj.platform.driverManagement.driverProfCert.domain.vo.HotDriverProfCertVo; +import com.hotwj.platform.driverManagement.driverProfCert.mapper.HotDriverProfCertMapper; +import com.hotwj.platform.driverManagement.driverProfCert.service.IHotDriverProfCertService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 从业资格证Service业务层处理 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotDriverProfCertServiceImpl implements IHotDriverProfCertService { + + private final HotDriverProfCertMapper baseMapper; + + /** + * 查询从业资格证 + * + * @param id 主键 + * @return 从业资格证 + */ + @Override + public HotDriverProfCertVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询从业资格证列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 从业资格证分页列表 + */ + @Override + public TableDataInfo queryPageList(HotDriverProfCertBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的从业资格证列表 + * + * @param bo 查询条件 + * @return 从业资格证列表 + */ + @Override + public List queryList(HotDriverProfCertBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotDriverProfCertBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotDriverProfCert::getId); + lqw.eq(bo.getCompanyId() != null, HotDriverProfCert::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getDriverId() != null, HotDriverProfCert::getDriverId, bo.getDriverId()); + lqw.eq(StringUtils.isNotBlank(bo.getCategory()), HotDriverProfCert::getCategory, bo.getCategory()); + lqw.eq(StringUtils.isNotBlank(bo.getCertNo()), HotDriverProfCert::getCertNo, bo.getCertNo()); + lqw.eq(bo.getFirstIssueDate() != null, HotDriverProfCert::getFirstIssueDate, bo.getFirstIssueDate()); + lqw.eq(bo.getValidFrom() != null, HotDriverProfCert::getValidFrom, bo.getValidFrom()); + lqw.eq(bo.getValidTo() != null, HotDriverProfCert::getValidTo, bo.getValidTo()); + lqw.eq(StringUtils.isNotBlank(bo.getValidityTerm()), HotDriverProfCert::getValidityTerm, bo.getValidityTerm()); + lqw.eq(StringUtils.isNotBlank(bo.getIssuingAuthority()), HotDriverProfCert::getIssuingAuthority, bo.getIssuingAuthority()); + lqw.eq(StringUtils.isNotBlank(bo.getAttachmentUrl()), HotDriverProfCert::getAttachmentUrl, bo.getAttachmentUrl()); + lqw.eq(bo.getStatus() != null, HotDriverProfCert::getStatus, bo.getStatus()); + lqw.eq(bo.getIsDeleted() != null, HotDriverProfCert::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增从业资格证 + * + * @param bo 从业资格证 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotDriverProfCertBo bo) { + HotDriverProfCert add = MapstructUtils.convert(bo, HotDriverProfCert.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改从业资格证 + * + * @param bo 从业资格证 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotDriverProfCertBo bo) { + HotDriverProfCert update = MapstructUtils.convert(bo, HotDriverProfCert.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotDriverProfCert entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除从业资格证信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/controller/HotDriverRewardPunishmentController.java b/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/controller/HotDriverRewardPunishmentController.java new file mode 100644 index 0000000..2a5537d --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/controller/HotDriverRewardPunishmentController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.driverManagement.driverRewardPunishment.controller; + +import com.hotwj.platform.driverManagement.driverRewardPunishment.domain.bo.HotDriverRewardPunishmentBo; +import com.hotwj.platform.driverManagement.driverRewardPunishment.domain.vo.HotDriverRewardPunishmentVo; +import com.hotwj.platform.driverManagement.driverRewardPunishment.service.IHotDriverRewardPunishmentService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 驾驶员奖惩记录 + * + * @author shihongwei + * @date 2025-12-16 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/driverManagement/driverRewardPunishment") +@Tag(name = "驾驶员奖惩记录", description = "奖惩记录管理") +public class HotDriverRewardPunishmentController extends BaseController { + + private final IHotDriverRewardPunishmentService hotDriverRewardPunishmentService; + + /** + * 查询驾驶员奖惩记录列表 + */ + //@SaCheckPermission("driverManagement:driverRewardPunishment:list") + @GetMapping("/list") + @Operation(summary = "分页查询驾驶员奖惩记录列表") + public TableDataInfo list(HotDriverRewardPunishmentBo bo, PageQuery pageQuery) { + return hotDriverRewardPunishmentService.queryPageList(bo, pageQuery); + } + + /** + * 导出驾驶员奖惩记录列表 + */ + //@SaCheckPermission("driverManagement:driverRewardPunishment:export") + @Log(title = "驾驶员奖惩记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出驾驶员奖惩记录列表") + public void export(HotDriverRewardPunishmentBo bo, HttpServletResponse response) { + List list = hotDriverRewardPunishmentService.queryList(bo); + ExcelUtil.exportExcel(list, "驾驶员奖惩记录", HotDriverRewardPunishmentVo.class, response); + } + + /** + * 获取驾驶员奖惩记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("driverManagement:driverRewardPunishment:query") + @GetMapping("/{id}") + @Operation(summary = "查询驾驶员奖惩记录详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotDriverRewardPunishmentService.queryById(id)); + } + + /** + * 新增驾驶员奖惩记录 + */ + //@SaCheckPermission("driverManagement:driverRewardPunishment:add") + @Log(title = "驾驶员奖惩记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增驾驶员奖惩记录") + public R add(@Validated(AddGroup.class) @RequestBody HotDriverRewardPunishmentBo bo) { + return toAjax(hotDriverRewardPunishmentService.insertByBo(bo)); + } + + /** + * 修改驾驶员奖惩记录 + */ + //@SaCheckPermission("driverManagement:driverRewardPunishment:edit") + @Log(title = "驾驶员奖惩记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改驾驶员奖惩记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotDriverRewardPunishmentBo bo) { + return toAjax(hotDriverRewardPunishmentService.updateByBo(bo)); + } + + /** + * 删除驾驶员奖惩记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("driverManagement:driverRewardPunishment:remove") + @Log(title = "驾驶员奖惩记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除驾驶员奖惩记录") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotDriverRewardPunishmentService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/domain/HotDriverRewardPunishment.java b/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/domain/HotDriverRewardPunishment.java new file mode 100644 index 0000000..6bbf917 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/domain/HotDriverRewardPunishment.java @@ -0,0 +1,100 @@ +package com.hotwj.platform.driverManagement.driverRewardPunishment.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 驾驶员奖惩记录对象 hot_driver_reward_punishment + * + * @author shihongwei + * @date 2025-12-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_driver_reward_punishment") +public class HotDriverRewardPunishment extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 驾驶员姓名 + */ + private String driverName; + + /** + * 奖惩类型:1=奖励 2=处分 + */ + private Long recordType; + + /** + * 奖惩日期 + */ + private Date eventDate; + + /** + * 奖惩类别(字典/下拉) + */ + private String category; + + /** + * 奖惩原因/事由 + */ + private String reason; + + /** + * 奖惩决定/处理意见 + */ + private String decision; + + /** + * 附件URL(图片/PDF) + */ + private String attachmentUrl; + + /** + * 备注 + */ + private String remark; + + /** + * 详细内容/说明 + */ + private String content; + + /** + * 记录人ID + */ + private Long recorderId; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/domain/bo/HotDriverRewardPunishmentBo.java b/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/domain/bo/HotDriverRewardPunishmentBo.java new file mode 100644 index 0000000..3d57326 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/domain/bo/HotDriverRewardPunishmentBo.java @@ -0,0 +1,96 @@ +package com.hotwj.platform.driverManagement.driverRewardPunishment.domain.bo; + +import com.hotwj.platform.driverManagement.driverRewardPunishment.domain.HotDriverRewardPunishment; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 驾驶员奖惩记录业务对象 hot_driver_reward_punishment + * + * @author shihongwei + * @date 2025-12-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotDriverRewardPunishment.class, reverseConvertGenerate = false) +public class HotDriverRewardPunishmentBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 驾驶员姓名 + */ + private String driverName; + + /** + * 奖惩类型:1=奖励 2=处分 + */ + private Long recordType; + + /** + * 奖惩日期 + */ + private Date eventDate; + + /** + * 奖惩类别(字典/下拉) + */ + private String category; + + /** + * 奖惩原因/事由 + */ + private String reason; + + /** + * 奖惩决定/处理意见 + */ + private String decision; + + /** + * 附件URL(图片/PDF) + */ + private String attachmentUrl; + + /** + * 备注 + */ + private String remark; + + /** + * 详细内容/说明 + */ + private String content; + + /** + * 记录人ID + */ + private Long recorderId; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/domain/vo/HotDriverRewardPunishmentVo.java b/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/domain/vo/HotDriverRewardPunishmentVo.java new file mode 100644 index 0000000..ae5429d --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/domain/vo/HotDriverRewardPunishmentVo.java @@ -0,0 +1,121 @@ +package com.hotwj.platform.driverManagement.driverRewardPunishment.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.driverManagement.driverRewardPunishment.domain.HotDriverRewardPunishment; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 驾驶员奖惩记录视图对象 hot_driver_reward_punishment + * + * @author shihongwei + * @date 2025-12-16 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotDriverRewardPunishment.class) +public class HotDriverRewardPunishmentVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 驾驶员ID + */ + @ExcelProperty(value = "驾驶员ID") + private String driverId; + + /** + * 驾驶员姓名 + */ + private String driverName; + + /** + * 奖惩类型:1=奖励 2=处分 + */ + @ExcelProperty(value = "奖惩类型:1=奖励 2=处分") + private Long recordType; + + /** + * 奖惩日期 + */ + @ExcelProperty(value = "奖惩日期") + private Date eventDate; + + /** + * 奖惩类别(字典/下拉) + */ + @ExcelProperty(value = "奖惩类别", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "字=典/下拉") + private String category; + + /** + * 奖惩原因/事由 + */ + @ExcelProperty(value = "奖惩原因/事由") + private String reason; + + /** + * 奖惩决定/处理意见 + */ + @ExcelProperty(value = "奖惩决定/处理意见") + private String decision; + + /** + * 附件URL(图片/PDF) + */ + @ExcelProperty(value = "附件URL", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "图=片/PDF") + private String attachmentUrl; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 详细内容/说明 + */ + @ExcelProperty(value = "详细内容/说明") + private String content; + + /** + * 记录人ID + */ + @ExcelProperty(value = "记录人ID") + private Long recorderId; + + @Translation(type = TransConstant.SAFETY_MANAGER_ID_TO_NAME, mapper = "recorderId") + private String recorderName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/mapper/HotDriverRewardPunishmentMapper.java b/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/mapper/HotDriverRewardPunishmentMapper.java new file mode 100644 index 0000000..2694e94 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/mapper/HotDriverRewardPunishmentMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.driverManagement.driverRewardPunishment.mapper; + +import com.hotwj.platform.driverManagement.driverRewardPunishment.domain.HotDriverRewardPunishment; +import com.hotwj.platform.driverManagement.driverRewardPunishment.domain.vo.HotDriverRewardPunishmentVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 驾驶员奖惩记录Mapper接口 + * + * @author shihongwei + * @date 2025-12-16 + */ +@Mapper +public interface HotDriverRewardPunishmentMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/service/IHotDriverRewardPunishmentService.java b/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/service/IHotDriverRewardPunishmentService.java new file mode 100644 index 0000000..03f922e --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/service/IHotDriverRewardPunishmentService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.driverManagement.driverRewardPunishment.service; + +import com.hotwj.platform.driverManagement.driverRewardPunishment.domain.bo.HotDriverRewardPunishmentBo; +import com.hotwj.platform.driverManagement.driverRewardPunishment.domain.vo.HotDriverRewardPunishmentVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 驾驶员奖惩记录Service接口 + * + * @author shihongwei + * @date 2025-12-16 + */ +public interface IHotDriverRewardPunishmentService { + + /** + * 查询驾驶员奖惩记录 + * + * @param id 主键 + * @return 驾驶员奖惩记录 + */ + HotDriverRewardPunishmentVo queryById(Long id); + + /** + * 分页查询驾驶员奖惩记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员奖惩记录分页列表 + */ + TableDataInfo queryPageList(HotDriverRewardPunishmentBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的驾驶员奖惩记录列表 + * + * @param bo 查询条件 + * @return 驾驶员奖惩记录列表 + */ + List queryList(HotDriverRewardPunishmentBo bo); + + /** + * 新增驾驶员奖惩记录 + * + * @param bo 驾驶员奖惩记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotDriverRewardPunishmentBo bo); + + /** + * 修改驾驶员奖惩记录 + * + * @param bo 驾驶员奖惩记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotDriverRewardPunishmentBo bo); + + /** + * 校验并批量删除驾驶员奖惩记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/service/impl/HotDriverRewardPunishmentServiceImpl.java b/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/service/impl/HotDriverRewardPunishmentServiceImpl.java new file mode 100644 index 0000000..37dda8a --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverRewardPunishment/service/impl/HotDriverRewardPunishmentServiceImpl.java @@ -0,0 +1,202 @@ +package com.hotwj.platform.driverManagement.driverRewardPunishment.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.driverRewardPunishment.domain.HotDriverRewardPunishment; +import com.hotwj.platform.driverManagement.driverRewardPunishment.domain.bo.HotDriverRewardPunishmentBo; +import com.hotwj.platform.driverManagement.driverRewardPunishment.domain.vo.HotDriverRewardPunishmentVo; +import com.hotwj.platform.driverManagement.driverRewardPunishment.mapper.HotDriverRewardPunishmentMapper; +import com.hotwj.platform.driverManagement.driverRewardPunishment.service.IHotDriverRewardPunishmentService; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员奖惩记录Service业务层处理 + * + * @author shihongwei + * @date 2025-12-16 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotDriverRewardPunishmentServiceImpl implements IHotDriverRewardPunishmentService { + + private final HotDriverRewardPunishmentMapper baseMapper; + private final IHotSystemNotificationService notificationService; + /** + * 查询驾驶员奖惩记录 + * + * @param id 主键 + * @return 驾驶员奖惩记录 + */ + @Override + public HotDriverRewardPunishmentVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询驾驶员奖惩记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员奖惩记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotDriverRewardPunishmentBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的驾驶员奖惩记录列表 + * + * @param bo 查询条件 + * @return 驾驶员奖惩记录列表 + */ + @Override + public List queryList(HotDriverRewardPunishmentBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotDriverRewardPunishmentBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotDriverRewardPunishment::getId); + lqw.eq(bo.getCompanyId() != null, HotDriverRewardPunishment::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getDriverName()), HotDriverRewardPunishment::getDriverName, bo.getDriverName()); + lqw.eq(bo.getDriverId() != null, HotDriverRewardPunishment::getDriverId, bo.getDriverId()); + lqw.eq(bo.getRecordType() != null, HotDriverRewardPunishment::getRecordType, bo.getRecordType()); + lqw.eq(bo.getEventDate() != null, HotDriverRewardPunishment::getEventDate, bo.getEventDate()); + lqw.eq(StringUtils.isNotBlank(bo.getCategory()), HotDriverRewardPunishment::getCategory, bo.getCategory()); + lqw.eq(StringUtils.isNotBlank(bo.getReason()), HotDriverRewardPunishment::getReason, bo.getReason()); + lqw.eq(StringUtils.isNotBlank(bo.getDecision()), HotDriverRewardPunishment::getDecision, bo.getDecision()); + lqw.eq(StringUtils.isNotBlank(bo.getAttachmentUrl()), HotDriverRewardPunishment::getAttachmentUrl, bo.getAttachmentUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getContent()), HotDriverRewardPunishment::getContent, bo.getContent()); + lqw.eq(bo.getRecorderId() != null, HotDriverRewardPunishment::getRecorderId, bo.getRecorderId()); + lqw.eq(bo.getIsDeleted() != null, HotDriverRewardPunishment::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增驾驶员奖惩记录 + * + * @param bo 驾驶员奖惩记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotDriverRewardPunishmentBo bo) { + HotDriverRewardPunishment add = MapstructUtils.convert(bo, HotDriverRewardPunishment.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + if (StringUtils.isNotBlank(add.getDriverId())) { + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + String content; + String rewardPunishmentContent = StringUtils.isNotBlank(add.getContent()) ? add.getContent() : "请查看详情。"; + if (add.getRecordType() != null && Long.valueOf(1L).equals(add.getRecordType())) { + content = "您有一条来自企业的奖励信息:" + rewardPunishmentContent; + } else if (add.getRecordType() != null && Long.valueOf(2L).equals(add.getRecordType())) { + content = "您有一条来自企业的处分信息:" + rewardPunishmentContent; + } else { + content = "您有一条来自企业的奖励/惩罚信息:" + rewardPunishmentContent; + } + bos.setContent(content); + bos.setSourceType("驾驶员管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("驾驶员"); + bos.setReceiverIds(java.util.Collections.singletonList(add.getDriverId())); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送驾驶员奖惩新增通知失败 driverId={} companyId={}", add.getDriverId(), add.getCompanyId(), e); + } + } + } + return flag; + } + + /** + * 修改驾驶员奖惩记录 + * + * @param bo 驾驶员奖惩记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotDriverRewardPunishmentBo bo) { + HotDriverRewardPunishment before = null; + if (bo.getId() != null) { + before = baseMapper.selectById(bo.getId()); + } + HotDriverRewardPunishment update = MapstructUtils.convert(bo, HotDriverRewardPunishment.class); + validEntityBeforeSave(update); + boolean ok = baseMapper.updateById(update) > 0; + if (ok) { + String driverId = org.dromara.common.core.utils.StringUtils.isNotBlank(update.getDriverId()) ? update.getDriverId() + : (before != null ? before.getDriverId() : null); + if (org.dromara.common.core.utils.StringUtils.isNotBlank(driverId)) { + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + String content; + Long type = update.getRecordType() != null ? update.getRecordType() : (before != null ? before.getRecordType() : null); + if (type != null && Long.valueOf(1L).equals(type)) { + content = "您有一条来自企业的奖励信息已更新,请查看。"; + } else if (type != null && Long.valueOf(2L).equals(type)) { + content = "您有一条来自企业的处分信息已更新,请查看。"; + } else { + content = "您有一条来自企业的奖励/惩罚信息已更新,请查看。"; + } + bos.setContent(content); + bos.setSourceType("驾驶员管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("驾驶员"); + bos.setReceiverIds(java.util.Collections.singletonList(driverId)); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送驾驶员奖惩修改通知失败 driverId={} companyId={}", driverId, update.getCompanyId(), e); + } + } + } + return ok; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotDriverRewardPunishment entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除驾驶员奖惩记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/controller/HotDriverSkillAssessmentController.java b/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/controller/HotDriverSkillAssessmentController.java new file mode 100644 index 0000000..14aaf3f --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/controller/HotDriverSkillAssessmentController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.driverManagement.driverSkillAssessment.controller; + +import com.hotwj.platform.driverManagement.driverSkillAssessment.domain.bo.HotDriverSkillAssessmentBo; +import com.hotwj.platform.driverManagement.driverSkillAssessment.domain.vo.HotDriverSkillAssessmentVo; +import com.hotwj.platform.driverManagement.driverSkillAssessment.service.IHotDriverSkillAssessmentService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 驾驶员技能考核(明细项) + * + * @author shihongwei + * @date 2025-12-16 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/driverManagement/driverSkillAssessment") +@Tag(name = "驾驶员技能考核", description = "技能考核管理") +public class HotDriverSkillAssessmentController extends BaseController { + + private final IHotDriverSkillAssessmentService hotDriverSkillAssessmentService; + + /** + * 查询驾驶员技能考核(明细项)列表 + */ + //@SaCheckPermission("driverManagement:driverSkillAssessment:list") + @GetMapping("/list") + @Operation(summary = "分页查询驾驶员技能考核(明细项)列表") + public TableDataInfo list(HotDriverSkillAssessmentBo bo, PageQuery pageQuery) { + return hotDriverSkillAssessmentService.queryPageList(bo, pageQuery); + } + + /** + * 导出驾驶员技能考核(明细项)列表 + */ + //@SaCheckPermission("driverManagement:driverSkillAssessment:export") + @Log(title = "驾驶员技能考核(明细项)", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出驾驶员技能考核(明细项)列表") + public void export(HotDriverSkillAssessmentBo bo, HttpServletResponse response) { + List list = hotDriverSkillAssessmentService.queryList(bo); + ExcelUtil.exportExcel(list, "驾驶员技能考核(明细项)", HotDriverSkillAssessmentVo.class, response); + } + + /** + * 获取驾驶员技能考核(明细项)详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("driverManagement:driverSkillAssessment:query") + @GetMapping("/{id}") + @Operation(summary = "查询驾驶员技能考核(明细项)详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotDriverSkillAssessmentService.queryById(id)); + } + + /** + * 新增驾驶员技能考核(明细项) + */ + //@SaCheckPermission("driverManagement:driverSkillAssessment:add") + @Log(title = "驾驶员技能考核(明细项)", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增驾驶员技能考核(明细项)") + public R add(@Validated(AddGroup.class) @RequestBody HotDriverSkillAssessmentBo bo) { + return toAjax(hotDriverSkillAssessmentService.insertByBo(bo)); + } + + /** + * 修改驾驶员技能考核(明细项) + */ + //@SaCheckPermission("driverManagement:driverSkillAssessment:edit") + @Log(title = "驾驶员技能考核(明细项)", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改驾驶员技能考核(明细项)") + public R edit(@Validated(EditGroup.class) @RequestBody HotDriverSkillAssessmentBo bo) { + return toAjax(hotDriverSkillAssessmentService.updateByBo(bo)); + } + + /** + * 删除驾驶员技能考核(明细项) + * + * @param ids 主键串 + */ + //@SaCheckPermission("driverManagement:driverSkillAssessment:remove") + @Log(title = "驾驶员技能考核(明细项)", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除驾驶员技能考核(明细项)") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotDriverSkillAssessmentService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/domain/HotDriverSkillAssessment.java b/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/domain/HotDriverSkillAssessment.java new file mode 100644 index 0000000..e585ffd --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/domain/HotDriverSkillAssessment.java @@ -0,0 +1,115 @@ +package com.hotwj.platform.driverManagement.driverSkillAssessment.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 驾驶员技能考核(明细项)对象 hot_driver_skill_assessment + * + * @author shihongwei + * @date 2025-12-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_driver_skill_assessment") +public class HotDriverSkillAssessment extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 驾驶员名称 + */ + private String driverName; + + /** + * 准驾车型 + */ + private String permittedVehicleType; + + /** + * 考核车型 + */ + private String examVehicleModel; + + /** + * 路考时间 + */ + private Date assessDate; + + /** + * 考核路线 + */ + private String examRoute; + + /** + * 驾龄 + */ + private String drivingExperience; + + /** + * 驾驶里程 + */ + private String drivingMileage; + + /** + * 考核内容 + */ + private String examContent; + + /** + * 主考签名 + */ + private String chiefExaminerSignature; + + /** + * 监考签名 + */ + private String invigilatorSignature; + + /** + * 主考意见 + */ + private String chiefOpinion; + + /** + * 监考意见 + */ + private String invigilatorOpinion; + + /** + * 公司领导意见 + */ + private String companyLeaderOpinion; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/domain/bo/HotDriverSkillAssessmentBo.java b/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/domain/bo/HotDriverSkillAssessmentBo.java new file mode 100644 index 0000000..c29e0f4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/domain/bo/HotDriverSkillAssessmentBo.java @@ -0,0 +1,112 @@ +package com.hotwj.platform.driverManagement.driverSkillAssessment.domain.bo; + +import com.hotwj.platform.driverManagement.driverSkillAssessment.domain.HotDriverSkillAssessment; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 驾驶员技能考核(明细项)业务对象 hot_driver_skill_assessment + * + * @author shihongwei + * @date 2025-12-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotDriverSkillAssessment.class, reverseConvertGenerate = false) +public class HotDriverSkillAssessmentBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private String driverId; + + + /** + * 驾驶员名称 + */ + private String driverName; + + /** + * 准驾车型 + */ + private String permittedVehicleType; + + /** + * 考核车型 + */ + private String examVehicleModel; + + /** + * 路考时间 + */ + private Date assessDate; + + /** + * 考核路线 + */ + private String examRoute; + + /** + * 驾龄 + */ + private String drivingExperience; + + /** + * 驾驶里程 + */ + private String drivingMileage; + + /** + * 考核内容 + */ + private String examContent; + + /** + * 主考签名 + */ + private String chiefExaminerSignature; + + /** + * 监考签名 + */ + private String invigilatorSignature; + + /** + * 主考意见 + */ + private String chiefOpinion; + + /** + * 监考意见 + */ + private String invigilatorOpinion; + + /** + * 公司领导意见 + */ + private String companyLeaderOpinion; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/domain/vo/HotDriverSkillAssessmentVo.java b/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/domain/vo/HotDriverSkillAssessmentVo.java new file mode 100644 index 0000000..56083b7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/domain/vo/HotDriverSkillAssessmentVo.java @@ -0,0 +1,130 @@ +package com.hotwj.platform.driverManagement.driverSkillAssessment.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.driverManagement.driverSkillAssessment.domain.HotDriverSkillAssessment; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 驾驶员技能考核(明细项)视图对象 hot_driver_skill_assessment + * + * @author shihongwei + * @date 2025-12-16 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotDriverSkillAssessment.class) +public class HotDriverSkillAssessmentVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 驾驶员ID + */ + @ExcelProperty(value = "驾驶员ID") + private String driverId; + + /** + * 驾驶员名称 + */ + private String driverName; + + /** + * 准驾车型 + */ + @ExcelProperty(value = "准驾车型") + private String permittedVehicleType; + + /** + * 考核车型 + */ + @ExcelProperty(value = "考核车型") + private String examVehicleModel; + + /** + * 路考时间 + */ + @ExcelProperty(value = "路考时间") + private Date assessDate; + + /** + * 考核路线 + */ + @ExcelProperty(value = "考核路线") + private String examRoute; + + /** + * 驾龄 + */ + @ExcelProperty(value = "驾龄") + private String drivingExperience; + + /** + * 驾驶里程 + */ + @ExcelProperty(value = "驾驶里程") + private String drivingMileage; + + /** + * 考核内容 + */ + @ExcelProperty(value = "考核内容") + private String examContent; + + /** + * 主考签名 + */ + @ExcelProperty(value = "主考签名") + private String chiefExaminerSignature; + + /** + * 监考签名 + */ + @ExcelProperty(value = "监考签名") + private String invigilatorSignature; + + /** + * 主考意见 + */ + @ExcelProperty(value = "主考意见") + private String chiefOpinion; + + /** + * 监考意见 + */ + @ExcelProperty(value = "监考意见") + private String invigilatorOpinion; + + /** + * 公司领导意见 + */ + @ExcelProperty(value = "公司领导意见") + private String companyLeaderOpinion; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/mapper/HotDriverSkillAssessmentMapper.java b/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/mapper/HotDriverSkillAssessmentMapper.java new file mode 100644 index 0000000..187f44b --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/mapper/HotDriverSkillAssessmentMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.driverManagement.driverSkillAssessment.mapper; + +import com.hotwj.platform.driverManagement.driverSkillAssessment.domain.HotDriverSkillAssessment; +import com.hotwj.platform.driverManagement.driverSkillAssessment.domain.vo.HotDriverSkillAssessmentVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 驾驶员技能考核(明细项)Mapper接口 + * + * @author shihongwei + * @date 2025-12-16 + */ +@Mapper +public interface HotDriverSkillAssessmentMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/service/IHotDriverSkillAssessmentService.java b/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/service/IHotDriverSkillAssessmentService.java new file mode 100644 index 0000000..7ef8073 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/service/IHotDriverSkillAssessmentService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.driverManagement.driverSkillAssessment.service; + +import com.hotwj.platform.driverManagement.driverSkillAssessment.domain.bo.HotDriverSkillAssessmentBo; +import com.hotwj.platform.driverManagement.driverSkillAssessment.domain.vo.HotDriverSkillAssessmentVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 驾驶员技能考核(明细项)Service接口 + * + * @author shihongwei + * @date 2025-12-16 + */ +public interface IHotDriverSkillAssessmentService { + + /** + * 查询驾驶员技能考核(明细项) + * + * @param id 主键 + * @return 驾驶员技能考核(明细项) + */ + HotDriverSkillAssessmentVo queryById(Long id); + + /** + * 分页查询驾驶员技能考核(明细项)列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员技能考核(明细项)分页列表 + */ + TableDataInfo queryPageList(HotDriverSkillAssessmentBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的驾驶员技能考核(明细项)列表 + * + * @param bo 查询条件 + * @return 驾驶员技能考核(明细项)列表 + */ + List queryList(HotDriverSkillAssessmentBo bo); + + /** + * 新增驾驶员技能考核(明细项) + * + * @param bo 驾驶员技能考核(明细项) + * @return 是否新增成功 + */ + Boolean insertByBo(HotDriverSkillAssessmentBo bo); + + /** + * 修改驾驶员技能考核(明细项) + * + * @param bo 驾驶员技能考核(明细项) + * @return 是否修改成功 + */ + Boolean updateByBo(HotDriverSkillAssessmentBo bo); + + /** + * 校验并批量删除驾驶员技能考核(明细项)信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/service/impl/HotDriverSkillAssessmentServiceImpl.java b/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/service/impl/HotDriverSkillAssessmentServiceImpl.java new file mode 100644 index 0000000..6fe1439 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverSkillAssessment/service/impl/HotDriverSkillAssessmentServiceImpl.java @@ -0,0 +1,163 @@ +package com.hotwj.platform.driverManagement.driverSkillAssessment.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.driverManagement.driverSkillAssessment.domain.HotDriverSkillAssessment; +import com.hotwj.platform.driverManagement.driverSkillAssessment.domain.bo.HotDriverSkillAssessmentBo; +import com.hotwj.platform.driverManagement.driverSkillAssessment.domain.vo.HotDriverSkillAssessmentVo; +import com.hotwj.platform.driverManagement.driverSkillAssessment.mapper.HotDriverSkillAssessmentMapper; +import com.hotwj.platform.driverManagement.driverSkillAssessment.service.IHotDriverSkillAssessmentService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员技能考核(明细项)Service业务层处理 + * + * @author shihongwei + * @date 2025-12-16 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotDriverSkillAssessmentServiceImpl implements IHotDriverSkillAssessmentService { + + private final HotDriverSkillAssessmentMapper baseMapper; + private final IHotDriverService driverService; + + /** + * 查询驾驶员技能考核(明细项) + * + * @param id 主键 + * @return 驾驶员技能考核(明细项) + */ + @Override + public HotDriverSkillAssessmentVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询驾驶员技能考核(明细项)列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员技能考核(明细项)分页列表 + */ + @Override + public TableDataInfo queryPageList(HotDriverSkillAssessmentBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的驾驶员技能考核(明细项)列表 + * + * @param bo 查询条件 + * @return 驾驶员技能考核(明细项)列表 + */ + @Override + public List queryList(HotDriverSkillAssessmentBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotDriverSkillAssessmentBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotDriverSkillAssessment::getId); + lqw.eq(bo.getCompanyId() != null, HotDriverSkillAssessment::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getDriverId() != null, HotDriverSkillAssessment::getDriverId, bo.getDriverId()); + lqw.eq(StringUtils.isNotBlank(bo.getPermittedVehicleType()), HotDriverSkillAssessment::getPermittedVehicleType, bo.getPermittedVehicleType()); + lqw.eq(StringUtils.isNotBlank(bo.getExamVehicleModel()), HotDriverSkillAssessment::getExamVehicleModel, bo.getExamVehicleModel()); + lqw.eq(bo.getAssessDate() != null, HotDriverSkillAssessment::getAssessDate, bo.getAssessDate()); + lqw.eq(StringUtils.isNotBlank(bo.getExamRoute()), HotDriverSkillAssessment::getExamRoute, bo.getExamRoute()); + lqw.eq(StringUtils.isNotBlank(bo.getDrivingExperience()), HotDriverSkillAssessment::getDrivingExperience, bo.getDrivingExperience()); + lqw.eq(StringUtils.isNotBlank(bo.getDrivingMileage()), HotDriverSkillAssessment::getDrivingMileage, bo.getDrivingMileage()); + lqw.eq(StringUtils.isNotBlank(bo.getExamContent()), HotDriverSkillAssessment::getExamContent, bo.getExamContent()); + lqw.eq(StringUtils.isNotBlank(bo.getChiefExaminerSignature()), HotDriverSkillAssessment::getChiefExaminerSignature, bo.getChiefExaminerSignature()); + lqw.eq(StringUtils.isNotBlank(bo.getInvigilatorSignature()), HotDriverSkillAssessment::getInvigilatorSignature, bo.getInvigilatorSignature()); + lqw.eq(StringUtils.isNotBlank(bo.getChiefOpinion()), HotDriverSkillAssessment::getChiefOpinion, bo.getChiefOpinion()); + lqw.eq(StringUtils.isNotBlank(bo.getInvigilatorOpinion()), HotDriverSkillAssessment::getInvigilatorOpinion, bo.getInvigilatorOpinion()); + lqw.eq(StringUtils.isNotBlank(bo.getCompanyLeaderOpinion()), HotDriverSkillAssessment::getCompanyLeaderOpinion, bo.getCompanyLeaderOpinion()); + lqw.eq(bo.getIsDeleted() != null, HotDriverSkillAssessment::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增驾驶员技能考核(明细项) + * + * @param bo 驾驶员技能考核(明细项) + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotDriverSkillAssessmentBo bo) { + HotDriverSkillAssessment add = MapstructUtils.convert(bo, HotDriverSkillAssessment.class); + // 填充驾驶员名称 + if (StringUtils.isNotBlank(add.getDriverId())) { + HotDriverVo driver = driverService.queryById(add.getDriverId()); + if (driver != null) { + add.setDriverName(driver.getName()); + } + } + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改驾驶员技能考核(明细项) + * + * @param bo 驾驶员技能考核(明细项) + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotDriverSkillAssessmentBo bo) { + HotDriverSkillAssessment update = MapstructUtils.convert(bo, HotDriverSkillAssessment.class); + // 填充驾驶员名称 + if (StringUtils.isNotBlank(update.getDriverId())) { + HotDriverVo driver = driverService.queryById(update.getDriverId()); + if (driver != null) { + update.setDriverName(driver.getName()); + } + } + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotDriverSkillAssessment entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除驾驶员技能考核(明细项)信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/controller/HotDriverWorkExperienceController.java b/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/controller/HotDriverWorkExperienceController.java new file mode 100644 index 0000000..4aa7d5a --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/controller/HotDriverWorkExperienceController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.driverManagement.driverWorkExperience.controller; + +import com.hotwj.platform.driverManagement.driverWorkExperience.domain.bo.HotDriverWorkExperienceBo; +import com.hotwj.platform.driverManagement.driverWorkExperience.domain.vo.HotDriverWorkExperienceVo; +import com.hotwj.platform.driverManagement.driverWorkExperience.service.IHotDriverWorkExperienceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 驾驶员工作经历 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/driverManagement/driverWorkExperience") +@Tag(name = "驾驶员工作经历", description = "工作经历管理") +public class HotDriverWorkExperienceController extends BaseController { + + private final IHotDriverWorkExperienceService hotDriverWorkExperienceService; + + /** + * 查询驾驶员工作经历列表 + */ + //@SaCheckPermission("driverManagement:driver:list") + @GetMapping("/list") + @Operation(summary = "分页查询驾驶员工作经历列表") + public TableDataInfo list(HotDriverWorkExperienceBo bo, PageQuery pageQuery) { + return hotDriverWorkExperienceService.queryPageList(bo, pageQuery); + } + + /** + * 导出驾驶员工作经历列表 + */ + //@SaCheckPermission("driverManagement:driver:export") + @Log(title = "驾驶员工作经历", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出驾驶员工作经历列表") + public void export(HotDriverWorkExperienceBo bo, HttpServletResponse response) { + List list = hotDriverWorkExperienceService.queryList(bo); + ExcelUtil.exportExcel(list, "驾驶员工作经历", HotDriverWorkExperienceVo.class, response); + } + + /** + * 获取驾驶员工作经历详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("driverManagement:driver:query") + @GetMapping("/{id}") + @Operation(summary = "查询驾驶员工作经历详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotDriverWorkExperienceService.queryById(id)); + } + + /** + * 新增驾驶员工作经历 + */ + //@SaCheckPermission("driverManagement:driver:add") + @Log(title = "驾驶员工作经历", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增驾驶员工作经历") + public R add(@Validated(AddGroup.class) @RequestBody HotDriverWorkExperienceBo bo) { + return toAjax(hotDriverWorkExperienceService.insertByBo(bo)); + } + + /** + * 修改驾驶员工作经历 + */ + //@SaCheckPermission("driverManagement:driver:edit") + @Log(title = "驾驶员工作经历", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改驾驶员工作经历") + public R edit(@Validated(EditGroup.class) @RequestBody HotDriverWorkExperienceBo bo) { + return toAjax(hotDriverWorkExperienceService.updateByBo(bo)); + } + + /** + * 删除驾驶员工作经历 + * + * @param ids 主键串 + */ + //@SaCheckPermission("driverManagement:driver:remove") + @Log(title = "驾驶员工作经历", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除驾驶员工作经历") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotDriverWorkExperienceService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/domain/HotDriverWorkExperience.java b/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/domain/HotDriverWorkExperience.java new file mode 100644 index 0000000..6abcfde --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/domain/HotDriverWorkExperience.java @@ -0,0 +1,75 @@ +package com.hotwj.platform.driverManagement.driverWorkExperience.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 驾驶员工作经历对象 hot_driver_work_experience + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_driver_work_experience") +public class HotDriverWorkExperience extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 开始时间 + */ + private Date startDate; + + /** + * 结束时间 + */ + private Date endDate; + + /** + * 工作单位 + */ + private String employer; + + /** + * 职务 + */ + private String jobTitle; + + /** + * 工作内容 + */ + private String description; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/domain/bo/HotDriverWorkExperienceBo.java b/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/domain/bo/HotDriverWorkExperienceBo.java new file mode 100644 index 0000000..9b9d2b4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/domain/bo/HotDriverWorkExperienceBo.java @@ -0,0 +1,71 @@ +package com.hotwj.platform.driverManagement.driverWorkExperience.domain.bo; + +import com.hotwj.platform.driverManagement.driverWorkExperience.domain.HotDriverWorkExperience; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 驾驶员工作经历业务对象 hot_driver_work_experience + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotDriverWorkExperience.class, reverseConvertGenerate = false) +public class HotDriverWorkExperienceBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 开始时间 + */ + private Date startDate; + + /** + * 结束时间 + */ + private Date endDate; + + /** + * 工作单位 + */ + private String employer; + + /** + * 职务 + */ + private String jobTitle; + + /** + * 工作内容 + */ + private String description; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/domain/vo/HotDriverWorkExperienceVo.java b/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/domain/vo/HotDriverWorkExperienceVo.java new file mode 100644 index 0000000..a017f2c --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/domain/vo/HotDriverWorkExperienceVo.java @@ -0,0 +1,83 @@ +package com.hotwj.platform.driverManagement.driverWorkExperience.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.driverManagement.driverWorkExperience.domain.HotDriverWorkExperience; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 驾驶员工作经历视图对象 hot_driver_work_experience + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotDriverWorkExperience.class) +public class HotDriverWorkExperienceVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 驾驶员ID + */ + @ExcelProperty(value = "驾驶员ID") + private String driverId; + + /** + * 开始时间 + */ + @ExcelProperty(value = "开始时间") + private Date startDate; + + /** + * 结束时间 + */ + @ExcelProperty(value = "结束时间") + private Date endDate; + + /** + * 工作单位 + */ + @ExcelProperty(value = "工作单位") + private String employer; + + /** + * 职务 + */ + @ExcelProperty(value = "职务") + private String jobTitle; + + /** + * 工作内容 + */ + @ExcelProperty(value = "工作内容") + private String description; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/mapper/HotDriverWorkExperienceMapper.java b/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/mapper/HotDriverWorkExperienceMapper.java new file mode 100644 index 0000000..24ac2c3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/mapper/HotDriverWorkExperienceMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.driverManagement.driverWorkExperience.mapper; + +import com.hotwj.platform.driverManagement.driverWorkExperience.domain.HotDriverWorkExperience; +import com.hotwj.platform.driverManagement.driverWorkExperience.domain.vo.HotDriverWorkExperienceVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 驾驶员工作经历Mapper接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Mapper +public interface HotDriverWorkExperienceMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/service/IHotDriverWorkExperienceService.java b/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/service/IHotDriverWorkExperienceService.java new file mode 100644 index 0000000..1fdd936 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/service/IHotDriverWorkExperienceService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.driverManagement.driverWorkExperience.service; + +import com.hotwj.platform.driverManagement.driverWorkExperience.domain.bo.HotDriverWorkExperienceBo; +import com.hotwj.platform.driverManagement.driverWorkExperience.domain.vo.HotDriverWorkExperienceVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 驾驶员工作经历Service接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +public interface IHotDriverWorkExperienceService { + + /** + * 查询驾驶员工作经历 + * + * @param id 主键 + * @return 驾驶员工作经历 + */ + HotDriverWorkExperienceVo queryById(Long id); + + /** + * 分页查询驾驶员工作经历列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员工作经历分页列表 + */ + TableDataInfo queryPageList(HotDriverWorkExperienceBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的驾驶员工作经历列表 + * + * @param bo 查询条件 + * @return 驾驶员工作经历列表 + */ + List queryList(HotDriverWorkExperienceBo bo); + + /** + * 新增驾驶员工作经历 + * + * @param bo 驾驶员工作经历 + * @return 是否新增成功 + */ + Boolean insertByBo(HotDriverWorkExperienceBo bo); + + /** + * 修改驾驶员工作经历 + * + * @param bo 驾驶员工作经历 + * @return 是否修改成功 + */ + Boolean updateByBo(HotDriverWorkExperienceBo bo); + + /** + * 校验并批量删除驾驶员工作经历信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/service/impl/HotDriverWorkExperienceServiceImpl.java b/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/service/impl/HotDriverWorkExperienceServiceImpl.java new file mode 100644 index 0000000..4401936 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/driverWorkExperience/service/impl/HotDriverWorkExperienceServiceImpl.java @@ -0,0 +1,139 @@ +package com.hotwj.platform.driverManagement.driverWorkExperience.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.driverWorkExperience.domain.HotDriverWorkExperience; +import com.hotwj.platform.driverManagement.driverWorkExperience.domain.bo.HotDriverWorkExperienceBo; +import com.hotwj.platform.driverManagement.driverWorkExperience.domain.vo.HotDriverWorkExperienceVo; +import com.hotwj.platform.driverManagement.driverWorkExperience.mapper.HotDriverWorkExperienceMapper; +import com.hotwj.platform.driverManagement.driverWorkExperience.service.IHotDriverWorkExperienceService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员工作经历Service业务层处理 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotDriverWorkExperienceServiceImpl implements IHotDriverWorkExperienceService { + + private final HotDriverWorkExperienceMapper baseMapper; + + /** + * 查询驾驶员工作经历 + * + * @param id 主键 + * @return 驾驶员工作经历 + */ + @Override + public HotDriverWorkExperienceVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询驾驶员工作经历列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员工作经历分页列表 + */ + @Override + public TableDataInfo queryPageList(HotDriverWorkExperienceBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的驾驶员工作经历列表 + * + * @param bo 查询条件 + * @return 驾驶员工作经历列表 + */ + @Override + public List queryList(HotDriverWorkExperienceBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotDriverWorkExperienceBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotDriverWorkExperience::getId); + lqw.eq(bo.getCompanyId() != null, HotDriverWorkExperience::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getDriverId() != null, HotDriverWorkExperience::getDriverId, bo.getDriverId()); + lqw.eq(bo.getStartDate() != null, HotDriverWorkExperience::getStartDate, bo.getStartDate()); + lqw.eq(bo.getEndDate() != null, HotDriverWorkExperience::getEndDate, bo.getEndDate()); + lqw.eq(StringUtils.isNotBlank(bo.getEmployer()), HotDriverWorkExperience::getEmployer, bo.getEmployer()); + lqw.eq(StringUtils.isNotBlank(bo.getJobTitle()), HotDriverWorkExperience::getJobTitle, bo.getJobTitle()); + lqw.eq(StringUtils.isNotBlank(bo.getDescription()), HotDriverWorkExperience::getDescription, bo.getDescription()); + lqw.eq(bo.getIsDeleted() != null, HotDriverWorkExperience::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增驾驶员工作经历 + * + * @param bo 驾驶员工作经历 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotDriverWorkExperienceBo bo) { + HotDriverWorkExperience add = MapstructUtils.convert(bo, HotDriverWorkExperience.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改驾驶员工作经历 + * + * @param bo 驾驶员工作经历 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotDriverWorkExperienceBo bo) { + HotDriverWorkExperience update = MapstructUtils.convert(bo, HotDriverWorkExperience.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotDriverWorkExperience entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除驾驶员工作经历信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/inviteLog/controller/HotDriverInviteLogController.java b/src/main/java/com/hotwj/platform/driverManagement/inviteLog/controller/HotDriverInviteLogController.java new file mode 100644 index 0000000..7a7cb8b --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/inviteLog/controller/HotDriverInviteLogController.java @@ -0,0 +1,105 @@ +package com.hotwj.platform.driverManagement.inviteLog.controller; + +import com.hotwj.platform.driverManagement.inviteLog.domain.bo.HotDriverInviteLogBo; +import com.hotwj.platform.driverManagement.inviteLog.domain.vo.HotDriverInviteLogVo; +import com.hotwj.platform.driverManagement.inviteLog.service.IHotDriverInviteLogService; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 驾驶员邀请码使用记录 + * + * @author shihongwei + * @date 2025-12-19 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/driverManagement/driverInviteLog") +public class HotDriverInviteLogController extends BaseController { + + private final IHotDriverInviteLogService hotDriverInviteLogService; + + /** + * 查询驾驶员邀请码使用记录列表 + */ + //@SaCheckPermission("driverManagement:driverInviteLog:list") + @GetMapping("/list") + public TableDataInfo list(HotDriverInviteLogBo bo, PageQuery pageQuery) { + return hotDriverInviteLogService.queryPageList(bo, pageQuery); + } + + /** + * 导出驾驶员邀请码使用记录列表 + */ + //@SaCheckPermission("driverManagement:driverInviteLog:export") + @Log(title = "驾驶员邀请码使用记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HotDriverInviteLogBo bo, HttpServletResponse response) { + List list = hotDriverInviteLogService.queryList(bo); + ExcelUtil.exportExcel(list, "驾驶员邀请码使用记录", HotDriverInviteLogVo.class, response); + } + + /** + * 获取驾驶员邀请码使用记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("driverManagement:driverInviteLog:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotDriverInviteLogService.queryById(id)); + } + + /** + * 新增驾驶员邀请码使用记录 + */ + //@SaCheckPermission("driverManagement:driverInviteLog:add") + @Log(title = "驾驶员邀请码使用记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody HotDriverInviteLogBo bo) { + return toAjax(hotDriverInviteLogService.insertByBo(bo)); + } + + /** + * 修改驾驶员邀请码使用记录 + */ + //@SaCheckPermission("driverManagement:driverInviteLog:edit") + @Log(title = "驾驶员邀请码使用记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody HotDriverInviteLogBo bo) { + return toAjax(hotDriverInviteLogService.updateByBo(bo)); + } + + /** + * 删除驾驶员邀请码使用记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("driverManagement:driverInviteLog:remove") + @Log(title = "驾驶员邀请码使用记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotDriverInviteLogService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/inviteLog/domain/HotDriverInviteLog.java b/src/main/java/com/hotwj/platform/driverManagement/inviteLog/domain/HotDriverInviteLog.java new file mode 100644 index 0000000..7b2aecb --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/inviteLog/domain/HotDriverInviteLog.java @@ -0,0 +1,62 @@ +package com.hotwj.platform.driverManagement.inviteLog.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 驾驶员邀请码使用记录对象 hot_driver_invite_log + * + * @author shihongwei + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_driver_invite_log") +public class HotDriverInviteLog extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id") + private Long id; + + /** + * 邀请码 + */ + private String inviteCode; + + /** + * 企业ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 驾驶员姓名 + */ + private String driverName; + + /** + * 驾驶员手机号 + */ + private String driverPhone; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/inviteLog/domain/bo/HotDriverInviteLogBo.java b/src/main/java/com/hotwj/platform/driverManagement/inviteLog/domain/bo/HotDriverInviteLogBo.java new file mode 100644 index 0000000..1bd4e12 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/inviteLog/domain/bo/HotDriverInviteLogBo.java @@ -0,0 +1,60 @@ +package com.hotwj.platform.driverManagement.inviteLog.domain.bo; + +import com.hotwj.platform.driverManagement.inviteLog.domain.HotDriverInviteLog; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 驾驶员邀请码使用记录业务对象 hot_driver_invite_log + * + * @author shihongwei + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotDriverInviteLog.class, reverseConvertGenerate = false) +public class HotDriverInviteLogBo extends BaseEntity { + + /** + * 主键ID + */ + @NotNull(message = "主键ID不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 邀请码 + */ + private String inviteCode; + + /** + * 企业ID + */ + private Long companyId; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 驾驶员姓名 + */ + private String driverName; + + /** + * 驾驶员手机号 + */ + private String driverPhone; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/inviteLog/domain/vo/HotDriverInviteLogVo.java b/src/main/java/com/hotwj/platform/driverManagement/inviteLog/domain/vo/HotDriverInviteLogVo.java new file mode 100644 index 0000000..6920661 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/inviteLog/domain/vo/HotDriverInviteLogVo.java @@ -0,0 +1,70 @@ +package com.hotwj.platform.driverManagement.inviteLog.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.driverManagement.inviteLog.domain.HotDriverInviteLog; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 驾驶员邀请码使用记录视图对象 hot_driver_invite_log + * + * @author shihongwei + * @date 2025-12-19 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotDriverInviteLog.class) +public class HotDriverInviteLogVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @ExcelProperty(value = "主键ID") + private Long id; + + /** + * 邀请码 + */ + @ExcelProperty(value = "邀请码") + private String inviteCode; + + /** + * 企业ID + */ + @ExcelProperty(value = "企业ID") + private Long companyId; + + /** + * 驾驶员ID + */ + @ExcelProperty(value = "驾驶员ID") + private String driverId; + + /** + * 驾驶员姓名 + */ + @ExcelProperty(value = "驾驶员姓名") + private String driverName; + + /** + * 驾驶员手机号 + */ + @ExcelProperty(value = "驾驶员手机号") + private String driverPhone; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/inviteLog/mapper/HotDriverInviteLogMapper.java b/src/main/java/com/hotwj/platform/driverManagement/inviteLog/mapper/HotDriverInviteLogMapper.java new file mode 100644 index 0000000..2a51fc9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/inviteLog/mapper/HotDriverInviteLogMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.driverManagement.inviteLog.mapper; + +import com.hotwj.platform.driverManagement.inviteLog.domain.HotDriverInviteLog; +import com.hotwj.platform.driverManagement.inviteLog.domain.vo.HotDriverInviteLogVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 驾驶员邀请码使用记录Mapper接口 + * + * @author shihongwei + * @date 2025-12-19 + */ +@Mapper +public interface HotDriverInviteLogMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/inviteLog/service/IHotDriverInviteLogService.java b/src/main/java/com/hotwj/platform/driverManagement/inviteLog/service/IHotDriverInviteLogService.java new file mode 100644 index 0000000..c13a592 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/inviteLog/service/IHotDriverInviteLogService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.driverManagement.inviteLog.service; + +import com.hotwj.platform.driverManagement.inviteLog.domain.vo.HotDriverInviteLogVo; +import com.hotwj.platform.driverManagement.inviteLog.domain.bo.HotDriverInviteLogBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 驾驶员邀请码使用记录Service接口 + * + * @author shihongwei + * @date 2025-12-19 + */ +public interface IHotDriverInviteLogService { + + /** + * 查询驾驶员邀请码使用记录 + * + * @param id 主键 + * @return 驾驶员邀请码使用记录 + */ + HotDriverInviteLogVo queryById(Long id); + + /** + * 分页查询驾驶员邀请码使用记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员邀请码使用记录分页列表 + */ + TableDataInfo queryPageList(HotDriverInviteLogBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的驾驶员邀请码使用记录列表 + * + * @param bo 查询条件 + * @return 驾驶员邀请码使用记录列表 + */ + List queryList(HotDriverInviteLogBo bo); + + /** + * 新增驾驶员邀请码使用记录 + * + * @param bo 驾驶员邀请码使用记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotDriverInviteLogBo bo); + + /** + * 修改驾驶员邀请码使用记录 + * + * @param bo 驾驶员邀请码使用记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotDriverInviteLogBo bo); + + /** + * 校验并批量删除驾驶员邀请码使用记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/driverManagement/inviteLog/service/impl/HotDriverInviteLogServiceImpl.java b/src/main/java/com/hotwj/platform/driverManagement/inviteLog/service/impl/HotDriverInviteLogServiceImpl.java new file mode 100644 index 0000000..93c52d7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/driverManagement/inviteLog/service/impl/HotDriverInviteLogServiceImpl.java @@ -0,0 +1,136 @@ +package com.hotwj.platform.driverManagement.inviteLog.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.inviteLog.domain.HotDriverInviteLog; +import com.hotwj.platform.driverManagement.inviteLog.domain.bo.HotDriverInviteLogBo; +import com.hotwj.platform.driverManagement.inviteLog.domain.vo.HotDriverInviteLogVo; +import com.hotwj.platform.driverManagement.inviteLog.mapper.HotDriverInviteLogMapper; +import com.hotwj.platform.driverManagement.inviteLog.service.IHotDriverInviteLogService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员邀请码使用记录Service业务层处理 + * + * @author shihongwei + * @date 2025-12-19 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotDriverInviteLogServiceImpl implements IHotDriverInviteLogService { + + private final HotDriverInviteLogMapper baseMapper; + + /** + * 查询驾驶员邀请码使用记录 + * + * @param id 主键 + * @return 驾驶员邀请码使用记录 + */ + @Override + public HotDriverInviteLogVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询驾驶员邀请码使用记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 驾驶员邀请码使用记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotDriverInviteLogBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的驾驶员邀请码使用记录列表 + * + * @param bo 查询条件 + * @return 驾驶员邀请码使用记录列表 + */ + @Override + public List queryList(HotDriverInviteLogBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotDriverInviteLogBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotDriverInviteLog::getId); + lqw.eq(StringUtils.isNotBlank(bo.getInviteCode()), HotDriverInviteLog::getInviteCode, bo.getInviteCode()); + lqw.eq(bo.getCompanyId() != null, HotDriverInviteLog::getCompanyId, bo.getCompanyId()); + lqw.eq(StringUtils.isNotBlank(bo.getDriverId()), HotDriverInviteLog::getDriverId, bo.getDriverId()); + lqw.like(StringUtils.isNotBlank(bo.getDriverName()), HotDriverInviteLog::getDriverName, bo.getDriverName()); + lqw.eq(StringUtils.isNotBlank(bo.getDriverPhone()), HotDriverInviteLog::getDriverPhone, bo.getDriverPhone()); + return lqw; + } + + /** + * 新增驾驶员邀请码使用记录 + * + * @param bo 驾驶员邀请码使用记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotDriverInviteLogBo bo) { + HotDriverInviteLog add = MapstructUtils.convert(bo, HotDriverInviteLog.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改驾驶员邀请码使用记录 + * + * @param bo 驾驶员邀请码使用记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotDriverInviteLogBo bo) { + HotDriverInviteLog update = MapstructUtils.convert(bo, HotDriverInviteLog.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotDriverInviteLog entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除驾驶员邀请码使用记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/flow/callback/FlowCallbackFactory.java b/src/main/java/com/hotwj/platform/flow/callback/FlowCallbackFactory.java new file mode 100644 index 0000000..55a820b --- /dev/null +++ b/src/main/java/com/hotwj/platform/flow/callback/FlowCallbackFactory.java @@ -0,0 +1,34 @@ +package com.hotwj.platform.flow.callback; + +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 流程回调工厂 + * + * @author shihongwei + */ +@Component +public class FlowCallbackFactory { + + private final Map callbackMap = new ConcurrentHashMap<>(); + + // Spring 自动注入所有实现类 + public FlowCallbackFactory(List callbacks) { + for (IFlowCallback callback : callbacks) { + callbackMap.put(callback.getFlowCode(), callback); + } + } + + public void register(IFlowCallback callback) { + callbackMap.put(callback.getFlowCode(), callback); + } + + public Optional getCallback(String flowCode) { + return Optional.ofNullable(callbackMap.get(flowCode)); + } +} diff --git a/src/main/java/com/hotwj/platform/flow/callback/IFlowCallback.java b/src/main/java/com/hotwj/platform/flow/callback/IFlowCallback.java new file mode 100644 index 0000000..47cc9d3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/flow/callback/IFlowCallback.java @@ -0,0 +1,26 @@ +package com.hotwj.platform.flow.callback; + +/** + * 流程回调接口 + */ +public interface IFlowCallback { + + /** + * 获取流程编码 + * + * @return 流程编码 + */ + default String getFlowCode() { + return null; + } + + /** + * 流程结束回调 + * + * @param instanceId 流程实例ID + * @param businessId 业务ID + * @param flowCode 流程编码 + * @param success 是否成功 (true=通过, false=驳回/撤销) + */ + void onProcessEnd(String instanceId, String businessId, String flowCode, boolean success); +} diff --git a/src/main/java/com/hotwj/platform/flow/controller/SysFlowController.java b/src/main/java/com/hotwj/platform/flow/controller/SysFlowController.java new file mode 100644 index 0000000..3d7a0ad --- /dev/null +++ b/src/main/java/com/hotwj/platform/flow/controller/SysFlowController.java @@ -0,0 +1,92 @@ +package com.hotwj.platform.flow.controller; + +import com.hotwj.platform.flow.domain.SysFlowTask; +import com.hotwj.platform.flow.domain.SysFlowTaskHis; +import com.hotwj.platform.flow.service.ISysFlowService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 流程通用接口 + * + * @author shihongwei + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/flow") +@Tag(name = "通用流程接口", description = "通用流程管理") +public class SysFlowController { + + private final ISysFlowService flowService; + + @GetMapping("/todo") + @Operation(summary = "获取我的待办") + public TableDataInfo getTodo(PageQuery pageQuery, String isRead) { + String userId = LoginHelper.getBusinessUserId(); + if (userId == null) { + userId = String.valueOf(LoginHelper.getUserId()); + } + return flowService.getTodoList(userId, pageQuery, isRead); + } + + @GetMapping("/todo/enterprise") + @Operation(summary = "获取企业待办") + public TableDataInfo getEnterpriseTodo(PageQuery pageQuery) { + return flowService.getEnterpriseTodoList(getCompanyId(), pageQuery); + } + + @PostMapping("/read/{taskId}") + @Operation(summary = "标记已读") + public R read(@PathVariable String taskId) { + String userId = LoginHelper.getBusinessUserId(); + if (userId == null) { + userId = String.valueOf(LoginHelper.getUserId()); + } + flowService.readTask(taskId, userId); + return R.ok(); + } + + @GetMapping("/history/{instanceId}") + @Operation(summary = "获取审批历史") + public R> getHistory(@PathVariable String instanceId) { + return R.ok(flowService.getHistory(instanceId)); + } + + @PostMapping("/audit") + @Operation(summary = "审批任务") + public R audit(@RequestBody AuditBody body) { + String userId = LoginHelper.getBusinessUserId(); + if (userId == null) { + userId = String.valueOf(LoginHelper.getUserId()); + } + flowService.audit(body.getTaskId(), body.isPass(), body.getComment(), userId); + return R.ok(); + } + + // 辅助方法获取 CompanyId + private Long getCompanyId() { + try { + return LoginHelper.getLoginUser().getCompanyId(); + } catch (Exception e) { + return 0L; + } + } + + @Data + public static class AuditBody { + private String taskId; + private boolean pass; + private String comment; + } +} diff --git a/src/main/java/com/hotwj/platform/flow/domain/SysFlowInstance.java b/src/main/java/com/hotwj/platform/flow/domain/SysFlowInstance.java new file mode 100644 index 0000000..c2b268d --- /dev/null +++ b/src/main/java/com/hotwj/platform/flow/domain/SysFlowInstance.java @@ -0,0 +1,92 @@ +package com.hotwj.platform.flow.domain; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 流程实例对象 sys_flow_instance + * + * @author shihongwei + * @date 2026-01-25 + */ +@Data +@TableName("sys_flow_instance") +public class SysFlowInstance implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "instance_id", type = IdType.ASSIGN_UUID) + private String instanceId; + + /** + * 流程编码 + */ + private String flowCode; + + /** + * 业务单据ID + */ + private String businessId; + + /** + * 状态: 0-进行中, 1-已完成, 2-已撤销, 9-已终止 + */ + private Integer status; + + /** + * 租户/公司ID + */ + private Long companyId; + + /** + * 发起人ID (可能为UUID字符串) + */ + private String initiatorId; + + /** + * 结束时间 + */ + private Date finishTime; + + /** + * 创建者 + */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private Date createTime; + + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** + * 备注 + */ + private String remark; + +} diff --git a/src/main/java/com/hotwj/platform/flow/domain/SysFlowTask.java b/src/main/java/com/hotwj/platform/flow/domain/SysFlowTask.java new file mode 100644 index 0000000..8f12d17 --- /dev/null +++ b/src/main/java/com/hotwj/platform/flow/domain/SysFlowTask.java @@ -0,0 +1,121 @@ +package com.hotwj.platform.flow.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 流程任务对象 sys_flow_task + * + * @author shihongwei + * @date 2026-01-25 + */ +@Data +@TableName("sys_flow_task") +public class SysFlowTask implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务ID + */ + @TableId(value = "task_id", type = IdType.ASSIGN_UUID) + private String taskId; + + /** + * 流程实例ID + */ + private String instanceId; + + /** + * 节点编码 + */ + private String nodeCode; + + /** + * 节点名称 + */ + private String nodeName; + + /** + * 审批人ID + */ + private String approverId; + + @TableField(exist = false) + private String approverName; + + /** + * 租户/公司ID + */ + private Long companyId; + + /** + * 是否已读: 0-未读, 1-已读 + */ + private Integer isRead; + + /** + * 创建者 + */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private Date createTime; + + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** + * 流程名称 (非数据库字段) + */ + @TableField(exist = false) + private String flowName; + + /** + * 流程编码 (非数据库字段) + */ + @TableField(exist = false) + private String flowCode; + + /** + * 业务ID (非数据库字段) + */ + @TableField(exist = false) + private String businessId; + + /** + * 计划ID (非数据库字段) + */ + @TableField(exist = false) + private Long planId; + + /** + * 会议类型 (非数据库字段) + */ + @TableField(exist = false) + private String meetingType; + + /** + * 开始时间 (非数据库字段) + */ + @TableField(exist = false) + private Date startTime; +} diff --git a/src/main/java/com/hotwj/platform/flow/domain/SysFlowTaskHis.java b/src/main/java/com/hotwj/platform/flow/domain/SysFlowTaskHis.java new file mode 100644 index 0000000..ae68a50 --- /dev/null +++ b/src/main/java/com/hotwj/platform/flow/domain/SysFlowTaskHis.java @@ -0,0 +1,102 @@ +package com.hotwj.platform.flow.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 流程任务历史对象 sys_flow_task_his + * + * @author shihongwei + * @date 2026-01-25 + */ +@Data +@TableName("sys_flow_task_his") +public class SysFlowTaskHis implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 历史ID + */ + @TableId(value = "his_id", type = IdType.ASSIGN_UUID) + private String hisId; + + /** + * 任务ID + */ + private String taskId; + + /** + * 流程实例ID + */ + private String instanceId; + + /** + * 节点编码 + */ + private String nodeCode; + + /** + * 节点名称 + */ + private String nodeName; + + /** + * 审批人ID + */ + private String approverId; + + /** + * 审批状态: 1-通过, 2-驳回 + */ + private Integer status; + + /** + * 审批意见 + */ + private String comment; + + /** + * 审批时间 + */ + private Date auditTime; + + /** + * 租户/公司ID + */ + private Long companyId; + + /** + * 是否已读: 0-未读, 1-已读 + */ + private Integer isRead; + + /** + * 创建者 + */ + @TableField(fill = FieldFill.INSERT) + private String createBy; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private Date createTime; + + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateTime; +} diff --git a/src/main/java/com/hotwj/platform/flow/domain/dto/FlowNextDto.java b/src/main/java/com/hotwj/platform/flow/domain/dto/FlowNextDto.java new file mode 100644 index 0000000..af2aa0a --- /dev/null +++ b/src/main/java/com/hotwj/platform/flow/domain/dto/FlowNextDto.java @@ -0,0 +1,33 @@ +package com.hotwj.platform.flow.domain.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 流程下一节点信息 + * + * @author shihongwei + * @date 2026-01-25 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class FlowNextDto { + + /** + * 节点编码 + */ + private String nodeCode; + + /** + * 节点名称 + */ + private String nodeName; + + /** + * 下一节点审批人ID + */ + private String approverId; + +} diff --git a/src/main/java/com/hotwj/platform/flow/event/StartFlowEvent.java b/src/main/java/com/hotwj/platform/flow/event/StartFlowEvent.java new file mode 100644 index 0000000..86f69fe --- /dev/null +++ b/src/main/java/com/hotwj/platform/flow/event/StartFlowEvent.java @@ -0,0 +1,37 @@ +package com.hotwj.platform.flow.event; + +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +/** + * 启动流程事件 + * + * @author shihongwei + * @date 2026-01-26 + */ +@Getter +public class StartFlowEvent extends ApplicationEvent { + private final String flowCode; + private final String businessId; + private final Long companyId; + private final String initiatorId; + private final String approverId; + + /** + * 流程实例ID (事件处理完成后回写) + */ + private String instanceId; + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + public StartFlowEvent(Object source, String flowCode, String businessId, Long companyId, String initiatorId, String approverId) { + super(source); + this.flowCode = flowCode; + this.businessId = businessId; + this.companyId = companyId; + this.initiatorId = initiatorId; + this.approverId = approverId; + } +} diff --git a/src/main/java/com/hotwj/platform/flow/listener/FlowEventListener.java b/src/main/java/com/hotwj/platform/flow/listener/FlowEventListener.java new file mode 100644 index 0000000..9a93a71 --- /dev/null +++ b/src/main/java/com/hotwj/platform/flow/listener/FlowEventListener.java @@ -0,0 +1,39 @@ +package com.hotwj.platform.flow.listener; + +import com.hotwj.platform.flow.event.StartFlowEvent; +import com.hotwj.platform.flow.service.ISysFlowService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +/** + * 流程事件监听器 + * + * @author shihongwei + * @date 2026-01-26 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class FlowEventListener { + + private final ISysFlowService flowService; + + @EventListener + public void handleStartFlowEvent(StartFlowEvent event) { + log.info("收到启动流程事件: flowCode={}, businessId={}", event.getFlowCode(), event.getBusinessId()); + try { + String instanceId = flowService.startFlow( + event.getFlowCode(), + event.getBusinessId(), + event.getCompanyId(), + event.getInitiatorId(), + event.getApproverId() + ); + event.setInstanceId(instanceId); + } catch (Exception e) { + log.error("启动流程失败: flowCode={}, businessId={}", event.getFlowCode(), event.getBusinessId(), e); + } + } +} diff --git a/src/main/java/com/hotwj/platform/flow/mapper/SysFlowInstanceMapper.java b/src/main/java/com/hotwj/platform/flow/mapper/SysFlowInstanceMapper.java new file mode 100644 index 0000000..d795f5f --- /dev/null +++ b/src/main/java/com/hotwj/platform/flow/mapper/SysFlowInstanceMapper.java @@ -0,0 +1,15 @@ +package com.hotwj.platform.flow.mapper; + +import com.hotwj.platform.flow.domain.SysFlowInstance; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 流程实例 Mapper + * + * @author shihongwei + * @date 2026-01-25 + */ +@Mapper +public interface SysFlowInstanceMapper extends BaseMapperPlus { +} diff --git a/src/main/java/com/hotwj/platform/flow/mapper/SysFlowTaskHisMapper.java b/src/main/java/com/hotwj/platform/flow/mapper/SysFlowTaskHisMapper.java new file mode 100644 index 0000000..7c03b98 --- /dev/null +++ b/src/main/java/com/hotwj/platform/flow/mapper/SysFlowTaskHisMapper.java @@ -0,0 +1,15 @@ +package com.hotwj.platform.flow.mapper; + +import com.hotwj.platform.flow.domain.SysFlowTaskHis; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 流程处理历史 Mapper + * + * @author shihongwei + * @date 2026-01-25 + */ +@Mapper +public interface SysFlowTaskHisMapper extends BaseMapperPlus { +} diff --git a/src/main/java/com/hotwj/platform/flow/mapper/SysFlowTaskMapper.java b/src/main/java/com/hotwj/platform/flow/mapper/SysFlowTaskMapper.java new file mode 100644 index 0000000..ebe4614 --- /dev/null +++ b/src/main/java/com/hotwj/platform/flow/mapper/SysFlowTaskMapper.java @@ -0,0 +1,15 @@ +package com.hotwj.platform.flow.mapper; + +import com.hotwj.platform.flow.domain.SysFlowTask; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 流程待办任务 Mapper + * + * @author shihongwei + * @date 2026-01-25 + */ +@Mapper +public interface SysFlowTaskMapper extends BaseMapperPlus { +} diff --git a/src/main/java/com/hotwj/platform/flow/service/ISysFlowService.java b/src/main/java/com/hotwj/platform/flow/service/ISysFlowService.java new file mode 100644 index 0000000..27f2386 --- /dev/null +++ b/src/main/java/com/hotwj/platform/flow/service/ISysFlowService.java @@ -0,0 +1,159 @@ +package com.hotwj.platform.flow.service; + +import com.hotwj.platform.flow.domain.SysFlowInstance; +import com.hotwj.platform.flow.domain.SysFlowTask; +import com.hotwj.platform.flow.domain.SysFlowTaskHis; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.List; + +/** + * 流程引擎服务接口 + */ +public interface ISysFlowService { + + /** + * 发起流程 + * + * @param flowCode 流程编码 + * @param businessId 业务ID + * @param companyId 公司ID + * @param initiatorId 发起人ID + * @param approverId 审批人ID (第一步) + * @return 流程实例ID + */ + String startFlow(String flowCode, String businessId, Long companyId, String initiatorId, String approverId); + + /** + * 审批流程 + * + * @param taskId 任务ID + * @param pass 是否通过 + * @param comment 审批意见 + * @param auditorId 审核人ID + */ + void audit(String taskId, boolean pass, String comment, String auditorId); + + /** + * 获取待办列表 + * + * @param userId 用户ID + * @param pageQuery 分页参数 + * @param isRead 是否已读(0-未读, 1-已读) + * @return 待办列表 + */ + TableDataInfo getTodoList(String userId, PageQuery pageQuery, String isRead); + + /** + * 获取企业待办列表 + * + * @param companyId 公司ID + * @param pageQuery 分页参数 + * @return 待办列表 + */ + TableDataInfo getEnterpriseTodoList(Long companyId, PageQuery pageQuery); + + /** + * 标记任务已读 + * + * @param taskId 任务ID + * @param userId 用户ID + */ + void readTask(String taskId, String userId); + + /** + * 获取流程历史 + * + * @param instanceId 流程实例ID + * @return 历史记录 + */ + List getHistory(String instanceId); + + /** + * 获取流程实例详情 + * + * @param instanceId 流程实例ID + * @return 流程实例 + */ + SysFlowInstance getInstance(String instanceId); + + /** + * 判断当前用户是否是当前节点的审批人 + * + * @param instanceId 流程实例ID + * @param userId 用户ID + * @return true/false + */ + boolean isApprover(String instanceId, String userId); + + /** + * 获取当前用户的任务ID + * + * @param instanceId 流程实例ID + * @param userId 用户ID + * @return 任务ID,如果无任务则返回null + */ + String getTaskId(String instanceId, String userId); + + /** + * 推送直接待办任务 (不依赖流程定义) + * + * @param flowCode 流程编码 + * @param businessId 业务ID + * @param title 待办标题 + * @param companyId 公司ID + * @param userId 接收人ID + * @return 任务ID + */ + String pushDirectTodo(String flowCode, String businessId, String title, Long companyId, String userId); + + /** + * 检查是否存在待办任务 + * + * @param flowCode 流程编码 + * @param businessId 业务ID + * @param userId 用户ID + * @return 是否存在 + */ + boolean hasPendingTask(String flowCode, String businessId, String userId); + + /** + * 删除直接待办任务 + * + * @param flowCode 流程编码 + * @param businessId 业务ID + * @param userId 用户ID + */ + void deleteDirectTodo(String flowCode, String businessId, String userId); + + /** + * 根据业务ID获取待办任务ID + * + * @param flowCode 流程编码 + * @param businessId 业务ID + * @param userId 用户ID + * @return 任务ID + */ + String getTaskIdByBusinessId(String flowCode, String businessId, String userId); + + /** + * 根据任务ID获取任务详情 + * + * @param taskId 任务ID + * @return 任务详情 + */ + SysFlowTask getTaskById(String taskId); + + /** + * 根据任务ID获取流程实例 + * + * @param taskId 任务ID + * @return 流程实例 + */ + SysFlowInstance getInstanceByTaskId(String taskId); + + List getApproverIdsByInstance(String instanceId); + + void deleteTodosByInstanceIds(java.util.Collection instanceIds); +} diff --git a/src/main/java/com/hotwj/platform/flow/service/impl/SysFlowServiceImpl.java b/src/main/java/com/hotwj/platform/flow/service/impl/SysFlowServiceImpl.java new file mode 100644 index 0000000..e159aaa --- /dev/null +++ b/src/main/java/com/hotwj/platform/flow/service/impl/SysFlowServiceImpl.java @@ -0,0 +1,779 @@ +package com.hotwj.platform.flow.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.flow.callback.FlowCallbackFactory; +import com.hotwj.platform.flow.callback.IFlowCallback; +import com.hotwj.platform.flow.domain.SysFlowInstance; +import com.hotwj.platform.flow.domain.SysFlowTask; +import com.hotwj.platform.flow.domain.SysFlowTaskHis; +import com.hotwj.platform.flow.domain.dto.FlowNextDto; +import com.hotwj.platform.flow.mapper.SysFlowInstanceMapper; +import com.hotwj.platform.flow.mapper.SysFlowTaskHisMapper; +import com.hotwj.platform.flow.mapper.SysFlowTaskMapper; +import com.hotwj.platform.flow.service.ISysFlowService; +import com.hotwj.platform.flow.strategy.FlowStrategyFactory; +import com.hotwj.platform.flow.strategy.IFlowStrategy; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.HotHiddenDangerInspection; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.mapper.HotHiddenDangerInspectionMapper; +import com.hotwj.platform.securityManagement.securityMeeting.domain.HotSecurityMeeting; +import com.hotwj.platform.securityManagement.securityMeeting.mapper.HotSecurityMeetingMapper; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.HotTrainingParticipant; +import com.hotwj.platform.securityManagement.trainingParticipant.mapper.HotTrainingParticipantMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.warm.flow.core.utils.CollUtil; +import org.dromara.warm.flow.core.utils.ObjectUtil; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 流程引擎服务实现 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class SysFlowServiceImpl implements ISysFlowService { + + private final SysFlowInstanceMapper instanceMapper; + private final SysFlowTaskMapper taskMapper; + private final SysFlowTaskHisMapper taskHisMapper; + + private final FlowStrategyFactory flowStrategyFactory; + private final FlowCallbackFactory flowCallbackFactory; + + private final HotCompanySafetyManagerMapper hotCompanySafetyManagerMapper; + private final HotDriverMapper hotDriverMapper; + private final HotHiddenDangerInspectionMapper hotHiddenDangerInspectionMapper; + private final HotSecurityMeetingMapper hotSecurityMeetingMapper; + private final HotTrainingParticipantMapper hotTrainingParticipantMapper; + + // 状态常量 + private static final int STATUS_RUNNING = 0; + private static final int STATUS_FINISHED = 1; + private static final int STATUS_REVOKED = 2; // 撤销 + private static final int STATUS_TERMINATED = 9; // 终止 + + private static final int TASK_STATUS_PASS = 1; + private static final int TASK_STATUS_REJECT = 2; + + @Override + @Transactional(rollbackFor = Exception.class) + public String startFlow(String flowCode, String businessId, Long companyId, String initiatorId, String approverId) { + // 1. 创建流程实例 + SysFlowInstance instance = new SysFlowInstance(); + instance.setFlowCode(flowCode); + instance.setBusinessId(businessId); + instance.setCompanyId(companyId); + instance.setInitiatorId(initiatorId); + instance.setStatus(STATUS_RUNNING); + instance.setCreateTime(new Date()); + + LoginUser loginUser = getLoginUser(); + if (ObjectUtil.isNotNull(loginUser)) { + Long userId = loginUser.getUserId(); + instance.setCreateBy(String.valueOf(userId)); + instance.setUpdateBy(String.valueOf(userId)); + } + + instanceMapper.insert(instance); + + // 2. 创建第一个任务 (开始节点 -> 下一个节点) + SysFlowTask task = new SysFlowTask(); + task.setInstanceId(instance.getInstanceId()); + + // 获取初始节点策略 + IFlowStrategy strategy = flowStrategyFactory.getStrategy(flowCode); + FlowNextDto initialNode; + if (strategy != null) { + initialNode = strategy.getInitialNode(businessId); + } else { + initialNode = new FlowNextDto("NODE_1", "一级审批", null); + } + + task.setNodeCode(initialNode.getNodeCode()); + task.setNodeName(initialNode.getNodeName()); + // 优先使用 startFlow 传入的审批人,如果为空则尝试使用策略返回的审批人 + if (StrUtil.isNotBlank(approverId)) { + task.setApproverId(approverId); + } else { + task.setApproverId(initialNode.getApproverId()); + } + + task.setCompanyId(companyId); + task.setCreateTime(new Date()); + if (ObjectUtil.isNotNull(loginUser)) { + Long userId = loginUser.getUserId(); + task.setCreateBy(String.valueOf(userId)); + task.setUpdateBy(String.valueOf(userId)); + } + taskMapper.insert(task); + + return instance.getInstanceId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void audit(String taskId, boolean pass, String comment, String auditorId) { + // 1. 获取任务 + SysFlowTask task = taskMapper.selectById(taskId); + if (task == null) { + throw new ServiceException("任务不存在或已处理"); + } + + // 校验审核人 + if (!auditorId.equals(task.getApproverId())) { + throw new ServiceException("您不是当前节点的审批人"); + } + + SysFlowInstance instance = instanceMapper.selectById(task.getInstanceId()); + if (instance == null) { + throw new ServiceException("流程实例不存在"); + } + + // 修正:避免空指针和类型错误 + if (instance.getStatus() != null && instance.getStatus() != STATUS_RUNNING) { + throw new ServiceException("流程已结束或非运行状态"); + } + + // 2. 归档当前任务到历史 + SysFlowTaskHis his = new SysFlowTaskHis(); + his.setTaskId(task.getTaskId()); + his.setInstanceId(task.getInstanceId()); + his.setNodeCode(task.getNodeCode()); + his.setNodeName(task.getNodeName()); + his.setApproverId(task.getApproverId()); + his.setStatus(pass ? TASK_STATUS_PASS : TASK_STATUS_REJECT); + his.setComment(comment); + his.setAuditTime(new Date()); + his.setCompanyId(task.getCompanyId()); + taskHisMapper.insert(his); + + // 删除待办任务 + taskMapper.deleteById(taskId); + + // 3. 决策下一步 + boolean processEnd = false; + boolean processSuccess = false; + + if (!pass) { + // 驳回 + IFlowStrategy strategy = flowStrategyFactory.getStrategy(instance.getFlowCode()); + // 如果驳回返回的下一节点不为空,则继续流程 + FlowNextDto rejectNode = (strategy != null) ? strategy.reject(instance, task.getNodeCode()) : null; + + if (rejectNode != null) { + // 策略返回了驳回节点,流程回退到该节点 + SysFlowTask newTask = new SysFlowTask(); + newTask.setInstanceId(instance.getInstanceId()); + newTask.setNodeCode(rejectNode.getNodeCode()); + newTask.setNodeName(rejectNode.getNodeName()); + newTask.setApproverId(rejectNode.getApproverId()); + newTask.setCompanyId(instance.getCompanyId()); + newTask.setCreateTime(new Date()); + if (ObjectUtil.isNotNull(getLoginUser())) { + newTask.setCreateBy(String.valueOf(getLoginUser().getUserId())); + newTask.setUpdateBy(String.valueOf(getLoginUser().getUserId())); + } + taskMapper.insert(newTask); + } else { + // 默认处理:流程直接结束 (简单模式) + instance.setStatus(STATUS_TERMINATED); // 或者 REVOKED,视业务定义,这里用9表示审核不通过终止 + instance.setFinishTime(new Date()); + instanceMapper.updateById(instance); + processEnd = true; + processSuccess = false; + } + } else { + // 通过,获取下一步 + IFlowStrategy strategy = flowStrategyFactory.getStrategy(instance.getFlowCode()); + FlowNextDto nextNode = null; + if (strategy != null) { + nextNode = strategy.next(instance, task.getNodeCode()); + } + + if (nextNode == null) { + // 没有下一步,流程结束 + instance.setStatus(STATUS_FINISHED); + instance.setFinishTime(new Date()); + instanceMapper.updateById(instance); + processEnd = true; + processSuccess = true; + } else { + // 有下一步,创建新任务 + SysFlowTask newTask = new SysFlowTask(); + newTask.setInstanceId(instance.getInstanceId()); + newTask.setNodeCode(nextNode.getNodeCode()); + newTask.setNodeName(nextNode.getNodeName()); + newTask.setApproverId(nextNode.getApproverId()); // 策略返回的审批人 + newTask.setCompanyId(instance.getCompanyId()); + newTask.setCreateTime(new Date()); + taskMapper.insert(newTask); + } + } + + // 4. 触发回调 + if (processEnd) { + Optional callback = flowCallbackFactory.getCallback(instance.getFlowCode()); + if (callback.isPresent()) { + IFlowCallback iFlowCallback = callback.get(); + iFlowCallback.onProcessEnd(instance.getInstanceId(), + instance.getBusinessId(), + instance.getFlowCode(), + processSuccess); + } + } + } + + @Override + public TableDataInfo getTodoList(String userId, PageQuery pageQuery, String isRead) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.eq(SysFlowTask::getApproverId, userId); + if (StrUtil.isNotBlank(isRead)) { + lqw.eq(SysFlowTask::getIsRead, Integer.valueOf(isRead)); + } + lqw.orderByDesc(SysFlowTask::getCreateTime); + Page page = taskMapper.selectPage(pageQuery.build(), lqw); + + fillFlowInfo(page.getRecords()); + + return TableDataInfo.build(page); + } + + @Override + public TableDataInfo getEnterpriseTodoList(Long companyId, PageQuery pageQuery) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.eq(SysFlowTask::getCompanyId, companyId); + LoginUser loginUser = getLoginUser(); + if (loginUser != null && cn.hutool.core.util.StrUtil.isNotBlank(loginUser.getBusinessUserId())) { + lqw.ne(SysFlowTask::getApproverId, loginUser.getBusinessUserId()); + } + lqw.orderByDesc(SysFlowTask::getCreateTime); + Page page = taskMapper.selectPage(pageQuery.build(), lqw); + + List records = page.getRecords(); + if (CollUtil.isNotEmpty(records)) { + List approverIds = records.stream() + .map(SysFlowTask::getApproverId) + .filter(id -> StrUtil.isNotBlank(id)) + .distinct() + .toList(); + + if (CollUtil.isNotEmpty(approverIds)) { + Map approverNameMap = new HashMap<>(); + + List managerIds = approverIds.stream() + .filter(StrUtil::isNumeric) + .map(Long::valueOf) + .distinct() + .toList(); + + if (CollUtil.isNotEmpty(managerIds)) { + List managers = hotCompanySafetyManagerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .in(HotCompanySafetyManager::getId, managerIds) + ); + for (HotCompanySafetyManager manager : managers) { + approverNameMap.put(String.valueOf(manager.getId()), manager.getName()); + } + } + + List drivers = hotDriverMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotDriver::getCompanyId, companyId) + .in(HotDriver::getId, approverIds) + ); + for (HotDriver driver : drivers) { + approverNameMap.put(driver.getId(), driver.getName()); + } + + for (SysFlowTask task : records) { + task.setApproverName(approverNameMap.get(task.getApproverId())); + } + } + } + + fillFlowInfo(records); + + return TableDataInfo.build(page); + } + + /** + * 填充流程信息 (名称、编码) + */ + private void fillFlowInfo(List taskList) { + if (CollUtil.isEmpty(taskList)) { + return; + } + + // 获取所有实例ID + List instanceIds = taskList.stream() + .map(SysFlowTask::getInstanceId) + .distinct() + .toList(); + + if (CollUtil.isEmpty(instanceIds)) { + return; + } + + // 查询实例信息 + List instances = instanceMapper.selectBatchIds(instanceIds); + Map instanceMap = instances.stream() + .collect(java.util.stream.Collectors.toMap(SysFlowInstance::getInstanceId, i -> i)); + + // 填充信息 + for (SysFlowTask task : taskList) { + SysFlowInstance instance = instanceMap.get(task.getInstanceId()); + if (instance != null) { + task.setBusinessId(instance.getBusinessId()); + String flowCode = instance.getFlowCode(); + if (StrUtil.isNotBlank(flowCode)) { + task.setFlowCode(flowCode); + IFlowStrategy strategy = flowStrategyFactory.getStrategy(flowCode); + if (strategy != null) { + task.setFlowName(strategy.getFlowName()); + } else if ("VIOLATION_HANDLE".equals(flowCode)) { + task.setFlowName("违规处理"); + } else if ("SIGN_FILE".equals(flowCode)) { + task.setFlowName("文件签署"); + } else if ("TRAINING_STUDY".equals(flowCode)) { + task.setFlowName("安全教育培训"); + } + } + } + } + + // 分组处理特定业务信息 + Map> tasksByFlowCode = taskList.stream() + .filter(t -> StrUtil.isNotBlank(t.getFlowCode()) && StrUtil.isNotBlank(t.getBusinessId())) + .collect(Collectors.groupingBy(SysFlowTask::getFlowCode)); + + // 隐患排查 + if (tasksByFlowCode.containsKey("HIDDEN_DANGER_CHECK")) { + fillHiddenDangerInfo(tasksByFlowCode.get("HIDDEN_DANGER_CHECK")); + } + + // 安全会议 + if (tasksByFlowCode.containsKey("MEETING_SIGN")) { + fillMeetingInfo(tasksByFlowCode.get("MEETING_SIGN")); + } + + if (tasksByFlowCode.containsKey("TRAINING_STUDY")) { + fillTrainingStudyInfo(tasksByFlowCode.get("TRAINING_STUDY")); + } + } + + /** + * 填充隐患排查相关信息 + */ + private void fillHiddenDangerInfo(List tasks) { + if (CollUtil.isEmpty(tasks)) return; + + Set inspectionIds = new HashSet<>(); + for (SysFlowTask task : tasks) { + try { + inspectionIds.add(Long.valueOf(task.getBusinessId())); + } catch (NumberFormatException e) { + // ignore + } + } + + if (CollUtil.isNotEmpty(inspectionIds)) { + List inspections = hotHiddenDangerInspectionMapper.selectBatchIds(inspectionIds); + if (CollUtil.isNotEmpty(inspections)) { + Map planIdMap = inspections.stream() + .filter(i -> i.getPlanId() != null) + .collect(Collectors.toMap(HotHiddenDangerInspection::getId, HotHiddenDangerInspection::getPlanId)); + + for (SysFlowTask task : tasks) { + try { + Long inspectionId = Long.valueOf(task.getBusinessId()); + Long planId = planIdMap.get(inspectionId); + if (planId != null) { + task.setPlanId(planId); + } + } catch (NumberFormatException e) { + // ignore + } + } + } + } + } + + /** + * 填充安全会议相关信息 + */ + private void fillMeetingInfo(List tasks) { + if (CollUtil.isEmpty(tasks)) return; + + Set meetingIds = new HashSet<>(); + for (SysFlowTask task : tasks) { + try { + meetingIds.add(Long.valueOf(task.getBusinessId())); + } catch (NumberFormatException e) { + // ignore + } + } + + if (CollUtil.isNotEmpty(meetingIds)) { + List meetings = hotSecurityMeetingMapper.selectBatchIds(meetingIds); + if (CollUtil.isNotEmpty(meetings)) { + Map meetingMap = meetings.stream() + .collect(Collectors.toMap(HotSecurityMeeting::getId, m -> m)); + + for (SysFlowTask task : tasks) { + try { + Long meetingId = Long.valueOf(task.getBusinessId()); + HotSecurityMeeting meeting = meetingMap.get(meetingId); + if (meeting != null) { + if (StrUtil.isNotBlank(meeting.getMeetingType())) { + task.setMeetingType(meeting.getMeetingType()); + } + if (meeting.getStartTime() != null) { + task.setStartTime(meeting.getStartTime()); + // 覆盖创建时间为会议开始时间,以便前端展示 + task.setCreateTime(meeting.getStartTime()); + } + } + } catch (NumberFormatException e) { + // ignore + } + } + } + } + } + + private void fillTrainingStudyInfo(List tasks) { + if (CollUtil.isEmpty(tasks)) return; + + Set trainingIds = new HashSet<>(); + Set userIds = new HashSet<>(); + for (SysFlowTask task : tasks) { + if (StrUtil.isNotBlank(task.getApproverId())) { + userIds.add(task.getApproverId()); + } + try { + trainingIds.add(Long.valueOf(task.getBusinessId())); + } catch (Exception ignored) { + } + } + if (CollUtil.isEmpty(trainingIds) || CollUtil.isEmpty(userIds)) { + return; + } + + List participants = hotTrainingParticipantMapper.selectList( + Wrappers.lambdaQuery() + .in(HotTrainingParticipant::getTrainingId, trainingIds) + .in(HotTrainingParticipant::getUserId, userIds) + .eq(HotTrainingParticipant::getIsDeleted, 0L) + ); + if (CollUtil.isEmpty(participants)) { + return; + } + + Map participantIdMap = new HashMap<>(); + for (HotTrainingParticipant participant : participants) { + if (participant == null || participant.getTrainingId() == null || StrUtil.isBlank(participant.getUserId()) || participant.getId() == null) { + continue; + } + String key = participant.getTrainingId() + "_" + participant.getUserId(); + participantIdMap.put(key, participant.getId()); + } + + for (SysFlowTask task : tasks) { + if (StrUtil.isBlank(task.getBusinessId()) || StrUtil.isBlank(task.getApproverId())) { + continue; + } + try { + Long trainingId = Long.valueOf(task.getBusinessId()); + Long participantId = participantIdMap.get(trainingId + "_" + task.getApproverId()); + if (participantId != null) { + task.setPlanId(participantId); + } + } catch (Exception ignored) { + } + } + } + + @Override + public void readTask(String taskId, String userId) { + SysFlowTask task = taskMapper.selectById(taskId); + if (task == null) { + throw new ServiceException("任务不存在"); + } + // 简单校验:只有任务审批人可以标记已读 + // 如果是企业管理员查看企业待办,通常是监控,不一定需要标记已读(或者标记已读意味着管理员已审阅) + // 这里暂时限制为审批人本人 + if (!userId.equals(task.getApproverId())) { + throw new ServiceException("您无权操作此任务"); + } + task.setIsRead(1); + taskMapper.updateById(task); + } + + @Override + public List getHistory(String instanceId) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.eq(SysFlowTaskHis::getInstanceId, instanceId); + lqw.orderByAsc(SysFlowTaskHis::getAuditTime); + return taskHisMapper.selectList(lqw); + } + + @Override + public SysFlowInstance getInstance(String instanceId) { + return instanceMapper.selectById(instanceId); + } + + @Override + public boolean isApprover(String instanceId, String userId) { + if (instanceId == null || userId == null) { + return false; + } + // 查询当前实例是否有该用户的待办任务 + Long count = taskMapper.selectCount(new LambdaQueryWrapper() + .eq(SysFlowTask::getInstanceId, instanceId) + .eq(SysFlowTask::getApproverId, userId)); + return count != null && count > 0; + } + + @Override + public String getTaskId(String instanceId, String userId) { + if (instanceId == null || userId == null) { + return null; + } + SysFlowTask task = taskMapper.selectOne(new LambdaQueryWrapper() + .eq(SysFlowTask::getInstanceId, instanceId) + .eq(SysFlowTask::getApproverId, userId) + .last("limit 1")); + return task != null ? task.getTaskId() : null; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public String pushDirectTodo(String flowCode, String businessId, String title, Long companyId, String userId) { + // 1. 创建虚拟流程实例 + SysFlowInstance instance = new SysFlowInstance(); + instance.setFlowCode(flowCode); + instance.setBusinessId(businessId); + instance.setCompanyId(companyId); + instance.setInitiatorId("SYSTEM"); // 系统推送 + instance.setStatus(STATUS_RUNNING); + instance.setCreateTime(new Date()); + + LoginUser loginUser = getLoginUser(); + if (loginUser != null) { + instance.setCreateBy(String.valueOf(loginUser.getUserId())); + } else { + instance.setCreateBy("SYSTEM"); + } + + instanceMapper.insert(instance); + + // 2. 创建任务 + SysFlowTask task = new SysFlowTask(); + task.setInstanceId(instance.getInstanceId()); + task.setNodeCode("DIRECT_TODO"); + task.setNodeName(title); // 使用标题作为节点名称显示 + task.setApproverId(userId); + task.setCompanyId(companyId); + task.setCreateTime(new Date()); + task.setIsRead(0); + + if (loginUser != null) { + task.setCreateBy(String.valueOf(loginUser.getUserId())); + } else { + task.setCreateBy("SYSTEM"); + } + + taskMapper.insert(task); + + return task.getTaskId(); + } + + @Override + public String getTaskIdByBusinessId(String flowCode, String businessId, String userId) { + // 1. 查找相关的运行中流程实例 + List instances = instanceMapper.selectList( + Wrappers.lambdaQuery() + .eq(SysFlowInstance::getFlowCode, flowCode) + .eq(SysFlowInstance::getBusinessId, businessId) + .eq(SysFlowInstance::getStatus, STATUS_RUNNING) + ); + + if (instances.isEmpty()) { + return null; + } + + List instanceIds = instances.stream() + .map(SysFlowInstance::getInstanceId) + .collect(Collectors.toList()); + + // 2. 检查是否有该用户的待办任务 + SysFlowTask task = taskMapper.selectOne( + Wrappers.lambdaQuery() + .in(SysFlowTask::getInstanceId, instanceIds) + .eq(SysFlowTask::getApproverId, userId) + .last("limit 1") + ); + return task != null ? task.getTaskId() : null; + } + + @Override + public boolean hasPendingTask(String flowCode, String businessId, String userId) { + // 1. 查找相关的运行中流程实例 + List instances = instanceMapper.selectList( + Wrappers.lambdaQuery() + .eq(SysFlowInstance::getFlowCode, flowCode) + .eq(SysFlowInstance::getBusinessId, businessId) + .eq(SysFlowInstance::getStatus, STATUS_RUNNING) + ); + + if (instances.isEmpty()) { + return false; + } + + List instanceIds = instances.stream() + .map(SysFlowInstance::getInstanceId) + .collect(Collectors.toList()); + + // 2. 检查是否有该用户的待办任务 + return taskMapper.selectCount( + Wrappers.lambdaQuery() + .in(SysFlowTask::getInstanceId, instanceIds) + .eq(SysFlowTask::getApproverId, userId) + ) > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteDirectTodo(String flowCode, String businessId, String userId) { + // 1. 查找相关的运行中流程实例 + List instances = instanceMapper.selectList( + Wrappers.lambdaQuery() + .eq(SysFlowInstance::getFlowCode, flowCode) + .eq(SysFlowInstance::getBusinessId, businessId) + .eq(SysFlowInstance::getStatus, STATUS_RUNNING) + ); + + if (instances.isEmpty()) { + return; + } + + List instanceIds = instances.stream() + .map(SysFlowInstance::getInstanceId) + .collect(Collectors.toList()); + + // 2. 查找该用户的待办任务 + List tasks = taskMapper.selectList( + Wrappers.lambdaQuery() + .in(SysFlowTask::getInstanceId, instanceIds) + .eq(SysFlowTask::getApproverId, userId) + ); + + if (tasks.isEmpty()) { + return; + } + + // 3. 删除任务 + List taskIds = tasks.stream().map(SysFlowTask::getTaskId).collect(Collectors.toList()); + taskMapper.deleteByIds(taskIds); + + // 4. 删除对应的流程实例(如果这些实例是为了DirectTodo创建的,通常是一对一) + // 这里为了安全起见,只删除那些关联了被删任务的实例,且假设这些实例只服务于这些任务 + // 对于 DIRECT_TODO,实例和任务是一对一创建的 + Set affectedInstanceIds = tasks.stream() + .map(SysFlowTask::getInstanceId) + .collect(Collectors.toSet()); + + if (!affectedInstanceIds.isEmpty()) { + instanceMapper.delete( + Wrappers.lambdaQuery() + .in(SysFlowInstance::getInstanceId, affectedInstanceIds) + ); + } + } + + @Override + public SysFlowTask getTaskById(String taskId) { + return taskMapper.selectById(taskId); + } + + @Override + public SysFlowInstance getInstanceByTaskId(String taskId) { + SysFlowTask task = taskMapper.selectById(taskId); + if (task == null) { + return null; + } + return instanceMapper.selectById(task.getInstanceId()); + } + + @Override + public List getApproverIdsByInstance(String instanceId) { + if (StrUtil.isBlank(instanceId)) { + return Collections.emptyList(); + } + List tasks = taskMapper.selectList( + new LambdaQueryWrapper() + .eq(SysFlowTask::getInstanceId, instanceId) + ); + if (CollUtil.isEmpty(tasks)) { + return Collections.emptyList(); + } + return tasks.stream() + .map(SysFlowTask::getApproverId) + .filter(StrUtil::isNotBlank) + .distinct() + .toList(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteTodosByInstanceIds(Collection instanceIds) { + if (CollUtil.isEmpty(instanceIds)) { + return; + } + List instances = instanceMapper.selectList( + Wrappers.lambdaQuery() + .in(SysFlowInstance::getInstanceId, instanceIds) + ); + if (CollUtil.isEmpty(instances)) { + return; + } + Set idSet = instances.stream().map(SysFlowInstance::getInstanceId).collect(Collectors.toSet()); + taskMapper.delete( + Wrappers.lambdaQuery() + .in(SysFlowTask::getInstanceId, idSet) + ); + Date now = new Date(); + for (SysFlowInstance ins : instances) { + ins.setStatus(STATUS_TERMINATED); + ins.setFinishTime(now); + instanceMapper.updateById(ins); + } + } + + private LoginUser getLoginUser() { + LoginUser loginUser; + try { + loginUser = LoginHelper.getLoginUser(); + } catch (Exception e) { + return null; + } + return loginUser; + } +} diff --git a/src/main/java/com/hotwj/platform/flow/strategy/FlowStrategyFactory.java b/src/main/java/com/hotwj/platform/flow/strategy/FlowStrategyFactory.java new file mode 100644 index 0000000..0729daa --- /dev/null +++ b/src/main/java/com/hotwj/platform/flow/strategy/FlowStrategyFactory.java @@ -0,0 +1,35 @@ +package com.hotwj.platform.flow.strategy; + +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 流程策略工厂 + * + * @author shihongwei + * @date 2026-01-25 + */ +@Component +public class FlowStrategyFactory { + + private final Map strategyMap = new ConcurrentHashMap<>(); + + public FlowStrategyFactory(List strategies) { + for (IFlowStrategy strategy : strategies) { + strategyMap.put(strategy.getFlowCode(), strategy); + } + } + + /** + * 获取流程策略 + * + * @param flowCode 流程编码 + * @return 策略对象 + */ + public IFlowStrategy getStrategy(String flowCode) { + return strategyMap.get(flowCode); + } +} diff --git a/src/main/java/com/hotwj/platform/flow/strategy/IFlowStrategy.java b/src/main/java/com/hotwj/platform/flow/strategy/IFlowStrategy.java new file mode 100644 index 0000000..f393362 --- /dev/null +++ b/src/main/java/com/hotwj/platform/flow/strategy/IFlowStrategy.java @@ -0,0 +1,57 @@ +package com.hotwj.platform.flow.strategy; + +import com.hotwj.platform.flow.domain.SysFlowInstance; +import com.hotwj.platform.flow.domain.dto.FlowNextDto; + +/** + * 流程策略接口 (定义流程流转逻辑) + * + * @author shihongwei + * @date 2026-01-25 + */ +public interface IFlowStrategy { + + /** + * 获取流程编码 + * + * @return 流程编码 + */ + String getFlowCode(); + + /** + * 获取流程名称 + * + * @return 流程名称 + */ + String getFlowName(); + + /** + * 获取下一节点信息 + * + * @param instance 流程实例 + * @param currentNodeCode 当前节点编码 + * @return 下一节点信息 (返回 null 表示流程结束) + */ + FlowNextDto next(SysFlowInstance instance, String currentNodeCode); + + /** + * 获取驳回节点信息 + * + * @param instance 流程实例 + * @param currentNodeCode 当前节点编码 + * @return 驳回节点信息 (返回 null 表示默认处理:终止流程) + */ + default FlowNextDto reject(SysFlowInstance instance, String currentNodeCode) { + return null; + } + + /** + * 获取初始节点信息 + * + * @param businessId 业务ID + * @return 初始节点信息 + */ + default FlowNextDto getInitialNode(String businessId) { + return new FlowNextDto("NODE_1", "一级审批", null); + } +} diff --git a/src/main/java/com/hotwj/platform/integration/face/FaceMatchResult.java b/src/main/java/com/hotwj/platform/integration/face/FaceMatchResult.java new file mode 100644 index 0000000..135002b --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/face/FaceMatchResult.java @@ -0,0 +1,11 @@ +package com.hotwj.platform.integration.face; + +import lombok.Data; + +@Data +public class FaceMatchResult { + private boolean match; + private double score; + private String rawResponse; +} + diff --git a/src/main/java/com/hotwj/platform/integration/face/FaceRecognitionService.java b/src/main/java/com/hotwj/platform/integration/face/FaceRecognitionService.java new file mode 100644 index 0000000..4cc1e18 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/face/FaceRecognitionService.java @@ -0,0 +1,8 @@ +package com.hotwj.platform.integration.face; + +public interface FaceRecognitionService { + boolean detect(String base64Image); + + FaceMatchResult match(String base64Image1, String base64Image2); +} + diff --git a/src/main/java/com/hotwj/platform/integration/face/baidu/BaiduFaceProperties.java b/src/main/java/com/hotwj/platform/integration/face/baidu/BaiduFaceProperties.java new file mode 100644 index 0000000..2570d13 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/face/baidu/BaiduFaceProperties.java @@ -0,0 +1,18 @@ +package com.hotwj.platform.integration.face.baidu; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "ext.baidu.face") +public class BaiduFaceProperties { + private String apiKey; + private String secretKey; + private String tokenUrl; + private String detectUrl; + private String matchUrl; + private int scoreThreshold = 80; +} + diff --git a/src/main/java/com/hotwj/platform/integration/face/baidu/BaiduFaceRecognitionService.java b/src/main/java/com/hotwj/platform/integration/face/baidu/BaiduFaceRecognitionService.java new file mode 100644 index 0000000..e3e4132 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/face/baidu/BaiduFaceRecognitionService.java @@ -0,0 +1,158 @@ +package com.hotwj.platform.integration.face.baidu; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.hotwj.platform.integration.face.FaceMatchResult; +import com.hotwj.platform.integration.face.FaceRecognitionService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.net.URI; +import java.net.URLEncoder; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.time.Duration; + +@Slf4j +@Service +@RequiredArgsConstructor +public class BaiduFaceRecognitionService implements FaceRecognitionService { + + private final BaiduFaceProperties properties; + private final ObjectMapper objectMapper = new ObjectMapper(); + private final HttpClient httpClient = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(10)) + .build(); + + private volatile String cachedToken; + private volatile long tokenExpireAt; + + @Override + public boolean detect(String base64Image) { + String token = getAccessToken(); + if (!StringUtils.hasText(token)) { + log.warn("人脸检测失败:访问令牌为空,无法调用百度接口"); + return false; + } + try { + String url = properties.getDetectUrl() + "?access_token=" + URLEncoder.encode(token, StandardCharsets.UTF_8); + String body = "{\"image\":\"" + base64Image + "\",\"image_type\":\"BASE64\"}"; + + log.info("开始人脸检测,请求URL:{}", url); + log.debug("检测图片长度:{}", base64Image == null ? 0 : base64Image.length()); + HttpRequest request = HttpRequest.newBuilder(URI.create(url)) + .timeout(Duration.ofSeconds(15)) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(body)) + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + log.info("人脸检测响应状态:{},响应体长度:{}", response.statusCode(), response.body() == null ? 0 : response.body().length()); + JsonNode root = objectMapper.readTree(response.body()); + String errorMsg = root.path("error_msg").asText(); + if (!"SUCCESS".equalsIgnoreCase(errorMsg)) { + log.warn("人脸检测返回失败,错误信息:{}", errorMsg); + } else { + log.info("人脸检测成功"); + } + return "SUCCESS".equalsIgnoreCase(errorMsg); + } catch (Exception e) { + log.error("人脸检测调用异常:{}", e.getMessage(), e); + return false; + } + } + + @Override + public FaceMatchResult match(String base64Image1, String base64Image2) { + String token = getAccessToken(); + if (!StringUtils.hasText(token)) { + log.warn("人脸比对失败:访问令牌为空,无法调用百度接口"); + FaceMatchResult result = new FaceMatchResult(); + result.setMatch(false); + result.setScore(0.0); + return result; + } + FaceMatchResult result = new FaceMatchResult(); + try { + String url = properties.getMatchUrl() + "?access_token=" + URLEncoder.encode(token, StandardCharsets.UTF_8); + String body = "[{\"image\":\"" + base64Image1 + "\",\"image_type\":\"BASE64\"}," + + "{\"image\":\"" + base64Image2 + "\",\"image_type\":\"BASE64\"}]"; + + log.info("开始人脸比对,请求URL:{}", url); + log.debug("比对图片1长度:{},图片2长度:{}", base64Image1 == null ? 0 : base64Image1.length(), base64Image2 == null ? 0 : base64Image2.length()); + HttpRequest request = HttpRequest.newBuilder(URI.create(url)) + .timeout(Duration.ofSeconds(15)) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(body)) + .build(); + + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + result.setRawResponse(response.body()); + log.info("人脸比对响应状态:{},响应体长度:{}", response.statusCode(), response.body() == null ? 0 : response.body().length()); + + JsonNode root = objectMapper.readTree(response.body()); + JsonNode resultNode = root.path("result"); + double score = resultNode.path("score").asDouble(0.0); + result.setScore(score); + result.setMatch(score >= properties.getScoreThreshold()); + log.info("人脸比对得分:{},阈值:{},是否匹配:{}", score, properties.getScoreThreshold(), result.isMatch()); + return result; + } catch (Exception e) { + log.error("人脸比对调用异常:{}", e.getMessage(), e); + result.setMatch(false); + result.setScore(0.0); + return result; + } + } + + private String getAccessToken() { + if (!StringUtils.hasText(properties.getApiKey()) || !StringUtils.hasText(properties.getSecretKey())) { + log.error("百度人脸识别配置缺失:API Key 或 Secret Key 为空。请在 application.yml 中配置 ext.baidu.face.api-key 和 secret-key"); + return ""; + } + long now = System.currentTimeMillis(); + if (StringUtils.hasText(cachedToken) && now < tokenExpireAt) { + log.debug("命中百度令牌缓存,剩余有效期(ms):{}", tokenExpireAt - now); + return cachedToken; + } + try { + log.info("开始获取百度访问令牌"); + String form = "grant_type=client_credentials" + + "&client_id=" + URLEncoder.encode(properties.getApiKey(), StandardCharsets.UTF_8) + + "&client_secret=" + URLEncoder.encode(properties.getSecretKey(), StandardCharsets.UTF_8); + HttpRequest request = HttpRequest.newBuilder(URI.create(properties.getTokenUrl())) + .timeout(Duration.ofSeconds(10)) + .header("Content-Type", "application/x-www-form-urlencoded") + .POST(HttpRequest.BodyPublishers.ofString(form)) + .build(); + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + String responseBody = response.body(); + JsonNode root = objectMapper.readTree(responseBody); + + if (root.has("error")) { + String error = root.path("error").asText(); + String errorDesc = root.path("error_description").asText(); + log.error("获取百度访问令牌失败,API返回错误:{} - {}", error, errorDesc); + return ""; + } + + if (!root.has("access_token")) { + log.error("获取百度访问令牌失败,响应中未包含access_token。响应内容:{}", responseBody); + return ""; + } + + cachedToken = root.path("access_token").asText(); + int expiresIn = root.path("expires_in").asInt(2592000); + tokenExpireAt = now + (expiresIn - 60) * 1000L; + log.info("成功获取百度令牌,过期秒:{},缓存到期时间戳:{}", expiresIn, tokenExpireAt); + return cachedToken; + } catch (Exception e) { + log.error("获取百度访问令牌失败:{}", e.getMessage(), e); + return ""; + } + } +} diff --git a/src/main/java/com/hotwj/platform/integration/ocr/OcrController.java b/src/main/java/com/hotwj/platform/integration/ocr/OcrController.java new file mode 100644 index 0000000..3deb028 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/OcrController.java @@ -0,0 +1,98 @@ +package com.hotwj.platform.integration.ocr; + +import com.hotwj.platform.integration.ocr.baidu.BaiduOcrService; +import com.hotwj.platform.integration.ocr.baidu.domain.bo.HandwritingRecognizeBo; +import com.hotwj.platform.integration.ocr.baidu.domain.vo.*; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Map; + +/** + * OCR 统一接口 + */ +@RestController +@RequestMapping("/integration/ocr") +@RequiredArgsConstructor +@Tag(name = "OCR接口", description = "营业执照、身份证、行驶证、驾驶证识别") +public class OcrController { + + private final Map ocrStrategyMap; + private final OcrProperties ocrProperties; + private final BaiduOcrService baiduOcrService; + + private OcrStrategy getStrategy(String specificProvider) { + // 优先使用指定的功能提供商,如果没有指定,则使用默认提供商 + String type = StringUtils.isNotBlank(specificProvider) ? specificProvider : ocrProperties.getProvider(); + OcrStrategy strategy = ocrStrategyMap.get(type + "OcrStrategy"); + if (strategy == null) { + // 尝试默认策略 + strategy = ocrStrategyMap.get(ocrProperties.getProvider() + "OcrStrategy"); + } + if (strategy == null) { + throw new IllegalArgumentException("未找到OCR提供商: " + type); + } + return strategy; + } + + /** + * 营业执照识别 + */ + @PostMapping(value = "/business-license", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @Operation(summary = "营业执照识别") + public R recognizeBusinessLicense( + @Parameter(description = "图片文件", required = true) @RequestParam("file") MultipartFile file) { + return R.ok(getStrategy(ocrProperties.getBusinessLicenseProvider()).recognizeBusinessLicense(file)); + } + + /** + * 身份证识别 + */ + @PostMapping(value = "/id-card", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @Operation(summary = "身份证识别") + public R recognizeIdCard( + @Parameter(description = "图片文件", required = true) @RequestParam("file") MultipartFile file, + @Parameter(description = "识别面: front-正面, back-反面", required = true) @RequestParam("side") String side) { + return R.ok(getStrategy(ocrProperties.getIdCardProvider()).recognizeIdCard(file, side)); + } + + /** + * 行驶证识别 + */ + @PostMapping(value = "/vehicle-license", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @Operation(summary = "行驶证识别") + public R recognizeVehicleLicense( + @Parameter(description = "图片文件", required = true) @RequestParam("file") MultipartFile file, + @Parameter(description = "识别面: front-正面, back-反面", required = true) @RequestParam("side") String side) { + return R.ok(getStrategy(ocrProperties.getVehicleLicenseProvider()).recognizeVehicleLicense(file, side)); + } + + /** + * 驾驶证识别 + */ + @PostMapping(value = "/driving-license", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @Operation(summary = "驾驶证识别") + public R recognizeDrivingLicense( + @Parameter(description = "图片文件", required = true) @RequestParam("file") MultipartFile file, + @Parameter(description = "识别面: front-正面, back-反面", required = true) @RequestParam("side") String side) { + return R.ok(getStrategy(ocrProperties.getDrivingLicenseProvider()).recognizeDrivingLicense(file, side)); + } + + @PostMapping(value = "/handwriting/recognize", consumes = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "手写体识别") + public R recognizeHandwriting(@RequestBody HandwritingRecognizeBo bo) { + // TODO 暂时去掉手写体校验,始终返回校验通过的成功结果;原逻辑保留,后续恢复时取消下方注释并移除这段 mock 返回即可 + // return R.ok(baiduOcrService.recognizeHandwriting(bo)); + HandwritingOcrVo vo = new HandwritingOcrVo(); + vo.setMatched(true); + vo.setReason("手写体校验已临时关闭,默认通过"); + return R.ok(vo); + } +} diff --git a/src/main/java/com/hotwj/platform/integration/ocr/OcrProperties.java b/src/main/java/com/hotwj/platform/integration/ocr/OcrProperties.java new file mode 100644 index 0000000..9b82c43 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/OcrProperties.java @@ -0,0 +1,35 @@ +package com.hotwj.platform.integration.ocr; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "ext.ocr") +public class OcrProperties { + /** + * 默认OCR提供商: baidu, aliyun + */ + private String provider = "baidu"; + + /** + * 身份证识别提供商 + */ + private String idCardProvider; + + /** + * 驾驶证识别提供商 + */ + private String drivingLicenseProvider; + + /** + * 行驶证识别提供商 + */ + private String vehicleLicenseProvider; + + /** + * 营业执照识别提供商 + */ + private String businessLicenseProvider; +} \ No newline at end of file diff --git a/src/main/java/com/hotwj/platform/integration/ocr/OcrStrategy.java b/src/main/java/com/hotwj/platform/integration/ocr/OcrStrategy.java new file mode 100644 index 0000000..0b5f55a --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/OcrStrategy.java @@ -0,0 +1,38 @@ +package com.hotwj.platform.integration.ocr; + +import com.hotwj.platform.integration.ocr.baidu.domain.vo.BusinessLicenseVo; +import com.hotwj.platform.integration.ocr.baidu.domain.vo.DrivingLicenseVo; +import com.hotwj.platform.integration.ocr.baidu.domain.vo.IdCardVo; +import com.hotwj.platform.integration.ocr.baidu.domain.vo.VehicleLicenseVo; +import org.springframework.web.multipart.MultipartFile; + +/** + * OCR 服务策略接口 + */ +public interface OcrStrategy { + + /** + * 获取策略类型 + */ + String getType(); + + /** + * 营业执照识别 + */ + BusinessLicenseVo recognizeBusinessLicense(MultipartFile file); + + /** + * 身份证识别 + */ + IdCardVo recognizeIdCard(MultipartFile file, String side); + + /** + * 行驶证识别 + */ + VehicleLicenseVo recognizeVehicleLicense(MultipartFile file, String side); + + /** + * 驾驶证识别 + */ + DrivingLicenseVo recognizeDrivingLicense(MultipartFile file, String side); +} \ No newline at end of file diff --git a/src/main/java/com/hotwj/platform/integration/ocr/SignatureVerifyService.java b/src/main/java/com/hotwj/platform/integration/ocr/SignatureVerifyService.java new file mode 100644 index 0000000..c0a0ae8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/SignatureVerifyService.java @@ -0,0 +1,5 @@ +package com.hotwj.platform.integration.ocr; + +public interface SignatureVerifyService { + void validateSelfSignature(Long signatureOssId, String userName); +} diff --git a/src/main/java/com/hotwj/platform/integration/ocr/SignatureVerifyServiceImpl.java b/src/main/java/com/hotwj/platform/integration/ocr/SignatureVerifyServiceImpl.java new file mode 100644 index 0000000..3ddb537 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/SignatureVerifyServiceImpl.java @@ -0,0 +1,77 @@ +package com.hotwj.platform.integration.ocr; + +import cn.hutool.http.HttpUtil; +import com.hotwj.platform.integration.ocr.baidu.BaiduOcrService; +import com.hotwj.platform.integration.ocr.baidu.domain.bo.HandwritingRecognizeBo; +import com.hotwj.platform.integration.ocr.baidu.domain.vo.HandwritingOcrVo; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.system.service.ISysOssService; +import org.springframework.stereotype.Service; + +import java.util.LinkedHashSet; + +@Service +@RequiredArgsConstructor +public class SignatureVerifyServiceImpl implements SignatureVerifyService { + + private final ISysOssService ossService; + private final OcrProperties ocrProperties; + private final BaiduOcrService baiduOcrService; + + @Override + public void validateSelfSignature(Long signatureOssId, String userName) { + +// if (signatureOssId == null) { +// throw new ServiceException("签名文件不能为空"); +// } +// if (StringUtils.isBlank(userName)) { +// throw new ServiceException("提交人姓名不能为空"); +// } +// SysOssVo ossVo = ossService.getById(signatureOssId); +// if (ossVo == null || StringUtils.isBlank(ossVo.getUrl())) { +// throw new ServiceException("签名文件不存在"); +// } +// byte[] imageBytes = downloadImageBytes(ossVo.getUrl()); +// HandwritingRecognizeBo bo = new HandwritingRecognizeBo(); +// bo.setImage(Base64.encode(imageBytes)); +// HandwritingOcrVo ocrVo = recognizeByProvider(bo); +// int sameCount = countSameChars(StringUtils.defaultString(ocrVo.getWords()), userName); +// if (sameCount < 2) { +// throw new ServiceException("签名校验失败"); +// } + } + + protected byte[] downloadImageBytes(String url) { + try { + return HttpUtil.downloadBytes(url); + } catch (Exception e) { + throw new ServiceException("下载签名图片失败"); + } + } + + private HandwritingOcrVo recognizeByProvider(HandwritingRecognizeBo bo) { + String provider = StringUtils.defaultIfBlank(ocrProperties.getProvider(), "baidu"); + if ("baidu".equalsIgnoreCase(provider)) { + return baiduOcrService.recognizeHandwriting(bo); + } + throw new ServiceException("当前OCR厂商暂不支持手写签名校验"); + } + + private int countSameChars(String recognizeText, String userName) { + if (StringUtils.isBlank(recognizeText) || StringUtils.isBlank(userName)) { + return 0; + } + String target = userName.replaceAll("\\s+", ""); + String source = recognizeText.replaceAll("\\s+", ""); + LinkedHashSet matchedChars = new LinkedHashSet<>(); + for (int i = 0; i < target.length(); i++) { + String ch = String.valueOf(target.charAt(i)); + if (source.contains(ch)) { + matchedChars.add(ch); + } + } + return matchedChars.size(); + } +} diff --git a/src/main/java/com/hotwj/platform/integration/ocr/aliyun/AliyunOcrIdCardProperties.java b/src/main/java/com/hotwj/platform/integration/ocr/aliyun/AliyunOcrIdCardProperties.java new file mode 100644 index 0000000..503897e --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/aliyun/AliyunOcrIdCardProperties.java @@ -0,0 +1,14 @@ +package com.hotwj.platform.integration.ocr.aliyun; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "ext.aliyun.ocr.id-card") +public class AliyunOcrIdCardProperties { + private String host; + private String path; + private String appcode; +} \ No newline at end of file diff --git a/src/main/java/com/hotwj/platform/integration/ocr/aliyun/AliyunOcrService.java b/src/main/java/com/hotwj/platform/integration/ocr/aliyun/AliyunOcrService.java new file mode 100644 index 0000000..a5540d9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/aliyun/AliyunOcrService.java @@ -0,0 +1,159 @@ +package com.hotwj.platform.integration.ocr.aliyun; + +import org.dromara.common.core.utils.StringUtils; +import cn.hutool.core.codec.Base64; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.hotwj.platform.integration.ocr.OcrStrategy; +import com.hotwj.platform.integration.ocr.baidu.domain.vo.BusinessLicenseVo; +import com.hotwj.platform.integration.ocr.baidu.domain.vo.DrivingLicenseVo; +import com.hotwj.platform.integration.ocr.baidu.domain.vo.IdCardVo; +import com.hotwj.platform.integration.ocr.baidu.domain.vo.VehicleLicenseVo; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.system.domain.vo.SysOssVo; +import org.dromara.system.service.ISysOssService; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * 阿里云OCR识别服务实现 + */ +@Slf4j +@RequiredArgsConstructor +@Service("aliyunOcrStrategy") +public class AliyunOcrService implements OcrStrategy { + + private final AliyunOcrIdCardProperties properties; + private final ISysOssService ossService; + + @Override + public String getType() { + return "aliyun"; + } + + private String formatDate(String dateStr) { + if (StringUtils.isBlank(dateStr) || dateStr.length() != 8) { + return dateStr; + } + return dateStr.substring(0, 4) + "-" + dateStr.substring(4, 6) + "-" + dateStr.substring(6, 8); + } + + @Override + public BusinessLicenseVo recognizeBusinessLicense(MultipartFile file) { + throw new ServiceException("阿里云OCR暂不支持营业执照识别"); + } + + @Override + public IdCardVo recognizeIdCard(MultipartFile file, String side) { + // ... (原有实现) ... + // 1. Upload to OSS (保持与百度OCR一致的逻辑,先上传再识别,方便前端回显) + SysOssVo ossVo; + try { + ossVo = ossService.upload(file); + } catch (Exception e) { + log.error("文件上传失败", e); + throw new ServiceException("文件上传失败: " + e.getMessage()); + } + + // 2. Read file content + String base64; + try { + base64 = Base64.encode(file.getBytes()); + } catch (IOException e) { + throw new ServiceException("读取文件失败"); + } + + // 3. Call Aliyun OCR API + String host = properties.getHost(); + String path = properties.getPath(); + String appcode = properties.getAppcode(); + String method = "POST"; + + Map headers = new HashMap<>(); + headers.put("Authorization", "APPCODE " + appcode); + headers.put("Content-Type", "application/json; charset=UTF-8"); + + JSONObject configObj = new JSONObject(); + // 阿里云参数: face=正面, back=反面. 兼容百度的 front 参数 + String aliSide = "front".equalsIgnoreCase(side) ? "face" : side; + configObj.put("side", aliSide); + + JSONObject requestObj = new JSONObject(); + requestObj.put("image", base64); + if (configObj.size() > 0) { + requestObj.put("configure", configObj.toString()); + } + String body = requestObj.toString(); + + try { + HttpResponse response = HttpRequest.post(host + path) + .headerMap(headers, true) + .body(body) + .timeout(10000) + .execute(); + + int stat = response.getStatus(); + String resBody = response.body(); + log.info("阿里云OCR响应: {}", resBody); + + if (stat != 200) { + log.error("阿里云OCR识别失败, Http code: {}, msg: {}", stat, resBody); + throw new ServiceException("识别失败,请重试"); + } + + JSONObject json = JSONUtil.parseObj(resBody); + // 阿里云返回结构解析 + // { "address" : "", "config_str" : "{\\\"side\\\":\\\"face\\\"}", "face_rect" : {...}, "name" : "张三", "nationality" : "汉", "num" : "...", "request_id" : "...", "sex" : "男", "success" : true, "birth" : "20000101" } + + Boolean success = json.getBool("success", false); + if (!success) { + throw new ServiceException("识别失败"); + } + + IdCardVo result = new IdCardVo(); + result.setOssId(ossVo.getOssId().toString()); + result.setUrl(ossVo.getUrl()); + + if ("face".equals(aliSide)) { + result.setName(json.getStr("name")); + result.setIdNumber(json.getStr("num")); + result.setAddress(json.getStr("address")); + result.setGender(json.getStr("sex")); + result.setEthnicity(json.getStr("nationality")); + // 阿里云返回格式 YYYYMMDD,转换为 YYYY-MM-DD (兼容百度) + result.setBirthDate(formatDate(json.getStr("birth"))); + } else { + result.setExpiryDate(formatDate(json.getStr("end_date"))); // 格式 YYYYMMDD -> YYYY-MM-DD + result.setIssueDate(formatDate(json.getStr("start_date"))); // 格式 YYYYMMDD -> YYYY-MM-DD + result.setAuthority(json.getStr("issue")); + } + + return result; + + } catch (Exception e) { + log.error("调用阿里云OCR接口异常", e); + if (e instanceof ServiceException) { + throw e; + } + throw new ServiceException("识别服务异常"); + } + } + + @Override + public VehicleLicenseVo recognizeVehicleLicense(MultipartFile file, String side) { + throw new ServiceException("阿里云OCR暂不支持行驶证识别"); + } + + @Override + public DrivingLicenseVo recognizeDrivingLicense(MultipartFile file, String side) { + throw new ServiceException("阿里云OCR暂不支持驾驶证识别"); + } +} \ No newline at end of file diff --git a/src/main/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrHttpClient.java b/src/main/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrHttpClient.java new file mode 100644 index 0000000..7af3df7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrHttpClient.java @@ -0,0 +1,5 @@ +package com.hotwj.platform.integration.ocr.baidu; + +public interface BaiduOcrHttpClient { + BaiduOcrHttpResponse postForm(String url, String body, int timeoutMs); +} diff --git a/src/main/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrHttpResponse.java b/src/main/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrHttpResponse.java new file mode 100644 index 0000000..02241bc --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrHttpResponse.java @@ -0,0 +1,11 @@ +package com.hotwj.platform.integration.ocr.baidu; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class BaiduOcrHttpResponse { + private int statusCode; + private String body; +} diff --git a/src/main/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrProperties.java b/src/main/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrProperties.java new file mode 100644 index 0000000..f2c8a57 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrProperties.java @@ -0,0 +1,24 @@ +package com.hotwj.platform.integration.ocr.baidu; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "ext.baidu.ocr") +public class BaiduOcrProperties { + private String apiKey; + private String secretKey; + private String tokenUrl = "https://aip.baidubce.com/oauth/2.0/token"; + + // Default endpoints + private String businessLicenseUrl = "https://aip.baidubce.com/rest/2.0/ocr/v1/business_license"; + private String idCardUrl = "https://aip.baidubce.com/rest/2.0/ocr/v1/idcard"; + private String vehicleLicenseUrl = "https://aip.baidubce.com/rest/2.0/ocr/v1/vehicle_license"; + private String drivingLicenseUrl = "https://aip.baidubce.com/rest/2.0/ocr/v1/driving_license"; + private String handwritingUrl = "https://aip.baidubce.com/rest/2.0/ocr/v1/handwriting"; + private Integer tokenTimeoutMs = 2000; + private Integer handwritingTimeoutMs = 5000; + private Integer retryTimes = 2; +} diff --git a/src/main/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrService.java b/src/main/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrService.java new file mode 100644 index 0000000..69a5e38 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrService.java @@ -0,0 +1,54 @@ +package com.hotwj.platform.integration.ocr.baidu; + +import com.hotwj.platform.integration.ocr.baidu.domain.bo.HandwritingRecognizeBo; +import com.hotwj.platform.integration.ocr.baidu.domain.vo.*; +import org.springframework.web.multipart.MultipartFile; + +/** + * 百度OCR识别服务接口 + */ +public interface BaiduOcrService { + + /** + * 营业执照识别 + * + * @param file 图片文件 + * @return 识别结果 + */ + BusinessLicenseVo recognizeBusinessLicense(MultipartFile file); + + /** + * 身份证识别 + * + * @param file 图片文件 + * @param side front/back + * @return 识别结果 + */ + IdCardVo recognizeIdCard(MultipartFile file, String side); + + /** + * 行驶证识别 + * + * @param file 图片文件 + * @param side front/back + * @return 识别结果 + */ + VehicleLicenseVo recognizeVehicleLicense(MultipartFile file, String side); + + /** + * 驾驶证识别 + * + * @param file 图片文件 + * @param side front/back + * @return 识别结果 + */ + DrivingLicenseVo recognizeDrivingLicense(MultipartFile file, String side); + + /** + * 手写识别识别 + * + * @param bo 识别参数 + * @return 识别结果 + */ + HandwritingOcrVo recognizeHandwriting(HandwritingRecognizeBo bo); +} diff --git a/src/main/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrServiceImpl.java b/src/main/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrServiceImpl.java new file mode 100644 index 0000000..5fba995 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrServiceImpl.java @@ -0,0 +1,513 @@ +package com.hotwj.platform.integration.ocr.baidu; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.hotwj.platform.config.vehicleType.domain.HotVehicleType; +import com.hotwj.platform.config.vehicleType.mapper.HotVehicleTypeMapper; +import com.hotwj.platform.integration.ocr.OcrStrategy; +import com.hotwj.platform.integration.ocr.baidu.domain.bo.HandwritingRecognizeBo; +import com.hotwj.platform.integration.ocr.baidu.domain.vo.*; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.service.DictService; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.system.domain.vo.SysOssVo; +import org.dromara.system.service.ISysOssService; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.math.BigDecimal; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@Slf4j +@Service("baiduOcrStrategy") +@RequiredArgsConstructor +public class BaiduOcrServiceImpl implements BaiduOcrService, OcrStrategy { + + private final BaiduOcrProperties properties; + private final ISysOssService ossService; + private final DictService dictService; + private final HotVehicleTypeMapper vehicleTypeMapper; + private final BaiduOcrHttpClient httpClient; + + private volatile String cachedToken; + private volatile long tokenExpireAt; + + @Override + public String getType() { + return "baidu"; + } + + @Override + public BusinessLicenseVo recognizeBusinessLicense(MultipartFile file) { + return uploadAndRecognize(file, properties.getBusinessLicenseUrl(), null, BusinessLicenseVo.class, this::processBusinessLicense); + } + + @Override + public IdCardVo recognizeIdCard(MultipartFile file, String side) { + if (!"front".equals(side) && !"back".equals(side)) { + throw new ServiceException("身份证识别类型错误,只能为front或back"); + } + return uploadAndRecognize(file, properties.getIdCardUrl(), "id_card_side=" + side, IdCardVo.class, + json -> "front".equals(side) ? processIdCardFront(json) : processIdCardBack(json)); + } + + @Override + public VehicleLicenseVo recognizeVehicleLicense(MultipartFile file, String side) { + if (!"front".equals(side) && !"back".equals(side)) { + throw new ServiceException("行驶证识别类型错误,只能为front或back"); + } + return uploadAndRecognize(file, properties.getVehicleLicenseUrl(), "vehicle_license_side=" + side, VehicleLicenseVo.class, + json -> "front".equals(side) ? processVehicleLicenseFront(json) : processVehicleLicenseBack(json)); + } + + @Override + public DrivingLicenseVo recognizeDrivingLicense(MultipartFile file, String side) { + if (!"front".equals(side) && !"back".equals(side)) { + throw new ServiceException("驾驶证识别类型错误,只能为front或back"); + } + return uploadAndRecognize(file, properties.getDrivingLicenseUrl(), "driving_license_side=" + side, DrivingLicenseVo.class, + json -> "front".equals(side) ? processDrivingLicenseFront(json) : processDrivingLicenseBack(json)); + } + + @Override + public HandwritingOcrVo recognizeHandwriting(HandwritingRecognizeBo bo) { + if (bo == null || StringUtils.isBlank(bo.getImage())) { + throw new ServiceException("手写体图片不能为空"); + } + String base64 = normalizeBase64(bo.getImage()); + String token = getAccessToken(); + String requestUrl = properties.getHandwritingUrl() + "?access_token=" + token; + String body = "image=" + URLEncoder.encode(base64, StandardCharsets.UTF_8) + + "&detect_direction=false&probability=false&detect_alteration=false"; + JSONObject json = executeBaiduRequest(requestUrl, body, properties.getHandwritingTimeoutMs(), true); + HandwritingOcrVo vo = new HandwritingOcrVo(); + Integer wordsResultNum = json.getInt("words_result_num", 0); + vo.setWordsResultNum(wordsResultNum); + JSONArray wordsResult = json.getJSONArray("words_result"); + List wordsList = new ArrayList<>(); + if (wordsResult != null) { + for (int i = 0; i < wordsResult.size(); i++) { + JSONObject item = wordsResult.getJSONObject(i); + if (item != null) { + String words = item.getStr("words"); + if (StringUtils.isNotBlank(words)) { + wordsList.add(words.trim()); + } + } + } + } + vo.setWordsList(wordsList); + String recognizeText = String.join("", wordsList); + vo.setWords(recognizeText); + fillMatchResult(vo, recognizeText, bo.getTargetName(), bo.getMatchMode()); + if (Boolean.TRUE.equals(bo.getSaveImage())) { + vo.setStoredPath(storeImage(base64)); + } + return vo; + } + + private T uploadAndRecognize(MultipartFile file, String url, String params, Class clazz, OcrProcessor processor) { + // 1. Upload to OSS + SysOssVo ossVo; + try { + ossVo = ossService.upload(file); + } catch (Exception e) { + log.error("文件上传失败", e); + throw new ServiceException("文件上传失败: " + e.getMessage()); + } + + // 2. Read file content + String base64; + try { + base64 = Base64.encode(file.getBytes()); + } catch (IOException e) { + throw new ServiceException("读取文件失败"); + } + + // 3. Call Baidu OCR API + String token = getAccessToken(); + if (StringUtils.isBlank(token)) { + throw new ServiceException("获取百度API访问令牌失败"); + } + + String requestUrl = url + "?access_token=" + token; + String body = "image=" + URLEncoder.encode(base64, StandardCharsets.UTF_8); + if (StringUtils.isNotBlank(params)) { + body += "&" + params; + } + + try { + JSONObject json = executeBaiduRequest(requestUrl, body, 10000, true); + T result = processor.process(json); + if (result == null) { + try { + result = clazz.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new ServiceException("创建VO失败"); + } + } + + result.setOssId(ossVo.getOssId().toString()); + result.setUrl(ossVo.getUrl()); + + return result; + } catch (Exception e) { + log.error("调用百度OCR接口异常", e); + if (e instanceof ServiceException) { + throw e; + } + throw new ServiceException("识别服务异常"); + } + } + + private String getAccessToken() { + long now = System.currentTimeMillis(); + if (StringUtils.isNotBlank(cachedToken) && now < tokenExpireAt) { + return cachedToken; + } + if (StringUtils.isBlank(properties.getApiKey()) || StringUtils.isBlank(properties.getSecretKey())) { + throw new ServiceException("百度OCR密钥未配置"); + } + String body = "grant_type=client_credentials" + + "&client_id=" + URLEncoder.encode(properties.getApiKey(), StandardCharsets.UTF_8) + + "&client_secret=" + URLEncoder.encode(properties.getSecretKey(), StandardCharsets.UTF_8); + JSONObject json = executeBaiduRequest(properties.getTokenUrl(), body, properties.getTokenTimeoutMs(), false); + cachedToken = json.getStr("access_token"); + Integer expiresIn = json.getInt("expires_in", 1800); + tokenExpireAt = now + Math.max(expiresIn - 120, 60) * 1000L; + return cachedToken; + } + + private JSONObject executeBaiduRequest(String url, String body, Integer timeoutMs, boolean allowRetry) { + int retryTimes = allowRetry ? Math.max(properties.getRetryTimes(), 0) : 0; + int maxAttempts = retryTimes + 1; + int attempt = 0; + while (attempt < maxAttempts) { + attempt++; + try { + long start = System.currentTimeMillis(); + BaiduOcrHttpResponse response = httpClient.postForm(url, body, timeoutMs); + long cost = System.currentTimeMillis() - start; + int status = response.getStatusCode(); + String responseBody = StringUtils.defaultString(response.getBody()); + log.info("百度OCR调用耗时={}ms,status={}", cost, status); + if (status >= 500) { + if (attempt < maxAttempts) { + sleepBeforeRetry(attempt); + continue; + } + throw new ServiceException("第三方服务异常"); + } + if (status >= 400) { + throw new ServiceException("第三方调用失败(" + status + ")"); + } + JSONObject json = JSONUtil.parseObj(responseBody); + if (json.containsKey("error_code")) { + int errorCode = json.getInt("error_code", -1); + String errorMsg = json.getStr("error_msg"); + if (errorCode == 110 || errorCode == 111) { + cachedToken = null; + tokenExpireAt = 0L; + } + throw mapBaiduError(errorCode, errorMsg); + } + return json; + } catch (ServiceException e) { + if (attempt < maxAttempts && shouldRetryServiceException(e, allowRetry)) { + sleepBeforeRetry(attempt); + continue; + } + throw e; + } catch (Exception e) { + if (attempt < maxAttempts && allowRetry) { + sleepBeforeRetry(attempt); + continue; + } + throw new ServiceException("第三方调用异常"); + } + } + throw new ServiceException("第三方调用异常"); + } + + private boolean shouldRetryServiceException(ServiceException e, boolean allowRetry) { + if (!allowRetry) { + return false; + } + String msg = e.getMessage(); + if (StringUtils.isBlank(msg)) { + return true; + } + return msg.contains("第三方服务异常") || msg.contains("第三方调用异常"); + } + + private ServiceException mapBaiduError(int errorCode, String errorMsg) { + if (errorCode == 17 || errorCode == 18 || errorCode == 19) { + return new ServiceException("OCR配额不足或限流"); + } + if (errorCode == 110 || errorCode == 111) { + return new ServiceException("OCR鉴权失败"); + } + return new ServiceException("OCR识别失败:" + StringUtils.defaultString(errorMsg)); + } + + private void sleepBeforeRetry(int attempt) { + long sleepMs = attempt == 1 ? 200L : 800L; + try { + Thread.sleep(sleepMs); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + private String normalizeBase64(String image) { + String trimmed = image.trim(); + if (trimmed.startsWith("data:image")) { + int idx = trimmed.indexOf(','); + if (idx > -1) { + trimmed = trimmed.substring(idx + 1); + } + } + if (StringUtils.isBlank(trimmed)) { + throw new ServiceException("手写体图片不能为空"); + } + return trimmed; + } + + private void fillMatchResult(HandwritingOcrVo vo, String recognizeText, String targetName, String matchMode) { + if (StringUtils.isBlank(targetName)) { + return; + } + String normalizedTarget = targetName.replaceAll("\\s+", ""); + String normalizedText = StringUtils.defaultString(recognizeText).replaceAll("\\s+", ""); + String mode = StringUtils.defaultIfBlank(matchMode, "contains").toLowerCase(); + boolean matched; + if ("strict".equals(mode)) { + matched = normalizedText.equals(normalizedTarget); + } else { + matched = normalizedText.contains(normalizedTarget); + } + vo.setMatched(matched); + vo.setReason(matched ? "匹配成功" : "匹配失败"); + } + + private String storeImage(String base64Image) { + try { + byte[] bytes = Base64.decode(base64Image); + LocalDate today = LocalDate.now(); + String dirName = today.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + Path dir = Paths.get("upload_file", dirName); + Files.createDirectories(dir); + String fileName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + + RandomUtil.randomNumbers(6) + ".png"; + Path filePath = dir.resolve(fileName); + Files.write(filePath, bytes); + return filePath.toString(); + } catch (Exception e) { + throw new ServiceException("保存手写体图片失败"); + } + } + + // --- Processors --- + + private BusinessLicenseVo processBusinessLicense(JSONObject json) { + BusinessLicenseVo vo = new BusinessLicenseVo(); + JSONObject words = json.getJSONObject("words_result"); + if (words == null) return vo; + + vo.setEnterpriseName(getWords(words, "单位名称")); + vo.setLegalRepresentative(getWords(words, "法人")); + vo.setAddress(getWords(words, "地址")); + vo.setEstablishmentDate(formatDate(getWords(words, "成立日期"))); + vo.setCreditCode(getWords(words, "社会信用代码")); + String regCap = getWords(words, "注册资本"); + vo.setRegisteredCapital(extractNumber(regCap)); + + return vo; + } + + private IdCardVo processIdCardFront(JSONObject json) { + IdCardVo vo = new IdCardVo(); + JSONObject words = json.getJSONObject("words_result"); + if (words == null) return vo; + + vo.setName(getWords(words, "姓名")); + vo.setIdNumber(getWords(words, "公民身份号码")); + vo.setAddress(getWords(words, "住址")); + vo.setGender(getWords(words, "性别")); + + String nation = getWords(words, "民族"); + vo.setEthnicity(getNationCode(nation)); + + vo.setBirthDate(formatDate(getWords(words, "出生"))); + + return vo; + } + + private IdCardVo processIdCardBack(JSONObject json) { + IdCardVo vo = new IdCardVo(); + JSONObject words = json.getJSONObject("words_result"); + if (words == null) return vo; + + String expiryDate = getWords(words, "失效日期"); + vo.setExpiryDate(formatDate(expiryDate)); + + return vo; + } + + private VehicleLicenseVo processVehicleLicenseFront(JSONObject json) { + VehicleLicenseVo vo = new VehicleLicenseVo(); + JSONObject words = json.getJSONObject("words_result"); + if (words == null) return vo; + + vo.setAddress(getWords(words, "住址")); + vo.setUseCharacter(getWords(words, "使用性质")); + vo.setEngineNumber(getWords(words, "发动机号码")); + vo.setIssueDate(formatDate(getWords(words, "发证日期"))); + vo.setModel(getWords(words, "品牌型号")); + vo.setOwner(getWords(words, "所有人")); + vo.setRegisterDate(formatDate(getWords(words, "注册日期"))); + vo.setPlateNumber(getWords(words, "号牌号码")); + vo.setVin(getWords(words, "车辆识别代号")); + + String vehicleType = getWords(words, "车辆类型"); + vo.setVehicleType(convertVehicleType(vehicleType)); + + return vo; + } + + private VehicleLicenseVo processVehicleLicenseBack(JSONObject json) { + VehicleLicenseVo vo = new VehicleLicenseVo(); + JSONObject words = json.getJSONObject("words_result"); + if (words == null) return vo; + + String dimension = getWords(words, "外廓尺寸"); + if (StrUtil.isNotBlank(dimension)) { + vo.setOverallDimension(dimension.replace("mm", "").trim()); + } + + String inspectionRecord = getWords(words, "检验记录"); + vo.setInspectionValidDate(extractDate(inspectionRecord)); + + vo.setApprovedLoad(extractNumber(getWords(words, "核定载质量"))); + vo.setCurbWeight(extractNumber(getWords(words, "整备质量"))); + vo.setApprovedPassengerCapacity(extractNumber(getWords(words, "核定载人数"))); + vo.setGrossMass(extractNumber(getWords(words, "总质量"))); + vo.setTractionMass(extractNumber(getWords(words, "准牵引总质量"))); + + String remarks = getWords(words, "备注"); + vo.setMandatoryScrapDate(extractDate(remarks)); + + return vo; + } + + private DrivingLicenseVo processDrivingLicenseFront(JSONObject json) { + DrivingLicenseVo vo = new DrivingLicenseVo(); + JSONObject words = json.getJSONObject("words_result"); + if (words == null) return vo; + + vo.setInitialIssueDate(formatDate(getWords(words, "初次领证日期"))); + vo.setEndDate(formatDate(getWords(words, "至"))); + vo.setVehicleClass(getWords(words, "准驾车型")); + vo.setIssuingAuthority(getWords(words, "发证单位")); + vo.setLicenseNumber(getWords(words, "证号")); + + return vo; + } + + private DrivingLicenseVo processDrivingLicenseBack(JSONObject json) { + DrivingLicenseVo vo = new DrivingLicenseVo(); + JSONObject words = json.getJSONObject("words_result"); + if (words == null) return vo; + + vo.setArchiveNumber(getWords(words, "档案编号")); + return vo; + } + + // --- Helpers --- + + private String getWords(JSONObject words, String key) { + if (words.containsKey(key)) { + return words.getJSONObject(key).getStr("words"); + } + return null; + } + + private String formatDate(String dateStr) { + if (StringUtils.isBlank(dateStr) || "长期".equals(dateStr)) { + return dateStr; + } + try { + String clean = dateStr.replaceAll("[年月日]", "-"); + if (clean.endsWith("-")) clean = clean.substring(0, clean.length() - 1); + // Try parsing with Hutool + Date date = DateUtil.parse(clean); + return DateUtil.format(date, DatePattern.NORM_DATE_PATTERN); + } catch (Exception e) { + return dateStr; + } + } + + private String extractDate(String text) { + if (StringUtils.isBlank(text)) return null; + String pattern = "\\d{4}[\\-年]\\d{1,2}[\\-月]?(\\d{1,2}[日]?)?"; + String date = ReUtil.get(pattern, text, 0); + if (date != null) { + return formatDate(date); + } + return null; + } + + private BigDecimal extractNumber(String text) { + if (StringUtils.isBlank(text)) return null; + String num = ReUtil.get("\\d+(\\.\\d+)?", text, 0); + if (num != null) { + return new BigDecimal(num); + } + return null; + } + + private String getNationCode(String nationName) { + if (StringUtils.isBlank(nationName)) return null; + String label = nationName.replace("族", ""); + String val = dictService.getDictValue("nation", label, ","); + if (StringUtils.isBlank(val)) { + val = dictService.getDictValue("nation", nationName, ","); + } + return StringUtils.isNotBlank(val) ? val : nationName; + } + + private String convertVehicleType(String typeName) { + if (StringUtils.isBlank(typeName)) return null; + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.eq(HotVehicleType::getName, typeName); + lqw.last("limit 1"); + HotVehicleType type = vehicleTypeMapper.selectOne(lqw); + return type != null ? type.getName() : typeName; + } + + @FunctionalInterface + interface OcrProcessor { + T process(JSONObject json); + } +} diff --git a/src/main/java/com/hotwj/platform/integration/ocr/baidu/DefaultBaiduOcrHttpClient.java b/src/main/java/com/hotwj/platform/integration/ocr/baidu/DefaultBaiduOcrHttpClient.java new file mode 100644 index 0000000..4d0e01f --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/baidu/DefaultBaiduOcrHttpClient.java @@ -0,0 +1,20 @@ +package com.hotwj.platform.integration.ocr.baidu; + +import cn.hutool.http.ContentType; +import cn.hutool.http.Header; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import org.springframework.stereotype.Component; + +@Component +public class DefaultBaiduOcrHttpClient implements BaiduOcrHttpClient { + @Override + public BaiduOcrHttpResponse postForm(String url, String body, int timeoutMs) { + HttpResponse response = HttpRequest.post(url) + .header(Header.CONTENT_TYPE, ContentType.FORM_URLENCODED.getValue()) + .body(body) + .timeout(timeoutMs) + .execute(); + return new BaiduOcrHttpResponse(response.getStatus(), response.body()); + } +} diff --git a/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/bo/HandwritingRecognizeBo.java b/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/bo/HandwritingRecognizeBo.java new file mode 100644 index 0000000..1af0c66 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/bo/HandwritingRecognizeBo.java @@ -0,0 +1,11 @@ +package com.hotwj.platform.integration.ocr.baidu.domain.bo; + +import lombok.Data; + +@Data +public class HandwritingRecognizeBo { + private String image; + private String targetName; + private String matchMode; + private Boolean saveImage; +} diff --git a/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/BaiduOcrBaseVo.java b/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/BaiduOcrBaseVo.java new file mode 100644 index 0000000..12256e8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/BaiduOcrBaseVo.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.integration.ocr.baidu.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 百度OCR基础结果 + */ +@Data +public class BaiduOcrBaseVo { + + @Schema(description = "OSS文件ID") + private String ossId; + + @Schema(description = "OSS文件URL") + private String url; +} diff --git a/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/BusinessLicenseVo.java b/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/BusinessLicenseVo.java new file mode 100644 index 0000000..5fe1e9e --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/BusinessLicenseVo.java @@ -0,0 +1,33 @@ +package com.hotwj.platform.integration.ocr.baidu.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; + +/** + * 营业执照识别结果 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class BusinessLicenseVo extends BaiduOcrBaseVo { + + @Schema(description = "单位名称") + private String enterpriseName; + + @Schema(description = "法人") + private String legalRepresentative; + + @Schema(description = "地址") + private String address; + + @Schema(description = "成立日期") + private String establishmentDate; + + @Schema(description = "注册资本") + private BigDecimal registeredCapital; + + @Schema(description = "统一社会信用代码") + private String creditCode; +} diff --git a/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/DrivingLicenseVo.java b/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/DrivingLicenseVo.java new file mode 100644 index 0000000..dddec6d --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/DrivingLicenseVo.java @@ -0,0 +1,33 @@ +package com.hotwj.platform.integration.ocr.baidu.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 驾驶证识别结果 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class DrivingLicenseVo extends BaiduOcrBaseVo { + + // 正面 (front) + @Schema(description = "初次领证日期") + private String initialIssueDate; + + @Schema(description = "有效期至") + private String endDate; + + @Schema(description = "准驾车型") + private String vehicleClass; + + @Schema(description = "发证单位") + private String issuingAuthority; + + @Schema(description = "证号") + private String licenseNumber; + + // 反面 (back) + @Schema(description = "档案编号") + private String archiveNumber; +} diff --git a/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/HandwritingOcrVo.java b/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/HandwritingOcrVo.java new file mode 100644 index 0000000..bd1e683 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/HandwritingOcrVo.java @@ -0,0 +1,15 @@ +package com.hotwj.platform.integration.ocr.baidu.domain.vo; + +import lombok.Data; + +import java.util.List; + +@Data +public class HandwritingOcrVo { + private Integer wordsResultNum; + private List wordsList; + private String words; + private Boolean matched; + private String reason; + private String storedPath; +} diff --git a/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/IdCardVo.java b/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/IdCardVo.java new file mode 100644 index 0000000..1d1274d --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/IdCardVo.java @@ -0,0 +1,42 @@ +package com.hotwj.platform.integration.ocr.baidu.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 身份证识别结果 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class IdCardVo extends BaiduOcrBaseVo { + + // 正面 (front) + @Schema(description = "姓名") + private String name; + + @Schema(description = "公民身份号码") + private String idNumber; + + @Schema(description = "住址") + private String address; + + @Schema(description = "性别") + private String gender; + + @Schema(description = "民族") + private String ethnicity; + + @Schema(description = "出生日期") + private String birthDate; + + // 反面 (back) + @Schema(description = "失效日期") + private String expiryDate; + + @Schema(description = "签发日期") + private String issueDate; + + @Schema(description = "签发机关") + private String authority; +} diff --git a/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/VehicleLicenseVo.java b/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/VehicleLicenseVo.java new file mode 100644 index 0000000..e8d396a --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/ocr/baidu/domain/vo/VehicleLicenseVo.java @@ -0,0 +1,71 @@ +package com.hotwj.platform.integration.ocr.baidu.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; + +/** + * 行驶证识别结果 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class VehicleLicenseVo extends BaiduOcrBaseVo { + + // 正面 (front) + @Schema(description = "住址") + private String address; + + @Schema(description = "使用性质") + private String useCharacter; + + @Schema(description = "发动机号码") + private String engineNumber; + + @Schema(description = "发证日期") + private String issueDate; + + @Schema(description = "品牌型号") + private String model; + + @Schema(description = "所有人") + private String owner; + + @Schema(description = "注册日期") + private String registerDate; + + @Schema(description = "号牌号码") + private String plateNumber; + + @Schema(description = "车辆识别代号") + private String vin; + + @Schema(description = "车辆类型") + private String vehicleType; + + // 反面 (back) + @Schema(description = "外廓尺寸") + private String overallDimension; + + @Schema(description = "检验有效期至") + private String inspectionValidDate; + + @Schema(description = "核定载质量") + private BigDecimal approvedLoad; + + @Schema(description = "整备质量") + private BigDecimal curbWeight; + + @Schema(description = "核定载人数") + private BigDecimal approvedPassengerCapacity; + + @Schema(description = "总质量") + private BigDecimal grossMass; + + @Schema(description = "准牵引总质量") + private BigDecimal tractionMass; + + @Schema(description = "强制报废期止") + private String mandatoryScrapDate; +} diff --git a/src/main/java/com/hotwj/platform/integration/sms/SmsBaoClient.java b/src/main/java/com/hotwj/platform/integration/sms/SmsBaoClient.java new file mode 100644 index 0000000..05178b8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/sms/SmsBaoClient.java @@ -0,0 +1,87 @@ +package com.hotwj.platform.integration.sms; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import com.hotwj.platform.integration.sms.domain.SmsRecord; +import com.hotwj.platform.integration.sms.mapper.SmsRecordMapper; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.service.ConfigService; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Date; + +@Slf4j +@Component +@RequiredArgsConstructor +public class SmsBaoClient { + + private final SmsBaoProperties properties; + private final ConfigService configService; + @Autowired + private SmsRecordMapper smsRecordMapper; + + @Data + @AllArgsConstructor + public static class SmsSendResult { + private boolean success; + private String code; + private String raw; + } + + public SmsSendResult send(String mobile, String content) { + String tokens = configService.getConfigValue("sys.sms.lock"); + if (StringUtils.isNotBlank(tokens) && "false".equals(tokens)) { + return new SmsSendResult(false, "sms_locked", "短信开关未开启"); + } +// HttpRequest request = HttpRequest.get(properties.getBaseUrl()) +// .form("u", properties.getU()) +// .form("p", properties.getP()) +// .form("m", mobile) +// .form("c", "【洛克石油】" + content) +// .timeout(properties.getTimeoutMs()); + HttpRequest request = HttpRequest.get(properties.getBaseUrl()) + .form("u", properties.getU()) + .form("p", properties.getP()) + .form("m", mobile) + .form("c", "【五彩行】" + content) + .timeout(properties.getTimeoutMs()); + HttpResponse resp = request.execute(); + String body = resp.body() == null ? "" : resp.body().trim(); + boolean ok = "0".equals(body); + if (!ok) { + log.warn("短信宝发送失败 code={} mobile={}", body, mobile); + } else { + SmsRecord record = new SmsRecord(); + if (LoginHelper.getUserId() == null) { + record.setSenderUserId(0L); + } else { + record.setSenderUserId(LoginHelper.getUserId()); + } + if (LoginHelper.getUsername() == null) { + record.setSenderUsername("系统发送短信"); + } else { + record.setSenderUsername(LoginHelper.getUsername()); + } + + record.setReceiverMobile(mobile); + record.setContent(content); + record.setSendTime(new Date()); + record.setSuccess(true); + record.setCode(body); + record.setProvider("smsbao"); + record.setRaw(resp.body()); + try { + smsRecordMapper.insert(record); + } catch (Exception e) { + log.warn("短信记录入库失败 mobile={} err={}", mobile, e.getMessage()); + } + } + return new SmsSendResult(ok, body, resp.body()); + } +} diff --git a/src/main/java/com/hotwj/platform/integration/sms/SmsBaoProperties.java b/src/main/java/com/hotwj/platform/integration/sms/SmsBaoProperties.java new file mode 100644 index 0000000..27bcb37 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/sms/SmsBaoProperties.java @@ -0,0 +1,15 @@ +package com.hotwj.platform.integration.sms; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "smsbao") +public class SmsBaoProperties { + private String baseUrl; + private String u; + private String p; + private Integer timeoutMs = 10000; +} diff --git a/src/main/java/com/hotwj/platform/integration/sms/domain/SmsRecord.java b/src/main/java/com/hotwj/platform/integration/sms/domain/SmsRecord.java new file mode 100644 index 0000000..603414b --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/sms/domain/SmsRecord.java @@ -0,0 +1,37 @@ +package com.hotwj.platform.integration.sms.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.tenant.core.TenantEntity; + +import java.util.Date; + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_sms_record") +public class SmsRecord extends BaseEntity { + + @TableId(value = "id") + private Long id; + + private String receiverMobile; + + private Long senderUserId; + + private String senderUsername; + + private String content; + + private Date sendTime; + + private Boolean success; + + private String code; + + private String provider; + + private String raw; +} diff --git a/src/main/java/com/hotwj/platform/integration/sms/domain/vo/SmsRecordVo.java b/src/main/java/com/hotwj/platform/integration/sms/domain/vo/SmsRecordVo.java new file mode 100644 index 0000000..d8b4aa4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/sms/domain/vo/SmsRecordVo.java @@ -0,0 +1,22 @@ +package com.hotwj.platform.integration.sms.domain.vo; + +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import com.hotwj.platform.integration.sms.domain.SmsRecord; + +import java.util.Date; + +@Data +@AutoMapper(target = SmsRecord.class, reverseConvertGenerate = false) +public class SmsRecordVo { + private Long id; + private String receiverMobile; + private Long senderUserId; + private String senderUsername; + private String content; + private Date sendTime; + private Boolean success; + private String code; + private String provider; + private String raw; +} diff --git a/src/main/java/com/hotwj/platform/integration/sms/mapper/SmsRecordMapper.java b/src/main/java/com/hotwj/platform/integration/sms/mapper/SmsRecordMapper.java new file mode 100644 index 0000000..1c2ba65 --- /dev/null +++ b/src/main/java/com/hotwj/platform/integration/sms/mapper/SmsRecordMapper.java @@ -0,0 +1,10 @@ +package com.hotwj.platform.integration.sms.mapper; + +import com.hotwj.platform.integration.sms.domain.SmsRecord; +import com.hotwj.platform.integration.sms.domain.vo.SmsRecordVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +@Mapper +public interface SmsRecordMapper extends BaseMapperPlus { +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/controller/HotAdminNoticeController.java b/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/controller/HotAdminNoticeController.java new file mode 100644 index 0000000..1136dfe --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/controller/HotAdminNoticeController.java @@ -0,0 +1,127 @@ +package com.hotwj.platform.noticeManagerment.adminNotice.controller; + +import com.hotwj.platform.noticeManagerment.adminNotice.domain.bo.HotAdminNoticeBo; +import com.hotwj.platform.noticeManagerment.adminNotice.domain.vo.HotAdminNoticeVo; +import com.hotwj.platform.noticeManagerment.adminNotice.service.IHotAdminNoticeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 行管通知 + * + * @author shihongwei + * @date 2026-01-13 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/noticeManagerment/adminNotice") +@Tag(name = "行管通知", description = "行管通知管理") +public class HotAdminNoticeController extends BaseController { + + private final IHotAdminNoticeService hotAdminNoticeService; + + /** + * 查询行管通知列表 + */ + //@SaCheckPermission("noticeManagerment:adminNotice:list") + @GetMapping("/list") + @Operation(summary = "分页查询行管通知列表") + public TableDataInfo list(HotAdminNoticeBo bo, PageQuery pageQuery) { + return hotAdminNoticeService.queryPageList(bo, pageQuery); + } + + /** + * 查询通知收到情况列表 + */ + //@SaCheckPermission("noticeManagerment:adminNotice:list") + @GetMapping("/receiptStatusList") + @Operation(summary = "分页查询通知收到情况列表") + public TableDataInfo receiptStatusList(HotAdminNoticeBo bo, PageQuery pageQuery) { + return hotAdminNoticeService.queryReceiptStatusList(bo, pageQuery); + } + + /** + * 导出行管通知列表 + */ + //@SaCheckPermission("noticeManagerment:adminNotice:export") + @Log(title = "行管通知", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出行管通知列表") + public void export(HotAdminNoticeBo bo, HttpServletResponse response) { + List list = hotAdminNoticeService.queryList(bo); + ExcelUtil.exportExcel(list, "行管通知", HotAdminNoticeVo.class, response); + } + + /** + * 获取行管通知详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("noticeManagerment:adminNotice:query") + @GetMapping("/{id}") + @Operation(summary = "获取行管通知详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotAdminNoticeService.queryById(id)); + } + + /** + * 新增行管通知 + */ + //@SaCheckPermission("noticeManagerment:adminNotice:add") + @Log(title = "行管通知", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增行管通知") + public R add(@Validated(AddGroup.class) @RequestBody HotAdminNoticeBo bo) { + return toAjax(hotAdminNoticeService.insertByBo(bo)); + } + + /** + * 修改行管通知 + */ + //@SaCheckPermission("noticeManagerment:adminNotice:edit") + @Log(title = "行管通知", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改行管通知") + public R edit(@Validated(EditGroup.class) @RequestBody HotAdminNoticeBo bo) { + return toAjax(hotAdminNoticeService.updateByBo(bo)); + } + + /** + * 删除行管通知 + * + * @param ids 主键串 + */ + //@SaCheckPermission("noticeManagerment:adminNotice:remove") + @Log(title = "行管通知", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除行管通知") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotAdminNoticeService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/domain/HotAdminNotice.java b/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/domain/HotAdminNotice.java new file mode 100644 index 0000000..50799df --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/domain/HotAdminNotice.java @@ -0,0 +1,95 @@ +package com.hotwj.platform.noticeManagerment.adminNotice.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 行管通知对象 hot_admin_notice + * + * @author shihongwei + * @date 2026-01-13 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_admin_notice") +public class HotAdminNotice extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 通知ID + */ + private Long noticeId; + + /** + * 文件名称 + */ + private String fileName; + + /** + * 通知类型字典值 + */ + private Long notifyType; + + /** + * 通知文件URL + */ + private String attachmentUrl; + + /** + * 下发时间 + */ + private Date issueTime; + + /** + * 是否收到: 0=否, 1=是 + */ + private Long isReceived; + + /** + * 接收时间 + */ + private Date receiveTime; + + /** + * 截至时间 + */ + private String deadlineTime; + + /** + * 回复文件URL + */ + private String replyAttachmentUrl; + + /** + * 状态: 0=待审核, 1=审核通过, 2=审核驳回 + */ + private Integer status; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/domain/bo/HotAdminNoticeBo.java b/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/domain/bo/HotAdminNoticeBo.java new file mode 100644 index 0000000..eeec5a0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/domain/bo/HotAdminNoticeBo.java @@ -0,0 +1,97 @@ +package com.hotwj.platform.noticeManagerment.adminNotice.domain.bo; + +import com.hotwj.platform.noticeManagerment.adminNotice.domain.HotAdminNotice; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 行管通知业务对象 hot_admin_notice + * + * @author shihongwei + * @date 2026-01-13 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotAdminNotice.class, reverseConvertGenerate = false) +public class HotAdminNoticeBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 通知ID + */ + @NotNull(message = "通知ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long noticeId; + + /** + * 文件名称 + */ + private String fileName; + + /** + * 通知类型字典值 + */ + private Long notifyType; + + /** + * 通知文件URL + */ + private String attachmentUrl; + + /** + * 下发时间 + */ + private Date issueTime; + + /** + * 是否收到: 0=否, 1=是 + */ + private Long isReceived; + + /** + * 接收时间 + */ + private Date receiveTime; + + /** + * 截至时间 + */ + private String deadlineTime; + + /** + * 回复文件URL + */ + private String replyAttachmentUrl; + + /** + * 状态: 0=待审核, 1=审核通过, 2=审核驳回 + */ + private Integer status; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + /** + * 区域编码(查询条件) + */ + private String districtCode; +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/domain/vo/HotAdminNoticeVo.java b/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/domain/vo/HotAdminNoticeVo.java new file mode 100644 index 0000000..1064bd0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/domain/vo/HotAdminNoticeVo.java @@ -0,0 +1,137 @@ +package com.hotwj.platform.noticeManagerment.adminNotice.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.noticeManagerment.adminNotice.domain.HotAdminNotice; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 行管通知视图对象 hot_admin_notice + * + * @author shihongwei + * @date 2026-01-13 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotAdminNotice.class) +public class HotAdminNoticeVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 公司名称 + */ + @ExcelProperty(value = "公司名称") + private String companyName; + + /** + * 负责人姓名 + */ + @ExcelProperty(value = "负责人姓名") + private String responsibleName; + + /** + * 负责人手机号 + */ + @ExcelProperty(value = "负责人手机号") + private String responsibleMobile; + + /** + * 通知ID + */ + @ExcelProperty(value = "通知ID") + private Long noticeId; + + /** + * 文件名称 + */ + @ExcelProperty(value = "文件名称") + private String fileName; + + /** + * 通知类型字典值 + */ + @ExcelProperty(value = "通知类型字典值") + private Long notifyType; + + /** + * 通知文件URL + */ + @ExcelProperty(value = "通知文件URL") + private String attachmentUrl; + + /** + * 下发时间 + */ + @ExcelProperty(value = "下发时间") + private Date issueTime; + + /** + * 是否收到: 0=否, 1=是 + */ + @ExcelProperty(value = "是否收到: 0=否, 1=是") + private Long isReceived; + + /** + * 接收时间 + */ + @ExcelProperty(value = "接收时间") + private Date receiveTime; + + /** + * 截至时间 + */ + @ExcelProperty(value = "截至时间") + private String deadlineTime; + + /** + * 最新审核意见 + */ + @ExcelProperty(value = "最新审核意见") + private String auditRemark; + + /** + * 回复文件URL + */ + @ExcelProperty(value = "回复文件URL") + private String replyAttachmentUrl; + + + /** + * 状态: 0=待审核, 1=审核通过, 2=审核驳回 + */ + private Integer status; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/mapper/HotAdminNoticeMapper.java b/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/mapper/HotAdminNoticeMapper.java new file mode 100644 index 0000000..a595c8c --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/mapper/HotAdminNoticeMapper.java @@ -0,0 +1,39 @@ +package com.hotwj.platform.noticeManagerment.adminNotice.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.noticeManagerment.adminNotice.domain.HotAdminNotice; +import com.hotwj.platform.noticeManagerment.adminNotice.domain.bo.HotAdminNoticeBo; +import com.hotwj.platform.noticeManagerment.adminNotice.domain.vo.HotAdminNoticeVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 行管通知Mapper接口 + * + * @author shihongwei + * @date 2026-01-13 + */ +@Mapper +public interface HotAdminNoticeMapper extends BaseMapperPlus { + + /** + * 分页查询行管通知列表(包含审核意见) + * + * @param page 分页对象 + * @param ew 查询条件 + * @return 结果 + */ + Page selectPageList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper ew); + + /** + * 查询通知收到情况列表 + * + * @param page 分页对象 + * @param bo 查询参数 + * @return 结果 + */ + Page selectReceiptStatusList(@Param("page") Page page, @Param("bo") HotAdminNoticeBo bo); +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/service/IHotAdminNoticeService.java b/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/service/IHotAdminNoticeService.java new file mode 100644 index 0000000..74b3eb4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/service/IHotAdminNoticeService.java @@ -0,0 +1,77 @@ +package com.hotwj.platform.noticeManagerment.adminNotice.service; + +import com.hotwj.platform.noticeManagerment.adminNotice.domain.bo.HotAdminNoticeBo; +import com.hotwj.platform.noticeManagerment.adminNotice.domain.vo.HotAdminNoticeVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 行管通知Service接口 + * + * @author shihongwei + * @date 2026-01-13 + */ +public interface IHotAdminNoticeService { + + /** + * 查询行管通知 + * + * @param id 主键 + * @return 行管通知 + */ + HotAdminNoticeVo queryById(Long id); + + /** + * 分页查询行管通知列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 行管通知分页列表 + */ + TableDataInfo queryPageList(HotAdminNoticeBo bo, PageQuery pageQuery); + + /** + * 分页查询通知收到情况列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 通知收到情况分页列表 + */ + TableDataInfo queryReceiptStatusList(HotAdminNoticeBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的行管通知列表 + * + * @param bo 查询条件 + * @return 行管通知列表 + */ + List queryList(HotAdminNoticeBo bo); + + /** + * 新增行管通知 + * + * @param bo 行管通知 + * @return 是否新增成功 + */ + Boolean insertByBo(HotAdminNoticeBo bo); + + /** + * 修改行管通知 + * + * @param bo 行管通知 + * @return 是否修改成功 + */ + Boolean updateByBo(HotAdminNoticeBo bo); + + /** + * 校验并批量删除行管通知信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/service/impl/HotAdminNoticeServiceImpl.java b/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/service/impl/HotAdminNoticeServiceImpl.java new file mode 100644 index 0000000..725d39b --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/adminNotice/service/impl/HotAdminNoticeServiceImpl.java @@ -0,0 +1,156 @@ +package com.hotwj.platform.noticeManagerment.adminNotice.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.noticeManagerment.adminNotice.domain.HotAdminNotice; +import com.hotwj.platform.noticeManagerment.adminNotice.domain.bo.HotAdminNoticeBo; +import com.hotwj.platform.noticeManagerment.adminNotice.domain.vo.HotAdminNoticeVo; +import com.hotwj.platform.noticeManagerment.adminNotice.mapper.HotAdminNoticeMapper; +import com.hotwj.platform.noticeManagerment.adminNotice.service.IHotAdminNoticeService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 行管通知Service业务层处理 + * + * @author shihongwei + * @date 2026-01-13 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotAdminNoticeServiceImpl implements IHotAdminNoticeService { + + private final HotAdminNoticeMapper baseMapper; + + /** + * 查询行管通知 + * + * @param id 主键 + * @return 行管通知 + */ + @Override + public HotAdminNoticeVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询行管通知列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 行管通知分页列表 + */ + @Override + public TableDataInfo queryPageList(HotAdminNoticeBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectPageList(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 分页查询通知收到情况列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 通知收到情况分页列表 + */ + @Override + public TableDataInfo queryReceiptStatusList(HotAdminNoticeBo bo, PageQuery pageQuery) { + Page result = baseMapper.selectReceiptStatusList(pageQuery.build(), bo); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的行管通知列表 + * + * @param bo 查询条件 + * @return 行管通知列表 + */ + @Override + public List queryList(HotAdminNoticeBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotAdminNoticeBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotAdminNotice::getId); + lqw.eq(bo.getCompanyId() != null, HotAdminNotice::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getNoticeId() != null, HotAdminNotice::getNoticeId, bo.getNoticeId()); + lqw.like(StringUtils.isNotBlank(bo.getFileName()), HotAdminNotice::getFileName, bo.getFileName()); + lqw.eq(bo.getNotifyType() != null, HotAdminNotice::getNotifyType, bo.getNotifyType()); + lqw.eq(StringUtils.isNotBlank(bo.getAttachmentUrl()), HotAdminNotice::getAttachmentUrl, bo.getAttachmentUrl()); + lqw.eq(bo.getIssueTime() != null, HotAdminNotice::getIssueTime, bo.getIssueTime()); + lqw.eq(bo.getIsReceived() != null, HotAdminNotice::getIsReceived, bo.getIsReceived()); + lqw.eq(bo.getReceiveTime() != null, HotAdminNotice::getReceiveTime, bo.getReceiveTime()); + lqw.eq(bo.getDeadlineTime() != null, HotAdminNotice::getDeadlineTime, bo.getDeadlineTime()); + lqw.eq(StringUtils.isNotBlank(bo.getReplyAttachmentUrl()), HotAdminNotice::getReplyAttachmentUrl, bo.getReplyAttachmentUrl()); + lqw.eq(bo.getIsDeleted() != null, HotAdminNotice::getIsDeleted, bo.getIsDeleted()); + lqw.eq(bo.getStatus() != null, HotAdminNotice::getStatus, bo.getStatus()); + return lqw; + } + + /** + * 新增行管通知 + * + * @param bo 行管通知 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotAdminNoticeBo bo) { + HotAdminNotice add = MapstructUtils.convert(bo, HotAdminNotice.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改行管通知 + * + * @param bo 行管通知 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotAdminNoticeBo bo) { + HotAdminNotice update = MapstructUtils.convert(bo, HotAdminNotice.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotAdminNotice entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除行管通知信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/controller/HotCompanyNoticeController.java b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/controller/HotCompanyNoticeController.java new file mode 100644 index 0000000..6ecf6fe --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/controller/HotCompanyNoticeController.java @@ -0,0 +1,132 @@ +package com.hotwj.platform.noticeManagerment.companyNotice.controller; + +import com.hotwj.platform.noticeManagerment.companyNotice.domain.bo.HotCompanyNoticeBo; +import com.hotwj.platform.noticeManagerment.companyNotice.domain.vo.HotCompanyNoticeVo; +import com.hotwj.platform.noticeManagerment.companyNotice.service.IHotCompanyNoticeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 公司公告 + * + * @author shihongwei + * @date 2026-01-07 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/noticeManagerment/companyNotice") +@Tag(name = "公司公告", description = "公司公告管理") +public class HotCompanyNoticeController extends BaseController { + + private final IHotCompanyNoticeService hotCompanyNoticeService; + + /** + * 查询公司公告列表 + */ + //@SaCheckPermission("noticeManagerment:companyNotice:list") + @GetMapping("/list") + @Operation(summary = "分页查询公司公告列表") + public TableDataInfo list(HotCompanyNoticeBo bo, PageQuery pageQuery) { + return hotCompanyNoticeService.queryPageList(bo, pageQuery); + } + + @GetMapping("/latestUnread") + @Operation(summary = "查询最新一条未读公司公告") + public R latestUnread() { + return R.ok(hotCompanyNoticeService.queryLatestUnread()); + } + + /** + * 导出公司公告列表 + */ + //@SaCheckPermission("noticeManagerment:companyNotice:export") + @Log(title = "公司公告", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出公司公告列表") + public void export(HotCompanyNoticeBo bo, HttpServletResponse response) { + List list = hotCompanyNoticeService.queryList(bo); + ExcelUtil.exportExcel(list, "公司公告", HotCompanyNoticeVo.class, response); + } + + /** + * 获取公司公告详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("noticeManagerment:companyNotice:query") + @GetMapping("/{id}") + @Operation(summary = "获取公司公告详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotCompanyNoticeService.queryById(id)); + } + + /** + * 新增公司公告 + */ + //@SaCheckPermission("noticeManagerment:companyNotice:add") + @Log(title = "公司公告", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增公司公告") + public R add(@Validated(AddGroup.class) @RequestBody HotCompanyNoticeBo bo) { + return toAjax(hotCompanyNoticeService.insertByBo(bo)); + } + + /** + * 修改公司公告 + */ + //@SaCheckPermission("noticeManagerment:companyNotice:edit") + @Log(title = "公司公告", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改公司公告") + public R edit(@Validated(EditGroup.class) @RequestBody HotCompanyNoticeBo bo) { + return toAjax(hotCompanyNoticeService.updateByBo(bo)); + } + + /** + * 删除公司公告 + * + * @param ids 主键串 + */ + //@SaCheckPermission("noticeManagerment:companyNotice:remove") + @Log(title = "公司公告", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除公司公告") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotCompanyNoticeService.deleteWithValidByIds(List.of(ids), true)); + } + + @Log(title = "公司公告", businessType = BusinessType.UPDATE) + @PutMapping("/read/{id}") + @Operation(summary = "标记公司公告为已读") + public R markRead(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return toAjax(hotCompanyNoticeService.markRead(id)); + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/domain/HotCompanyNotice.java b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/domain/HotCompanyNotice.java new file mode 100644 index 0000000..0e0651a --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/domain/HotCompanyNotice.java @@ -0,0 +1,84 @@ +package com.hotwj.platform.noticeManagerment.companyNotice.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 公司公告对象 hot_company_notice + * + * @author shihongwei + * @date 2026-01-07 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_company_notice") +public class HotCompanyNotice extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 新闻类型(company=企业,head=总部) + */ + private Long noticeType; + + /** + * 标题 + */ + private String title; + + /** + * 作者 + */ + private String author; + + /** + * 封面图URL + */ + private String coverImage; + + /** + * 公告内容(富文本) + */ + private String content; + + /** + * 排序值,越大越靠前 + */ + private Long sortNo; + + /** + * 是否置顶:0=否 1=是 + */ + private Long isTop; + + /** + * 是否发布:0=未发布 1=已发布 + */ + private Long isPublished; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/domain/HotCompanyNoticeRead.java b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/domain/HotCompanyNoticeRead.java new file mode 100644 index 0000000..85e306d --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/domain/HotCompanyNoticeRead.java @@ -0,0 +1,34 @@ +package com.hotwj.platform.noticeManagerment.companyNotice.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_company_notice_read") +public class HotCompanyNoticeRead extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @TableId(value = "id") + private Long id; + + private Long noticeId; + + private Long companyId; + + private String readerId; + + private Date readTime; + + @TableLogic + private Long isDeleted; +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/domain/bo/HotCompanyNoticeBo.java b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/domain/bo/HotCompanyNoticeBo.java new file mode 100644 index 0000000..5522d33 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/domain/bo/HotCompanyNoticeBo.java @@ -0,0 +1,85 @@ +package com.hotwj.platform.noticeManagerment.companyNotice.domain.bo; + +import com.hotwj.platform.noticeManagerment.companyNotice.domain.HotCompanyNotice; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 公司公告业务对象 hot_company_notice + * + * @author shihongwei + * @date 2026-01-07 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotCompanyNotice.class, reverseConvertGenerate = false) +public class HotCompanyNoticeBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 新闻类型(company=企业,head=总部) + */ + private Long noticeType; + + /** + * 标题 + */ + @NotBlank(message = "标题不能为空", groups = {AddGroup.class, EditGroup.class}) + private String title; + + /** + * 作者 + */ + private String author; + + /** + * 封面图URL + */ + private String coverImage; + + /** + * 公告内容(富文本) + */ + private String content; + + /** + * 排序值,越大越靠前 + */ + private Long sortNo; + + /** + * 是否置顶:0=否 1=是 + */ + @NotNull(message = "是否置顶:0=否 1=是不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isTop; + + /** + * 是否发布:0=未发布 1=已发布 + */ + @NotNull(message = "是否发布:0=未发布 1=已发布不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isPublished; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/domain/vo/HotCompanyNoticeVo.java b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/domain/vo/HotCompanyNoticeVo.java new file mode 100644 index 0000000..8653a6d --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/domain/vo/HotCompanyNoticeVo.java @@ -0,0 +1,107 @@ +package com.hotwj.platform.noticeManagerment.companyNotice.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.noticeManagerment.companyNotice.domain.HotCompanyNotice; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 公司公告视图对象 hot_company_notice + * + * @author shihongwei + * @date 2026-01-07 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotCompanyNotice.class) +public class HotCompanyNoticeVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 新闻类型(company=企业,head=总部) + */ + @ExcelProperty(value = "新闻类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "c=ompany=企业,head=总部") + private Long noticeType; + + /** + * 标题 + */ + @ExcelProperty(value = "标题") + private String title; + + /** + * 作者 + */ + @ExcelProperty(value = "作者") + private String author; + + /** + * 封面图URL + */ + @ExcelProperty(value = "封面图URL") + private String coverImage; + + /** + * 封面图URLUrl + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "coverImage") + private String coverImageUrl; + /** + * 公告内容(富文本) + */ + @ExcelProperty(value = "公告内容", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "富=文本") + private String content; + + /** + * 排序值,越大越靠前 + */ + @ExcelProperty(value = "排序值,越大越靠前") + private Long sortNo; + + /** + * 是否置顶:0=否 1=是 + */ + @ExcelProperty(value = "是否置顶:0=否 1=是") + private Long isTop; + + /** + * 是否发布:0=未发布 1=已发布 + */ + @ExcelProperty(value = "是否发布:0=未发布 1=已发布") + private Long isPublished; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + private Date createTime; + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/mapper/HotCompanyNoticeMapper.java b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/mapper/HotCompanyNoticeMapper.java new file mode 100644 index 0000000..baac6e9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/mapper/HotCompanyNoticeMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.noticeManagerment.companyNotice.mapper; + +import com.hotwj.platform.noticeManagerment.companyNotice.domain.HotCompanyNotice; +import com.hotwj.platform.noticeManagerment.companyNotice.domain.vo.HotCompanyNoticeVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 公司公告Mapper接口 + * + * @author shihongwei + * @date 2026-01-07 + */ +@Mapper +public interface HotCompanyNoticeMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/mapper/HotCompanyNoticeReadMapper.java b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/mapper/HotCompanyNoticeReadMapper.java new file mode 100644 index 0000000..500ccc7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/mapper/HotCompanyNoticeReadMapper.java @@ -0,0 +1,9 @@ +package com.hotwj.platform.noticeManagerment.companyNotice.mapper; + +import com.hotwj.platform.noticeManagerment.companyNotice.domain.HotCompanyNoticeRead; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +@Mapper +public interface HotCompanyNoticeReadMapper extends BaseMapperPlus { +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/service/IHotCompanyNoticeService.java b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/service/IHotCompanyNoticeService.java new file mode 100644 index 0000000..3079565 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/service/IHotCompanyNoticeService.java @@ -0,0 +1,72 @@ +package com.hotwj.platform.noticeManagerment.companyNotice.service; + +import com.hotwj.platform.noticeManagerment.companyNotice.domain.bo.HotCompanyNoticeBo; +import com.hotwj.platform.noticeManagerment.companyNotice.domain.vo.HotCompanyNoticeVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 公司公告Service接口 + * + * @author shihongwei + * @date 2026-01-07 + */ +public interface IHotCompanyNoticeService { + + /** + * 查询公司公告 + * + * @param id 主键 + * @return 公司公告 + */ + HotCompanyNoticeVo queryById(Long id); + + /** + * 分页查询公司公告列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 公司公告分页列表 + */ + TableDataInfo queryPageList(HotCompanyNoticeBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的公司公告列表 + * + * @param bo 查询条件 + * @return 公司公告列表 + */ + List queryList(HotCompanyNoticeBo bo); + + HotCompanyNoticeVo queryLatestUnread(); + + /** + * 新增公司公告 + * + * @param bo 公司公告 + * @return 是否新增成功 + */ + Boolean insertByBo(HotCompanyNoticeBo bo); + + /** + * 修改公司公告 + * + * @param bo 公司公告 + * @return 是否修改成功 + */ + Boolean updateByBo(HotCompanyNoticeBo bo); + + /** + * 校验并批量删除公司公告信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + Boolean markRead(Long id); +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/service/impl/HotCompanyNoticeServiceImpl.java b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/service/impl/HotCompanyNoticeServiceImpl.java new file mode 100644 index 0000000..7dbe215 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/companyNotice/service/impl/HotCompanyNoticeServiceImpl.java @@ -0,0 +1,225 @@ +package com.hotwj.platform.noticeManagerment.companyNotice.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.noticeManagerment.companyNotice.domain.HotCompanyNotice; +import com.hotwj.platform.noticeManagerment.companyNotice.domain.HotCompanyNoticeRead; +import com.hotwj.platform.noticeManagerment.companyNotice.domain.bo.HotCompanyNoticeBo; +import com.hotwj.platform.noticeManagerment.companyNotice.domain.vo.HotCompanyNoticeVo; +import com.hotwj.platform.noticeManagerment.companyNotice.mapper.HotCompanyNoticeMapper; +import com.hotwj.platform.noticeManagerment.companyNotice.mapper.HotCompanyNoticeReadMapper; +import com.hotwj.platform.noticeManagerment.companyNotice.service.IHotCompanyNoticeService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 公司公告Service业务层处理 + * + * @author shihongwei + * @date 2026-01-07 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotCompanyNoticeServiceImpl implements IHotCompanyNoticeService { + + private final HotCompanyNoticeMapper baseMapper; + private final HotCompanyNoticeReadMapper hotCompanyNoticeReadMapper; + + /** + * 查询公司公告 + * + * @param id 主键 + * @return 公司公告 + */ + @Override + public HotCompanyNoticeVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询公司公告列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 公司公告分页列表 + */ + @Override + public TableDataInfo queryPageList(HotCompanyNoticeBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的公司公告列表 + * + * @param bo 查询条件 + * @return 公司公告列表 + */ + @Override + public List queryList(HotCompanyNoticeBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + @Override + public HotCompanyNoticeVo queryLatestUnread() { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || loginUser.getCompanyId() == null) { + return null; + } + String readerId = getReaderId(loginUser); + if (StringUtils.isBlank(readerId)) { + return null; + } + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(HotCompanyNotice::getCompanyId, loginUser.getCompanyId()); + lqw.eq(HotCompanyNotice::getIsPublished, 1L); + lqw.apply("NOT EXISTS (SELECT 1 FROM hot_company_notice_read r WHERE r.notice_id = hot_company_notice.id AND r.reader_id = {0} AND r.is_deleted = 0)", readerId); + lqw.orderByDesc(HotCompanyNotice::getIsTop); + lqw.orderByDesc(HotCompanyNotice::getCreateTime); + lqw.orderByDesc(HotCompanyNotice::getId); + lqw.last("limit 1"); + HotCompanyNotice notice = baseMapper.selectOne(lqw); + if (notice == null) { + return null; + } + return baseMapper.selectVoById(notice.getId()); + } + + private LambdaQueryWrapper buildQueryWrapper(HotCompanyNoticeBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotCompanyNotice::getCreateTime); + Object beginTime = params.get("beginTime"); + Object endTime = params.get("endTime"); + if (beginTime != null && endTime != null) { + lqw.between(HotCompanyNotice::getCreateTime, beginTime, endTime); + } else if (beginTime != null) { + lqw.ge(HotCompanyNotice::getCreateTime, beginTime); + } else if (endTime != null) { + lqw.le(HotCompanyNotice::getCreateTime, endTime); + } + lqw.eq(bo.getCompanyId() != null, HotCompanyNotice::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getTitle()), HotCompanyNotice::getTitle, bo.getTitle()); + lqw.eq(StringUtils.isNotBlank(bo.getAuthor()), HotCompanyNotice::getAuthor, bo.getAuthor()); + lqw.eq(StringUtils.isNotBlank(bo.getCoverImage()), HotCompanyNotice::getCoverImage, bo.getCoverImage()); + lqw.eq(StringUtils.isNotBlank(bo.getContent()), HotCompanyNotice::getContent, bo.getContent()); + lqw.eq(bo.getSortNo() != null, HotCompanyNotice::getSortNo, bo.getSortNo()); + lqw.eq(bo.getIsTop() != null, HotCompanyNotice::getIsTop, bo.getIsTop()); + lqw.eq(bo.getIsPublished() != null, HotCompanyNotice::getIsPublished, bo.getIsPublished()); + lqw.eq(bo.getIsDeleted() != null, HotCompanyNotice::getIsDeleted, bo.getIsDeleted()); + lqw.eq(bo.getNoticeType() != null, HotCompanyNotice::getNoticeType, bo.getNoticeType()); + return lqw; + } + + /** + * 新增公司公告 + * + * @param bo 公司公告 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotCompanyNoticeBo bo) { + HotCompanyNotice add = MapstructUtils.convert(bo, HotCompanyNotice.class); + // 获取当前公司最大的排序号 + HotCompanyNotice maxSortNotice = baseMapper.selectOne(new LambdaQueryWrapper() + .select(HotCompanyNotice::getSortNo) + .eq(HotCompanyNotice::getCompanyId, bo.getCompanyId()) + .orderByDesc(HotCompanyNotice::getSortNo) + .last("limit 1")); + long maxSort = (maxSortNotice != null && maxSortNotice.getSortNo() != null) ? maxSortNotice.getSortNo() : 0L; + add.setSortNo(maxSort + 1); + + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改公司公告 + * + * @param bo 公司公告 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotCompanyNoticeBo bo) { + HotCompanyNotice update = MapstructUtils.convert(bo, HotCompanyNotice.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + @Override + public Boolean markRead(Long id) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || loginUser.getCompanyId() == null) { + return false; + } + String readerId = getReaderId(loginUser); + if (StringUtils.isBlank(readerId)) { + return false; + } + HotCompanyNotice notice = baseMapper.selectOne(Wrappers.lambdaQuery(HotCompanyNotice.class) + .eq(HotCompanyNotice::getId, id) + .eq(HotCompanyNotice::getCompanyId, loginUser.getCompanyId()) + .eq(HotCompanyNotice::getIsPublished, 1L)); + if (notice == null) { + return false; + } + Long existCount = hotCompanyNoticeReadMapper.selectCount(Wrappers.lambdaQuery(HotCompanyNoticeRead.class) + .eq(HotCompanyNoticeRead::getNoticeId, id) + .eq(HotCompanyNoticeRead::getReaderId, readerId)); + if (existCount != null && existCount > 0) { + return true; + } + HotCompanyNoticeRead read = new HotCompanyNoticeRead(); + read.setNoticeId(id); + read.setCompanyId(loginUser.getCompanyId()); + read.setReaderId(readerId); + read.setReadTime(new Date()); + return hotCompanyNoticeReadMapper.insert(read) > 0; + } + + private String getReaderId(LoginUser loginUser) { + return loginUser.getUserId() == null ? null : String.valueOf(loginUser.getUserId()); + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotCompanyNotice entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除公司公告信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/controller/HotComplaintAdviceController.java b/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/controller/HotComplaintAdviceController.java new file mode 100644 index 0000000..0e98638 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/controller/HotComplaintAdviceController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.noticeManagerment.complaintAdvice.controller; + +import com.hotwj.platform.noticeManagerment.complaintAdvice.domain.bo.HotComplaintAdviceBo; +import com.hotwj.platform.noticeManagerment.complaintAdvice.domain.vo.HotComplaintAdviceVo; +import com.hotwj.platform.noticeManagerment.complaintAdvice.service.IHotComplaintAdviceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 投诉建议 + * + * @author shihongwei + * @date 2026-01-23 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/noticeManagerment/complaintAdvice") +@Tag(name = "投诉建议", description = "投诉建议管理") +public class HotComplaintAdviceController extends BaseController { + + private final IHotComplaintAdviceService hotComplaintAdviceService; + + /** + * 查询投诉建议列表 + */ + //@SaCheckPermission("noticeManagerment:complaintAdvice:list") + @GetMapping("/list") + @Operation(summary = "分页查询投诉建议列表") + public TableDataInfo list(HotComplaintAdviceBo bo, PageQuery pageQuery) { + return hotComplaintAdviceService.queryPageList(bo, pageQuery); + } + + /** + * 导出投诉建议列表 + */ + //@SaCheckPermission("noticeManagerment:complaintAdvice:export") + @Log(title = "投诉建议", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出投诉建议列表") + public void export(HotComplaintAdviceBo bo, HttpServletResponse response) { + List list = hotComplaintAdviceService.queryList(bo); + ExcelUtil.exportExcel(list, "投诉建议", HotComplaintAdviceVo.class, response); + } + + /** + * 获取投诉建议详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("noticeManagerment:complaintAdvice:query") + @GetMapping("/{id}") + @Operation(summary = "获取投诉建议详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotComplaintAdviceService.queryById(id)); + } + + /** + * 新增投诉建议 + */ + //@SaCheckPermission("noticeManagerment:complaintAdvice:add") + @Log(title = "投诉建议", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增投诉建议") + public R add(@Validated(AddGroup.class) @RequestBody HotComplaintAdviceBo bo) { + return toAjax(hotComplaintAdviceService.insertByBo(bo)); + } + + /** + * 修改投诉建议 + */ + //@SaCheckPermission("noticeManagerment:complaintAdvice:edit") + @Log(title = "投诉建议", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改投诉建议") + public R edit(@Validated(EditGroup.class) @RequestBody HotComplaintAdviceBo bo) { + return toAjax(hotComplaintAdviceService.updateByBo(bo)); + } + + /** + * 删除投诉建议 + * + * @param ids 主键串 + */ + //@SaCheckPermission("noticeManagerment:complaintAdvice:remove") + @Log(title = "投诉建议", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除投诉建议") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotComplaintAdviceService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/domain/HotComplaintAdvice.java b/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/domain/HotComplaintAdvice.java new file mode 100644 index 0000000..92ab7d9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/domain/HotComplaintAdvice.java @@ -0,0 +1,95 @@ +package com.hotwj.platform.noticeManagerment.complaintAdvice.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 投诉建议对象 hot_complaint_advice + * + * @author shihongwei + * @date 2026-01-23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_complaint_advice") +public class HotComplaintAdvice extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 投诉问题 + */ + private String question; + + /** + * 投诉单位、企业ID + */ + private Long companyId; + + /** + * 投诉单位、企业名称 + */ + private String companyName; + + /** + * 投诉人员id + */ + private String complainantId; + + /** + * 投诉人员 + */ + private String complainantName; + + /** + * 联系电话 + */ + private String contactPhone; + + /** + * 人员类型 + */ + private String staffType; + + /** + * 投诉内容 + */ + private String complainContent; + + /** + * 是否回复 0=否,1=是 + */ + private Long isReplied; + + /** + * 回复内容 + */ + private String replyContent; + + /** + * 回复时间 + */ + private Date replyTime; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/domain/bo/HotComplaintAdviceBo.java b/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/domain/bo/HotComplaintAdviceBo.java new file mode 100644 index 0000000..bbe33e6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/domain/bo/HotComplaintAdviceBo.java @@ -0,0 +1,97 @@ +package com.hotwj.platform.noticeManagerment.complaintAdvice.domain.bo; + +import com.hotwj.platform.noticeManagerment.complaintAdvice.domain.HotComplaintAdvice; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 投诉建议业务对象 hot_complaint_advice + * + * @author shihongwei + * @date 2026-01-23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotComplaintAdvice.class, reverseConvertGenerate = false) +public class HotComplaintAdviceBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 投诉问题 + */ + @NotBlank(message = "投诉问题不能为空", groups = {AddGroup.class, EditGroup.class}) + private String question; + + /** + * 投诉单位、企业ID + */ + @NotNull(message = "投诉单位、企业ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 投诉单位、企业名称 + */ + private String companyName; + + /** + * 投诉人员id + */ + @NotBlank(message = "投诉人员id不能为空", groups = {AddGroup.class, EditGroup.class}) + private String complainantId; + + /** + * 投诉人员 + */ + private String complainantName; + + /** + * 联系电话 + */ + private String contactPhone; + + /** + * 人员类型 + */ + private String staffType; + + /** + * 投诉内容 + */ + @NotBlank(message = "投诉内容不能为空", groups = {AddGroup.class, EditGroup.class}) + private String complainContent; + + /** + * 是否回复 0=否,1=是 + */ + private Long isReplied; + + /** + * 回复内容 + */ + private String replyContent; + + /** + * 回复时间 + */ + private Date replyTime; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/domain/vo/HotComplaintAdviceVo.java b/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/domain/vo/HotComplaintAdviceVo.java new file mode 100644 index 0000000..d52db77 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/domain/vo/HotComplaintAdviceVo.java @@ -0,0 +1,113 @@ +package com.hotwj.platform.noticeManagerment.complaintAdvice.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.noticeManagerment.complaintAdvice.domain.HotComplaintAdvice; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 投诉建议视图对象 hot_complaint_advice + * + * @author shihongwei + * @date 2026-01-23 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotComplaintAdvice.class) +public class HotComplaintAdviceVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 投诉问题 + */ + @ExcelProperty(value = "投诉问题") + private String question; + + /** + * 投诉单位、企业ID + */ + @ExcelProperty(value = "投诉单位、企业ID") + private Long companyId; + + /** + * 投诉单位、企业名称 + */ + @ExcelProperty(value = "投诉单位、企业名称") + private String companyName; + + /** + * 投诉人员id + */ + @ExcelProperty(value = "投诉人员id") + private String complainantId; + + /** + * 投诉人员 + */ + @ExcelProperty(value = "投诉人员") + private String complainantName; + + /** + * 联系电话 + */ + @ExcelProperty(value = "联系电话") + private String contactPhone; + + /** + * 人员类型 + */ + @ExcelProperty(value = "人员类型") + private String staffType; + + /** + * 投诉内容 + */ + @ExcelProperty(value = "投诉内容") + private String complainContent; + + /** + * 是否回复 0=否,1=是 + */ + @ExcelProperty(value = "是否回复 0=否,1=是") + private Long isReplied; + + /** + * 回复内容 + */ + @ExcelProperty(value = "回复内容") + private String replyContent; + + /** + * 回复时间 + */ + @ExcelProperty(value = "回复时间") + private Date replyTime; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/mapper/HotComplaintAdviceMapper.java b/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/mapper/HotComplaintAdviceMapper.java new file mode 100644 index 0000000..dfc27c6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/mapper/HotComplaintAdviceMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.noticeManagerment.complaintAdvice.mapper; + +import com.hotwj.platform.noticeManagerment.complaintAdvice.domain.HotComplaintAdvice; +import com.hotwj.platform.noticeManagerment.complaintAdvice.domain.vo.HotComplaintAdviceVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 投诉建议Mapper接口 + * + * @author shihongwei + * @date 2026-01-23 + */ +@Mapper +public interface HotComplaintAdviceMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/service/IHotComplaintAdviceService.java b/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/service/IHotComplaintAdviceService.java new file mode 100644 index 0000000..60358dc --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/service/IHotComplaintAdviceService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.noticeManagerment.complaintAdvice.service; + +import com.hotwj.platform.noticeManagerment.complaintAdvice.domain.bo.HotComplaintAdviceBo; +import com.hotwj.platform.noticeManagerment.complaintAdvice.domain.vo.HotComplaintAdviceVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 投诉建议Service接口 + * + * @author shihongwei + * @date 2026-01-23 + */ +public interface IHotComplaintAdviceService { + + /** + * 查询投诉建议 + * + * @param id 主键 + * @return 投诉建议 + */ + HotComplaintAdviceVo queryById(Long id); + + /** + * 分页查询投诉建议列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 投诉建议分页列表 + */ + TableDataInfo queryPageList(HotComplaintAdviceBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的投诉建议列表 + * + * @param bo 查询条件 + * @return 投诉建议列表 + */ + List queryList(HotComplaintAdviceBo bo); + + /** + * 新增投诉建议 + * + * @param bo 投诉建议 + * @return 是否新增成功 + */ + Boolean insertByBo(HotComplaintAdviceBo bo); + + /** + * 修改投诉建议 + * + * @param bo 投诉建议 + * @return 是否修改成功 + */ + Boolean updateByBo(HotComplaintAdviceBo bo); + + /** + * 校验并批量删除投诉建议信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/service/impl/HotComplaintAdviceServiceImpl.java b/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/service/impl/HotComplaintAdviceServiceImpl.java new file mode 100644 index 0000000..8a3d617 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/complaintAdvice/service/impl/HotComplaintAdviceServiceImpl.java @@ -0,0 +1,242 @@ +package com.hotwj.platform.noticeManagerment.complaintAdvice.service.impl; + +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.noticeManagerment.complaintAdvice.domain.HotComplaintAdvice; +import com.hotwj.platform.noticeManagerment.complaintAdvice.domain.bo.HotComplaintAdviceBo; +import com.hotwj.platform.noticeManagerment.complaintAdvice.domain.vo.HotComplaintAdviceVo; +import com.hotwj.platform.noticeManagerment.complaintAdvice.mapper.HotComplaintAdviceMapper; +import com.hotwj.platform.noticeManagerment.complaintAdvice.service.IHotComplaintAdviceService; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 投诉建议Service业务层处理 + * + * @author shihongwei + * @date 2026-01-23 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotComplaintAdviceServiceImpl implements IHotComplaintAdviceService { + + private final HotComplaintAdviceMapper baseMapper; + private final HotCompanySafetyManagerMapper managerMapper; + private final IHotSystemNotificationService notificationService; + /** + * 查询投诉建议 + * + * @param id 主键 + * @return 投诉建议 + */ + @Override + public HotComplaintAdviceVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询投诉建议列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 投诉建议分页列表 + */ + @Override + public TableDataInfo queryPageList(HotComplaintAdviceBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的投诉建议列表 + * + * @param bo 查询条件 + * @return 投诉建议列表 + */ + @Override + public List queryList(HotComplaintAdviceBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotComplaintAdviceBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotComplaintAdvice::getCreateTime, HotComplaintAdvice::getId); + String beginTime = params == null ? null : String.valueOf(params.get("beginTime")); + String endTime = params == null ? null : String.valueOf(params.get("endTime")); + Date beginDate = StringUtils.isNotBlank(beginTime) && !"null".equalsIgnoreCase(beginTime) + ? DateUtil.beginOfDay(DateUtil.parse(beginTime)) + : null; + Date endDate = StringUtils.isNotBlank(endTime) && !"null".equalsIgnoreCase(endTime) + ? DateUtil.endOfDay(DateUtil.parse(endTime)) + : null; + lqw.ge(beginDate != null, HotComplaintAdvice::getCreateTime, beginDate); + lqw.le(endDate != null, HotComplaintAdvice::getCreateTime, endDate); + lqw.eq(bo.getId() != null, HotComplaintAdvice::getId, bo.getId()); + lqw.eq(StringUtils.isNotBlank(bo.getQuestion()), HotComplaintAdvice::getQuestion, bo.getQuestion()); + lqw.eq(bo.getCompanyId() != null, HotComplaintAdvice::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getCompanyName()), HotComplaintAdvice::getCompanyName, bo.getCompanyName()); + lqw.eq(StringUtils.isNotBlank(bo.getComplainantId()), HotComplaintAdvice::getComplainantId, bo.getComplainantId()); + lqw.like(StringUtils.isNotBlank(bo.getComplainantName()), HotComplaintAdvice::getComplainantName, bo.getComplainantName()); + lqw.eq(StringUtils.isNotBlank(bo.getContactPhone()), HotComplaintAdvice::getContactPhone, bo.getContactPhone()); + lqw.eq(StringUtils.isNotBlank(bo.getStaffType()), HotComplaintAdvice::getStaffType, bo.getStaffType()); + lqw.eq(StringUtils.isNotBlank(bo.getComplainContent()), HotComplaintAdvice::getComplainContent, bo.getComplainContent()); + lqw.eq(bo.getIsReplied() != null, HotComplaintAdvice::getIsReplied, bo.getIsReplied()); + lqw.eq(StringUtils.isNotBlank(bo.getReplyContent()), HotComplaintAdvice::getReplyContent, bo.getReplyContent()); + lqw.eq(bo.getReplyTime() != null, HotComplaintAdvice::getReplyTime, bo.getReplyTime()); + lqw.eq(bo.getCreateTime() != null, HotComplaintAdvice::getCreateTime, bo.getCreateTime()); + lqw.eq(bo.getIsDeleted() != null, HotComplaintAdvice::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增投诉建议 + * + * @param bo 投诉建议 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotComplaintAdviceBo bo) { + HotComplaintAdvice add = MapstructUtils.convert(bo, HotComplaintAdvice.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + if (add.getCompanyId() != null) { + List managers = managerMapper.selectList( + com.baomidou.mybatisplus.core.toolkit.Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, add.getCompanyId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers != null && !managers.isEmpty()) { + List receiverIds = managers.stream().map(m -> String.valueOf(m.getId())).toList(); + String title = StringUtils.blankToDefault(add.getQuestion(), "投诉建议"); + String content = "收到新的投诉/建议:【" + title + "】,请及时处理。"; + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("投诉建议"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送投诉建议新增通知失败 companyId={} adviceId={}", add.getCompanyId(), add.getId(), e); + } + } + } + } + return flag; + } + + /** + * 修改投诉建议 + * + * @param bo 投诉建议 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotComplaintAdviceBo bo) { + HotComplaintAdvice old = null; + if (bo.getId() != null) { + old = baseMapper.selectById(bo.getId()); + } + HotComplaintAdvice update = MapstructUtils.convert(bo, HotComplaintAdvice.class); + validEntityBeforeSave(update); + boolean updated = baseMapper.updateById(update) > 0; + if (updated) { + sendReplyNotificationIfNeeded(old, bo); + } + return updated; + } + + private void sendReplyNotificationIfNeeded(HotComplaintAdvice old, HotComplaintAdviceBo bo) { + if (bo == null || bo.getId() == null) { + return; + } + Long newIsReplied = bo.getIsReplied(); + Long oldIsReplied = old == null ? null : old.getIsReplied(); + if (!Long.valueOf(1L).equals(newIsReplied) || Long.valueOf(1L).equals(oldIsReplied)) { + return; + } + + String receiverId = StringUtils.isNotBlank(bo.getComplainantId()) + ? bo.getComplainantId() + : (old == null ? null : old.getComplainantId()); + if (StringUtils.isBlank(receiverId)) { + log.warn("投诉建议回复通知发送失败,缺少投诉人ID complaintId={}", bo.getId()); + return; + } + + String staffType = StringUtils.isNotBlank(bo.getStaffType()) + ? bo.getStaffType() + : (old == null ? null : old.getStaffType()); + String receiverType = "0".equals(staffType) ? "管理员" : "驾驶员"; + + String question = StringUtils.isNotBlank(bo.getQuestion()) + ? bo.getQuestion() + : (old == null ? null : old.getQuestion()); + String title = StringUtils.blankToDefault(question, "投诉建议"); + String content = "您提交的投诉建议【" + title + "】已收到回复,请前往“投诉建议”查看。"; + + HotSystemNotificationGroupBo notifyBo = new HotSystemNotificationGroupBo(); + notifyBo.setLevel("普通"); + notifyBo.setContent(content); + notifyBo.setSourceType("投诉建议"); + notifyBo.setSenderType("SYSTEM"); + notifyBo.setReceiverType(receiverType); + notifyBo.setReceiverId(receiverId); + notifyBo.setIsDeleted(0L); + try { + notificationService.insertByBo(notifyBo); + } catch (Exception e) { + log.warn("投诉建议回复通知发送异常 complaintId={} receiverType={} receiverId={}", + bo.getId(), receiverType, receiverId, e); + } + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotComplaintAdvice entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除投诉建议信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/controller/HotFileTypeConfigController.java b/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/controller/HotFileTypeConfigController.java new file mode 100644 index 0000000..480f44f --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/controller/HotFileTypeConfigController.java @@ -0,0 +1,126 @@ +package com.hotwj.platform.noticeManagerment.fileTypeConfig.controller; + +import com.hotwj.platform.noticeManagerment.fileTypeConfig.domain.bo.HotFileTypeConfigBo; +import com.hotwj.platform.noticeManagerment.fileTypeConfig.domain.vo.HotFileTypeConfigVo; +import com.hotwj.platform.noticeManagerment.fileTypeConfig.service.IHotFileTypeConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 文件类型配置 + * + * @author shihongwei + * @date 2026-03-25 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/noticeManagerment/fileTypeConfig") +@Tag(name = "文件类型配置", description = "文件类型配置管理") +public class HotFileTypeConfigController extends BaseController { + + private final IHotFileTypeConfigService hotFileTypeConfigService; + + /** + * 查询文件类型配置列表 + */ + //@SaCheckPermission("noticeManagerment:fileTypeConfig:list") + @GetMapping("/list") + @Operation(summary = "分页查询文件类型配置列表") + public TableDataInfo list(HotFileTypeConfigBo bo, PageQuery pageQuery) { + return hotFileTypeConfigService.queryPageList(bo, pageQuery); + } + + /** + * 导出文件类型配置列表 + */ + //@SaCheckPermission("noticeManagerment:fileTypeConfig:export") + @Log(title = "文件类型配置", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出文件类型配置列表") + public void export(HotFileTypeConfigBo bo, HttpServletResponse response) { + List list = hotFileTypeConfigService.queryList(bo); + ExcelUtil.exportExcel(list, "文件类型配置", HotFileTypeConfigVo.class, response); + } + + /** + * 获取文件类型配置详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("noticeManagerment:fileTypeConfig:query") + @GetMapping("/{id}") + @Operation(summary = "获取文件类型配置详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotFileTypeConfigService.queryById(id)); + } + + /** + * 新增文件类型配置 + */ + //@SaCheckPermission("noticeManagerment:fileTypeConfig:add") + @Log(title = "文件类型配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增文件类型配置") + public R add(@Validated(AddGroup.class) @RequestBody HotFileTypeConfigBo bo) { + return toAjax(hotFileTypeConfigService.insertByBo(bo)); + } + + /** + * 修改文件类型配置 + */ + //@SaCheckPermission("noticeManagerment:fileTypeConfig:edit") + @Log(title = "文件类型配置", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改文件类型配置") + public R edit(@Validated(EditGroup.class) @RequestBody HotFileTypeConfigBo bo) { + return toAjax(hotFileTypeConfigService.updateByBo(bo)); + } + + /** + * 删除文件类型配置 + * + * @param ids 主键串 + */ + //@SaCheckPermission("noticeManagerment:fileTypeConfig:remove") + @Log(title = "文件类型配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除文件类型配置") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotFileTypeConfigService.deleteWithValidByIds(List.of(ids), true)); + } + + //@SaCheckPermission("noticeManagerment:fileTypeConfig:add") + @Log(title = "文件类型配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/useDefault") + @Operation(summary = "采用默认配置") + public R useDefault() { + return toAjax(hotFileTypeConfigService.useDefault()); + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/domain/HotFileTypeConfig.java b/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/domain/HotFileTypeConfig.java new file mode 100644 index 0000000..230b8ec --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/domain/HotFileTypeConfig.java @@ -0,0 +1,64 @@ +package com.hotwj.platform.noticeManagerment.fileTypeConfig.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 文件类型配置对象 hot_file_type_config + * + * @author shihongwei + * @date 2026-03-25 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_file_type_config") +public class HotFileTypeConfig extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(多租户标识) + */ + private Long companyId; + + /** + * 名称(如:图片、文档、视频) + */ + private String name; + + /** + * 值(如:jpg,png,pdf,mp4) + */ + private String value; + + /** + * 排序(数值越小越靠前) + */ + private Long sortOrder; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/domain/bo/HotFileTypeConfigBo.java b/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/domain/bo/HotFileTypeConfigBo.java new file mode 100644 index 0000000..c1d952e --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/domain/bo/HotFileTypeConfigBo.java @@ -0,0 +1,59 @@ +package com.hotwj.platform.noticeManagerment.fileTypeConfig.domain.bo; + +import com.hotwj.platform.noticeManagerment.fileTypeConfig.domain.HotFileTypeConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 文件类型配置业务对象 hot_file_type_config + * + * @author shihongwei + * @date 2026-03-25 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotFileTypeConfig.class, reverseConvertGenerate = false) +public class HotFileTypeConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(多租户标识) + */ + private Long companyId; + + /** + * 名称(如:图片、文档、视频) + */ + private String name; + + /** + * 值(如:jpg,png,pdf,mp4) + */ + private String value; + + /** + * 排序(数值越小越靠前) + */ + private Long sortOrder; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/domain/vo/HotFileTypeConfigVo.java b/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/domain/vo/HotFileTypeConfigVo.java new file mode 100644 index 0000000..f482049 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/domain/vo/HotFileTypeConfigVo.java @@ -0,0 +1,90 @@ +package com.hotwj.platform.noticeManagerment.fileTypeConfig.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.noticeManagerment.fileTypeConfig.domain.HotFileTypeConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 文件类型配置视图对象 hot_file_type_config + * + * @author shihongwei + * @date 2026-03-25 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotFileTypeConfig.class) +public class HotFileTypeConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(多租户标识) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "多=租户标识") + private Long companyId; + + /** + * 名称(如:图片、文档、视频) + */ + @ExcelProperty(value = "名称", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "如=:图片、文档、视频") + private String name; + + /** + * 值(如:jpg,png,pdf,mp4) + */ + @ExcelProperty(value = "值", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "如=:jpg,png,pdf,mp4") + private String value; + + /** + * 排序(数值越小越靠前) + */ + @ExcelProperty(value = "排序", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "数=值越小越靠前") + private Long sortOrder; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + private Date createTime; + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/mapper/HotFileTypeConfigMapper.java b/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/mapper/HotFileTypeConfigMapper.java new file mode 100644 index 0000000..0336761 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/mapper/HotFileTypeConfigMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.noticeManagerment.fileTypeConfig.mapper; + +import com.hotwj.platform.noticeManagerment.fileTypeConfig.domain.HotFileTypeConfig; +import com.hotwj.platform.noticeManagerment.fileTypeConfig.domain.vo.HotFileTypeConfigVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 文件类型配置Mapper接口 + * + * @author shihongwei + * @date 2026-03-25 + */ +@Mapper +public interface HotFileTypeConfigMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/service/IHotFileTypeConfigService.java b/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/service/IHotFileTypeConfigService.java new file mode 100644 index 0000000..b15f501 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/service/IHotFileTypeConfigService.java @@ -0,0 +1,70 @@ +package com.hotwj.platform.noticeManagerment.fileTypeConfig.service; + +import com.hotwj.platform.noticeManagerment.fileTypeConfig.domain.bo.HotFileTypeConfigBo; +import com.hotwj.platform.noticeManagerment.fileTypeConfig.domain.vo.HotFileTypeConfigVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 文件类型配置Service接口 + * + * @author shihongwei + * @date 2026-03-25 + */ +public interface IHotFileTypeConfigService { + + /** + * 查询文件类型配置 + * + * @param id 主键 + * @return 文件类型配置 + */ + HotFileTypeConfigVo queryById(Long id); + + /** + * 分页查询文件类型配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 文件类型配置分页列表 + */ + TableDataInfo queryPageList(HotFileTypeConfigBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的文件类型配置列表 + * + * @param bo 查询条件 + * @return 文件类型配置列表 + */ + List queryList(HotFileTypeConfigBo bo); + + /** + * 新增文件类型配置 + * + * @param bo 文件类型配置 + * @return 是否新增成功 + */ + Boolean insertByBo(HotFileTypeConfigBo bo); + + /** + * 修改文件类型配置 + * + * @param bo 文件类型配置 + * @return 是否修改成功 + */ + Boolean updateByBo(HotFileTypeConfigBo bo); + + /** + * 校验并批量删除文件类型配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + Boolean useDefault(); +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/service/impl/HotFileTypeConfigServiceImpl.java b/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/service/impl/HotFileTypeConfigServiceImpl.java new file mode 100644 index 0000000..32636d7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/fileTypeConfig/service/impl/HotFileTypeConfigServiceImpl.java @@ -0,0 +1,186 @@ +package com.hotwj.platform.noticeManagerment.fileTypeConfig.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.noticeManagerment.fileTypeConfig.domain.HotFileTypeConfig; +import com.hotwj.platform.noticeManagerment.fileTypeConfig.domain.bo.HotFileTypeConfigBo; +import com.hotwj.platform.noticeManagerment.fileTypeConfig.domain.vo.HotFileTypeConfigVo; +import com.hotwj.platform.noticeManagerment.fileTypeConfig.mapper.HotFileTypeConfigMapper; +import com.hotwj.platform.noticeManagerment.fileTypeConfig.service.IHotFileTypeConfigService; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 文件类型配置Service业务层处理 + * + * @author shihongwei + * @date 2026-03-25 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotFileTypeConfigServiceImpl implements IHotFileTypeConfigService { + + private final HotFileTypeConfigMapper baseMapper; + private final ISysCompanyService sysCompanyService; + + /** + * 查询文件类型配置 + * + * @param id 主键 + * @return 文件类型配置 + */ + @Override + public HotFileTypeConfigVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询文件类型配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 文件类型配置分页列表 + */ + @Override + public TableDataInfo queryPageList(HotFileTypeConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的文件类型配置列表 + * + * @param bo 查询条件 + * @return 文件类型配置列表 + */ + @Override + public List queryList(HotFileTypeConfigBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean useDefault() { + Long companyId = LoginHelper.getCompanyId(); + if (companyId == null) { + throw new ServiceException("当前登录用户无公司信息"); + } + SysCompanyVo company = sysCompanyService.queryById(companyId); + if (company == null) { + throw new ServiceException("公司不存在"); + } + Long count = baseMapper.selectCount( + Wrappers.lambdaQuery(HotFileTypeConfig.class) + .eq(HotFileTypeConfig::getCompanyId, companyId) + ); + if (count != null && count > 0) { + throw new ServiceException("当前公司已存在文件类型配置,请先清除已存在数据"); + } + List defaultConfigs = baseMapper.selectList( + Wrappers.lambdaQuery(HotFileTypeConfig.class) + .eq(HotFileTypeConfig::getCompanyId, 1L) + ); + if (CollUtil.isEmpty(defaultConfigs)) { + throw new ServiceException("默认配置不存在,请联系管理员"); + } + List newConfigs = new ArrayList<>(); + for (HotFileTypeConfig cfg : defaultConfigs) { + HotFileTypeConfig newCfg = new HotFileTypeConfig(); + newCfg.setCompanyId(companyId); + newCfg.setName(cfg.getName()); + newCfg.setValue(cfg.getValue()); + newCfg.setSortOrder(cfg.getSortOrder()); + newCfg.setRemark(cfg.getRemark()); + newCfg.setIsDeleted(0L); + newConfigs.add(newCfg); + } + return baseMapper.insertBatch(newConfigs); + } + + private LambdaQueryWrapper buildQueryWrapper(HotFileTypeConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotFileTypeConfig::getSortOrder).orderByDesc(BaseEntity::getCreateTime); + lqw.eq(bo.getCompanyId() != null, HotFileTypeConfig::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getName()), HotFileTypeConfig::getName, bo.getName()); + lqw.eq(StringUtils.isNotBlank(bo.getValue()), HotFileTypeConfig::getValue, bo.getValue()); + lqw.eq(bo.getSortOrder() != null, HotFileTypeConfig::getSortOrder, bo.getSortOrder()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotFileTypeConfig::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotFileTypeConfig::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotFileTypeConfig::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增文件类型配置 + * + * @param bo 文件类型配置 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotFileTypeConfigBo bo) { + HotFileTypeConfig add = MapstructUtils.convert(bo, HotFileTypeConfig.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改文件类型配置 + * + * @param bo 文件类型配置 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotFileTypeConfigBo bo) { + HotFileTypeConfig update = MapstructUtils.convert(bo, HotFileTypeConfig.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotFileTypeConfig entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除文件类型配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/controller/HotLawRegulationController.java b/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/controller/HotLawRegulationController.java new file mode 100644 index 0000000..491dc46 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/controller/HotLawRegulationController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.noticeManagerment.lawRegulation.controller; + +import com.hotwj.platform.noticeManagerment.lawRegulation.domain.bo.HotLawRegulationBo; +import com.hotwj.platform.noticeManagerment.lawRegulation.domain.vo.HotLawRegulationVo; +import com.hotwj.platform.noticeManagerment.lawRegulation.service.IHotLawRegulationService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 法律法规 + * + * @author shihongwei + * @date 2026-01-08 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/noticeManagerment/lawRegulation") +@Tag(name = "法律法规", description = "法律法规管理") +public class HotLawRegulationController extends BaseController { + + private final IHotLawRegulationService hotLawRegulationService; + + /** + * 查询法律法规列表 + */ + //@SaCheckPermission("noticeManagerment:lawRegulation:list") + @GetMapping("/list") + @Operation(summary = "分页查询法律法规列表") + public TableDataInfo list(HotLawRegulationBo bo, PageQuery pageQuery) { + return hotLawRegulationService.queryPageList(bo, pageQuery); + } + + /** + * 导出法律法规列表 + */ + //@SaCheckPermission("noticeManagerment:lawRegulation:export") + @Log(title = "法律法规", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出法律法规列表") + public void export(HotLawRegulationBo bo, HttpServletResponse response) { + List list = hotLawRegulationService.queryList(bo); + ExcelUtil.exportExcel(list, "法律法规", HotLawRegulationVo.class, response); + } + + /** + * 获取法律法规详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("noticeManagerment:lawRegulation:query") + @GetMapping("/{id}") + @Operation(summary = "获取法律法规详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotLawRegulationService.queryById(id)); + } + + /** + * 新增法律法规 + */ + //@SaCheckPermission("noticeManagerment:lawRegulation:add") + @Log(title = "法律法规", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增法律法规") + public R add(@Validated(AddGroup.class) @RequestBody HotLawRegulationBo bo) { + return toAjax(hotLawRegulationService.insertByBo(bo)); + } + + /** + * 修改法律法规 + */ + //@SaCheckPermission("noticeManagerment:lawRegulation:edit") + @Log(title = "法律法规", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改法律法规") + public R edit(@Validated(EditGroup.class) @RequestBody HotLawRegulationBo bo) { + return toAjax(hotLawRegulationService.updateByBo(bo)); + } + + /** + * 删除法律法规 + * + * @param ids 主键串 + */ + //@SaCheckPermission("noticeManagerment:lawRegulation:remove") + @Log(title = "法律法规", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除法律法规") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotLawRegulationService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/domain/HotLawRegulation.java b/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/domain/HotLawRegulation.java new file mode 100644 index 0000000..3d45b66 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/domain/HotLawRegulation.java @@ -0,0 +1,74 @@ +package com.hotwj.platform.noticeManagerment.lawRegulation.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 法律法规对象 hot_law_regulation + * + * @author shihongwei + * @date 2026-01-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_law_regulation") +public class HotLawRegulation extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 制度名称 + */ + private String ruleName; + + /** + * 制度类型 + */ + private Long ruleType; + + /** + * 附件URL + */ + private String attachmentUrl; + + /** + * 制度内容(富文本,附件优先展示) + */ + private String content; + + /** + * 是否发布 + */ + private Long isPublished; + + /** + * 排序 + */ + private Long sortno; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/domain/bo/HotLawRegulationBo.java b/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/domain/bo/HotLawRegulationBo.java new file mode 100644 index 0000000..6bbb63c --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/domain/bo/HotLawRegulationBo.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.noticeManagerment.lawRegulation.domain.bo; + +import com.hotwj.platform.noticeManagerment.lawRegulation.domain.HotLawRegulation; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 法律法规业务对象 hot_law_regulation + * + * @author shihongwei + * @date 2026-01-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotLawRegulation.class, reverseConvertGenerate = false) +public class HotLawRegulationBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 制度名称 + */ + @NotBlank(message = "制度名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String ruleName; + + /** + * 制度类型 + */ + @NotNull(message = "制度类型不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long ruleType; + + /** + * 附件URL + */ + private String attachmentUrl; + + /** + * 制度内容(富文本,附件优先展示) + */ + private String content; + + /** + * 是否发布 + */ + @NotNull(message = "是否发布不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isPublished; + + /** + * 排序 + */ + private Long sortno; + + /** + * 0=正常, 1=已删除 + */ + @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/domain/vo/HotLawRegulationVo.java b/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/domain/vo/HotLawRegulationVo.java new file mode 100644 index 0000000..9e333ab --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/domain/vo/HotLawRegulationVo.java @@ -0,0 +1,87 @@ +package com.hotwj.platform.noticeManagerment.lawRegulation.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.noticeManagerment.lawRegulation.domain.HotLawRegulation; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 法律法规视图对象 hot_law_regulation + * + * @author shihongwei + * @date 2026-01-08 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotLawRegulation.class) +public class HotLawRegulationVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 制度名称 + */ + @ExcelProperty(value = "制度名称") + private String ruleName; + + /** + * 制度类型 + */ + @ExcelProperty(value = "制度类型") + private Long ruleType; + + /** + * 附件URL + */ + @ExcelProperty(value = "附件URL") + private String attachmentUrl; + + /** + * 制度内容(富文本,附件优先展示) + */ + @ExcelProperty(value = "制度内容", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "富=文本,附件优先展示") + private String content; + + /** + * 是否发布 + */ + @ExcelProperty(value = "是否发布") + private Long isPublished; + + /** + * 排序 + */ + @ExcelProperty(value = "排序") + private Long sortno; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + private Date createTime; + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/mapper/HotLawRegulationMapper.java b/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/mapper/HotLawRegulationMapper.java new file mode 100644 index 0000000..b0ba54e --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/mapper/HotLawRegulationMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.noticeManagerment.lawRegulation.mapper; + +import com.hotwj.platform.noticeManagerment.lawRegulation.domain.HotLawRegulation; +import com.hotwj.platform.noticeManagerment.lawRegulation.domain.vo.HotLawRegulationVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 法律法规Mapper接口 + * + * @author shihongwei + * @date 2026-01-08 + */ +@Mapper +public interface HotLawRegulationMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/service/IHotLawRegulationService.java b/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/service/IHotLawRegulationService.java new file mode 100644 index 0000000..3234c76 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/service/IHotLawRegulationService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.noticeManagerment.lawRegulation.service; + +import com.hotwj.platform.noticeManagerment.lawRegulation.domain.bo.HotLawRegulationBo; +import com.hotwj.platform.noticeManagerment.lawRegulation.domain.vo.HotLawRegulationVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 法律法规Service接口 + * + * @author shihongwei + * @date 2026-01-08 + */ +public interface IHotLawRegulationService { + + /** + * 查询法律法规 + * + * @param id 主键 + * @return 法律法规 + */ + HotLawRegulationVo queryById(Long id); + + /** + * 分页查询法律法规列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 法律法规分页列表 + */ + TableDataInfo queryPageList(HotLawRegulationBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的法律法规列表 + * + * @param bo 查询条件 + * @return 法律法规列表 + */ + List queryList(HotLawRegulationBo bo); + + /** + * 新增法律法规 + * + * @param bo 法律法规 + * @return 是否新增成功 + */ + Boolean insertByBo(HotLawRegulationBo bo); + + /** + * 修改法律法规 + * + * @param bo 法律法规 + * @return 是否修改成功 + */ + Boolean updateByBo(HotLawRegulationBo bo); + + /** + * 校验并批量删除法律法规信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/service/impl/HotLawRegulationServiceImpl.java b/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/service/impl/HotLawRegulationServiceImpl.java new file mode 100644 index 0000000..b743a0e --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/lawRegulation/service/impl/HotLawRegulationServiceImpl.java @@ -0,0 +1,145 @@ +package com.hotwj.platform.noticeManagerment.lawRegulation.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.noticeManagerment.lawRegulation.domain.HotLawRegulation; +import com.hotwj.platform.noticeManagerment.lawRegulation.domain.bo.HotLawRegulationBo; +import com.hotwj.platform.noticeManagerment.lawRegulation.domain.vo.HotLawRegulationVo; +import com.hotwj.platform.noticeManagerment.lawRegulation.mapper.HotLawRegulationMapper; +import com.hotwj.platform.noticeManagerment.lawRegulation.service.IHotLawRegulationService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 法律法规Service业务层处理 + * + * @author shihongwei + * @date 2026-01-08 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotLawRegulationServiceImpl implements IHotLawRegulationService { + + private final HotLawRegulationMapper baseMapper; + + /** + * 查询法律法规 + * + * @param id 主键 + * @return 法律法规 + */ + @Override + public HotLawRegulationVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询法律法规列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 法律法规分页列表 + */ + @Override + public TableDataInfo queryPageList(HotLawRegulationBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的法律法规列表 + * + * @param bo 查询条件 + * @return 法律法规列表 + */ + @Override + public List queryList(HotLawRegulationBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotLawRegulationBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotLawRegulation::getSortno).orderByAsc(HotLawRegulation::getId); + lqw.eq(bo.getCompanyId() != null, HotLawRegulation::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getRuleName()), HotLawRegulation::getRuleName, bo.getRuleName()); + lqw.eq(bo.getRuleType() != null, HotLawRegulation::getRuleType, bo.getRuleType()); + lqw.eq(StringUtils.isNotBlank(bo.getAttachmentUrl()), HotLawRegulation::getAttachmentUrl, bo.getAttachmentUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getContent()), HotLawRegulation::getContent, bo.getContent()); + lqw.eq(bo.getIsPublished() != null, HotLawRegulation::getIsPublished, bo.getIsPublished()); + lqw.eq(bo.getIsDeleted() != null, HotLawRegulation::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增法律法规 + * + * @param bo 法律法规 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotLawRegulationBo bo) { + HotLawRegulation add = MapstructUtils.convert(bo, HotLawRegulation.class); + HotLawRegulation maxEntity = baseMapper.selectOne(Wrappers.lambdaQuery() + .select(HotLawRegulation::getSortno) + .eq(add.getCompanyId() != null, HotLawRegulation::getCompanyId, add.getCompanyId()) + .orderByDesc(HotLawRegulation::getSortno) + .last("LIMIT 1")); + Long maxSort = (maxEntity != null && maxEntity.getSortno() != null) ? maxEntity.getSortno() : 0L; + add.setSortno(maxSort + 1); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改法律法规 + * + * @param bo 法律法规 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotLawRegulationBo bo) { + HotLawRegulation update = MapstructUtils.convert(bo, HotLawRegulation.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotLawRegulation entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除法律法规信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/controller/HotNoticeRecordController.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/controller/HotNoticeRecordController.java new file mode 100644 index 0000000..77de78e --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/controller/HotNoticeRecordController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.noticeManagerment.noticeRecord.controller; + +import com.hotwj.platform.noticeManagerment.noticeRecord.domain.bo.HotNoticeRecordBo; +import com.hotwj.platform.noticeManagerment.noticeRecord.domain.vo.HotNoticeRecordVo; +import com.hotwj.platform.noticeManagerment.noticeRecord.service.IHotNoticeRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 通知记录 + * + * @author shihongwei + * @date 2026-01-14 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/noticeManagerment/noticeRecord") +@Tag(name = "通知记录", description = "通知记录管理") +public class HotNoticeRecordController extends BaseController { + + private final IHotNoticeRecordService hotNoticeRecordService; + + /** + * 查询通知记录列表 + */ + //@SaCheckPermission("noticeManagerment:noticeRecord:list") + @GetMapping("/list") + @Operation(summary = "分页查询通知记录列表") + public TableDataInfo list(HotNoticeRecordBo bo, PageQuery pageQuery) { + return hotNoticeRecordService.queryPageList(bo, pageQuery); + } + + /** + * 导出通知记录列表 + */ + //@SaCheckPermission("noticeManagerment:noticeRecord:export") + @Log(title = "通知记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出通知记录列表") + public void export(HotNoticeRecordBo bo, HttpServletResponse response) { + List list = hotNoticeRecordService.queryList(bo); + ExcelUtil.exportExcel(list, "通知记录", HotNoticeRecordVo.class, response); + } + + /** + * 获取通知记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("noticeManagerment:noticeRecord:query") + @GetMapping("/{id}") + @Operation(summary = "获取通知记录详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotNoticeRecordService.queryById(id)); + } + + /** + * 新增通知记录 + */ + //@SaCheckPermission("noticeManagerment:noticeRecord:add") + @Log(title = "通知记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增通知记录") + public R add(@Validated(AddGroup.class) @RequestBody HotNoticeRecordBo bo) { + return toAjax(hotNoticeRecordService.insertByBo(bo)); + } + + /** + * 修改通知记录 + */ + //@SaCheckPermission("noticeManagerment:noticeRecord:edit") + @Log(title = "通知记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改通知记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotNoticeRecordBo bo) { + return toAjax(hotNoticeRecordService.updateByBo(bo)); + } + + /** + * 删除通知记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("noticeManagerment:noticeRecord:remove") + @Log(title = "通知记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除通知记录") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotNoticeRecordService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/domain/HotNoticeRecord.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/domain/HotNoticeRecord.java new file mode 100644 index 0000000..2d9b383 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/domain/HotNoticeRecord.java @@ -0,0 +1,80 @@ +package com.hotwj.platform.noticeManagerment.noticeRecord.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 通知记录对象 hot_notice_record + * + * @author shihongwei + * @date 2026-01-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_notice_record") +public class HotNoticeRecord extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 通知方式 + */ + private String notifyChannel; + + /** + * 通知类型 + */ + private String noticeType; + + /** + * 通知对象 + */ + private String noticeTarget; + + /** + * 对象类型 + */ + private String noticeTargetType; + + /** + * 通知手机号 + */ + private String noticeMobile; + + /** + * 通知时间 + */ + private Date noticeTime; + + /** + * 通知人员ID + */ + private Long noticeUserId; + + /** + * 通知人员名称 + */ + private String noticeUserName; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/domain/bo/HotNoticeRecordBo.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/domain/bo/HotNoticeRecordBo.java new file mode 100644 index 0000000..b0c1bf8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/domain/bo/HotNoticeRecordBo.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.noticeManagerment.noticeRecord.domain.bo; + +import com.hotwj.platform.noticeManagerment.noticeRecord.domain.HotNoticeRecord; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 通知记录业务对象 hot_notice_record + * + * @author shihongwei + * @date 2026-01-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotNoticeRecord.class, reverseConvertGenerate = false) +public class HotNoticeRecordBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 通知方式 + */ + private String notifyChannel; + + /** + * 通知类型 + */ + private String noticeType; + + /** + * 通知对象 + */ + private String noticeTarget; + + /** + * 对象类型 + */ + private String noticeTargetType; + + /** + * 通知手机号 + */ + private String noticeMobile; + + /** + * 通知时间 + */ + private Date noticeTime; + + /** + * 通知人员ID + */ + private Long noticeUserId; + + /** + * 通知人员名称 + */ + private String noticeUserName; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/domain/vo/HotNoticeRecordVo.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/domain/vo/HotNoticeRecordVo.java new file mode 100644 index 0000000..d84c832 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/domain/vo/HotNoticeRecordVo.java @@ -0,0 +1,89 @@ +package com.hotwj.platform.noticeManagerment.noticeRecord.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.noticeManagerment.noticeRecord.domain.HotNoticeRecord; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 通知记录视图对象 hot_notice_record + * + * @author shihongwei + * @date 2026-01-14 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotNoticeRecord.class) +public class HotNoticeRecordVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 通知方式 + */ + @ExcelProperty(value = "通知方式") + private String notifyChannel; + + /** + * 通知类型 + */ + @ExcelProperty(value = "通知类型") + private String noticeType; + + /** + * 通知对象 + */ + @ExcelProperty(value = "通知对象") + private String noticeTarget; + + /** + * 对象类型 + */ + @ExcelProperty(value = "对象类型") + private String noticeTargetType; + + /** + * 通知手机号 + */ + @ExcelProperty(value = "通知手机号") + private String noticeMobile; + + /** + * 通知时间 + */ + @ExcelProperty(value = "通知时间") + private Date noticeTime; + + /** + * 通知人员ID + */ + @ExcelProperty(value = "通知人员ID") + private Long noticeUserId; + + /** + * 通知人员名称 + */ + @ExcelProperty(value = "通知人员名称") + private String noticeUserName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/mapper/HotNoticeRecordMapper.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/mapper/HotNoticeRecordMapper.java new file mode 100644 index 0000000..26c48aa --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/mapper/HotNoticeRecordMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.noticeManagerment.noticeRecord.mapper; + +import com.hotwj.platform.noticeManagerment.noticeRecord.domain.HotNoticeRecord; +import com.hotwj.platform.noticeManagerment.noticeRecord.domain.vo.HotNoticeRecordVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 通知记录Mapper接口 + * + * @author shihongwei + * @date 2026-01-14 + */ +@Mapper +public interface HotNoticeRecordMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/service/IHotNoticeRecordService.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/service/IHotNoticeRecordService.java new file mode 100644 index 0000000..05f404a --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/service/IHotNoticeRecordService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.noticeManagerment.noticeRecord.service; + +import com.hotwj.platform.noticeManagerment.noticeRecord.domain.bo.HotNoticeRecordBo; +import com.hotwj.platform.noticeManagerment.noticeRecord.domain.vo.HotNoticeRecordVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 通知记录Service接口 + * + * @author shihongwei + * @date 2026-01-14 + */ +public interface IHotNoticeRecordService { + + /** + * 查询通知记录 + * + * @param id 主键 + * @return 通知记录 + */ + HotNoticeRecordVo queryById(Long id); + + /** + * 分页查询通知记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 通知记录分页列表 + */ + TableDataInfo queryPageList(HotNoticeRecordBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的通知记录列表 + * + * @param bo 查询条件 + * @return 通知记录列表 + */ + List queryList(HotNoticeRecordBo bo); + + /** + * 新增通知记录 + * + * @param bo 通知记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotNoticeRecordBo bo); + + /** + * 修改通知记录 + * + * @param bo 通知记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotNoticeRecordBo bo); + + /** + * 校验并批量删除通知记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/service/impl/HotNoticeRecordServiceImpl.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/service/impl/HotNoticeRecordServiceImpl.java new file mode 100644 index 0000000..a170db6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeRecord/service/impl/HotNoticeRecordServiceImpl.java @@ -0,0 +1,140 @@ +package com.hotwj.platform.noticeManagerment.noticeRecord.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.noticeManagerment.noticeRecord.domain.HotNoticeRecord; +import com.hotwj.platform.noticeManagerment.noticeRecord.domain.bo.HotNoticeRecordBo; +import com.hotwj.platform.noticeManagerment.noticeRecord.domain.vo.HotNoticeRecordVo; +import com.hotwj.platform.noticeManagerment.noticeRecord.mapper.HotNoticeRecordMapper; +import com.hotwj.platform.noticeManagerment.noticeRecord.service.IHotNoticeRecordService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 通知记录Service业务层处理 + * + * @author shihongwei + * @date 2026-01-14 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotNoticeRecordServiceImpl implements IHotNoticeRecordService { + + private final HotNoticeRecordMapper baseMapper; + + /** + * 查询通知记录 + * + * @param id 主键 + * @return 通知记录 + */ + @Override + public HotNoticeRecordVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询通知记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 通知记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotNoticeRecordBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的通知记录列表 + * + * @param bo 查询条件 + * @return 通知记录列表 + */ + @Override + public List queryList(HotNoticeRecordBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotNoticeRecordBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotNoticeRecord::getId); + lqw.eq(StringUtils.isNotBlank(bo.getNotifyChannel()), HotNoticeRecord::getNotifyChannel, bo.getNotifyChannel()); + lqw.eq(StringUtils.isNotBlank(bo.getNoticeType()), HotNoticeRecord::getNoticeType, bo.getNoticeType()); + lqw.eq(StringUtils.isNotBlank(bo.getNoticeTarget()), HotNoticeRecord::getNoticeTarget, bo.getNoticeTarget()); + lqw.eq(StringUtils.isNotBlank(bo.getNoticeTargetType()), HotNoticeRecord::getNoticeTargetType, bo.getNoticeTargetType()); + lqw.eq(StringUtils.isNotBlank(bo.getNoticeMobile()), HotNoticeRecord::getNoticeMobile, bo.getNoticeMobile()); + lqw.eq(bo.getNoticeTime() != null, HotNoticeRecord::getNoticeTime, bo.getNoticeTime()); + lqw.eq(bo.getNoticeUserId() != null, HotNoticeRecord::getNoticeUserId, bo.getNoticeUserId()); + lqw.like(StringUtils.isNotBlank(bo.getNoticeUserName()), HotNoticeRecord::getNoticeUserName, bo.getNoticeUserName()); + lqw.eq(bo.getIsDeleted() != null, HotNoticeRecord::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增通知记录 + * + * @param bo 通知记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotNoticeRecordBo bo) { + HotNoticeRecord add = MapstructUtils.convert(bo, HotNoticeRecord.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改通知记录 + * + * @param bo 通知记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotNoticeRecordBo bo) { + HotNoticeRecord update = MapstructUtils.convert(bo, HotNoticeRecord.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotNoticeRecord entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除通知记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/controller/HotNoticeReplyController.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/controller/HotNoticeReplyController.java new file mode 100644 index 0000000..029bfde --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/controller/HotNoticeReplyController.java @@ -0,0 +1,141 @@ +package com.hotwj.platform.noticeManagerment.noticeReply.controller; + +import com.hotwj.platform.noticeManagerment.noticeReply.domain.bo.HotNoticeReplyBo; +import com.hotwj.platform.noticeManagerment.noticeReply.domain.vo.HotNoticeReplyVo; +import com.hotwj.platform.noticeManagerment.noticeReply.service.IHotNoticeReplyService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 通知整改回复记录 + * + * @author shihongwei + * @date 2026-02-05 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/noticeManagerment/noticeReply") +@Tag(name = "通知整改回复记录", description = "通知整改回复记录管理") +public class HotNoticeReplyController extends BaseController { + + private final IHotNoticeReplyService hotNoticeReplyService; + + /** + * 查询通知整改回复记录列表 + */ + //@SaCheckPermission("noticeManagerment:noticeReply:list") + @GetMapping("/list") + @Operation(summary = "分页查询通知整改回复记录列表") + public TableDataInfo list(HotNoticeReplyBo bo, PageQuery pageQuery) { + return hotNoticeReplyService.queryPageList(bo, pageQuery); + } + + /** + * 导出通知整改回复记录列表 + */ + //@SaCheckPermission("noticeManagerment:noticeReply:export") + @Log(title = "通知整改回复记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出通知整改回复记录列表") + public void export(HotNoticeReplyBo bo, HttpServletResponse response) { + List list = hotNoticeReplyService.queryList(bo); + ExcelUtil.exportExcel(list, "通知整改回复记录", HotNoticeReplyVo.class, response); + } + + /** + * 获取通知整改回复记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("noticeManagerment:noticeReply:query") + @GetMapping("/{id}") + @Operation(summary = "获取通知整改回复记录详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotNoticeReplyService.queryById(id)); + } + + /** + * 新增通知整改回复记录 + */ + //@SaCheckPermission("noticeManagerment:noticeReply:add") + @Log(title = "通知整改回复记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增通知整改回复记录") + public R add(@Validated(AddGroup.class) @RequestBody HotNoticeReplyBo bo) { + return toAjax(hotNoticeReplyService.insertByBo(bo)); + } + + /** + * 修改通知整改回复记录 + */ + //@SaCheckPermission("noticeManagerment:noticeReply:edit") + @Log(title = "通知整改回复记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改通知整改回复记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotNoticeReplyBo bo) { + return toAjax(hotNoticeReplyService.updateByBo(bo)); + } + + /** + * 企业提交整改回复 + */ + //@SaCheckPermission("noticeManagerment:noticeReply:submit") + @Log(title = "通知整改回复记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/submit") + @Operation(summary = "企业提交整改回复") + public R submit(@Validated(AddGroup.class) @RequestBody HotNoticeReplyBo bo) { + return toAjax(hotNoticeReplyService.submitReply(bo)); + } + + /** + * 政府审核整改回复 + */ + //@SaCheckPermission("noticeManagerment:noticeReply:audit") + @Log(title = "通知整改回复记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/audit") + @Operation(summary = "政府审核整改回复") + public R audit(@Validated(EditGroup.class) @RequestBody HotNoticeReplyBo bo) { + return toAjax(hotNoticeReplyService.auditReply(bo)); + } + + /** + * 删除通知整改回复记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("noticeManagerment:noticeReply:remove") + @Log(title = "通知整改回复记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除通知整改回复记录") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotNoticeReplyService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/domain/HotNoticeReply.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/domain/HotNoticeReply.java new file mode 100644 index 0000000..432d1cf --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/domain/HotNoticeReply.java @@ -0,0 +1,82 @@ +package com.hotwj.platform.noticeManagerment.noticeReply.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 通知整改回复记录对象 hot_notice_reply + * + * @author shihongwei + * @date 2026-02-05 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_notice_reply") +public class HotNoticeReply extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 关联通知记录ID (hot_admin_notice主键) + */ + private Long relationId; + + /** + * 回复内容/整改说明 + */ + private String replyContent; + + /** + * 附件URL (逗号分隔) + */ + private String replyFiles; + + /** + * 回复时间 + */ + private Date replyTime; + + /** + * 审核意见/驳回原因 + */ + private String auditRemark; + + /** + * 审核时间 + */ + private Date auditTime; + + /** + * 审核人 + */ + private String auditBy; + + /** + * 审核结果 (1=通过, 2=驳回) + */ + @TableField(value = "status") + private Integer auditStatus; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/domain/bo/HotNoticeReplyBo.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/domain/bo/HotNoticeReplyBo.java new file mode 100644 index 0000000..22ae08e --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/domain/bo/HotNoticeReplyBo.java @@ -0,0 +1,73 @@ +package com.hotwj.platform.noticeManagerment.noticeReply.domain.bo; + +import com.hotwj.platform.noticeManagerment.noticeReply.domain.HotNoticeReply; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 通知整改回复记录业务对象 hot_notice_reply + * + * @author shihongwei + * @date 2026-02-05 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotNoticeReply.class, reverseConvertGenerate = false) +public class HotNoticeReplyBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 关联通知记录ID (hot_admin_notice主键) + */ + @NotNull(message = "关联通知记录ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long relationId; + + /** + * 回复内容/整改说明 + */ + private String replyContent; + + /** + * 附件URL (逗号分隔) + */ + private String replyFiles; + + /** + * 回复时间 + */ + private Date replyTime; + + /** + * 审核意见/驳回原因 + */ + private String auditRemark; + + /** + * 审核时间 + */ + private Date auditTime; + + /** + * 审核人 + */ + private String auditBy; + + /** + * 审核结果 (1=通过, 2=驳回) + */ + private Integer auditStatus; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/domain/vo/HotNoticeReplyVo.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/domain/vo/HotNoticeReplyVo.java new file mode 100644 index 0000000..4e4fc79 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/domain/vo/HotNoticeReplyVo.java @@ -0,0 +1,83 @@ +package com.hotwj.platform.noticeManagerment.noticeReply.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.noticeManagerment.noticeReply.domain.HotNoticeReply; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 通知整改回复记录视图对象 hot_notice_reply + * + * @author shihongwei + * @date 2026-02-05 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotNoticeReply.class) +public class HotNoticeReplyVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 关联通知记录ID (hot_admin_notice主键) + */ + @ExcelProperty(value = "关联通知记录ID (hot_admin_notice主键)") + private Long relationId; + + /** + * 回复内容/整改说明 + */ + @ExcelProperty(value = "回复内容/整改说明") + private String replyContent; + + /** + * 附件URL (逗号分隔) + */ + @ExcelProperty(value = "附件URL (逗号分隔)") + private String replyFiles; + + /** + * 回复时间 + */ + @ExcelProperty(value = "回复时间") + private Date replyTime; + + /** + * 审核意见/驳回原因 + */ + @ExcelProperty(value = "审核意见/驳回原因") + private String auditRemark; + + /** + * 审核时间 + */ + @ExcelProperty(value = "审核时间") + private Date auditTime; + + /** + * 审核人 + */ + @ExcelProperty(value = "审核人") + private String auditBy; + + /** + * 审核结果 (1=通过, 2=驳回) + */ + @ExcelProperty(value = "审核结果 (1=通过, 2=驳回)") + private Integer auditStatus; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/mapper/HotNoticeReplyMapper.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/mapper/HotNoticeReplyMapper.java new file mode 100644 index 0000000..3cc1f70 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/mapper/HotNoticeReplyMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.noticeManagerment.noticeReply.mapper; + +import com.hotwj.platform.noticeManagerment.noticeReply.domain.HotNoticeReply; +import com.hotwj.platform.noticeManagerment.noticeReply.domain.vo.HotNoticeReplyVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 通知整改回复记录Mapper接口 + * + * @author shihongwei + * @date 2026-02-05 + */ +@Mapper +public interface HotNoticeReplyMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/service/IHotNoticeReplyService.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/service/IHotNoticeReplyService.java new file mode 100644 index 0000000..894955e --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/service/IHotNoticeReplyService.java @@ -0,0 +1,84 @@ +package com.hotwj.platform.noticeManagerment.noticeReply.service; + +import com.hotwj.platform.noticeManagerment.noticeReply.domain.bo.HotNoticeReplyBo; +import com.hotwj.platform.noticeManagerment.noticeReply.domain.vo.HotNoticeReplyVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 通知整改回复记录Service接口 + * + * @author shihongwei + * @date 2026-02-05 + */ +public interface IHotNoticeReplyService { + + /** + * 查询通知整改回复记录 + * + * @param id 主键 + * @return 通知整改回复记录 + */ + HotNoticeReplyVo queryById(Long id); + + /** + * 分页查询通知整改回复记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 通知整改回复记录分页列表 + */ + TableDataInfo queryPageList(HotNoticeReplyBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的通知整改回复记录列表 + * + * @param bo 查询条件 + * @return 通知整改回复记录列表 + */ + List queryList(HotNoticeReplyBo bo); + + /** + * 新增通知整改回复记录 + * + * @param bo 通知整改回复记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotNoticeReplyBo bo); + + /** + * 修改通知整改回复记录 + * + * @param bo 通知整改回复记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotNoticeReplyBo bo); + + /** + * 校验并批量删除通知整改回复记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 企业提交整改回复 + * + * @param bo 整改回复信息 + * @return 是否提交成功 + */ + Boolean submitReply(HotNoticeReplyBo bo); + + /** + * 政府审核整改回复 + * + * @param bo 审核信息 + * @return 是否审核成功 + */ + Boolean auditReply(HotNoticeReplyBo bo); +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/service/impl/HotNoticeReplyServiceImpl.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/service/impl/HotNoticeReplyServiceImpl.java new file mode 100644 index 0000000..01b51c0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeReply/service/impl/HotNoticeReplyServiceImpl.java @@ -0,0 +1,380 @@ +package com.hotwj.platform.noticeManagerment.noticeReply.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.noticeManagerment.adminNotice.domain.HotAdminNotice; +import com.hotwj.platform.noticeManagerment.adminNotice.mapper.HotAdminNoticeMapper; +import com.hotwj.platform.noticeManagerment.noticeReply.domain.HotNoticeReply; +import com.hotwj.platform.noticeManagerment.noticeReply.domain.bo.HotNoticeReplyBo; +import com.hotwj.platform.noticeManagerment.noticeReply.domain.vo.HotNoticeReplyVo; +import com.hotwj.platform.noticeManagerment.noticeReply.mapper.HotNoticeReplyMapper; +import com.hotwj.platform.noticeManagerment.noticeReply.service.IHotNoticeReplyService; +import com.hotwj.platform.noticeManagerment.notificationIssue.domain.HotNotificationIssue; +import com.hotwj.platform.noticeManagerment.notificationIssue.mapper.HotNotificationIssueMapper; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.company.domain.SysCompany; +import com.hotwj.platform.resourceManagement.company.mapper.SysCompanyMapper; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.HotGovEnterpriseUnit; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.mapper.HotGovEnterpriseUnitMapper; +import com.hotwj.platform.resourceManagement.personnelConfig.mapper.HotPersonnelConfigMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 通知整改回复记录Service业务层处理 + * + * @author shihongwei + * @date 2026-02-05 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotNoticeReplyServiceImpl implements IHotNoticeReplyService { + + private final HotNoticeReplyMapper baseMapper; + private final HotAdminNoticeMapper hotAdminNoticeMapper; + private final HotNotificationIssueMapper hotNotificationIssueMapper; + private final HotGovEnterpriseUnitMapper hotGovEnterpriseUnitMapper; + private final HotPersonnelConfigMapper hotPersonnelConfigMapper; + private final SysCompanyMapper sysCompanyMapper; + private final HotCompanySafetyManagerMapper hotCompanySafetyManagerMapper; + private final IHotSystemNotificationService hotSystemNotificationService; + + /** + * 查询通知整改回复记录 + * + * @param id 主键 + * @return 通知整改回复记录 + */ + @Override + public HotNoticeReplyVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询通知整改回复记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 通知整改回复记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotNoticeReplyBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的通知整改回复记录列表 + * + * @param bo 查询条件 + * @return 通知整改回复记录列表 + */ + @Override + public List queryList(HotNoticeReplyBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotNoticeReplyBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotNoticeReply::getId); + lqw.eq(bo.getRelationId() != null, HotNoticeReply::getRelationId, bo.getRelationId()); + lqw.eq(StringUtils.isNotBlank(bo.getReplyContent()), HotNoticeReply::getReplyContent, bo.getReplyContent()); + lqw.eq(StringUtils.isNotBlank(bo.getReplyFiles()), HotNoticeReply::getReplyFiles, bo.getReplyFiles()); + lqw.eq(bo.getReplyTime() != null, HotNoticeReply::getReplyTime, bo.getReplyTime()); + lqw.eq(StringUtils.isNotBlank(bo.getAuditRemark()), HotNoticeReply::getAuditRemark, bo.getAuditRemark()); + lqw.eq(bo.getAuditTime() != null, HotNoticeReply::getAuditTime, bo.getAuditTime()); + lqw.eq(StringUtils.isNotBlank(bo.getAuditBy()), HotNoticeReply::getAuditBy, bo.getAuditBy()); + lqw.eq(bo.getAuditStatus() != null, HotNoticeReply::getAuditStatus, bo.getAuditStatus()); + return lqw; + } + + /** + * 新增通知整改回复记录 + * + * @param bo 通知整改回复记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotNoticeReplyBo bo) { + HotNoticeReply add = MapstructUtils.convert(bo, HotNoticeReply.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改通知整改回复记录 + * + * @param bo 通知整改回复记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotNoticeReplyBo bo) { + HotNoticeReply update = MapstructUtils.convert(bo, HotNoticeReply.class); + validEntityBeforeUpdate(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotNoticeReply entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 修改前的数据校验 + */ + private void validEntityBeforeUpdate(HotNoticeReply entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除通知整改回复记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + public Boolean submitReply(HotNoticeReplyBo bo) { + // 1. 校验通知是否存在 + HotAdminNotice notice = hotAdminNoticeMapper.selectById(bo.getRelationId()); + if (notice == null) { + throw new RuntimeException("关联的通知记录不存在"); + } + + // 2. 新增回复记录 + HotNoticeReply reply = MapstructUtils.convert(bo, HotNoticeReply.class); + reply.setReplyTime(new Date()); + reply.setAuditStatus(0); // 待审核 + boolean flag = baseMapper.insert(reply) > 0; + + if (flag) { + bo.setId(reply.getId()); + notice.setStatus(0); // 待审核 + notice.setIsReceived(1L); // 已收到 + if (notice.getReceiveTime() == null) { + notice.setReceiveTime(new Date()); + } + notice.setReplyAttachmentUrl(bo.getReplyFiles()); + hotAdminNoticeMapper.updateById(notice); + pushGovernmentReplyNotification(notice); + } + return flag; + } + + private void pushGovernmentReplyNotification(HotAdminNotice notice) { + HotNotificationIssue issue = hotNotificationIssueMapper.selectById(notice.getNoticeId()); + if (issue == null) { + return; + } + List receiverIds = listGovernmentReceiverIds(issue.getDistrictCode()); + if (receiverIds.isEmpty()) { + return; + } + HotSystemNotificationGroupBo notificationBo = new HotSystemNotificationGroupBo(); + notificationBo.setLevel("普通"); + notificationBo.setSourceType("通知下发"); + notificationBo.setSenderType("COMPANY"); + notificationBo.setReceiverType("企业回复"); + notificationBo.setReceiverIds(receiverIds); + notificationBo.setIsDeleted(0L); + + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser != null) { + if (StringUtils.isNotBlank(loginUser.getBusinessUserId())) { + try { + notificationBo.setSenderId(Long.valueOf(loginUser.getBusinessUserId())); + } catch (NumberFormatException ignored) { + } + } + notificationBo.setSenderName(loginUser.getUsername()); + } + + SysCompany company = notice.getCompanyId() == null ? null : sysCompanyMapper.selectById(notice.getCompanyId()); + String companyName = company == null ? "" : StringUtils.blankToDefault(company.getCompanyName(), ""); + if (StringUtils.isBlank(notificationBo.getSenderName())) { + notificationBo.setSenderName(companyName); + } + String noticeTitle = StringUtils.blankToDefault(notice.getFileName(), "通知"); + notificationBo.setContent("企业【" + StringUtils.blankToDefault(companyName, "未知企业") + "】已提交通知《" + noticeTitle + "》的整改回复"); + hotSystemNotificationService.insertByBo(notificationBo); + } + + private List listGovernmentReceiverIds(String districtCode) { + LinkedHashSet receiverIds = new LinkedHashSet<>(); + receiverIds.add("1"); + if (StringUtils.isBlank(districtCode)) { + return new ArrayList<>(receiverIds); + } + List govUnits = hotGovEnterpriseUnitMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotGovEnterpriseUnit::getUnitType, 1L) + .eq(HotGovEnterpriseUnit::getDistrictCode, districtCode) + ); + if (govUnits == null || govUnits.isEmpty()) { + return new ArrayList<>(receiverIds); + } + receiverIds.addAll(govUnits.stream() + .map(HotGovEnterpriseUnit::getId) + .filter(id -> id != null) + .map(String::valueOf) + .collect(Collectors.toList())); + return new ArrayList<>(receiverIds); + } + + @Override + public Boolean auditReply(HotNoticeReplyBo bo) { + // 1. 获取回复记录 + HotNoticeReply reply = baseMapper.selectById(bo.getId()); + if (reply == null) { + throw new RuntimeException("回复记录不存在"); + } + + // 2. 更新回复记录 + reply.setAuditStatus(bo.getAuditStatus()); + reply.setAuditRemark(bo.getAuditRemark()); + reply.setAuditTime(new Date()); + try { + reply.setAuditBy(LoginHelper.getLoginUser().getUsername()); + } catch (Exception e) { + reply.setAuditBy("system"); + } + boolean flag = baseMapper.updateById(reply) > 0; + + if (flag) { + HotAdminNotice notice = hotAdminNoticeMapper.selectById(reply.getRelationId()); + if (notice != null) { + notice.setStatus(bo.getAuditStatus()); + hotAdminNoticeMapper.updateById(notice); + pushCompanyAuditResultNotification(notice, bo.getAuditStatus(), bo.getAuditRemark()); + } + } + return flag; + } + + private void pushCompanyAuditResultNotification(HotAdminNotice notice, Integer auditStatus, String auditRemark) { + String receiverId = findCompanyHeadReceiverId(notice.getCompanyId()); + if (StringUtils.isBlank(receiverId)) { + return; + } + HotSystemNotificationGroupBo notificationBo = new HotSystemNotificationGroupBo(); + notificationBo.setLevel("普通"); + notificationBo.setSourceType("行管通知审核"); + notificationBo.setReceiverType("管理员"); + notificationBo.setReceiverId(receiverId); + notificationBo.setIsDeleted(0L); + + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser != null) { + String loginPort = loginUser.getLoginPort(); + if (StringUtils.equals(loginPort, ISysUserLoginPortService.GOVERNMENT_PORT)) { + notificationBo.setSenderType("GOVERNMENT"); + } else { + notificationBo.setSenderType("SYSTEM"); + } + notificationBo.setSenderName(loginUser.getUsername()); + if (StringUtils.isNotBlank(loginUser.getBusinessUserId())) { + try { + notificationBo.setSenderId(Long.valueOf(loginUser.getBusinessUserId())); + } catch (NumberFormatException ignored) { + } + } + } else { + notificationBo.setSenderType("SYSTEM"); + notificationBo.setSenderName("系统"); + } + + String noticeTitle = StringUtils.blankToDefault(notice.getFileName(), "通知"); + if (auditStatus != null && auditStatus == 1) { + notificationBo.setContent("您提交的行管通知《" + noticeTitle + "》整改回复已审核通过。"); + } else if (auditStatus != null && auditStatus == 2) { + String reason = StringUtils.isBlank(auditRemark) ? "无" : auditRemark; + notificationBo.setContent("您提交的行管通知《" + noticeTitle + "》整改回复已驳回,原因:" + reason); + } else { + notificationBo.setContent("您提交的行管通知《" + noticeTitle + "》整改回复审核状态已更新。"); + } + hotSystemNotificationService.insertByBo(notificationBo); + + HotSystemNotificationGroupBo notificationBo2 = new HotSystemNotificationGroupBo(); + notificationBo2.setLevel("普通"); + notificationBo2.setSourceType("行管通知审核"); + notificationBo2.setReceiverType("政府审核通过"); + + notificationBo2.setReceiverIds(List.of("1")); + notificationBo2.setIsDeleted(0L); + + notificationBo2.setSenderType("GOVERNMENT"); + + if (auditStatus != null && auditStatus == 1) { + notificationBo2.setContent("行管通知《" + noticeTitle + "》整改回复已审核通过。"); + } else if (auditStatus != null && auditStatus == 2) { + String reason = StringUtils.isBlank(auditRemark) ? "无" : auditRemark; + notificationBo2.setContent("行管通知《" + noticeTitle + "》整改回复已驳回,原因:" + reason); + } else { + notificationBo2.setContent("行管通知《" + noticeTitle + "》整改回复审核状态已更新。"); + } + hotSystemNotificationService.insertByBo(notificationBo2); + } + + private String findCompanyHeadReceiverId(Long companyId) { + if (companyId == null) { + return null; + } + List heads = hotCompanySafetyManagerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getCompanyHead, 1L) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + .orderByDesc(HotCompanySafetyManager::getCreateTime) + ); + if (heads != null && !heads.isEmpty()) { + return String.valueOf(heads.get(0).getId()); + } + List managers = hotCompanySafetyManagerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + .orderByDesc(HotCompanySafetyManager::getCreateTime) + ); + if (managers == null || managers.isEmpty()) { + return null; + } + return String.valueOf(managers.get(0).getId()); + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/controller/HotNoticeSignDocumentController.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/controller/HotNoticeSignDocumentController.java new file mode 100644 index 0000000..84f3819 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/controller/HotNoticeSignDocumentController.java @@ -0,0 +1,118 @@ +package com.hotwj.platform.noticeManagerment.noticeSignDocument.controller; + +import com.hotwj.platform.noticeManagerment.noticeSignDocument.domain.bo.HotNoticeSignDocumentBo; +import com.hotwj.platform.noticeManagerment.noticeSignDocument.domain.vo.HotNoticeSignDocumentVo; +import com.hotwj.platform.noticeManagerment.noticeSignDocument.service.IHotNoticeSignDocumentService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 签订文件 + * + * @author shihongwei + * @date 2026-01-07 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/noticeManagerment/noticeSignDocument") +@Tag(name = "签订文件", description = "签订文件管理") +public class HotNoticeSignDocumentController extends BaseController { + + private final IHotNoticeSignDocumentService hotNoticeSignDocumentService; + + /** + * 查询签订文件列表 + */ + //@SaCheckPermission("noticeManagerment:noticeSignDocument:list") + @GetMapping("/list") + @Operation(summary = "分页查询签订文件列表") + public TableDataInfo list(HotNoticeSignDocumentBo bo, PageQuery pageQuery) { + return hotNoticeSignDocumentService.queryPageList(bo, pageQuery); + } + + /** + * 导出签订文件列表 + */ + //@SaCheckPermission("noticeManagerment:noticeSignDocument:export") + @Log(title = "签订文件", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出签订文件列表") + public void export(HotNoticeSignDocumentBo bo, HttpServletResponse response) { + List list = hotNoticeSignDocumentService.queryList(bo); + ExcelUtil.exportExcel(list, "签订文件", HotNoticeSignDocumentVo.class, response); + } + + /** + * 获取签订文件详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("noticeManagerment:noticeSignDocument:query") + @GetMapping("/{id}") + @Operation(summary = "获取签订文件详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotNoticeSignDocumentService.queryById(id)); + } + + /** + * 新增签订文件 + */ + //@SaCheckPermission("noticeManagerment:noticeSignDocument:add") + @Log(title = "签订文件", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增签订文件") + public R add(@Validated(AddGroup.class) @RequestBody HotNoticeSignDocumentBo bo) { + Long id = hotNoticeSignDocumentService.insertByBo(bo); + return R.ok(id); + } + + /** + * 修改签订文件 + */ + //@SaCheckPermission("noticeManagerment:noticeSignDocument:edit") + @Log(title = "签订文件", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改签订文件") + public R edit(@Validated(EditGroup.class) @RequestBody HotNoticeSignDocumentBo bo) { + return toAjax(hotNoticeSignDocumentService.updateByBo(bo)); + } + + /** + * 删除签订文件 + * + * @param ids 主键串 + */ + //@SaCheckPermission("noticeManagerment:noticeSignDocument:remove") + @Log(title = "签订文件", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除签订文件") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotNoticeSignDocumentService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/domain/HotNoticeSignDocument.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/domain/HotNoticeSignDocument.java new file mode 100644 index 0000000..7c1b03f --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/domain/HotNoticeSignDocument.java @@ -0,0 +1,133 @@ +package com.hotwj.platform.noticeManagerment.noticeSignDocument.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 签订文件对象 hot_notice_sign_document + * + * @author shihongwei + * @date 2026-01-07 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_notice_sign_document") +public class HotNoticeSignDocument extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 文件名称 + */ + private String fileName; + + /** + * 下发人员类型 + */ + private Long issuerType; + + /** + * 文件类型 + */ + private String fileType; + + /** + * 创建类型(1=企业 2=下发) + */ + private Long createType; + + /** + * 已下发企业ID集合 + */ + private String issueCompanyIds; + + /** + * 省编码 + */ + private String provinceCode; + + /** + * 市编码 + */ + private String cityCode; + + /** + * 区/县编码 + */ + private String districtCode; + + /** + * 是否需要填写 0=否 1=是 + */ + private Long needFill; + + /** + * 是否启用电子公章 0=否 1=是 + */ + private Long enableESeal; + + /** + * 是否默认新进人员 0=否 1=是 + */ + private Long isDefaultNewStaff; + + /** + * 有效期类型 0=短期 1=定期 2=长期 + */ + private Long validType; + + /** + * 有效期 + */ + private String validPeriod; + + /** + * 下发人员ID列表(逗号分隔) + */ + private String issuePersonIds; + + /** + * 模板ID + */ + private Long templateId; + + /** + * 文件内容(富文本) + */ + private String content; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 模板名称快照 + */ + private String templateNameSnapshot; + + /** + * 源公司id + */ + private Long sourceCompanyId; + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/domain/bo/HotNoticeSignDocumentBo.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/domain/bo/HotNoticeSignDocumentBo.java new file mode 100644 index 0000000..9016ca1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/domain/bo/HotNoticeSignDocumentBo.java @@ -0,0 +1,138 @@ +package com.hotwj.platform.noticeManagerment.noticeSignDocument.domain.bo; + +import com.hotwj.platform.noticeManagerment.noticeSignDocument.domain.HotNoticeSignDocument; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 签订文件业务对象 hot_notice_sign_document + * + * @author shihongwei + * @date 2026-01-07 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotNoticeSignDocument.class, reverseConvertGenerate = false) +public class HotNoticeSignDocumentBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 文件名称 + */ + @NotBlank(message = "文件名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String fileName; + + /** + * 下发人员类型 + */ + @NotNull(message = "下发人员类型不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long issuerType; + + /** + * 文件类型 + */ + @NotNull(message = "文件类型不能为空", groups = {AddGroup.class, EditGroup.class}) + private String fileType; + + /** + * 创建类型(1=企业 2=下发) + */ + @NotNull(message = "创建类型(1=企业 2=下发)不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long createType; + + /** + * 已下发企业ID集合 + */ + private String issueCompanyIds; + + /** + * 省编码 + */ + private String provinceCode; + + /** + * 市编码 + */ + private String cityCode; + + /** + * 区/县编码 + */ + private String districtCode; + + /** + * 是否需要填写 0=否 1=是 + */ +// @NotNull(message = "是否需要填写 0=否 1=是不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long needFill; + + /** + * 是否启用电子公章 0=否 1=是 + */ +// @NotNull(message = "是否启用电子公章 0=否 1=是不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long enableESeal; + + /** + * 是否默认新进人员 0=否 1=是 + */ +// @NotNull(message = "是否默认新进人员 0=否 1=是不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDefaultNewStaff; + + /** + * 有效期类型 0=短期 1=定期 2=长期 + */ + @NotNull(message = "有效期类型 0=短期 1=定期 2=长期 不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long validType; + + /** + * 有效期 + */ + private String validPeriod; + + /** + * 下发人员ID列表(逗号分隔) + */ + private String issuePersonIds; + + /** + * 模板ID + */ + private Long templateId; + + /** + * 文件内容(富文本) + */ + private String content; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + /** + * 模板名称快照 + */ + private String templateNameSnapshot; + + /** + * 源公司id + */ + private Long sourceCompanyId; +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/domain/vo/HotNoticeSignDocumentVo.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/domain/vo/HotNoticeSignDocumentVo.java new file mode 100644 index 0000000..472a12d --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/domain/vo/HotNoticeSignDocumentVo.java @@ -0,0 +1,156 @@ +package com.hotwj.platform.noticeManagerment.noticeSignDocument.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.noticeManagerment.noticeSignDocument.domain.HotNoticeSignDocument; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 签订文件视图对象 hot_notice_sign_document + * + * @author shihongwei + * @date 2026-01-07 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotNoticeSignDocument.class) +public class HotNoticeSignDocumentVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 文件名称 + */ + @ExcelProperty(value = "文件名称") + private String fileName; + + /** + * 下发人员类型 + */ + @ExcelProperty(value = "下发人员类型") + private Long issuerType; + + /** + * 文件类型 + */ + @ExcelProperty(value = "文件类型") + private String fileType; + + /** + * 创建类型(1=企业 2=下发) + */ + @ExcelProperty(value = "创建类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1==企业,2==下发") + private Long createType; + + /** + * 已下发企业ID集合 + */ + @ExcelProperty(value = "已下发企业ID集合") + private String issueCompanyIds; + + /** + * 省编码 + */ + @ExcelProperty(value = "省编码") + private String provinceCode; + + /** + * 市编码 + */ + @ExcelProperty(value = "市编码") + private String cityCode; + + /** + * 区/县编码 + */ + @ExcelProperty(value = "区/县编码") + private String districtCode; + + /** + * 是否需要填写 0=否 1=是 + */ + @ExcelProperty(value = "是否需要填写 0=否 1=是") + private Long needFill; + + /** + * 是否启用电子公章 0=否 1=是 + */ + @ExcelProperty(value = "是否启用电子公章 0=否 1=是") + private Long enableESeal; + + /** + * 是否默认新进人员 0=否 1=是 + */ + @ExcelProperty(value = "是否默认新进人员 0=否 1=是") + private Long isDefaultNewStaff; + + /** + * 有效期类型 0=短期 1=定期 2=长期 + */ + @ExcelProperty(value = "有效期类型 0=短期 1=定期 2=长期 ") + private Long validType; + + /** + * 有效期 + */ + @ExcelProperty(value = "有效期") + private String validPeriod; + + /** + * 下发人员ID列表(逗号分隔) + */ + @ExcelProperty(value = "下发人员ID列表", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逗=号分隔") + private String issuePersonIds; + + /** + * 模板ID + */ + @ExcelProperty(value = "模板ID") + private Long templateId; + + /** + * 文件内容(富文本) + */ + @ExcelProperty(value = "文件内容", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "富=文本") + private String content; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 模板名称快照 + */ + private String templateNameSnapshot; + + /** + * 源公司id + */ + private Long sourceCompanyId; + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/mapper/HotNoticeSignDocumentMapper.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/mapper/HotNoticeSignDocumentMapper.java new file mode 100644 index 0000000..42eba1c --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/mapper/HotNoticeSignDocumentMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.noticeManagerment.noticeSignDocument.mapper; + +import com.hotwj.platform.noticeManagerment.noticeSignDocument.domain.HotNoticeSignDocument; +import com.hotwj.platform.noticeManagerment.noticeSignDocument.domain.vo.HotNoticeSignDocumentVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 签订文件Mapper接口 + * + * @author shihongwei + * @date 2026-01-07 + */ +@Mapper +public interface HotNoticeSignDocumentMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/service/IHotNoticeSignDocumentService.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/service/IHotNoticeSignDocumentService.java new file mode 100644 index 0000000..33b0818 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/service/IHotNoticeSignDocumentService.java @@ -0,0 +1,84 @@ +package com.hotwj.platform.noticeManagerment.noticeSignDocument.service; + +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.noticeManagerment.noticeSignDocument.domain.bo.HotNoticeSignDocumentBo; +import com.hotwj.platform.noticeManagerment.noticeSignDocument.domain.vo.HotNoticeSignDocumentVo; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 签订文件Service接口 + * + * @author shihongwei + * @date 2026-01-07 + */ +public interface IHotNoticeSignDocumentService { + + /** + * 查询签订文件 + * + * @param id 主键 + * @return 签订文件 + */ + HotNoticeSignDocumentVo queryById(Long id); + + /** + * 分页查询签订文件列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 签订文件分页列表 + */ + TableDataInfo queryPageList(HotNoticeSignDocumentBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的签订文件列表 + * + * @param bo 查询条件 + * @return 签订文件列表 + */ + List queryList(HotNoticeSignDocumentBo bo); + + /** + * 新增签订文件 + * + * @param bo 签订文件 + * @return 新增的id + */ + Long insertByBo(HotNoticeSignDocumentBo bo); + + /** + * 修改签订文件 + * + * @param bo 签订文件 + * @return 是否修改成功 + */ + Boolean updateByBo(HotNoticeSignDocumentBo bo); + + /** + * 新增驾驶员后,自动下发默认签订文件 + * + * @param driver 驾驶员信息 + */ + void issueDefaultForNewDriver(HotDriver driver); + + /** + * 新增管理人员后,自动下发默认签订文件 + * + * @param manager 管理人员信息 + */ + void issueDefaultForNewManager(HotCompanySafetyManager manager); + + /** + * 校验并批量删除签订文件信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/service/impl/HotNoticeSignDocumentServiceImpl.java b/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/service/impl/HotNoticeSignDocumentServiceImpl.java new file mode 100644 index 0000000..3b7405a --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/noticeSignDocument/service/impl/HotNoticeSignDocumentServiceImpl.java @@ -0,0 +1,675 @@ +package com.hotwj.platform.noticeManagerment.noticeSignDocument.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.dispatchTemplate.domain.HotDispatchTemplate; +import com.hotwj.platform.config.dispatchTemplate.mapper.HotDispatchTemplateMapper; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.flow.service.ISysFlowService; +import com.hotwj.platform.noticeManagerment.noticeSignDocument.domain.HotNoticeSignDocument; +import com.hotwj.platform.noticeManagerment.noticeSignDocument.domain.bo.HotNoticeSignDocumentBo; +import com.hotwj.platform.noticeManagerment.noticeSignDocument.domain.vo.HotNoticeSignDocumentVo; +import com.hotwj.platform.noticeManagerment.noticeSignDocument.mapper.HotNoticeSignDocumentMapper; +import com.hotwj.platform.noticeManagerment.noticeSignDocument.service.IHotNoticeSignDocumentService; +import com.hotwj.platform.noticeManagerment.signFileDetail.domain.HotSignFileDetail; +import com.hotwj.platform.noticeManagerment.signFileDetail.mapper.HotSignFileDetailMapper; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.*; + +/** + * 签订文件Service业务层处理 + * + * @author shihongwei + * @date 2026-01-07 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotNoticeSignDocumentServiceImpl implements IHotNoticeSignDocumentService { + + private static final long STAFF_TYPE_DRIVER = 1L; + private static final long STAFF_TYPE_MANAGER = 2L; + private static final long STAFF_TYPE_DRIVER_PRACTITIONER = 3L; + private static final long STAFF_TYPE_ALL = 4L; + private static final long DEFAULT_NEW_STAFF_ENABLED = 1L; + private static final long ISSUER_TYPE_DRIVER = 1L; + private static final long ISSUER_TYPE_MANAGER = 2L; + private static final long ISSUER_TYPE_DRIVER_PRACTITIONER = 3L; + private static final long ISSUER_TYPE_ALL = 4L; + private static final String PERSON_TYPE_DRIVER = "driver"; + private static final String PERSON_TYPE_ESCORT = "escort"; + private static final String HOT_PERSON_TYPE_DRIVER = "1"; + private static final String HOT_PERSON_TYPE_ESCORT = "2"; + private static final DateTimeFormatter VALID_PERIOD_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + private final HotNoticeSignDocumentMapper baseMapper; + private final HotSignFileDetailMapper signFileDetailMapper; + private final HotCompanySafetyManagerMapper safetyManagerMapper; + private final HotDriverMapper driverMapper; + private final HotDispatchTemplateMapper dispatchTemplateMapper; + private final ISysFlowService flowService; + + /** + * 查询签订文件 + * + * @param id 主键 + * @return 签订文件 + */ + @Override + public HotNoticeSignDocumentVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询签订文件列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 签订文件分页列表 + */ + @Override + public TableDataInfo queryPageList(HotNoticeSignDocumentBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的签订文件列表 + * + * @param bo 查询条件 + * @return 签订文件列表 + */ + @Override + public List queryList(HotNoticeSignDocumentBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotNoticeSignDocumentBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotNoticeSignDocument::getId); + lqw.eq(bo.getCompanyId() != null, HotNoticeSignDocument::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getFileName()), HotNoticeSignDocument::getFileName, bo.getFileName()); + lqw.eq(bo.getIssuerType() != null, HotNoticeSignDocument::getIssuerType, bo.getIssuerType()); + lqw.eq(bo.getFileType() != null, HotNoticeSignDocument::getFileType, bo.getFileType()); + lqw.eq(bo.getNeedFill() != null, HotNoticeSignDocument::getNeedFill, bo.getNeedFill()); + lqw.eq(bo.getEnableESeal() != null, HotNoticeSignDocument::getEnableESeal, bo.getEnableESeal()); + lqw.eq(bo.getIsDefaultNewStaff() != null, HotNoticeSignDocument::getIsDefaultNewStaff, bo.getIsDefaultNewStaff()); + lqw.eq(bo.getValidType() != null, HotNoticeSignDocument::getValidType, bo.getValidType()); + lqw.eq(StringUtils.isNotBlank(bo.getValidPeriod()), HotNoticeSignDocument::getValidPeriod, bo.getValidPeriod()); + lqw.eq(StringUtils.isNotBlank(bo.getIssuePersonIds()), HotNoticeSignDocument::getIssuePersonIds, bo.getIssuePersonIds()); + lqw.eq(bo.getTemplateId() != null, HotNoticeSignDocument::getTemplateId, bo.getTemplateId()); + lqw.eq(StringUtils.isNotBlank(bo.getContent()), HotNoticeSignDocument::getContent, bo.getContent()); + lqw.eq(bo.getIsDeleted() != null, HotNoticeSignDocument::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增签订文件 + * + * @param bo 签订文件 + * @return 是否新增成功 + */ + @Override + public Long insertByBo(HotNoticeSignDocumentBo bo) { + HotNoticeSignDocument add = MapstructUtils.convert(bo, HotNoticeSignDocument.class); + add.setSourceCompanyId(resolveSourceCompanyId()); + add.setTemplateNameSnapshot(resolveTemplateNameSnapshot(bo.getTemplateId())); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return add.getId(); + } + + /** + * 修改签订文件 + * + * @param bo 签订文件 + * @return 是否修改成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(HotNoticeSignDocumentBo bo) { + HotNoticeSignDocument old = baseMapper.selectById(bo.getId()); + if (old == null) { + throw new ServiceException("签订文件不存在"); + } + + Set oldPersonIds = parsePersonIds(old.getIssuePersonIds()); + Set finalPersonIds = normalizePersonIds(oldPersonIds, parsePersonIds(bo.getIssuePersonIds())); + validateRemovedPersons(old, oldPersonIds, finalPersonIds); + + HotNoticeSignDocument update = MapstructUtils.convert(bo, HotNoticeSignDocument.class); + update.setIssuePersonIds(String.join(",", finalPersonIds)); + update.setSourceCompanyId(resolveSourceCompanyId()); + if (Objects.equals(old.getTemplateId(), bo.getTemplateId())) { + update.setTemplateNameSnapshot(old.getTemplateNameSnapshot()); + } else { + update.setTemplateNameSnapshot(resolveTemplateNameSnapshot(bo.getTemplateId())); + } + validEntityBeforeSave(update); + boolean ok = baseMapper.updateById(update) > 0; + if (ok) { + issueNewDetailsAndTodos(old, update, oldPersonIds, finalPersonIds); + } + return ok; + } + + @Override + public void issueDefaultForNewDriver(HotDriver driver) { + if (driver == null || StringUtils.isBlank(driver.getId()) || driver.getCompanyId() == null) { + return; + } + if (driver.getIsDeleted() != null && Long.valueOf(1L).equals(driver.getIsDeleted())) { + return; + } + if (driver.getStatus() != null && !Long.valueOf(1L).equals(driver.getStatus())) { + return; + } + String staffId = driver.getId(); + String staffName = driver.getName(); + long staffType = mapDriverStaffType(driver.getPersonType()); + List issuerTypes = buildIssuerTypesForDriver(driver.getPersonType()); + issueDefaultDocumentsToNewStaff(driver.getCompanyId(), issuerTypes, staffId, staffName, staffType); + } + + @Override + public void issueDefaultForNewManager(HotCompanySafetyManager manager) { + if (manager == null || manager.getId() == null || manager.getCompanyId() == null) { + return; + } + if (manager.getIsDeleted() != null && Long.valueOf(1L).equals(manager.getIsDeleted())) { + return; + } + if (manager.getIsResigned() != null && Long.valueOf(1L).equals(manager.getIsResigned())) { + return; + } + if (manager.getStatus() != null && !Long.valueOf(1L).equals(manager.getStatus())) { + return; + } + String staffId = String.valueOf(manager.getId()); + issueDefaultDocumentsToNewStaff( + manager.getCompanyId(), + Arrays.asList(ISSUER_TYPE_MANAGER, ISSUER_TYPE_ALL), + staffId, + manager.getName(), + STAFF_TYPE_MANAGER + ); + } + + private List buildIssuerTypesForDriver(String personType) { + if (isDriverOrEscortPersonType(personType)) { + return Arrays.asList(ISSUER_TYPE_DRIVER, ISSUER_TYPE_DRIVER_PRACTITIONER, ISSUER_TYPE_ALL); + } + return Arrays.asList(ISSUER_TYPE_DRIVER_PRACTITIONER, ISSUER_TYPE_ALL); + } + + private boolean isDriverOrEscortPersonType(String personType) { + if (StringUtils.isBlank(personType)) { + return false; + } + String value = personType.trim().toLowerCase(Locale.ROOT); + return HOT_PERSON_TYPE_DRIVER.equals(value) + || HOT_PERSON_TYPE_ESCORT.equals(value) + || PERSON_TYPE_DRIVER.equals(value) + || PERSON_TYPE_ESCORT.equals(value); + } + + private void issueDefaultDocumentsToNewStaff(Long companyId, List issuerTypes, String staffId, String staffName, Long staffType) { + if (companyId == null || StringUtils.isBlank(staffId) || staffType == null || issuerTypes == null || issuerTypes.isEmpty()) { + return; + } + List defaultDocs = baseMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotNoticeSignDocument::getCompanyId, companyId) + .eq(HotNoticeSignDocument::getIsDeleted, 0L) + .eq(HotNoticeSignDocument::getIsDefaultNewStaff, DEFAULT_NEW_STAFF_ENABLED) + .in(HotNoticeSignDocument::getIssuerType, issuerTypes) + ); + if (defaultDocs == null || defaultDocs.isEmpty()) { + return; + } + for (HotNoticeSignDocument doc : defaultDocs) { + if (doc == null || doc.getId() == null) { + continue; + } + if (!isWithinValidPeriod(doc)) { + continue; + } + issueDocumentToStaff(doc, staffId, staffName, staffType); + } + } + + private boolean isWithinValidPeriod(HotNoticeSignDocument doc) { + if (doc == null || StringUtils.isBlank(doc.getValidPeriod())) { + return false; + } + try { + LocalDate expireDate = LocalDate.parse(doc.getValidPeriod().trim(), VALID_PERIOD_FORMATTER); + return !expireDate.isBefore(LocalDate.now()); + } catch (DateTimeParseException e) { + log.warn("签订文件有效期格式非法,跳过自动下发。docId={}, validPeriod={}", doc.getId(), doc.getValidPeriod()); + return false; + } + } + + private void issueDocumentToStaff(HotNoticeSignDocument doc, String staffId, String staffName, Long staffType) { + HotSignFileDetail existing = signFileDetailMapper.selectOne( + Wrappers.lambdaQuery() + .eq(HotSignFileDetail::getIssueFileId, doc.getId()) + .eq(HotSignFileDetail::getStaffId, staffId) + .eq(HotSignFileDetail::getIsDeleted, 0L) + .last("limit 1") + ); + appendIssuePersonId(doc, staffId); + String fileName = StringUtils.isNotBlank(doc.getFileName()) ? doc.getFileName() : "未知文件"; + String title = "待签署文件:" + fileName; + if (existing != null) { + ensureTodoForUnsignedDetail(existing, title); + return; + } + HotSignFileDetail detail = new HotSignFileDetail(); + detail.setCompanyId(doc.getCompanyId()); + detail.setIssueFileId(doc.getId()); + detail.setStaffId(staffId); + detail.setStaffName(staffName); + detail.setStaffType(staffType); + detail.setSignStatus(0L); + detail.setIsArchived(0L); + detail.setIsDeleted(0L); + signFileDetailMapper.insert(detail); + if (detail.getId() != null) { + flowService.pushDirectTodo("SIGN_FILE", String.valueOf(detail.getId()), title, detail.getCompanyId(), staffId); + } + } + + private void ensureTodoForUnsignedDetail(HotSignFileDetail detail, String title) { + if (detail == null || detail.getId() == null || StringUtils.isBlank(detail.getStaffId())) { + return; + } + if (detail.getSignStatus() != null && detail.getSignStatus() == 1L) { + return; + } + String taskId = flowService.getTaskIdByBusinessId("SIGN_FILE", String.valueOf(detail.getId()), detail.getStaffId()); + if (StringUtils.isBlank(taskId)) { + flowService.pushDirectTodo("SIGN_FILE", String.valueOf(detail.getId()), title, detail.getCompanyId(), detail.getStaffId()); + } + } + + private void appendIssuePersonId(HotNoticeSignDocument doc, String staffId) { + if (doc == null || doc.getId() == null || StringUtils.isBlank(staffId)) { + return; + } + Set personIds = parsePersonIds(doc.getIssuePersonIds()); + if (!personIds.add(staffId)) { + return; + } + HotNoticeSignDocument update = new HotNoticeSignDocument(); + update.setId(doc.getId()); + update.setIssuePersonIds(String.join(",", personIds)); + baseMapper.updateById(update); + doc.setIssuePersonIds(update.getIssuePersonIds()); + } + + private String resolveTemplateNameSnapshot(Long templateId) { + if (templateId == null) { + return null; + } + HotDispatchTemplate template = dispatchTemplateMapper.selectById(templateId); + if (template == null) { + throw new ServiceException("下发模板不存在"); + } + return template.getTemplateName(); + } + + private Long resolveSourceCompanyId() { + if (LoginHelper.isSuperAdmin()) { + return 1L; + } + Long companyId = LoginHelper.getCompanyId(); + if (companyId == null) { + throw new ServiceException("当前登录用户无公司信息"); + } + return companyId; + } + + private Set parsePersonIds(String ids) { + Set set = new LinkedHashSet<>(); + if (StringUtils.isBlank(ids)) { + return set; + } + String[] parts = ids.split(","); + for (String part : parts) { + if (part == null) { + continue; + } + String v = part.trim(); + if (StringUtils.isNotBlank(v)) { + set.add(v); + } + } + return set; + } + + private Set findInvalidPersonIds(Set personIds) { + if (personIds == null || personIds.isEmpty()) { + return Collections.emptySet(); + } + Set valid = findValidPersonIds(personIds); + Set invalid = new LinkedHashSet<>(personIds); + invalid.removeAll(valid); + return invalid; + } + + private Set findValidPersonIds(Set personIds) { + Set rawIds = personIds.stream() + .filter(StringUtils::isNotBlank) + .map(String::trim) + .distinct() + .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); + if (rawIds.isEmpty()) { + return Collections.emptySet(); + } + + List managerIds = rawIds.stream() + .filter(v -> v.matches("\\d+")) + .map(Long::valueOf) + .distinct() + .toList(); + + Set valid = new HashSet<>(rawIds.size()); + + if (!managerIds.isEmpty()) { + List managers = safetyManagerMapper.selectList( + Wrappers.lambdaQuery() + .in(HotCompanySafetyManager::getId, managerIds) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers != null) { + for (HotCompanySafetyManager m : managers) { + if (m != null && m.getId() != null) { + valid.add(String.valueOf(m.getId())); + } + } + } + } + + List drivers = driverMapper.selectList( + Wrappers.lambdaQuery() + .in(HotDriver::getId, rawIds) + .eq(HotDriver::getIsDeleted, 0L) + .eq(HotDriver::getStatus, 1L) + .eq(HotDriver::getAuditStatus, 1) + ); + if (drivers != null) { + for (HotDriver d : drivers) { + if (d != null && d.getId() != null) { + valid.add(String.valueOf(d.getId())); + } + } + } + + return valid; + } + + private Set normalizePersonIds(Set oldPersonIds, Set requestedPersonIds) { + Set invalidOldPersonIds = findInvalidPersonIds(oldPersonIds); + if (invalidOldPersonIds.isEmpty()) { + return requestedPersonIds; + } + Set merged = new LinkedHashSet<>(requestedPersonIds); + merged.addAll(invalidOldPersonIds); + return merged; + } + + private void validateRemovedPersons(HotNoticeSignDocument old, Set oldPersonIds, Set finalPersonIds) { + if (old == null || oldPersonIds == null || finalPersonIds == null || oldPersonIds.equals(finalPersonIds)) { + return; + } + Set removedIds = new HashSet<>(oldPersonIds); + removedIds.removeAll(finalPersonIds); + if (removedIds.isEmpty()) { + return; + } + List existingDetails = signFileDetailMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotSignFileDetail::getIssueFileId, old.getId()) + .eq(HotSignFileDetail::getIsDeleted, 0) + .in(HotSignFileDetail::getStaffId, removedIds) + ); + if (existingDetails == null || existingDetails.isEmpty()) { + return; + } + Set blockedNames = new LinkedHashSet<>(); + for (HotSignFileDetail detail : existingDetails) { + if (detail == null) { + continue; + } + String name = StringUtils.isNotBlank(detail.getStaffName()) ? detail.getStaffName() : "未知人员"; + blockedNames.add(name); + } + if (!blockedNames.isEmpty()) { + throw new ServiceException("已下发人员不能被移出: " + String.join(",", blockedNames)); + } + } + + private void issueNewDetailsAndTodos(HotNoticeSignDocument old, HotNoticeSignDocument update, Set oldPersonIds, Set finalPersonIds) { + if (old == null || update == null || update.getId() == null || update.getCompanyId() == null) { + return; + } + Set validFinalIds = findValidPersonIds(finalPersonIds); + if (validFinalIds.isEmpty()) { + return; + } + Set addedValidIds = new LinkedHashSet<>(validFinalIds); + if (oldPersonIds != null) { + addedValidIds.removeAll(oldPersonIds); + } + if (addedValidIds.isEmpty()) { + return; + } + + Map idToName = resolvePersonNames(addedValidIds); + Map idToStaffType = resolvePersonTypes(addedValidIds); + List existing = signFileDetailMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotSignFileDetail::getIssueFileId, update.getId()) + .eq(HotSignFileDetail::getIsDeleted, 0) + .in(HotSignFileDetail::getStaffId, addedValidIds) + ); + Set existingStaffIds = new HashSet<>(); + if (existing != null) { + for (HotSignFileDetail d : existing) { + if (d != null && StringUtils.isNotBlank(d.getStaffId())) { + existingStaffIds.add(d.getStaffId()); + } + } + } + + String fileName = StringUtils.isNotBlank(update.getFileName()) ? update.getFileName() : old.getFileName(); + String title = "待签署文件:" + (StringUtils.isNotBlank(fileName) ? fileName : "未知文件"); + + if (existing != null) { + for (HotSignFileDetail d : existing) { + if (d == null || d.getId() == null || StringUtils.isBlank(d.getStaffId())) { + continue; + } + if (d.getSignStatus() != null && d.getSignStatus() == 1L) { + continue; + } + String taskId = flowService.getTaskIdByBusinessId("SIGN_FILE", String.valueOf(d.getId()), d.getStaffId()); + if (StringUtils.isBlank(taskId)) { + flowService.pushDirectTodo("SIGN_FILE", String.valueOf(d.getId()), title, d.getCompanyId(), d.getStaffId()); + } + } + } + + for (String staffId : addedValidIds) { + if (StringUtils.isBlank(staffId) || existingStaffIds.contains(staffId)) { + continue; + } + HotSignFileDetail detail = new HotSignFileDetail(); + detail.setCompanyId(update.getCompanyId()); + detail.setIssueFileId(update.getId()); + detail.setStaffId(staffId); + detail.setStaffName(idToName.getOrDefault(staffId, null)); + detail.setStaffType(idToStaffType.getOrDefault(staffId, STAFF_TYPE_DRIVER_PRACTITIONER)); + detail.setSignStatus(0L); + detail.setIsArchived(0L); + detail.setIsDeleted(0L); + signFileDetailMapper.insert(detail); + + if (detail.getId() != null) { + flowService.pushDirectTodo("SIGN_FILE", String.valueOf(detail.getId()), title, detail.getCompanyId(), staffId); + } + } + } + + private Map resolvePersonNames(Set personIds) { + Set rawIds = personIds.stream() + .filter(StringUtils::isNotBlank) + .map(String::trim) + .distinct() + .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); + if (rawIds.isEmpty()) { + return Collections.emptyMap(); + } + + Map map = new HashMap<>(); + + List managerIds = rawIds.stream() + .filter(v -> v.matches("\\d+")) + .map(Long::valueOf) + .distinct() + .toList(); + if (!managerIds.isEmpty()) { + List managers = safetyManagerMapper.selectList( + Wrappers.lambdaQuery() + .in(HotCompanySafetyManager::getId, managerIds) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + ); + if (managers != null) { + for (HotCompanySafetyManager m : managers) { + if (m == null || m.getId() == null || StringUtils.isBlank(m.getName())) { + continue; + } + map.putIfAbsent(String.valueOf(m.getId()), m.getName()); + } + } + } + + List drivers = driverMapper.selectList( + Wrappers.lambdaQuery() + .in(HotDriver::getId, rawIds) + .eq(HotDriver::getIsDeleted, 0L) + ); + if (drivers != null) { + for (HotDriver d : drivers) { + if (d == null || d.getId() == null || StringUtils.isBlank(d.getName())) { + continue; + } + map.putIfAbsent(String.valueOf(d.getId()), d.getName()); + } + } + + return map; + } + + private Map resolvePersonTypes(Set personIds) { + Set rawIds = personIds.stream() + .filter(StringUtils::isNotBlank) + .map(String::trim) + .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); + if (rawIds.isEmpty()) { + return Collections.emptyMap(); + } + Map typeMap = new HashMap<>(); + List managerIds = rawIds.stream() + .filter(v -> v.matches("\\d+")) + .map(Long::valueOf) + .distinct() + .toList(); + if (!managerIds.isEmpty()) { + List managers = safetyManagerMapper.selectList( + Wrappers.lambdaQuery() + .in(HotCompanySafetyManager::getId, managerIds) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + ); + if (managers != null) { + for (HotCompanySafetyManager m : managers) { + if (m != null && m.getId() != null) { + typeMap.put(String.valueOf(m.getId()), STAFF_TYPE_MANAGER); + } + } + } + } + List drivers = driverMapper.selectList( + Wrappers.lambdaQuery() + .in(HotDriver::getId, rawIds) + .eq(HotDriver::getIsDeleted, 0L) + ); + if (drivers != null) { + for (HotDriver d : drivers) { + if (d != null && d.getId() != null) { + typeMap.putIfAbsent(d.getId(), mapDriverStaffType(d.getPersonType())); + } + } + } + return typeMap; + } + + private long mapDriverStaffType(String personType) { + if (isDriverOrEscortPersonType(personType)) { + return STAFF_TYPE_DRIVER; + } + if (StringUtils.isBlank(personType)) { + return STAFF_TYPE_DRIVER_PRACTITIONER; + } + return STAFF_TYPE_DRIVER_PRACTITIONER; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotNoticeSignDocument entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除签订文件信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/controller/HotNotificationIssueController.java b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/controller/HotNotificationIssueController.java new file mode 100644 index 0000000..1e0bf8d --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/controller/HotNotificationIssueController.java @@ -0,0 +1,359 @@ +package com.hotwj.platform.noticeManagerment.notificationIssue.controller; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.hotwj.platform.noticeManagerment.adminNotice.domain.bo.HotAdminNoticeBo; +import com.hotwj.platform.noticeManagerment.adminNotice.domain.vo.HotAdminNoticeVo; +import com.hotwj.platform.noticeManagerment.adminNotice.service.IHotAdminNoticeService; +import com.hotwj.platform.noticeManagerment.notificationIssue.domain.bo.HotNotificationIssueBo; +import com.hotwj.platform.noticeManagerment.notificationIssue.domain.bo.IssueRequest; +import com.hotwj.platform.noticeManagerment.notificationIssue.domain.vo.CompanyNoticeStatusVo; +import com.hotwj.platform.noticeManagerment.notificationIssue.domain.vo.HotNotificationIssueVo; +import com.hotwj.platform.noticeManagerment.notificationIssue.service.IHotNotificationIssueService; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.company.domain.bo.SysCompanyBo; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import com.hotwj.platform.resourceManagement.personnelConfig.domain.HotPersonnelConfig; +import com.hotwj.platform.resourceManagement.personnelConfig.mapper.HotPersonnelConfigMapper; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.sse.dto.SseMessageDto; +import org.dromara.common.sse.utils.SseMessageUtils; +import org.dromara.common.web.core.BaseController; +import org.dromara.system.domain.vo.SysUserVo; +import org.dromara.system.service.ISysUserService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 通知下发 + * + * @author shihongwei + * @date 2026-01-13 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/noticeManagerment/notificationIssue") +@Tag(name = "通知下发", description = "通知下发管理") +public class HotNotificationIssueController extends BaseController { + + private final IHotNotificationIssueService hotNotificationIssueService; + private final ISysCompanyService sysCompanyService; + private final IHotAdminNoticeService hotAdminNoticeService; + private final IHotSystemNotificationService hotSystemNotificationService; + private final HotCompanySafetyManagerMapper hotCompanySafetyManagerMapper; + private final HotPersonnelConfigMapper hotPersonnelConfigMapper; + private final ISysUserService sysUserService; + + /** + * 查询通知下发列表 + */ + //@SaCheckPermission("noticeManagerment:notificationIssue:list") + @GetMapping("/list") + @Operation(summary = "分页查询通知下发列表") + public TableDataInfo list(HotNotificationIssueBo bo, PageQuery pageQuery) { + return hotNotificationIssueService.queryPageList(bo, pageQuery); + } + + /** + * 导出通知下发列表 + */ + //@SaCheckPermission("noticeManagerment:notificationIssue:export") + @Log(title = "通知下发", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出通知下发列表") + public void export(HotNotificationIssueBo bo, HttpServletResponse response) { + List list = hotNotificationIssueService.queryList(bo); + ExcelUtil.exportExcel(list, "通知下发", HotNotificationIssueVo.class, response); + } + + /** + * 获取通知下发详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("noticeManagerment:notificationIssue:query") + @GetMapping("/{id}") + @Operation(summary = "获取通知下发详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotNotificationIssueService.queryById(id)); + } + + /** + * 新增通知下发 + */ + //@SaCheckPermission("noticeManagerment:notificationIssue:add") + @Log(title = "通知下发", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增通知下发") + public R add(@Validated(AddGroup.class) @RequestBody HotNotificationIssueBo bo) { + return toAjax(hotNotificationIssueService.insertByBo(bo)); + } + + /** + * 修改通知下发 + */ + //@SaCheckPermission("noticeManagerment:notificationIssue:edit") + @Log(title = "通知下发", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改通知下发") + public R edit(@Validated(EditGroup.class) @RequestBody HotNotificationIssueBo bo) { + return toAjax(hotNotificationIssueService.updateByBo(bo)); + } + + /** + * 删除通知下发 + * + * @param ids 主键串 + */ + //@SaCheckPermission("noticeManagerment:notificationIssue:remove") + @Log(title = "通知下发", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除通知下发") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotNotificationIssueService.deleteWithValidByIds(List.of(ids), true)); + } + + + //@SaCheckPermission("noticeManagerment:notificationIssue:list") + @GetMapping("/companyStatus") + @Operation(summary = "查询辖区企业对于指定通知文件的状态") + public R> companyStatus(@RequestParam("districtCode") String districtCode, + @RequestParam("issueId") Long issueId, + @RequestParam(value = "companyName", required = false) String companyName, + @RequestParam(value = "responsibleName", required = false) String responsibleName, + @RequestParam(value = "responsibleMobile", required = false) String responsibleMobile, + @RequestParam(value = "companyStatus", required = false) Long companyStatus) { + HotNotificationIssueVo issue = hotNotificationIssueService.queryById(issueId); + if (issue == null) { + return R.ok(new ArrayList<>()); + } + SysCompanyBo companyBo = new SysCompanyBo(); + companyBo.setRegionCode(districtCode); + companyBo.setCompanyName(companyName); + companyBo.setResponsibleName(responsibleName); + companyBo.setResponsibleMobile(responsibleMobile); + companyBo.setStatus(companyStatus); + List companies = sysCompanyService.queryList(companyBo); + List result = new ArrayList<>(); + if (companies != null) { + for (SysCompanyVo c : companies) { + HotAdminNoticeBo recordBo = new HotAdminNoticeBo(); + recordBo.setCompanyId(c.getId()); + recordBo.setNoticeId(issueId); + List records = hotAdminNoticeService.queryList(recordBo); + HotAdminNoticeVo record = (records != null && !records.isEmpty()) ? records.get(0) : null; + CompanyNoticeStatusVo vo = new CompanyNoticeStatusVo(); + vo.setCompanyId(c.getId()); + vo.setCompanyName(c.getCompanyName()); + vo.setCompanyStatus(c.getStatus()); + vo.setManagerName(c.getResponsibleName()); + vo.setManagerPhone(c.getResponsibleMobile()); + vo.setIsIssued(record != null ? 1L : 0L); + vo.setIsReceived(record != null ? record.getIsReceived() : 0L); + vo.setReceivedTime(record != null ? record.getReceiveTime() : null); + vo.setNotifyType(issue.getNotifyType()); + result.add(vo); + } + } + return R.ok(result); + } + + //@SaCheckPermission("noticeManagerment:notificationIssue:issue") + @PostMapping("/issue") + @Operation(summary = "下发文件到具体的单位") + @RepeatSubmit() + public R issueToCompanies(@Validated @RequestBody IssueRequest req) { + HotNotificationIssueVo issue = hotNotificationIssueService.queryById(req.getIssueId()); + if (issue == null) { + return R.fail("通知下发不存在"); + } + boolean hasIssued = false; + for (Long companyId : req.getCompanyIds()) { + if (companyId == null) { + continue; + } + // 判断是否已经下发过 + HotAdminNoticeBo checkBo = new HotAdminNoticeBo(); + checkBo.setCompanyId(companyId); + checkBo.setNoticeId(req.getIssueId()); + List exists = hotAdminNoticeService.queryList(checkBo); + if (exists != null && !exists.isEmpty()) { + continue; + } + + HotAdminNoticeBo bo = new HotAdminNoticeBo(); + bo.setCompanyId(companyId); + bo.setNoticeId(req.getIssueId()); + bo.setFileName(issue.getFileName()); + bo.setNotifyType(issue.getNotifyType()); + bo.setAttachmentUrl(issue.getAttachmentUrl()); + bo.setIssueTime(new Date()); + bo.setIsReceived(0L); + bo.setReceiveTime(null); + bo.setDeadlineTime(issue.getReplyDeadline() != null ? issue.getReplyDeadline().toString() : null); + hotAdminNoticeService.insertByBo(bo); + sendIssueNotificationToCompanyHead(companyId, issue); + hasIssued = true; + } + + // 更新下发公司ids + Set existingIds = new HashSet<>(); + if (issue.getIssueCompanyIds() != null && !issue.getIssueCompanyIds().isEmpty()) { + String[] split = issue.getIssueCompanyIds().split(","); + for (String s : split) { + if (s != null && !s.trim().isEmpty()) { + try { + existingIds.add(Long.parseLong(s.trim())); + } catch (NumberFormatException e) { + // ignore + } + } + } + } + for (Long id : req.getCompanyIds()) { + if (id != null) { + existingIds.add(id); + } + } + String newIssueCompanyIds = existingIds.stream() + .map(String::valueOf) + .collect(Collectors.joining(",")); + + HotNotificationIssueBo updateBo = new HotNotificationIssueBo(); + updateBo.setId(issue.getId()); + updateBo.setIssueCompanyIds(newIssueCompanyIds); + hotNotificationIssueService.updateByBo(updateBo); + if (hasIssued) { + sendIssueSseToHeadquarters(); + } + + return R.ok(); + } + + private void sendIssueNotificationToCompanyHead(Long companyId, HotNotificationIssueVo issue) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null) { + return; + } + String loginPort = loginUser.getLoginPort(); + if (!StringUtils.equals(loginPort, ISysUserLoginPortService.GOVERNMENT_PORT) + && !StringUtils.equals(loginPort, ISysUserLoginPortService.HEADQUARTERS_PORT)) { + return; + } + String receiverId = findCompanyHeadReceiverId(companyId); + if (StringUtils.isBlank(receiverId)) { + return; + } + HotSystemNotificationGroupBo notificationBo = new HotSystemNotificationGroupBo(); + notificationBo.setLevel("普通"); + notificationBo.setSourceType("行管通知"); + notificationBo.setSenderType(StringUtils.equals(loginPort, ISysUserLoginPortService.GOVERNMENT_PORT) ? "GOVERNMENT" : "SYSTEM"); + notificationBo.setSenderName(loginUser.getUsername()); + if (StringUtils.equals(loginPort, ISysUserLoginPortService.GOVERNMENT_PORT) + && StringUtils.isNotBlank(loginUser.getBusinessUserId()) + && StringUtils.isNumeric(loginUser.getBusinessUserId())) { + notificationBo.setSenderId(Long.valueOf(loginUser.getBusinessUserId())); + } + notificationBo.setReceiverType("管理员"); + notificationBo.setReceiverId(receiverId); + notificationBo.setContent("收到新的行管通知《" + StringUtils.blankToDefault(issue.getFileName(), "未命名通知") + "》,请及时查看并处理。"); + notificationBo.setIsDeleted(0L); + hotSystemNotificationService.insertByBo(notificationBo); + } + + private String findCompanyHeadReceiverId(Long companyId) { + if (companyId == null) { + return null; + } + List heads = hotCompanySafetyManagerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getCompanyHead, 1L) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + .orderByDesc(HotCompanySafetyManager::getCreateTime) + ); + if (heads != null && !heads.isEmpty()) { + return String.valueOf(heads.get(0).getId()); + } + List managers = hotCompanySafetyManagerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + .orderByDesc(HotCompanySafetyManager::getCreateTime) + ); + if (managers == null || managers.isEmpty()) { + return null; + } + return String.valueOf(managers.get(0).getId()); + } + + private void sendIssueSseToHeadquarters() { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !StringUtils.equals(loginUser.getLoginPort(), ISysUserLoginPortService.GOVERNMENT_PORT)) { + return; + } + List hqUsers = hotPersonnelConfigMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotPersonnelConfig::getUnitId, 1L) + .eq(HotPersonnelConfig::getIsEnabled, 1L) + .eq(HotPersonnelConfig::getIsDeleted, 0L) + ); + if (hqUsers == null || hqUsers.isEmpty()) { + return; + } + Set userIds = new LinkedHashSet<>(); + for (HotPersonnelConfig user : hqUsers) { + if (user == null || StringUtils.isBlank(user.getMobile())) { + continue; + } + SysUserVo sysUser = sysUserService.selectUserByPhonenumber(user.getMobile()); + if (sysUser != null && sysUser.getUserId() != null) { + userIds.add(sysUser.getUserId()); + } + } + if (userIds.isEmpty()) { + return; + } + SseMessageDto dto = new SseMessageDto(); + dto.setUserIds(new ArrayList<>(userIds)); + dto.setMessage(JsonUtils.toJsonString(Map.of("isSilent", true))); + SseMessageUtils.publishMessage(dto); + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/domain/HotNotificationIssue.java b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/domain/HotNotificationIssue.java new file mode 100644 index 0000000..6b28e41 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/domain/HotNotificationIssue.java @@ -0,0 +1,88 @@ +package com.hotwj.platform.noticeManagerment.notificationIssue.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 通知下发对象 hot_notification_issue + * + * @author shihongwei + * @date 2026-01-13 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_notification_issue") +public class HotNotificationIssue extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 下发公司ids + */ + private String issueCompanyIds; + + /** + * 下发内容 + */ + private String issueContent; + + /** + * 文件名称 + */ + private String fileName; + + /** + * 通知类型 + */ + private Long notifyType; + + /** + * 回复期限 + */ + private String deadline; + + /** + * 附件URL + */ + private String attachmentUrl; + + /** + * 省编码 + */ + private String provinceCode; + + /** + * 市编码 + */ + private String cityCode; + + /** + * 区/县编码 + */ + private String districtCode; + + /** + * 区域名称(省市区组合展示) + */ + private String areaName; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/domain/bo/HotNotificationIssueBo.java b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/domain/bo/HotNotificationIssueBo.java new file mode 100644 index 0000000..9140618 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/domain/bo/HotNotificationIssueBo.java @@ -0,0 +1,92 @@ +package com.hotwj.platform.noticeManagerment.notificationIssue.domain.bo; + +import com.hotwj.platform.noticeManagerment.notificationIssue.domain.HotNotificationIssue; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 通知下发业务对象 hot_notification_issue + * + * @author shihongwei + * @date 2026-01-13 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotNotificationIssue.class, reverseConvertGenerate = false) +public class HotNotificationIssueBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 下发公司ids + */ + private String issueCompanyIds; + + /** + * 下发内容 + */ + private String issueContent; + + /** + * 文件名称 + */ + @NotBlank(message = "文件名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String fileName; + + /** + * 通知类型 + */ + @NotNull(message = "通知类型不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long notifyType; + + /** + * 回复期限 + */ + private String deadline; + + /** + * 附件URL + */ + @NotBlank(message = "附件URL不能为空", groups = {AddGroup.class, EditGroup.class}) + private String attachmentUrl; + + /** + * 省编码 + */ + @NotBlank(message = "省编码不能为空", groups = {AddGroup.class, EditGroup.class}) + private String provinceCode; + + /** + * 市编码 + */ + @NotBlank(message = "市编码不能为空", groups = {AddGroup.class, EditGroup.class}) + private String cityCode; + + /** + * 区/县编码 + */ + @NotBlank(message = "区/县编码不能为空", groups = {AddGroup.class, EditGroup.class}) + private String districtCode; + + /** + * 区域名称(省市区组合展示) + */ + private String areaName; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/domain/bo/IssueRequest.java b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/domain/bo/IssueRequest.java new file mode 100644 index 0000000..bd2347f --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/domain/bo/IssueRequest.java @@ -0,0 +1,16 @@ +package com.hotwj.platform.noticeManagerment.notificationIssue.domain.bo; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Data +public class IssueRequest { + @NotNull(message = "通知文件ID不能为空") + private Long issueId; + + @NotEmpty(message = "接收企业不能为空") + private List companyIds; +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/domain/vo/CompanyNoticeStatusVo.java b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/domain/vo/CompanyNoticeStatusVo.java new file mode 100644 index 0000000..ba958de --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/domain/vo/CompanyNoticeStatusVo.java @@ -0,0 +1,18 @@ +package com.hotwj.platform.noticeManagerment.notificationIssue.domain.vo; + +import lombok.Data; + +import java.util.Date; + +@Data +public class CompanyNoticeStatusVo { + private Long companyId; + private String companyName; + private Long companyStatus; + private String managerName; + private String managerPhone; + private Long isIssued; + private Long isReceived; + private Date receivedTime; + private Long notifyType; +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/domain/vo/HotNotificationIssueVo.java b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/domain/vo/HotNotificationIssueVo.java new file mode 100644 index 0000000..d0d16a5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/domain/vo/HotNotificationIssueVo.java @@ -0,0 +1,122 @@ +package com.hotwj.platform.noticeManagerment.notificationIssue.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.noticeManagerment.notificationIssue.domain.HotNotificationIssue; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 通知下发视图对象 hot_notification_issue + * + * @author shihongwei + * @date 2026-01-13 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotNotificationIssue.class) +public class HotNotificationIssueVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 下发公司ids + */ + @ExcelProperty(value = "下发公司ids") + private String issueCompanyIds; + + /** + * 下发内容 + */ + @ExcelProperty(value = "下发内容") + private String issueContent; + + /** + * 文件名称 + */ + @ExcelProperty(value = "文件名称") + private String fileName; + + /** + * 通知类型 + */ + @ExcelProperty(value = "通知类型") + private Long notifyType; + + /** + * 回复期限 + */ + @ExcelProperty(value = "回复期限") + private Date replyDeadline; + + /** + * 回复期限(字符串) + */ + private String deadline; + + /** + * 附件地址 + */ + @ExcelProperty(value = "附件地址") + private String attachmentUrl; + + /** + * 省代码 + */ + @ExcelProperty(value = "省代码") + private String provinceCode; + + /** + * 市代码 + */ + @ExcelProperty(value = "市代码") + private String cityCode; + + /** + * 区代码 + */ + @ExcelProperty(value = "区代码") + private String districtCode; + + /** + * 区域名称 + */ + @ExcelProperty(value = "区域名称") + private String areaName; + + /** + * 接收/整改情况 + */ + @ExcelProperty(value = "接收/整改情况") + private String distributionStatus; + + /** + * 已读数量 + */ + private Long readCount; + + /** + * 总数量 + */ + private Long totalCount; + + /** + * 审核通过数量 + */ + private Long passCount; + + private Date createTime; + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/mapper/HotNotificationIssueMapper.java b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/mapper/HotNotificationIssueMapper.java new file mode 100644 index 0000000..47c0ee5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/mapper/HotNotificationIssueMapper.java @@ -0,0 +1,23 @@ +package com.hotwj.platform.noticeManagerment.notificationIssue.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.noticeManagerment.notificationIssue.domain.HotNotificationIssue; +import com.hotwj.platform.noticeManagerment.notificationIssue.domain.vo.HotNotificationIssueVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 通知下发Mapper接口 + * + * @author shihongwei + * @date 2026-01-13 + */ +@Mapper +public interface HotNotificationIssueMapper extends BaseMapperPlus { + + Page selectPageList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper ew); + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/service/IHotNotificationIssueService.java b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/service/IHotNotificationIssueService.java new file mode 100644 index 0000000..822c929 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/service/IHotNotificationIssueService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.noticeManagerment.notificationIssue.service; + +import com.hotwj.platform.noticeManagerment.notificationIssue.domain.bo.HotNotificationIssueBo; +import com.hotwj.platform.noticeManagerment.notificationIssue.domain.vo.HotNotificationIssueVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 通知下发Service接口 + * + * @author shihongwei + * @date 2026-01-13 + */ +public interface IHotNotificationIssueService { + + /** + * 查询通知下发 + * + * @param id 主键 + * @return 通知下发 + */ + HotNotificationIssueVo queryById(Long id); + + /** + * 分页查询通知下发列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 通知下发分页列表 + */ + TableDataInfo queryPageList(HotNotificationIssueBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的通知下发列表 + * + * @param bo 查询条件 + * @return 通知下发列表 + */ + List queryList(HotNotificationIssueBo bo); + + /** + * 新增通知下发 + * + * @param bo 通知下发 + * @return 是否新增成功 + */ + Boolean insertByBo(HotNotificationIssueBo bo); + + /** + * 修改通知下发 + * + * @param bo 通知下发 + * @return 是否修改成功 + */ + Boolean updateByBo(HotNotificationIssueBo bo); + + /** + * 校验并批量删除通知下发信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/service/impl/HotNotificationIssueServiceImpl.java b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/service/impl/HotNotificationIssueServiceImpl.java new file mode 100644 index 0000000..cc383c1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/notificationIssue/service/impl/HotNotificationIssueServiceImpl.java @@ -0,0 +1,178 @@ +package com.hotwj.platform.noticeManagerment.notificationIssue.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.noticeManagerment.notificationIssue.domain.HotNotificationIssue; +import com.hotwj.platform.noticeManagerment.notificationIssue.domain.bo.HotNotificationIssueBo; +import com.hotwj.platform.noticeManagerment.notificationIssue.domain.vo.HotNotificationIssueVo; +import com.hotwj.platform.noticeManagerment.notificationIssue.mapper.HotNotificationIssueMapper; +import com.hotwj.platform.noticeManagerment.notificationIssue.service.IHotNotificationIssueService; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.vo.HotGovEnterpriseUnitVo; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.service.IHotGovEnterpriseUnitService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 通知下发Service业务层处理 + * + * @author shihongwei + * @date 2026-01-13 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotNotificationIssueServiceImpl implements IHotNotificationIssueService { + + private final HotNotificationIssueMapper baseMapper; + private final IHotGovEnterpriseUnitService hotGovEnterpriseUnitService; + + /** + * 查询通知下发 + * + * @param id 主键 + * @return 通知下发 + */ + @Override + public HotNotificationIssueVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询通知下发列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 通知下发分页列表 + */ + @Override + public TableDataInfo queryPageList(HotNotificationIssueBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectPageList(pageQuery.build(), lqw); + for (HotNotificationIssueVo vo : result.getRecords()) { + long read = vo.getReadCount() == null ? 0L : vo.getReadCount(); + long total = vo.getTotalCount() == null ? 0L : vo.getTotalCount(); + StringBuilder sb = new StringBuilder(); + sb.append("已读: ").append(read).append("/").append(total); + // 如果有审核通过的数据,或者有截止日期(说明是整改类通知) + if (StringUtils.isNotBlank(vo.getDeadline()) || (vo.getPassCount() != null && vo.getPassCount() > 0)) { + sb.append(",审核通过: ").append(vo.getPassCount() == null ? 0L : vo.getPassCount()); + } + vo.setDistributionStatus(sb.toString()); + } + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的通知下发列表 + * + * @param bo 查询条件 + * @return 通知下发列表 + */ + @Override + public List queryList(HotNotificationIssueBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotNotificationIssueBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotNotificationIssue::getId); + lqw.like(StringUtils.isNotBlank(bo.getFileName()), HotNotificationIssue::getFileName, bo.getFileName()); + lqw.eq(bo.getNotifyType() != null, HotNotificationIssue::getNotifyType, bo.getNotifyType()); + lqw.eq(StringUtils.isNotBlank(bo.getAttachmentUrl()), HotNotificationIssue::getAttachmentUrl, bo.getAttachmentUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getProvinceCode()), HotNotificationIssue::getProvinceCode, bo.getProvinceCode()); + lqw.eq(StringUtils.isNotBlank(bo.getCityCode()), HotNotificationIssue::getCityCode, bo.getCityCode()); + lqw.eq(StringUtils.isNotBlank(bo.getDistrictCode()), HotNotificationIssue::getDistrictCode, bo.getDistrictCode()); + lqw.like(StringUtils.isNotBlank(bo.getAreaName()), HotNotificationIssue::getAreaName, bo.getAreaName()); + lqw.eq(bo.getIsDeleted() != null, HotNotificationIssue::getIsDeleted, bo.getIsDeleted()); + + // 权限过滤逻辑 + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser != null) { + if (ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + // 政府端:只能查看政府下发的,且通过区划代码过滤 + HotGovEnterpriseUnitVo unitVo = hotGovEnterpriseUnitService.queryById(loginUser.getCompanyId()); + if (unitVo != null) { + lqw.eq(HotNotificationIssue::getDistrictCode, unitVo.getDistrictCode()); + } else { + // 查不到单位信息,什么都不返回 + lqw.eq(HotNotificationIssue::getId, -1L); + } + } + } + + return lqw; + } + + /** + * 新增通知下发 + * + * @param bo 通知下发 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotNotificationIssueBo bo) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null) { + throw new ServiceException("未登录用户无法操作"); + } + + HotNotificationIssue add = MapstructUtils.convert(bo, HotNotificationIssue.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改通知下发 + * + * @param bo 通知下发 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotNotificationIssueBo bo) { + HotNotificationIssue update = MapstructUtils.convert(bo, HotNotificationIssue.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotNotificationIssue entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除通知下发信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/controller/HotSignFileDetailController.java b/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/controller/HotSignFileDetailController.java new file mode 100644 index 0000000..62588b2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/controller/HotSignFileDetailController.java @@ -0,0 +1,153 @@ +package com.hotwj.platform.noticeManagerment.signFileDetail.controller; + +import com.hotwj.platform.noticeManagerment.signFileDetail.domain.bo.HotSignFileCompleteBo; +import com.hotwj.platform.noticeManagerment.signFileDetail.domain.bo.HotSignFileDetailBo; +import com.hotwj.platform.noticeManagerment.signFileDetail.domain.vo.HotSignFileDetailVo; +import com.hotwj.platform.noticeManagerment.signFileDetail.service.IHotSignFileDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.utils.DateUtils; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * 签订文件明细 + * + * @author shihongwei + * @date 2026-01-14 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/noticeManagerment/signFileDetail") +@Tag(name = "签订文件明细", description = "签订文件明细管理") +public class HotSignFileDetailController extends BaseController { + + private final IHotSignFileDetailService hotSignFileDetailService; + + /** + * 查询签订文件明细列表 + */ + //@SaCheckPermission("noticeManagerment:signFileDetail:list") + @GetMapping("/list") + @Operation(summary = "分页查询签订文件明细列表") + public TableDataInfo list(HotSignFileDetailBo bo, PageQuery pageQuery) { + return hotSignFileDetailService.queryPageList(bo, pageQuery); + } + + /** + * 导出签订文件明细列表 + */ + //@SaCheckPermission("noticeManagerment:signFileDetail:export") + @Log(title = "签订文件明细", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出签订文件明细列表") + public void export(HotSignFileDetailBo bo, HttpServletResponse response) { + List list = hotSignFileDetailService.queryList(bo); + if (list == null) { + list = new ArrayList<>(); + } + for (HotSignFileDetailVo v : list) { + if (v == null) { + continue; + } + v.setSignTimeStr(v.getSignTime() != null ? DateUtils.formatDate(v.getSignTime()) : null); + v.setExpireTimeStr(v.getExpireTime() != null ? DateUtils.formatDate(v.getExpireTime()) : null); + v.setCreateTimeStr(v.getCreateTime() != null ? DateUtils.formatDate(v.getCreateTime()) : null); + } + ExcelUtil.exportExcel(list, "签订文件明细", HotSignFileDetailVo.class, response); + } + + /** + * 获取签订文件明细详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("noticeManagerment:signFileDetail:query") + @GetMapping("/{id}") + @Operation(summary = "获取签订文件明细详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotSignFileDetailService.queryById(id)); + } + + @Log(title = "签订文件详情打印", businessType = BusinessType.EXPORT) + @PostMapping("/exportPdf/{id}") + @Operation(summary = "导出签订文件详情PDF") + @Deprecated // 已经在前端实现打印了,这个可以考虑后续移除 + public void exportDetailPdf(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id, + HttpServletResponse response) { + hotSignFileDetailService.exportDetailPdf(id, response); + } + + /** + * 新增签订文件明细 + */ + //@SaCheckPermission("noticeManagerment:signFileDetail:add") + @Log(title = "签订文件明细", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增签订文件明细") + public R add(@Validated(AddGroup.class) @RequestBody HotSignFileDetailBo bo) { + return toAjax(hotSignFileDetailService.insertByBo(bo)); + } + + /** + * 修改签订文件明细 + */ + //@SaCheckPermission("noticeManagerment:signFileDetail:edit") + @Log(title = "签订文件明细", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改签订文件明细") + public R edit(@Validated(EditGroup.class) @RequestBody HotSignFileDetailBo bo) { + return toAjax(hotSignFileDetailService.updateByBo(bo)); + } + + /** + * 删除签订文件明细 + * + * @param ids 主键串 + */ + //@SaCheckPermission("noticeManagerment:signFileDetail:remove") + @Log(title = "签订文件明细", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除签订文件明细") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotSignFileDetailService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 完成文件签署 + */ + //@SaCheckPermission("noticeManagerment:signFileDetail:edit") + @Log(title = "完成文件签署", businessType = BusinessType.UPDATE) + @PostMapping("/sign") + @Operation(summary = "完成文件签署") + public R completeSign(@Validated @RequestBody HotSignFileCompleteBo bo) { + return R.ok(hotSignFileDetailService.completeSign(bo.getTaskId(), bo.getSignOssId())); + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/domain/HotSignFileDetail.java b/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/domain/HotSignFileDetail.java new file mode 100644 index 0000000..13b6dc4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/domain/HotSignFileDetail.java @@ -0,0 +1,100 @@ +package com.hotwj.platform.noticeManagerment.signFileDetail.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 签订文件明细对象 hot_sign_file_detail + * + * @author shihongwei + * @date 2026-01-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_sign_file_detail") +public class HotSignFileDetail extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 文件ID + */ + private Long issueFileId; + + /** + * 人员ID + */ + private String staffId; + + /** + * 人员名称 + */ + private String staffName; + + /** + * 人员类型 + */ + private Long staffType; + + /** + * 签订时间 + */ + private Date signTime; + + /** + * 是否签订 0=否 1=是 + */ + private Long signStatus; + + /** + * 到期时间 + */ + private Date expireTime; + + /** + * 是否归档 0=否 1=是 + */ + private Long isArchived; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 创建人显示名称 + */ + private String createByName; + + /** + * 最后修改人显示名称 + */ + private String updateByName; + + /** + * 签名ossId + */ + private String signOssId; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/domain/bo/HotSignFileCompleteBo.java b/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/domain/bo/HotSignFileCompleteBo.java new file mode 100644 index 0000000..58338ce --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/domain/bo/HotSignFileCompleteBo.java @@ -0,0 +1,19 @@ +package com.hotwj.platform.noticeManagerment.signFileDetail.domain.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +@Schema(description = "文件签署完成业务对象") +public class HotSignFileCompleteBo { + + @Schema(description = "任务ID") + @NotBlank(message = "任务ID不能为空") + private String taskId; + + @Schema(description = "签名文件OSS ID") + @NotNull(message = "签名文件不能为空") + private Long signOssId; +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/domain/bo/HotSignFileDetailBo.java b/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/domain/bo/HotSignFileDetailBo.java new file mode 100644 index 0000000..89e61f8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/domain/bo/HotSignFileDetailBo.java @@ -0,0 +1,101 @@ +package com.hotwj.platform.noticeManagerment.signFileDetail.domain.bo; + +import com.hotwj.platform.noticeManagerment.signFileDetail.domain.HotSignFileDetail; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 签订文件明细业务对象 hot_sign_file_detail + * + * @author shihongwei + * @date 2026-01-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotSignFileDetail.class, reverseConvertGenerate = false) +public class HotSignFileDetailBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 文件ID + */ + @NotNull(message = "文件ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long issueFileId; + + /** + * 人员ID + */ + @NotNull(message = "人员ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private String staffId; + + /** + * 人员名称 + */ + private String staffName; + + /** + * 人员类型 + */ + private Long staffType; + + /** + * 签订时间 + */ + private Date signTime; + + /** + * 是否签订 0=否 1=是 + */ + @NotNull(message = "是否签订 0=否 1=是不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long signStatus; + + /** + * 到期时间 + */ + private Date expireTime; + + /** + * 是否归档 0=否 1=是 + */ + private Long isArchived; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + /** + * 创建人显示名称 + */ + private String createByName; + + /** + * 最后修改人显示名称 + */ + private String updateByName; + + /** + * 签名ossId + */ + private String signOssId; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/domain/vo/HotSignFileDetailVo.java b/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/domain/vo/HotSignFileDetailVo.java new file mode 100644 index 0000000..621624e --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/domain/vo/HotSignFileDetailVo.java @@ -0,0 +1,137 @@ +package com.hotwj.platform.noticeManagerment.signFileDetail.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnore; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.noticeManagerment.signFileDetail.domain.HotSignFileDetail; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 签订文件明细视图对象 hot_sign_file_detail + * + * @author shihongwei + * @date 2026-01-14 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotSignFileDetail.class) +public class HotSignFileDetailVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelIgnore + private Long id; + + private String taskId; + + /** + * 公司ID + */ + @ExcelIgnore + private Long companyId; + + /** + * 文件ID + */ + @ExcelIgnore + private Long issueFileId; + + @ExcelIgnore + private String fileName; + + @ExcelIgnore + private String content; + + /** + * 人员ID + */ + @ExcelIgnore + private String staffId; + + /** + * 人员名称 + */ + @ExcelProperty(value = "姓名", index = 0) + private String staffName; + + /** + * 人员类型 + */ + @ExcelProperty(value = "人员类型", index = 1, converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_training_person_type") + private Long staffType; + + /** + * 签订时间 + */ + @ExcelIgnore + private Date signTime; + + /** + * 是否签订 0=否 1=是 + */ + @ExcelIgnore + private Long signStatus; + + /** + * 到期时间 + */ + @ExcelIgnore + private Date expireTime; + + /** + * 是否归档 0=否 1=是 + */ + @ExcelProperty(value = "是否归档", index = 5, converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1=已归档,0=否") + private Long isArchived; + + /** + * 0=正常, 1=已删除 + */ + @ExcelIgnore + private Long isDeleted; + + /** + * 创建人显示名称 + */ + @ExcelIgnore + private String createByName; + + /** + * 最后修改人显示名称 + */ + @ExcelIgnore + private String updateByName; + + /** + * 签名ossId + */ + @ExcelIgnore + private String signOssId; + + @ExcelIgnore + private Date createTime; + + @ExcelProperty(value = "签订时间", index = 2) + private String signTimeStr; + + @ExcelProperty(value = "到期时间", index = 3) + private String expireTimeStr; + + @ExcelProperty(value = "创建时间", index = 4) + private String createTimeStr; + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/mapper/HotSignFileDetailMapper.java b/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/mapper/HotSignFileDetailMapper.java new file mode 100644 index 0000000..2619bc4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/mapper/HotSignFileDetailMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.noticeManagerment.signFileDetail.mapper; + +import com.hotwj.platform.noticeManagerment.signFileDetail.domain.HotSignFileDetail; +import com.hotwj.platform.noticeManagerment.signFileDetail.domain.vo.HotSignFileDetailVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 签订文件明细Mapper接口 + * + * @author shihongwei + * @date 2026-01-14 + */ +@Mapper +public interface HotSignFileDetailMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/service/IHotSignFileDetailService.java b/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/service/IHotSignFileDetailService.java new file mode 100644 index 0000000..21f1d1b --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/service/IHotSignFileDetailService.java @@ -0,0 +1,80 @@ +package com.hotwj.platform.noticeManagerment.signFileDetail.service; + +import com.hotwj.platform.noticeManagerment.signFileDetail.domain.bo.HotSignFileDetailBo; +import com.hotwj.platform.noticeManagerment.signFileDetail.domain.vo.HotSignFileDetailVo; +import jakarta.servlet.http.HttpServletResponse; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 签订文件明细Service接口 + * + * @author shihongwei + * @date 2026-01-14 + */ +public interface IHotSignFileDetailService { + + /** + * 查询签订文件明细 + * + * @param id 主键 + * @return 签订文件明细 + */ + HotSignFileDetailVo queryById(Long id); + + /** + * 分页查询签订文件明细列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 签订文件明细分页列表 + */ + TableDataInfo queryPageList(HotSignFileDetailBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的签订文件明细列表 + * + * @param bo 查询条件 + * @return 签订文件明细列表 + */ + List queryList(HotSignFileDetailBo bo); + + /** + * 新增签订文件明细 + * + * @param bo 签订文件明细 + * @return 是否新增成功 + */ + Boolean insertByBo(HotSignFileDetailBo bo); + + /** + * 修改签订文件明细 + * + * @param bo 签订文件明细 + * @return 是否修改成功 + */ + Boolean updateByBo(HotSignFileDetailBo bo); + + /** + * 校验并批量删除签订文件明细信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 完成文件签署(更新状态并结束待办) + * + * @param taskId 待办任务ID + * @param signOssId 签名文件OSS ID + * @return 结果 + */ + Boolean completeSign(String taskId, Long signOssId); + + void exportDetailPdf(Long id, HttpServletResponse response); +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/service/impl/HotSignFileDetailServiceImpl.java b/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/service/impl/HotSignFileDetailServiceImpl.java new file mode 100644 index 0000000..01ec0a0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/signFileDetail/service/impl/HotSignFileDetailServiceImpl.java @@ -0,0 +1,366 @@ +package com.hotwj.platform.noticeManagerment.signFileDetail.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.common.helper.DriverLoginContextHelper; +import com.hotwj.platform.common.service.ITemplateService; +import com.hotwj.platform.flow.domain.SysFlowInstance; +import com.hotwj.platform.flow.domain.SysFlowTask; +import com.hotwj.platform.flow.mapper.SysFlowInstanceMapper; +import com.hotwj.platform.flow.mapper.SysFlowTaskMapper; +import com.hotwj.platform.flow.service.ISysFlowService; +import com.hotwj.platform.integration.ocr.SignatureVerifyService; +import com.hotwj.platform.noticeManagerment.noticeSignDocument.domain.vo.HotNoticeSignDocumentVo; +import com.hotwj.platform.noticeManagerment.noticeSignDocument.service.IHotNoticeSignDocumentService; +import com.hotwj.platform.noticeManagerment.signFileDetail.domain.HotSignFileDetail; +import com.hotwj.platform.noticeManagerment.signFileDetail.domain.bo.HotSignFileDetailBo; +import com.hotwj.platform.noticeManagerment.signFileDetail.domain.vo.HotSignFileDetailVo; +import com.hotwj.platform.noticeManagerment.signFileDetail.mapper.HotSignFileDetailMapper; +import com.hotwj.platform.noticeManagerment.signFileDetail.service.IHotSignFileDetailService; +import com.hotwj.platform.reportStatistics.integration.GotenbergClient; +import com.hotwj.platform.reportStatistics.template.VelocityTemplateRenderer; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.velocity.VelocityContext; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.file.FileUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.IOException; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 签订文件明细Service业务层处理 + * + * @author shihongwei + * @date 2026-01-14 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotSignFileDetailServiceImpl implements IHotSignFileDetailService { + private static final long STAFF_TYPE_ALL = 4L; + private static final long STAFF_TYPE_DRIVER = 1L; + private static final long STAFF_TYPE_DRIVER_PRACTITIONER = 3L; + + private final HotSignFileDetailMapper baseMapper; + private final ISysFlowService flowService; + private final IHotNoticeSignDocumentService documentService; + private final SysFlowInstanceMapper instanceMapper; + private final SysFlowTaskMapper taskMapper; + private final ITemplateService templateService; + private final GotenbergClient gotenbergClient; + private final VelocityTemplateRenderer velocityTemplateRenderer; + private final SignatureVerifyService signatureVerifyService; + + /** + * 查询签订文件明细 + * + * @param id 主键 + * @return 签订文件明细 + */ + @Override + public HotSignFileDetailVo queryById(Long id) { + HotSignFileDetailVo detailVo = baseMapper.selectVoById(id); + if (detailVo == null) { + return null; + } + List records = new ArrayList<>(1); + records.add(detailVo); + fillTodoTaskId(records); + fillTemplateContent(detailVo); + return detailVo; + } + + private void fillTemplateContent(HotSignFileDetailVo detailVo) { + if (detailVo == null || detailVo.getIssueFileId() == null) { + return; + } + HotNoticeSignDocumentVo documentVo = documentService.queryById(detailVo.getIssueFileId()); + if (documentVo == null) { + return; + } + detailVo.setFileName(documentVo.getFileName()); + String templateContent = documentVo.getContent(); + if (StringUtils.isBlank(templateContent)) { + detailVo.setContent(templateContent); + return; + } + Map dataMap = templateService.prepareDataMapForSignDetail(detailVo.getId()); + detailVo.setContent(templateService.fillTemplate(templateContent, dataMap)); + } + + @Override + public void exportDetailPdf(Long id, HttpServletResponse response) { + HotSignFileDetailVo detailVo = queryById(id); + if (detailVo == null) { + throw new ServiceException("签订文件明细不存在"); + } + String title = StringUtils.isNotBlank(detailVo.getFileName()) ? detailVo.getFileName() : "签订文件详情"; + String body = StringUtils.defaultString(detailVo.getContent()); + VelocityContext layoutContext = new VelocityContext(); + layoutContext.put("title", title); + layoutContext.put("bodies", List.of("
" + body + "
")); + String finalHtml = velocityTemplateRenderer.render("templates/layout/printLayout.html.vm", layoutContext); + byte[] pdfBytes = gotenbergClient.convertHtmlToPdf(finalHtml); + try { + FileUtils.setAttachmentResponseHeader(response, title + ".pdf"); + response.setContentType("application/pdf"); + response.getOutputStream().write(pdfBytes); + } catch (IOException e) { + log.error("导出签订文件详情PDF失败, id={}", id, e); + throw new ServiceException("导出PDF失败"); + } + } + + /** + * 分页查询签订文件明细列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 签订文件明细分页列表 + */ + @Override + public TableDataInfo queryPageList(HotSignFileDetailBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + fillTodoTaskId(result.getRecords()); + return TableDataInfo.build(result); + } + + private void fillTodoTaskId(List records) { + if (records == null || records.isEmpty()) { + return; + } + + List businessIds = records.stream() + .map(HotSignFileDetailVo::getId) + .filter(id -> id != null) + .map(String::valueOf) + .distinct() + .toList(); + + if (businessIds.isEmpty()) { + return; + } + + List instances = instanceMapper.selectList( + Wrappers.lambdaQuery() + .eq(SysFlowInstance::getFlowCode, "SIGN_FILE") + .eq(SysFlowInstance::getStatus, 0) + .in(SysFlowInstance::getBusinessId, businessIds) + ); + + if (instances == null || instances.isEmpty()) { + return; + } + + Map businessIdToInstanceId = instances.stream() + .collect(Collectors.toMap(SysFlowInstance::getBusinessId, SysFlowInstance::getInstanceId, (a, b) -> a)); + + List instanceIds = instances.stream() + .map(SysFlowInstance::getInstanceId) + .distinct() + .toList(); + + List tasks = taskMapper.selectList( + Wrappers.lambdaQuery() + .in(SysFlowTask::getInstanceId, instanceIds) + ); + + if (tasks == null || tasks.isEmpty()) { + return; + } + + Map instanceIdToTaskId = tasks.stream() + .collect(Collectors.toMap(SysFlowTask::getInstanceId, SysFlowTask::getTaskId, (a, b) -> a)); + + for (HotSignFileDetailVo record : records) { + if (record == null || record.getId() == null) { + continue; + } + String instanceId = businessIdToInstanceId.get(String.valueOf(record.getId())); + if (instanceId == null) { + continue; + } + record.setTaskId(instanceIdToTaskId.get(instanceId)); + } + } + + /** + * 查询符合条件的签订文件明细列表 + * + * @param bo 查询条件 + * @return 签订文件明细列表 + */ + @Override + public List queryList(HotSignFileDetailBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotSignFileDetailBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotSignFileDetail::getId); + lqw.eq(bo.getCompanyId() != null, HotSignFileDetail::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getIssueFileId() != null, HotSignFileDetail::getIssueFileId, bo.getIssueFileId()); + lqw.eq(bo.getStaffId() != null, HotSignFileDetail::getStaffId, bo.getStaffId()); + lqw.like(StringUtils.isNotBlank(bo.getStaffName()), HotSignFileDetail::getStaffName, bo.getStaffName()); + if (bo.getStaffType() != null) { + if (Objects.equals(bo.getStaffType(), STAFF_TYPE_ALL)) { + // 全部人员不追加staffType过滤 + } else if (Objects.equals(bo.getStaffType(), STAFF_TYPE_DRIVER_PRACTITIONER)) { + // 从业人员查询口径:覆盖驾驶员表中的驾驶员与从业人员 + lqw.in(HotSignFileDetail::getStaffType, STAFF_TYPE_DRIVER, STAFF_TYPE_DRIVER_PRACTITIONER); + } else { + lqw.eq(HotSignFileDetail::getStaffType, bo.getStaffType()); + } + } + lqw.eq(bo.getSignTime() != null, HotSignFileDetail::getSignTime, bo.getSignTime()); + lqw.eq(bo.getSignStatus() != null, HotSignFileDetail::getSignStatus, bo.getSignStatus()); + lqw.eq(bo.getExpireTime() != null, HotSignFileDetail::getExpireTime, bo.getExpireTime()); + lqw.eq(bo.getIsArchived() != null, HotSignFileDetail::getIsArchived, bo.getIsArchived()); + lqw.eq(bo.getIsDeleted() != null, HotSignFileDetail::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增签订文件明细 + * + * @param bo 签订文件明细 + * @return 是否新增成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(HotSignFileDetailBo bo) { + HotSignFileDetail add = MapstructUtils.convert(bo, HotSignFileDetail.class); + if (add.getSignStatus() == null) { + add.setSignStatus(0L); + } + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改签订文件明细 + * + * @param bo 签订文件明细 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotSignFileDetailBo bo) { + HotSignFileDetail update = MapstructUtils.convert(bo, HotSignFileDetail.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotSignFileDetail entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除签订文件明细信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 完成文件签署(更新状态并结束待办) + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean completeSign(String taskId, Long signOssId) { + SysFlowTask task = flowService.getTaskById(taskId); + if (task == null) { + throw new ServiceException("任务不存在"); + } + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser != null && !task.getApproverId().equals(String.valueOf(loginUser.getBusinessUserId()))) { + throw new ServiceException("无权操作此任务"); + } + SysFlowInstance instance = flowService.getInstanceByTaskId(taskId); + if (instance == null) { + throw new ServiceException("流程实例不存在"); + } + Long detailId; + try { + detailId = Long.valueOf(instance.getBusinessId()); + } catch (NumberFormatException e) { + throw new ServiceException("业务数据异常"); + } + HotSignFileDetail detail = baseMapper.selectById(detailId); + if (detail == null) { + throw new ServiceException("文件记录不存在"); + } + if (DriverLoginContextHelper.isDriverPort() && signOssId != null) { + if (StringUtils.isBlank(detail.getStaffName())) { + throw new ServiceException("签署人姓名不能为空"); + } + signatureVerifyService.validateSelfSignature(signOssId, detail.getStaffName()); + } + detail.setSignStatus(1L); + detail.setSignTime(new Date()); + detail.setSignOssId(String.valueOf(signOssId)); + // 计算并设置到期时间 + HotNoticeSignDocumentVo documentVo = documentService.queryById(detail.getIssueFileId()); + if (documentVo != null && documentVo.getValidType() != null) { + Long validType = documentVo.getValidType(); + String validPeriod = documentVo.getValidPeriod(); + try { + if (Long.valueOf(0L).equals(validType)) { + // 短期:validPeriod 是具体日期字符串(YYYY-MM-DD) + if (validPeriod != null) { + LocalDate date = LocalDate.parse(validPeriod, DateTimeFormatter.ISO_LOCAL_DATE); + detail.setExpireTime(Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant())); + } + } else if (Long.valueOf(1L).equals(validType)) { + // 定期:validPeriod 是月份数 + long months = 0L; + if (validPeriod != null) { + months = Long.parseLong(validPeriod); + } + LocalDate date = LocalDate.now().plusMonths(months); + detail.setExpireTime(Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant())); + } + // 长期(2)或其他,不设置到期时间 + } catch (Exception e) { + // 忽略解析异常,避免影响签署流程 + log.warn("设置到期时间失败:{}", e.getMessage()); + } + } + + baseMapper.updateById(detail); + flowService.audit(taskId, true, "签署完成", task.getApproverId()); + return true; + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/controller/HotSystemNotificationController.java b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/controller/HotSystemNotificationController.java new file mode 100644 index 0000000..4053389 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/controller/HotSystemNotificationController.java @@ -0,0 +1,154 @@ +package com.hotwj.platform.noticeManagerment.systemNotification.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationBo; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.vo.HotSystemNotificationVo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 系统通知 + * + * @author shihongwei + * @date 2026-01-07 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/noticeSignDocument/systemNotification") +@Tag(name = "系统通知", description = "系统通知管理") +public class HotSystemNotificationController extends BaseController { + + private final IHotSystemNotificationService hotSystemNotificationService; + + /** + * 查询系统通知列表 + */ + //@SaCheckPermission("noticeSignDocument:systemNotification:list") + @GetMapping("/list") + @Operation(summary = "分页查询系统通知列表") + public TableDataInfo list(HotSystemNotificationBo bo, PageQuery pageQuery) { + return hotSystemNotificationService.queryPageList(bo, pageQuery); + } + + /** + * 查询系统通知列表 + */ + //@SaCheckPermission("noticeSignDocument:systemNotification:list") + @GetMapping("/listByType") + @Operation(summary = "分页查询系统通知列表") + public TableDataInfo listByType(HotSystemNotificationBo bo, PageQuery pageQuery) { + return hotSystemNotificationService.queryPageListByType(bo, pageQuery); + } + + /** + * 导出系统通知列表 + */ + //@SaCheckPermission("noticeSignDocument:systemNotification:export") + @Log(title = "系统通知", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出系统通知列表") + public void export(HotSystemNotificationBo bo, HttpServletResponse response) { + List list = hotSystemNotificationService.queryList(bo); + ExcelUtil.exportExcel(list, "系统通知", HotSystemNotificationVo.class, response); + } + + /** + * 获取系统通知详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("noticeSignDocument:systemNotification:query") + @GetMapping("/{id}") + @Operation(summary = "获取系统通知详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotSystemNotificationService.queryById(id)); + } + + /** + * 新增系统通知 + */ + //@SaCheckPermission("noticeSignDocument:systemNotification:add") + @Log(title = "系统通知", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增系统通知") + public R add(@Validated(AddGroup.class) @RequestBody HotSystemNotificationGroupBo bo) { + LoginUser loginUser = LoginHelper.getLoginUser(); + assert loginUser != null; + //手动发放消息,只有企业端人员和政府端人员可以发放 + if (StringUtils.equals(loginUser.getLoginPort(), ISysUserLoginPortService.ENTERPRISE_PORT)) { + bo.setSenderType("COMPANY"); + return toAjax(hotSystemNotificationService.insertByBo(bo)); + } + if (StringUtils.equals(loginUser.getLoginPort(), ISysUserLoginPortService.GOVERNMENT_PORT)) { + bo.setSenderType("GOVERNMENT"); + return toAjax(hotSystemNotificationService.insertByBo(bo)); + } + return toAjax(false); + } + + /** + * 修改系统通知 + */ + //@SaCheckPermission("noticeSignDocument:systemNotification:edit") + @Log(title = "系统通知", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改系统通知") + public R edit(@Validated(EditGroup.class) @RequestBody HotSystemNotificationBo bo) { + return toAjax(hotSystemNotificationService.updateByBo(bo)); + } + + /** + * 删除系统通知 + * + * @param ids 主键串 + */ + //@SaCheckPermission("noticeSignDocument:systemNotification:remove") + @Log(title = "系统通知", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除系统通知") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotSystemNotificationService.deleteWithValidByIds(List.of(ids), true)); + } + + @SaIgnore + @Log(title = "系统通知", businessType = BusinessType.UPDATE) + @PutMapping("/read/{id}") + @Operation(summary = "标记系统通知为已读") + public R markRead(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return toAjax(hotSystemNotificationService.markRead(id)); + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/domain/HotSystemNotification.java b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/domain/HotSystemNotification.java new file mode 100644 index 0000000..4b9ba16 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/domain/HotSystemNotification.java @@ -0,0 +1,110 @@ +package com.hotwj.platform.noticeManagerment.systemNotification.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 系统通知对象 hot_system_notification + * + * @author shihongwei + * @date 2026-01-07 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_system_notification") +public class HotSystemNotification extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 等级(紧急和普通) + */ + private String level; + + /** + * 消息内容 + */ + private String content; + + /** + * 来源类型 + */ + private String sourceType; + + /** + * 发送人类型(系统、政府端人员、企业端人员) + */ + private String senderType; + + /** + * 发送人ID + */ + private Long senderId; + + /** + * 发送人姓名 + */ + private String senderName; + + /** + * 发送人手机号 + */ + private String senderMobile; + + /** + * 发送时间 + */ + private Date sendTime; + + /** + * 状态(已读、未读) + */ + private String status; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 接收人类型(企业端人员、驾驶员) + */ + private String receiverType; + + /** + * 接收人ID + */ + private String receiverId; + + /** + * 接收人姓名 + */ + private String receiverName; + + /** + * 接收人所属企业Id + */ + private Long companyId; + + /** + * 收到时间 + */ + private Date receiveTime; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/domain/bo/HotSystemNotificationBo.java b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/domain/bo/HotSystemNotificationBo.java new file mode 100644 index 0000000..d4d8951 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/domain/bo/HotSystemNotificationBo.java @@ -0,0 +1,106 @@ +package com.hotwj.platform.noticeManagerment.systemNotification.domain.bo; + +import com.hotwj.platform.noticeManagerment.systemNotification.domain.HotSystemNotification; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 系统通知业务对象 hot_system_notification + * + * @author shihongwei + * @date 2026-01-07 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotSystemNotification.class, reverseConvertGenerate = false) +public class HotSystemNotificationBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 等级(紧急和普通) + */ + private String level; + + /** + * 消息内容 + */ + private String content; + + /** + * 来源类型 + */ + private String sourceType; + + /** + * 发送人类型(系统、政府端人员、企业端人员) + */ + private String senderType; + + /** + * 发送人ID + */ + private Long senderId; + + /** + * 发送人姓名 + */ + private String senderName; + + /** + * 发送人手机号 + */ + private String senderMobile; + + /** + * 发送时间 + */ + private Date sendTime; + + /** + * 状态(已读、未读) + */ + private String status; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + /** + * 接收人类型(企业端人员、驾驶员) + */ + private String receiverType; + + /** + * 接收人ID + */ + private String receiverId; + + /** + * 接收人姓名 + */ + private String receiverName; + + /** + * 接收人所属企业Id + */ + private Long companyId; + + /** + * 收到时间 + */ + private Date receiveTime; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/domain/bo/HotSystemNotificationGroupBo.java b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/domain/bo/HotSystemNotificationGroupBo.java new file mode 100644 index 0000000..919c9e1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/domain/bo/HotSystemNotificationGroupBo.java @@ -0,0 +1,97 @@ +package com.hotwj.platform.noticeManagerment.systemNotification.domain.bo; + +import com.hotwj.platform.noticeManagerment.systemNotification.domain.HotSystemNotification; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; +import java.util.List; + +/** + * 系统通知业务对象 hot_system_notification + * + * @author shihongwei + * @date 2026-01-07 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotSystemNotification.class, reverseConvertGenerate = false) +public class HotSystemNotificationGroupBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 等级(紧急和普通) + */ + private String level; + + /** + * 消息内容 + */ + private String content; + + /** + * 来源类型 + */ + private String sourceType; + + /** + * 发送人类型(系统SYSTEM、政府端人员GOVERNMENT、企业端人员COMPANY) + */ + private String senderType; + + /** + * 发送人ID + */ + private Long senderId; + + /** + * 发送人姓名 + */ + private String senderName; + + /** + * 发送人手机号 + */ + private String senderMobile; + + /** + * 发送时间 + */ + private Date sendTime; + + /** + * 状态(已读、未读) + */ + private String status; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + /** + * 接收人类型(企业端人员ENTERPRISE、驾驶员DRIVER) + */ + private String receiverType; + + /** + * 接收人ID + */ + private List receiverIds; + + + /** + * 接收人IDs,逗号分隔 + */ + private String receiverId; + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/domain/vo/HotSystemNotificationVo.java b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/domain/vo/HotSystemNotificationVo.java new file mode 100644 index 0000000..c5e9636 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/domain/vo/HotSystemNotificationVo.java @@ -0,0 +1,125 @@ +package com.hotwj.platform.noticeManagerment.systemNotification.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.HotSystemNotification; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 系统通知视图对象 hot_system_notification + * + * @author shihongwei + * @date 2026-01-07 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotSystemNotification.class) +public class HotSystemNotificationVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 等级(紧急和普通) + */ + @ExcelProperty(value = "等级") + private String level; + + /** + * 消息内容 + */ + @ExcelProperty(value = "消息内容") + private String content; + + /** + * 来源类型 + */ + @ExcelProperty(value = "来源类型") + private String sourceType; + + /** + * 发送人类型(系统、政府端人员、企业端人员) + */ + @ExcelProperty(value = "发送人类型") + private String senderType; + + /** + * 发送人ID + */ + @ExcelProperty(value = "发送人ID") + private Long senderId; + + /** + * 发送人姓名 + */ + @ExcelProperty(value = "发送人姓名") + private String senderName; + + /** + * 发送人手机号 + */ + @ExcelProperty(value = "发送人手机号") + private String senderMobile; + + /** + * 发送时间 + */ + @ExcelProperty(value = "发送时间") + private Date sendTime; + + /** + * 状态(已读、未读) + */ + @ExcelProperty(value = "状态") + private String status; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "删除状态") + private Long isDeleted; + + /** + * 接收人类型(企业端人员、驾驶员) + */ + @ExcelProperty(value = "接收人类型") + private String receiverType; + + /** + * 接收人ID + */ + @ExcelProperty(value = "接收人ID") + private String receiverId; + + /** + * 接收人姓名 + */ + @ExcelProperty(value = "接收人姓名") + private String receiverName; + + /** + * 接收人所属企业Id + */ + @ExcelProperty(value = "接收人所属企业Id") + private Long companyId; + + /** + * 收到时间 + */ + @ExcelProperty(value = "收到时间") + private Date receiveTime; + + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/enums/LevelType.java b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/enums/LevelType.java new file mode 100644 index 0000000..ddc082c --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/enums/LevelType.java @@ -0,0 +1,32 @@ +package com.hotwj.platform.noticeManagerment.systemNotification.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Objects; +import java.util.stream.Stream; + +/** + * 消息等级类型 + *

+ */ +@Getter +@RequiredArgsConstructor +public enum LevelType { + NORMAL(1L, "普通"), // + EMERGENCY(2L, "紧急"); + + /** + * 类型 + */ + private final Long type; + /** + * 类型名 + */ + private final String name; + + public static LevelType toType(Long type) { + return Stream.of(LevelType.values()).filter(p -> Objects.equals(p.type, type)).findAny().orElse(null); + } + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/enums/ReceiverType.java b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/enums/ReceiverType.java new file mode 100644 index 0000000..a51815f --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/enums/ReceiverType.java @@ -0,0 +1,32 @@ +package com.hotwj.platform.noticeManagerment.systemNotification.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Objects; +import java.util.stream.Stream; + +/** + * 消息接受人类型 + *

+ */ +@Getter +@RequiredArgsConstructor +public enum ReceiverType { + ENTERPRISE(1L, "企业管理员"), // + DRIVER(2L, "驾驶员"); + + /** + * 类型 + */ + private final Long type; + /** + * 类型名 + */ + private final String name; + + public static ReceiverType toType(Long type) { + return Stream.of(ReceiverType.values()).filter(p -> Objects.equals(p.type, type)).findAny().orElse(null); + } + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/enums/SenderType.java b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/enums/SenderType.java new file mode 100644 index 0000000..49aa97d --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/enums/SenderType.java @@ -0,0 +1,33 @@ +package com.hotwj.platform.noticeManagerment.systemNotification.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Objects; +import java.util.stream.Stream; + +/** + * 消息发送人类型 + *

+ */ +@Getter +@RequiredArgsConstructor +public enum SenderType { + SYSTEM(1L, "系统"), // + GOVERNMENT(2L, "政府端"), + COMPANY(3L, "企业端"); + + /** + * 类型 + */ + private final Long type; + /** + * 类型名 + */ + private final String name; + + public static SenderType toType(Long type) { + return Stream.of(SenderType.values()).filter(p -> Objects.equals(p.type, type)).findAny().orElse(null); + } + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/enums/SourceType.java b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/enums/SourceType.java new file mode 100644 index 0000000..e9bb0ec --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/enums/SourceType.java @@ -0,0 +1,39 @@ +package com.hotwj.platform.noticeManagerment.systemNotification.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Objects; +import java.util.stream.Stream; + +/** + * 消息来源类型 + *

+ */ +@Getter +@RequiredArgsConstructor +public enum SourceType { + COMPANY(1L, "企业"), // + CAR(2L, "车辆"), + DRIVER(3L, "驾驶员"), + SYSTEM(4L, "制度与会议"), + DANGER(5L, "隐患"), + VIOLATION(6L, "违规"), + INTERVIEW(7L, "约谈"), + TRAINING(8L, "培训"), + SUGGESTION(9L, "投诉建议"); + + /** + * 类型 + */ + private final Long type; + /** + * 类型名 + */ + private final String name; + + public static SourceType toType(Long type) { + return Stream.of(SourceType.values()).filter(p -> Objects.equals(p.type, type)).findAny().orElse(null); + } + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/mapper/HotSystemNotificationMapper.java b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/mapper/HotSystemNotificationMapper.java new file mode 100644 index 0000000..04aa55d --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/mapper/HotSystemNotificationMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.noticeManagerment.systemNotification.mapper; + +import com.hotwj.platform.noticeManagerment.systemNotification.domain.HotSystemNotification; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.vo.HotSystemNotificationVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 系统通知Mapper接口 + * + * @author shihongwei + * @date 2026-01-07 + */ +@Mapper +public interface HotSystemNotificationMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/service/IHotSystemNotificationService.java b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/service/IHotSystemNotificationService.java new file mode 100644 index 0000000..7ee0432 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/service/IHotSystemNotificationService.java @@ -0,0 +1,88 @@ +package com.hotwj.platform.noticeManagerment.systemNotification.service; + +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationBo; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.vo.HotSystemNotificationVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 系统通知Service接口 + * + * @author shihongwei + * @date 2026-01-07 + */ +public interface IHotSystemNotificationService { + + /** + * 查询系统通知 + * + * @param id 主键 + * @return 系统通知 + */ + HotSystemNotificationVo queryById(Long id); + + /** + * 分页查询系统通知列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 系统通知分页列表 + */ + TableDataInfo queryPageList(HotSystemNotificationBo bo, PageQuery pageQuery); + + /** + * 分页查询系统通知列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 系统通知分页列表 + */ + TableDataInfo queryPageListByType(HotSystemNotificationBo bo, PageQuery pageQuery); + + + + /** + * 查询符合条件的系统通知列表 + * + * @param bo 查询条件 + * @return 系统通知列表 + */ + List queryList(HotSystemNotificationBo bo); + + /** + * 新增系统通知 + * + * @param bo 系统通知 + * @return 是否新增成功 + */ + Boolean insertByBo(HotSystemNotificationGroupBo bo); + + /** + * 修改系统通知 + * + * @param bo 系统通知 + * @return 是否修改成功 + */ + Boolean updateByBo(HotSystemNotificationBo bo); + + /** + * 校验并批量删除系统通知信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 标记系统通知为已读 + * + * @param id 系统通知主键 + * @return 是否标记成功 + */ + Boolean markRead(Long id); +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/service/impl/HotSystemNotificationServiceImpl.java b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/service/impl/HotSystemNotificationServiceImpl.java new file mode 100644 index 0000000..ba9b2c0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/service/impl/HotSystemNotificationServiceImpl.java @@ -0,0 +1,571 @@ +package com.hotwj.platform.noticeManagerment.systemNotification.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.HotSystemNotification; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationBo; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.vo.HotSystemNotificationVo; +import com.hotwj.platform.noticeManagerment.systemNotification.mapper.HotSystemNotificationMapper; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import com.hotwj.platform.resourceManagement.personnelConfig.domain.HotPersonnelConfig; +import com.hotwj.platform.resourceManagement.personnelConfig.mapper.HotPersonnelConfigMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.sse.dto.SseMessageDto; +import org.dromara.common.sse.utils.SseMessageUtils; +import org.dromara.system.domain.vo.SysUserVo; +import org.dromara.system.service.ISysUserService; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * 系统通知Service业务层处理 + * + * @author shihongwei + * @date 2026-01-07 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotSystemNotificationServiceImpl implements IHotSystemNotificationService { + + private final HotSystemNotificationMapper baseMapper; + private final HotDriverMapper driverMapper; + private final HotCompanySafetyManagerMapper companySafetyManagerMapper; + private final HotPersonnelConfigMapper personnelConfigMapper; + private final ISysUserService sysUserService; + + /** + * 查询系统通知 + * + * @param id 主键 + * @return 系统通知 + */ + @Override + public HotSystemNotificationVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询系统通知列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 系统通知分页列表 + */ + @Override + public TableDataInfo queryPageList(HotSystemNotificationBo bo, PageQuery pageQuery) { + LoginUser loginUser = LoginHelper.getLoginUser(); + assert loginUser != null; + String businessUserId = loginUser.getBusinessUserId(); + if (StringUtils.equals(loginUser.getLoginPort(), ISysUserLoginPortService.DRIVER_PORT)) { + bo.setReceiverId(businessUserId); + } + if (StringUtils.equals(loginUser.getLoginPort(), ISysUserLoginPortService.ENTERPRISE_PORT)) { + bo.setCompanyId(loginUser.getCompanyId()); + } + + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + @Override + public TableDataInfo queryPageListByType(HotSystemNotificationBo bo, PageQuery pageQuery) { + LoginUser loginUser = LoginHelper.getLoginUser(); + assert loginUser != null; + String businessUserId = loginUser.getBusinessUserId(); + if (StringUtils.equals(loginUser.getLoginPort(), ISysUserLoginPortService.DRIVER_PORT)) { + bo.setReceiverId(businessUserId); + } + if (StringUtils.equals(loginUser.getLoginPort(), ISysUserLoginPortService.ENTERPRISE_PORT)) { + bo.setCompanyId(loginUser.getCompanyId()); + } + + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的系统通知列表 + * + * @param bo 查询条件 + * @return 系统通知列表 + */ + @Override + public List queryList(HotSystemNotificationBo bo) { + LoginUser loginUser = LoginHelper.getLoginUser(); + assert loginUser != null; + String businessUserId = loginUser.getBusinessUserId(); + if (StringUtils.equals(loginUser.getLoginPort(), ISysUserLoginPortService.DRIVER_PORT)) { + bo.setReceiverId(businessUserId); + } + if (StringUtils.equals(loginUser.getLoginPort(), ISysUserLoginPortService.ENTERPRISE_PORT)) { + bo.setCompanyId(loginUser.getCompanyId()); + } + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotSystemNotificationBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotSystemNotification::getSendTime); + Object beginTime = params == null ? null : params.get("beginTime"); + Object endTime = params == null ? null : params.get("endTime"); + if (beginTime != null && endTime != null) { + lqw.between(HotSystemNotification::getSendTime, beginTime, endTime); + } else if (beginTime != null) { + lqw.ge(HotSystemNotification::getSendTime, beginTime); + } else if (endTime != null) { + lqw.le(HotSystemNotification::getSendTime, endTime); + } + lqw.eq(StringUtils.isNotBlank(bo.getLevel()), HotSystemNotification::getLevel, bo.getLevel()); + lqw.eq(StringUtils.isNotBlank(bo.getSourceType()), HotSystemNotification::getSourceType, bo.getSourceType()); + if (StringUtils.isNotBlank(bo.getSenderType())) { + lqw.in(HotSystemNotification::getSenderType, Arrays.asList(bo.getSenderType().split(","))); + } + lqw.eq(bo.getSenderId() != null, HotSystemNotification::getSenderId, bo.getSenderId()); + lqw.like(StringUtils.isNotBlank(bo.getSenderName()), HotSystemNotification::getSenderName, bo.getSenderName()); + lqw.like(StringUtils.isNotBlank(bo.getSenderMobile()), HotSystemNotification::getSenderMobile, bo.getSenderMobile()); + lqw.eq(bo.getSendTime() != null, HotSystemNotification::getSendTime, bo.getSendTime()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), HotSystemNotification::getStatus, bo.getStatus()); + lqw.eq(StringUtils.isNotBlank(bo.getReceiverType()), HotSystemNotification::getReceiverType, bo.getReceiverType()); + lqw.eq(StringUtils.isNotBlank(bo.getReceiverId()), HotSystemNotification::getReceiverId, bo.getReceiverId()); + lqw.like(StringUtils.isNotBlank(bo.getReceiverName()), HotSystemNotification::getReceiverName, bo.getReceiverName()); + lqw.eq(bo.getCompanyId() != null, HotSystemNotification::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getReceiveTime() != null, HotSystemNotification::getReceiveTime, bo.getReceiveTime()); + lqw.like(StringUtils.isNotBlank(bo.getContent()), HotSystemNotification::getContent, bo.getContent()); + lqw.eq(bo.getIsDeleted() != null, HotSystemNotification::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增系统通知 + * + * @param bos 系统通知 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotSystemNotificationGroupBo bos) { + + if ("GOVERNMENT".equals(bos.getSenderType())) { + if (bos.getSenderId() != null) { + HotPersonnelConfig config = personnelConfigMapper.selectById(bos.getSenderId()); + if (config != null) { + if (StringUtils.isBlank(bos.getSenderName())) { + bos.setSenderName(config.getStaffName()); + } + bos.setSenderMobile(config.getMobile()); + } + } + } else if ("COMPANY".equals(bos.getSenderType())) { + if (bos.getSenderId() != null) { + HotCompanySafetyManager manager = companySafetyManagerMapper.selectById(bos.getSenderId()); + if (manager != null) { + if (StringUtils.isBlank(bos.getSenderName())) { + bos.setSenderName(manager.getName()); + } + bos.setSenderMobile(manager.getPhone()); + } + } + } + + if ((bos.getReceiverIds() == null || bos.getReceiverIds().isEmpty()) && StringUtils.isEmpty(bos.getReceiverId())) { + throw new IllegalArgumentException("接受人ID列表不能为空"); + } + if (bos.getReceiverIds() != null && !bos.getReceiverIds().isEmpty()) { + List entityList = new ArrayList<>(); + for (String receiverId : bos.getReceiverIds()) { + HotSystemNotificationBo singleBo = new HotSystemNotificationBo(); + singleBo.setLevel(bos.getLevel()); + singleBo.setContent(bos.getContent()); + singleBo.setSourceType(bos.getSourceType()); + singleBo.setSenderType(bos.getSenderType()); + singleBo.setSenderId(bos.getSenderId()); + singleBo.setSenderName(bos.getSenderName()); + singleBo.setSenderMobile(bos.getSenderMobile()); + singleBo.setReceiverType(bos.getReceiverType()); + singleBo.setReceiverId(receiverId); + + if ("政府端".equals(singleBo.getReceiverType())) { + if (StringUtils.isNotBlank(singleBo.getReceiverId()) && (StringUtils.isBlank(singleBo.getReceiverName()) || singleBo.getCompanyId() == null)) { + try { + HotPersonnelConfig config = personnelConfigMapper.selectById(Long.valueOf(singleBo.getReceiverId())); + if (config != null) { + if (StringUtils.isBlank(singleBo.getReceiverName())) { + singleBo.setReceiverName(config.getStaffName()); + } + if (singleBo.getCompanyId() == null) { + singleBo.setCompanyId(config.getUnitId()); + } + } + } catch (NumberFormatException e) { + log.warn("政府端人员ID格式错误: {}", singleBo.getReceiverId()); + } + } + } else if ("管理员".equals(singleBo.getReceiverType())) { + if (StringUtils.isNotBlank(singleBo.getReceiverId()) && (StringUtils.isBlank(singleBo.getReceiverName()) || singleBo.getCompanyId() == null)) { + try { + Long id = Long.valueOf(singleBo.getReceiverId()); + HotCompanySafetyManager manager = companySafetyManagerMapper.selectById(id); + if (manager != null) { + if (StringUtils.isBlank(singleBo.getReceiverName())) { + singleBo.setReceiverName(manager.getName()); + } + if (singleBo.getCompanyId() == null) { + singleBo.setCompanyId(manager.getCompanyId()); + } + } + } catch (NumberFormatException e) { + log.warn("企业人员ID格式错误: {}", singleBo.getReceiverId()); + } + } + } else if ("驾驶员".equals(singleBo.getReceiverType())) { + if (StringUtils.isNotBlank(singleBo.getReceiverId()) && (StringUtils.isBlank(singleBo.getReceiverName()) || singleBo.getCompanyId() == null)) { + HotDriver driver = driverMapper.selectById(singleBo.getReceiverId()); + if (driver != null) { + if (StringUtils.isBlank(singleBo.getReceiverName())) { + singleBo.setReceiverName(driver.getName()); + } + if (singleBo.getCompanyId() == null) { + singleBo.setCompanyId(driver.getCompanyId()); + } + } + } + } else if ("企业回复".equals(singleBo.getReceiverType())) { + if (StringUtils.isNotBlank(singleBo.getReceiverId()) && (StringUtils.isBlank(singleBo.getReceiverName()) || singleBo.getCompanyId() == null)) { + singleBo.setCompanyId(Long.valueOf(singleBo.getReceiverId())); + } + if (singleBo.getReceiverId().equals("1")) { + singleBo.setReceiverName("总部端"); + } else { + singleBo.setReceiverName("政府端"); + } + } else if ("政府审核通过".equals(singleBo.getReceiverType())) { + singleBo.setCompanyId(1L); + singleBo.setReceiverName("总部端"); + } else { + if (StringUtils.isNotBlank(singleBo.getReceiverId()) && (StringUtils.isBlank(singleBo.getReceiverName()) || singleBo.getCompanyId() == null)) { + try { + Long id = Long.valueOf(singleBo.getReceiverId()); + HotCompanySafetyManager manager = companySafetyManagerMapper.selectById(id); + if (manager != null) { + if (StringUtils.isBlank(singleBo.getReceiverName())) { + singleBo.setReceiverName(manager.getName()); + } + if (singleBo.getCompanyId() == null) { + singleBo.setCompanyId(manager.getCompanyId()); + } + } else { + HotDriver driver = driverMapper.selectById(singleBo.getReceiverId()); + if (driver != null) { + if (StringUtils.isBlank(singleBo.getReceiverName())) { + singleBo.setReceiverName(driver.getName()); + } + if (singleBo.getCompanyId() == null) { + singleBo.setCompanyId(driver.getCompanyId()); + } + } + } + } catch (NumberFormatException e) { + log.warn("企业人员ID格式错误: {}", singleBo.getReceiverId()); + } + } + } + + if (singleBo.getSendTime() == null) { + singleBo.setSendTime(new Date()); + } + if (StringUtils.isBlank(singleBo.getStatus())) { + singleBo.setStatus("未读"); + } + + HotSystemNotification entity = MapstructUtils.convert(singleBo, HotSystemNotification.class); + validEntityBeforeSave(entity); + entityList.add(entity); + } + boolean result = baseMapper.insertBatch(entityList); + if (result) { + entityList.forEach(entity -> sendSseMessage(entity, false)); + } + return result; + } else { + String receiverId = bos.getReceiverId(); + HotSystemNotificationBo singleBo = new HotSystemNotificationBo(); + singleBo.setLevel(bos.getLevel()); + singleBo.setContent(bos.getContent()); + singleBo.setSourceType(bos.getSourceType()); + singleBo.setSenderType(bos.getSenderType()); + singleBo.setSenderId(bos.getSenderId()); + singleBo.setSenderName(bos.getSenderName()); + singleBo.setSenderMobile(bos.getSenderMobile()); + singleBo.setReceiverType(bos.getReceiverType()); + singleBo.setReceiverId(receiverId); + + if ("政府端".equals(singleBo.getReceiverType())) { + if (StringUtils.isNotBlank(singleBo.getReceiverId()) && (StringUtils.isBlank(singleBo.getReceiverName()) || singleBo.getCompanyId() == null)) { + try { + HotPersonnelConfig config = personnelConfigMapper.selectById(Long.valueOf(singleBo.getReceiverId())); + if (config != null) { + if (StringUtils.isBlank(singleBo.getReceiverName())) { + singleBo.setReceiverName(config.getStaffName()); + } + if (singleBo.getCompanyId() == null) { + singleBo.setCompanyId(config.getUnitId()); + } + } + } catch (NumberFormatException e) { + log.warn("政府端人员ID格式错误: {}", singleBo.getReceiverId()); + } + } + } else if ("管理员".equals(singleBo.getReceiverType())) { + if (StringUtils.isNotBlank(singleBo.getReceiverId()) && (StringUtils.isBlank(singleBo.getReceiverName()) || singleBo.getCompanyId() == null)) { + try { + Long id = Long.valueOf(singleBo.getReceiverId()); + HotCompanySafetyManager manager = companySafetyManagerMapper.selectById(id); + if (manager != null) { + if (StringUtils.isBlank(singleBo.getReceiverName())) { + singleBo.setReceiverName(manager.getName()); + } + if (singleBo.getCompanyId() == null) { + singleBo.setCompanyId(manager.getCompanyId()); + } + } + } catch (NumberFormatException e) { + log.warn("企业人员ID格式错误: {}", singleBo.getReceiverId()); + } + } + } else if ("驾驶员".equals(singleBo.getReceiverType())) { + if (StringUtils.isNotBlank(singleBo.getReceiverId()) && (StringUtils.isBlank(singleBo.getReceiverName()) || singleBo.getCompanyId() == null)) { + HotDriver driver = driverMapper.selectById(singleBo.getReceiverId()); + if (driver != null) { + if (StringUtils.isBlank(singleBo.getReceiverName())) { + singleBo.setReceiverName(driver.getName()); + } + if (singleBo.getCompanyId() == null) { + singleBo.setCompanyId(driver.getCompanyId()); + } + } + } + } else { + if (StringUtils.isNotBlank(singleBo.getReceiverId()) && (StringUtils.isBlank(singleBo.getReceiverName()) || singleBo.getCompanyId() == null)) { + try { + Long id = Long.valueOf(singleBo.getReceiverId()); + HotCompanySafetyManager manager = companySafetyManagerMapper.selectById(id); + if (manager != null) { + if (StringUtils.isBlank(singleBo.getReceiverName())) { + singleBo.setReceiverName(manager.getName()); + } + if (singleBo.getCompanyId() == null) { + singleBo.setCompanyId(manager.getCompanyId()); + } + singleBo.setReceiverType("管理员"); + } + } catch (NumberFormatException e) { + HotDriver driver = driverMapper.selectById(singleBo.getReceiverId()); + if (driver != null) { + if (StringUtils.isBlank(singleBo.getReceiverName())) { + singleBo.setReceiverName(driver.getName()); + } + if (singleBo.getCompanyId() == null) { + singleBo.setCompanyId(driver.getCompanyId()); + } + singleBo.setReceiverType("驾驶员"); + } + } + } + } + + if (singleBo.getSendTime() == null) { + singleBo.setSendTime(new Date()); + } + if (StringUtils.isBlank(singleBo.getStatus())) { + singleBo.setStatus("未读"); + } + + HotSystemNotification entity = MapstructUtils.convert(singleBo, HotSystemNotification.class); + validEntityBeforeSave(entity); + int rows = baseMapper.insert(entity); + if (rows > 0) { + sendSseMessage(entity, false); + } + return rows > 0; + } + + } + + /** + * 发送SSE消息 + */ + private void sendSseMessage(HotSystemNotification entity, boolean isSilent) { + try { + Long userId = null; + Set userIds = new LinkedHashSet<>(); + if ("政府端".equals(entity.getReceiverType())) { + try { + HotPersonnelConfig config = personnelConfigMapper.selectById(Long.valueOf(entity.getReceiverId())); + if (config != null && StringUtils.isNotBlank(config.getMobile())) { + SysUserVo user = sysUserService.selectUserByPhonenumber(config.getMobile()); + if (user != null) { + userId = user.getUserId(); + userIds.add(userId); + } + } + } catch (NumberFormatException e) { + log.warn("发送SSE消息失败,政府端人员ID格式错误: {}", entity.getReceiverId()); + } + } else if ("管理员".equals(entity.getReceiverType())) { + try { + HotCompanySafetyManager manager = companySafetyManagerMapper.selectById(Long.valueOf(entity.getReceiverId())); + if (manager != null) { + userId = manager.getUserId(); + if (userId != null) { + userIds.add(userId); + } + } + } catch (NumberFormatException e) { + log.warn("发送SSE消息失败,管理员ID格式错误: {}", entity.getReceiverId()); + } + } else if ("驾驶员".equals(entity.getReceiverType())) { + HotDriver driver = driverMapper.selectById(entity.getReceiverId()); + if (driver != null && StringUtils.isNotBlank(driver.getPhone())) { + SysUserVo user = sysUserService.selectUserByPhonenumber(driver.getPhone()); + if (user != null) { + userId = user.getUserId(); + userIds.add(userId); + } + } + } else if ("企业回复".equals(entity.getReceiverType()) || "政府审核通过".equals(entity.getReceiverType())) { + try { + Long unitId = Long.valueOf(entity.getReceiverId()); + List configs = personnelConfigMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotPersonnelConfig::getUnitId, unitId) + .eq(HotPersonnelConfig::getIsEnabled, 1L) + .eq(HotPersonnelConfig::getIsDeleted, 0L) + ); + if (configs != null) { + for (HotPersonnelConfig config : configs) { + if (config == null || StringUtils.isBlank(config.getMobile())) { + continue; + } + SysUserVo user = sysUserService.selectUserByPhonenumber(config.getMobile()); + if (user != null && user.getUserId() != null) { + userIds.add(user.getUserId()); + } + } + } + } catch (NumberFormatException e) { + log.warn("发送SSE消息失败,单位ID格式错误 receiverType={} receiverId={}", entity.getReceiverType(), entity.getReceiverId()); + } + } + + if (!userIds.isEmpty()) { + HotSystemNotificationVo vo = MapstructUtils.convert(entity, HotSystemNotificationVo.class); + SseMessageDto dto = new SseMessageDto(); + dto.setUserIds(new ArrayList<>(userIds)); + if (isSilent) { + dto.setMessage(JsonUtils.toJsonString(Map.of("isSilent", true))); + } else { + dto.setMessage(JsonUtils.toJsonString(vo)); + } + SseMessageUtils.publishMessage(dto); + } + } catch (Exception e) { + log.error("发送SSE消息失败", e); + } + } + + /** + * 修改系统通知 + * + * @param bo 系统通知 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotSystemNotificationBo bo) { + HotSystemNotification update = MapstructUtils.convert(bo, HotSystemNotification.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotSystemNotification entity) { + + // 1. 参数判断 + if (StringUtils.isBlank(entity.getLevel())) { + throw new IllegalArgumentException("等级不能为空"); + } + if (StringUtils.isBlank(entity.getContent())) { + throw new IllegalArgumentException("消息内容不能为空"); + } + if (StringUtils.isBlank(entity.getSourceType())) { + throw new IllegalArgumentException("来源类型不能为空"); + } + if (StringUtils.isBlank(entity.getSenderType())) { + throw new IllegalArgumentException("发送人类型不能为空"); + } + if (StringUtils.isBlank(entity.getReceiverType())) { + throw new IllegalArgumentException("接受人类型不能为空"); + } + if (StringUtils.isBlank(entity.getReceiverId())) { + throw new IllegalArgumentException("接受人ID不能为空"); + } + if (entity.getCompanyId() == null) { + throw new IllegalArgumentException("公司ID不能为空"); + } + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除系统通知信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + + @Override + public Boolean markRead(Long id) { + LambdaUpdateWrapper uw = Wrappers.lambdaUpdate(); + uw.eq(HotSystemNotification::getId, id); + uw.set(HotSystemNotification::getStatus, "已读"); + uw.set(HotSystemNotification::getReceiveTime, new Date()); + boolean success = baseMapper.update(null, uw) > 0; + if (success) { + HotSystemNotification entity = baseMapper.selectById(id); + if (entity != null) { + sendSseMessage(entity, true); + } + } + return success; + } +} diff --git a/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/task/SystemNotificationTask.java b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/task/SystemNotificationTask.java new file mode 100644 index 0000000..ddd8709 --- /dev/null +++ b/src/main/java/com/hotwj/platform/noticeManagerment/systemNotification/task/SystemNotificationTask.java @@ -0,0 +1,778 @@ +package com.hotwj.platform.noticeManagerment.systemNotification.task; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.hotwj.platform.config.companyDeptConfig.domain.HotCompanyDeptConfig; +import com.hotwj.platform.config.companyDeptConfig.mapper.HotCompanyDeptConfigMapper; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.bo.HotVehicleSecondaryMaintenanceConfigBo; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.vo.HotVehicleSecondaryMaintenanceConfigVo; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.service.IHotVehicleSecondaryMaintenanceConfigService; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.integration.sms.SmsBaoClient; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.company.domain.SysCompany; +import com.hotwj.platform.resourceManagement.company.mapper.SysCompanyMapper; +import com.hotwj.platform.resourceManagement.companyChangeHistory.domain.HotCompanyChangeHistory; +import com.hotwj.platform.resourceManagement.companyChangeHistory.mapper.HotCompanyChangeHistoryMapper; +import com.hotwj.platform.resourceManagement.companyInsurance.domain.HotCompanyInsurance; +import com.hotwj.platform.resourceManagement.companyInsurance.mapper.HotCompanyInsuranceMapper; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.vo.HotVehicleMaintenanceVo; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.service.IHotVehicleMaintenanceService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle; +import com.hotwj.platform.resourceManagement.vehicleManagement.mapper.HotVehicleMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.DateUtils; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.YearMonth; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 系统消息定时任务 + * + * @author shihongwei + * @date 2026-02-25 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class SystemNotificationTask { + + private final SysCompanyMapper sysCompanyMapper; + private final HotCompanySafetyManagerMapper managerMapper; + private final IHotSystemNotificationService notificationService; + private final HotCompanyChangeHistoryMapper changeHistoryMapper; + private final HotCompanyDeptConfigMapper deptConfigMapper; + private final HotCompanyInsuranceMapper insuranceMapper; + private final HotVehicleMapper vehicleMapper; + private final IHotVehicleSecondaryMaintenanceConfigService secondaryMaintenanceConfigService; + private final IHotVehicleMaintenanceService vehicleMaintenanceService; + private final HotDriverMapper hotDriverMapper; + private final SmsBaoClient smsBaoClient; + + /** + * 道路运输许可证到期 + */ + @Scheduled(cron = "0 0 9 * * ?") + public void notifyUpcomingExpiry() { + LocalDate today = LocalDate.now(); + List offsets = Arrays.asList(30, 15, 7, 1); + for (Integer days : offsets) { + LocalDate target = today.plusDays(days); + List companies = sysCompanyMapper.selectList( + Wrappers.lambdaQuery() + .eq(SysCompany::getStatus, 1L) + .eq(SysCompany::getIsDeleted, 0L) + .eq(SysCompany::getRoadExpireDate, target) + ); + for (SysCompany c : companies) { + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, c.getId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers == null || managers.isEmpty()) { + continue; + } + List receiverIds = managers.stream() + .map(m -> String.valueOf(m.getId())) + .collect(Collectors.toList()); + + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("紧急"); + bos.setContent("企业《道路运输经营许可证》将于" + days + "天后到期(" + c.getRoadExpireDate() + "),请及时办理。"); + bos.setSourceType("企业管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送道路运输许可证到期提醒失败 companyId={} days={}", c.getId(), days, e); + } + } + } + } + + /** + * 企业营业期到期 + */ + @Scheduled(cron = "0 0 9 * * ?") + public void notifyBusinessTermExpiry() { + LocalDate today = LocalDate.now(); + List offsets = Arrays.asList(30, 15, 7, 1); + for (Integer days : offsets) { + LocalDate target = today.plusDays(days); + List companies = sysCompanyMapper.selectList( + Wrappers.lambdaQuery() + .eq(SysCompany::getStatus, 1L) + .eq(SysCompany::getIsDeleted, 0L) + .eq(SysCompany::getBusinessLongTerm, false) + .eq(SysCompany::getBusinessTerm, target) + ); + for (SysCompany c : companies) { + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, c.getId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers == null || managers.isEmpty()) { + continue; + } + List receiverIds = managers.stream() + .map(m -> String.valueOf(m.getId())) + .collect(Collectors.toList()); + + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("紧急"); + bos.setContent("企业营业期限将于" + days + "天后到期(" + c.getBusinessTerm() + "),请及时办理相关手续。"); + bos.setSourceType("企业管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送企业营业期限到期提醒失败 companyId={} days={}", c.getId(), days, e); + } + } + } + } + + /** + * 上岗资格证 + */ + @Scheduled(cron = "0 0 9 * * ?") + public void notifyManagerQualificationExpiry() { + LocalDate today = LocalDate.now(); + List offsets = Arrays.asList(30, 15, 7, 1); + for (Integer days : offsets) { + LocalDate target = today.plusDays(days); + java.sql.Date targetDate = java.sql.Date.valueOf(target); + List expiringManagers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + .eq(HotCompanySafetyManager::getCertificateExpireDate, targetDate) + ); + if (expiringManagers == null || expiringManagers.isEmpty()) { + continue; + } + var grouped = expiringManagers.stream().collect(Collectors.groupingBy(HotCompanySafetyManager::getCompanyId)); + for (Long companyId : grouped.keySet()) { + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers == null || managers.isEmpty()) { + continue; + } + List receiverIds = managers.stream() + .map(m -> String.valueOf(m.getId())) + .collect(Collectors.toList()); + + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("紧急"); + bos.setContent("企业上岗资格证将于" + days + "天后到期(到期日:" + targetDate + "),请及时办理。"); + bos.setSourceType("企业管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送企业上岗资格证到期提醒失败 companyId={} days={}", companyId, days, e); + } + } + } + } + + /** + * 企业管理人员变更职位、部门 + */ + @Scheduled(cron = "0 0/10 * * * ?") + public void notifyManagerDeptOrJobChange() { + LocalDateTime end = LocalDateTime.now(); + LocalDateTime start = end.minusMinutes(10); + List changes = changeHistoryMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanyChangeHistory::getIsDeleted, 0) + .in(HotCompanyChangeHistory::getChangeType, Arrays.asList("调岗位", "调部门", "调岗调部门")) + .ge(HotCompanyChangeHistory::getChangeTime, java.sql.Timestamp.valueOf(start)) + .lt(HotCompanyChangeHistory::getChangeTime, java.sql.Timestamp.valueOf(end)) + ); + if (changes == null || changes.isEmpty()) { + return; + } + List deptIds = changes.stream() + .flatMap(h -> Arrays.stream(new Long[]{h.getBeforeDeptId(), h.getAfterDeptId()})) + .filter(id -> id != null && id > 0) + .distinct() + .collect(Collectors.toList()); + List depts = deptIds.isEmpty() ? java.util.Collections.emptyList() : deptConfigMapper.selectBatchIds(deptIds); + java.util.Map deptNameMap = depts.stream().collect(Collectors.toMap(HotCompanyDeptConfig::getId, HotCompanyDeptConfig::getDeptName)); + + for (HotCompanyChangeHistory h : changes) { + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, h.getCompanyId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers == null || managers.isEmpty()) { + continue; + } + List receiverIds = managers.stream() + .map(m -> String.valueOf(m.getId())) + .collect(Collectors.toList()); + + String beforeDept = h.getBeforeDeptId() != null ? deptNameMap.getOrDefault(h.getBeforeDeptId(), String.valueOf(h.getBeforeDeptId())) : "未填写"; + String afterDept = h.getAfterDeptId() != null ? deptNameMap.getOrDefault(h.getAfterDeptId(), String.valueOf(h.getAfterDeptId())) : "未填写"; + String beforeJob = h.getBeforeJobName() != null ? h.getBeforeJobName() : "未填写"; + String afterJob = h.getAfterJobName() != null ? h.getAfterJobName() : "未填写"; + + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent("企业管理人员【" + h.getName() + "】" + + "发生" + h.getChangeType() + + ":岗位【" + beforeJob + "】→【" + afterJob + "】," + + "部门【" + beforeDept + "】→【" + afterDept + "】。"); + bos.setSourceType("企业管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送企业人员变更提醒失败 companyId={} employeeId={}", h.getCompanyId(), h.getEmployeeId(), e); + } + } + } + + + /** + * 车辆强制报废提醒 + */ + @Scheduled(cron = "0 0 10 * * ?") + public void notifyVehicleMandatoryScrap() { + LocalDate today = LocalDate.now(); + List offsets = Arrays.asList(30, 15, 7, 1); + for (Integer days : offsets) { + LocalDate target = today.plusDays(days); + java.sql.Date targetDate = java.sql.Date.valueOf(target); + List vehicles = vehicleMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotVehicle::getIsDeleted, 0L) + .eq(HotVehicle::getMandatoryScrapEndDate, targetDate) + ); + if (vehicles == null || vehicles.isEmpty()) { + continue; + } + for (HotVehicle v : vehicles) { + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, v.getCompanyId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers == null || managers.isEmpty()) { + continue; + } + List receiverIds = managers.stream() + .map(m -> String.valueOf(m.getId())) + .collect(Collectors.toList()); + + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("紧急"); + bos.setContent("车辆【" + v.getPlateNumber() + "】将于" + days + "天后达到强制报废期(" + v.getMandatoryScrapEndDate() + "),请按规定及时办理相关处置。"); + bos.setSourceType("车辆管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送车辆强制报废期提醒失败 vehicleId={} companyId={} days={}", v.getId(), v.getCompanyId(), days, e); + } + } + } + } + + /** + * 车辆年检到期提醒 + */ + @Scheduled(cron = "0 0 10 * * ?") + public void notifyVehicleInspectionExpiry() { + LocalDate today = LocalDate.now(); + List offsets = Arrays.asList(30, 15, 7, 1); + List all = vehicleMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotVehicle::getIsDeleted, 0L) + .isNotNull(HotVehicle::getInspectionValidMonth) + ); + if (all == null || all.isEmpty()) { + return; + } + for (Integer days : offsets) { + LocalDate target = today.plusDays(days); + for (HotVehicle v : all) { + String ymStr = v.getInspectionValidMonth(); + if (ymStr == null || ymStr.isEmpty()) { + continue; + } + try { + YearMonth ym = YearMonth.parse(ymStr); + LocalDate lastDay = ym.atEndOfMonth(); + if (!lastDay.equals(target)) { + continue; + } + } catch (Exception ignore) { + continue; + } + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, v.getCompanyId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers == null || managers.isEmpty()) { + continue; + } + List receiverIds = managers.stream() + .map(m -> String.valueOf(m.getId())) + .collect(Collectors.toList()); + + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("紧急"); + bos.setContent("车辆【" + v.getPlateNumber() + "】检验有效期将于" + days + "天后到期(" + v.getInspectionValidMonth() + "),请及时办理检验/年审。"); + bos.setSourceType("车辆管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送车辆检验有效期提醒失败 vehicleId={} companyId={} days={}", v.getId(), v.getCompanyId(), days, e); + } + } + } + } + + + /** + * 驾驶员身份证到期提醒 + */ + @Scheduled(cron = "0 45 10 * * ?") + public void notifyDriverIdCardExpiry() { + LocalDate today = LocalDate.now(); + List offsets = Arrays.asList(30, 15, 7, 1); + for (Integer days : offsets) { + LocalDate target = today.plusDays(days); + java.util.Date start = DateUtils.toDate(target.atStartOfDay()); + java.util.Date next = DateUtils.toDate(target.plusDays(1).atStartOfDay()); + List drivers = hotDriverMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotDriver::getIsDeleted, 0L) + .eq(HotDriver::getStatus, 1L) + .ge(HotDriver::getIdCardExpireDate, start) + .lt(HotDriver::getIdCardExpireDate, next) + ); + if (drivers == null || drivers.isEmpty()) { + continue; + } + List receiverIds = drivers.stream().map(HotDriver::getId).collect(java.util.stream.Collectors.toList()); + String endStr = DateUtils.formatDate(DateUtils.toDate(target)); + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("紧急"); + bos.setContent("您的身份证将于" + days + "天后到期(到期日:" + endStr + "),请及时更新身份信息。"); + bos.setSourceType("驾驶员管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("驾驶员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送驾驶员身份证到期提醒失败 days={} size={}", days, receiverIds.size(), e); + } + } + } + + /** + * 车辆运输证到期提醒 + */ + @Scheduled(cron = "0 0 10 * * ?") + public void notifyVehicleTransportExpiry() { + LocalDate today = LocalDate.now(); + List offsets = Arrays.asList(30, 15, 7, 1); + List all = vehicleMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotVehicle::getIsDeleted, 0L) + .isNotNull(HotVehicle::getTransportValidMonth) + ); + if (all == null || all.isEmpty()) { + return; + } + for (Integer days : offsets) { + LocalDate target = today.plusDays(days); + for (HotVehicle v : all) { + String ymStr = v.getTransportValidMonth(); + if (ymStr == null || ymStr.isEmpty()) { + continue; + } + try { + YearMonth ym = YearMonth.parse(ymStr); + LocalDate lastDay = ym.atEndOfMonth(); + if (!lastDay.equals(target)) { + continue; + } + } catch (Exception ignore) { + continue; + } + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, v.getCompanyId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers == null || managers.isEmpty()) { + continue; + } + List receiverIds = managers.stream() + .map(m -> String.valueOf(m.getId())) + .collect(Collectors.toList()); + + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("紧急"); + bos.setContent("车辆【" + v.getPlateNumber() + "】运输证有效期将于" + days + "天后到期(" + v.getTransportValidMonth() + "),请及时办理续证。"); + bos.setSourceType("车辆管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送车辆运输证有效期提醒失败 vehicleId={} companyId={} days={}", v.getId(), v.getCompanyId(), days, e); + } + } + } + } + + /** + * 驾驶员驾驶证到期提醒 + */ + @Scheduled(cron = "0 50 10 * * ?") + public void notifyDriverLicenseExpiry() { + LocalDate today = LocalDate.now(); + List offsets = Arrays.asList(30, 15, 7, 1); + for (Integer days : offsets) { + LocalDate target = today.plusDays(days); + java.util.Date start = DateUtils.toDate(target.atStartOfDay()); + java.util.Date next = DateUtils.toDate(target.plusDays(1).atStartOfDay()); + List drivers = hotDriverMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotDriver::getIsDeleted, 0L) + .eq(HotDriver::getStatus, 1L) + .ne(HotDriver::getDriverLicenseLongTerm, 1L) + .ge(HotDriver::getDriverLicenseExpireDate, start) + .lt(HotDriver::getDriverLicenseExpireDate, next) + ); + if (drivers == null || drivers.isEmpty()) { + continue; + } + List receiverIds = drivers.stream().map(HotDriver::getId).collect(java.util.stream.Collectors.toList()); + String endStr = DateUtils.formatDate(DateUtils.toDate(target)); + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("紧急"); + bos.setContent("您的驾驶证将于" + days + "天后到期(到期日:" + endStr + "),请及时更新驾驶证。"); + bos.setSourceType("驾驶员管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("驾驶员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送驾驶员驾驶证到期提醒失败 days={} size={}", days, receiverIds.size(), e); + } + } + } + + /** + * 驾驶员从业资格证到期提醒 + */ + @Scheduled(cron = "0 55 10 * * ?") + public void notifyDriverQualificationExpiry() { + LocalDate today = LocalDate.now(); + List offsets = Arrays.asList(30, 15, 7, 1); + for (Integer days : offsets) { + LocalDate target = today.plusDays(days); + java.util.Date start = DateUtils.toDate(target.atStartOfDay()); + java.util.Date next = DateUtils.toDate(target.plusDays(1).atStartOfDay()); + List drivers = hotDriverMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotDriver::getIsDeleted, 0L) + .eq(HotDriver::getStatus, 1L) + .ge(HotDriver::getQualificationValidEndDate, start) + .lt(HotDriver::getQualificationValidEndDate, next) + ); + if (drivers == null || drivers.isEmpty()) { + continue; + } + List receiverIds = drivers.stream().map(HotDriver::getId).collect(java.util.stream.Collectors.toList()); + String endStr = DateUtils.formatDate(DateUtils.toDate(target)); + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("紧急"); + bos.setContent("您的从业资格证将于" + days + "天后到期(到期日:" + endStr + "),请及时更新从业资格。"); + bos.setSourceType("驾驶员管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("驾驶员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送驾驶员从业资格证到期提醒失败 days={} size={}", days, receiverIds.size(), e); + } + } + } + + /** + * 驾驶员诚信承诺书到期提醒 + */ + @Scheduled(cron = "0 0 10 * * ?") + public void notifyDriverPromiseExpiry() { + LocalDate today = LocalDate.now(); + List offsets = Arrays.asList(30, 15, 7, 1); + for (Integer days : offsets) { + LocalDate target = today.plusDays(days); + java.util.Date start = DateUtils.toDate(target.atStartOfDay()); + java.util.Date next = DateUtils.toDate(target.plusDays(1).atStartOfDay()); + List drivers = hotDriverMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotDriver::getIsDeleted, 0L) + .eq(HotDriver::getStatus, 1L) + .ge(HotDriver::getIntegrityPromiseExpireDate, start) + .lt(HotDriver::getIntegrityPromiseExpireDate, next) + ); + if (drivers == null || drivers.isEmpty()) { + continue; + } + List receiverIds = drivers.stream().map(HotDriver::getId).collect(java.util.stream.Collectors.toList()); + String endStr = DateUtils.formatDate(DateUtils.toDate(target)); + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("紧急"); + bos.setContent("您的诚信承诺书将于" + days + "天后到期(到期日:" + endStr + "),请及时更新承诺书。"); + bos.setSourceType("驾驶员管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("驾驶员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送驾驶员承诺书到期提醒失败 days={} size={}", days, receiverIds.size(), e); + } + } + } + + + /** + * 企业保险到期短信提醒 + */ + @Scheduled(cron = "0 10 9 * * ?") + public void notifyInsuranceExpiryBySms() { + LocalDate today = LocalDate.now(); + java.util.Date start = DateUtils.toDate(today.atStartOfDay()); + java.util.Date next = DateUtils.toDate(today.plusDays(1).atStartOfDay()); + List ins = insuranceMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanyInsurance::getIsDeleted, 0L) + .ge(HotCompanyInsurance::getEndDate, start) + .lt(HotCompanyInsurance::getEndDate, next) + ); + if (ins == null || ins.isEmpty()) { + return; + } + for (HotCompanyInsurance i : ins) { + SysCompany c = sysCompanyMapper.selectById(i.getCompanyId()); + if (c == null) { + continue; + } + String phone = c.getResponsibleMobile(); + if (StringUtils.isBlank(phone)) { + continue; + } + String endStr = DateUtils.formatDate(i.getEndDate()); + String content = "【企业保险提醒】贵单位保险已到期(险种:" + i.getInsuranceType() + ",终保日期:" + endStr + "),请尽快办理续费。"; + try { + smsBaoClient.send(phone, content); + } catch (Exception e) { + log.warn("企业保险到期短信发送失败 companyId={} phone={}", i.getCompanyId(), phone, e); + } + } + } + + /** + * 车辆年审到期短信提醒 + */ + @Scheduled(cron = "0 20 9 * * ?") + public void notifyVehicleInspectionExpiryBySms() { + LocalDate today = LocalDate.now(); + List offsets = Arrays.asList(30, 15, 7, 1); + List all = vehicleMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotVehicle::getIsDeleted, 0L) + .isNotNull(HotVehicle::getInspectionValidMonth) + ); + if (all == null || all.isEmpty()) { + return; + } + for (Integer days : offsets) { + LocalDate target = today.plusDays(days); + for (HotVehicle v : all) { + String ymStr = v.getInspectionValidMonth(); + if (ymStr == null || ymStr.isEmpty()) { + continue; + } + try { + YearMonth ym = YearMonth.parse(ymStr); + LocalDate lastDay = ym.atEndOfMonth(); + if (!lastDay.equals(target)) { + continue; + } + } catch (Exception ignore) { + continue; + } + SysCompany c = sysCompanyMapper.selectById(v.getCompanyId()); + if (c == null) { + continue; + } + String phone = c.getResponsibleMobile(); + if (StringUtils.isBlank(phone)) { + continue; + } + String plate = StringUtils.blankToDefault(v.getPlateNumber(), "未知车牌"); + String content = "【车辆年审提醒】车辆【" + plate + "】检验有效期将于" + days + "天后到期(" + ymStr + "),请及时办理检验/年审。"; + try { + smsBaoClient.send(phone, content); + } catch (Exception e) { + log.warn("车辆年审到期短信发送失败 vehicleId={} companyId={} phone={}", v.getId(), v.getCompanyId(), phone, e); + } + } + } + } + + /** + * 车辆二级保养到期短信提醒 + */ + @Scheduled(cron = "0 30 9 * * ?") + public void notifyVehicleSecondaryMaintenanceBySms() { + LocalDate today = LocalDate.now(); + List offsets = Arrays.asList(30, 15, 7, 1); + List vehicles = vehicleMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotVehicle::getIsDeleted, 0L) + ); + if (vehicles == null || vehicles.isEmpty()) { + return; + } + for (Integer days : offsets) { + LocalDate target = today.plusDays(days); + for (HotVehicle v : vehicles) { + if (v.getCompanyId() == null || v.getCertificateIssueDate() == null) { + continue; + } + HotVehicleSecondaryMaintenanceConfigBo configBo = new HotVehicleSecondaryMaintenanceConfigBo(); + configBo.setCompanyId(v.getCompanyId()); + List allConfigs = secondaryMaintenanceConfigService.queryList(configBo); + if (allConfigs == null || allConfigs.isEmpty()) { + continue; + } + List configs = allConfigs.stream() + .filter(c -> c.getIsEnabled() == null || c.getIsEnabled() == 1L) + .sorted(java.util.Comparator.comparing(c -> c.getIntervalYears() == null ? Long.MAX_VALUE : c.getIntervalYears())) + .toList(); + if (configs.isEmpty()) { + continue; + } + LocalDate issueDate = v.getCertificateIssueDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + long ageYears = ChronoUnit.YEARS.between(issueDate, today); + HotVehicleSecondaryMaintenanceConfigVo matchConfig = null; + for (HotVehicleSecondaryMaintenanceConfigVo c : configs) { + if (c.getIntervalYears() != null && c.getIntervalYears() > ageYears) { + matchConfig = c; + break; + } + } + if (matchConfig == null) { + matchConfig = configs.get(configs.size() - 1); + } + if (matchConfig == null || matchConfig.getInspectionTimes() == null || matchConfig.getInspectionTimes() == 0) { + continue; + } + long intervalMonths = 12 / matchConfig.getInspectionTimes(); + HotVehicleMaintenanceVo lastMaintenance = vehicleMaintenanceService.queryLatestByVehicleId(v.getId()); + LocalDate lastMaintenanceDate; + if (lastMaintenance != null && lastMaintenance.getFinishTime() != null) { + lastMaintenanceDate = lastMaintenance.getFinishTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } else { + lastMaintenanceDate = issueDate; + } + LocalDate dueDate = lastMaintenanceDate.plusMonths(intervalMonths); + if (!dueDate.equals(target)) { + continue; + } + SysCompany c = sysCompanyMapper.selectById(v.getCompanyId()); + if (c == null) { + continue; + } + String phone = c.getResponsibleMobile(); + if (StringUtils.isBlank(phone)) { + continue; + } + String plate = StringUtils.blankToDefault(v.getPlateNumber(), "未知车牌"); + String dueStr = DateUtils.formatDate(DateUtils.toDate(dueDate)); + String content = "【车辆二级维护提醒】车辆【" + plate + "】将于" + days + "天后到期(二级维护到期日:" + dueStr + "),请及时安排维护。"; + try { + smsBaoClient.send(phone, content); + } catch (Exception e) { + log.warn("车辆二级维护到期短信发送失败 vehicleId={} companyId={} phone={}", v.getId(), v.getCompanyId(), phone, e); + } + } + } + } + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/contractManage/controller/HotContractManageController.java b/src/main/java/com/hotwj/platform/operationManagement/contractManage/controller/HotContractManageController.java new file mode 100644 index 0000000..d5c3834 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/contractManage/controller/HotContractManageController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.operationManagement.contractManage.controller; + +import com.hotwj.platform.operationManagement.contractManage.domain.bo.HotContractManageBo; +import com.hotwj.platform.operationManagement.contractManage.domain.vo.HotContractManageVo; +import com.hotwj.platform.operationManagement.contractManage.service.IHotContractManageService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 合同管理 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/contractManage") +@Tag(name = "合同管理", description = "合同管理管理") +public class HotContractManageController extends BaseController { + + private final IHotContractManageService hotContractManageService; + + /** + * 查询合同管理列表 + */ + //@SaCheckPermission("operationManagement:contractManage:list") + @GetMapping("/list") + @Operation(summary = "分页查询合同管理列表") + public TableDataInfo list(HotContractManageBo bo, PageQuery pageQuery) { + return hotContractManageService.queryPageList(bo, pageQuery); + } + + /** + * 导出合同管理列表 + */ + //@SaCheckPermission("operationManagement:contractManage:export") + @Log(title = "合同管理", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出合同管理列表") + public void export(HotContractManageBo bo, HttpServletResponse response) { + List list = hotContractManageService.queryList(bo); + ExcelUtil.exportExcel(list, "合同管理", HotContractManageVo.class, response); + } + + /** + * 获取合同管理详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("operationManagement:contractManage:query") + @GetMapping("/{id}") + @Operation(summary = "获取合同管理详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotContractManageService.queryById(id)); + } + + /** + * 新增合同管理 + */ + //@SaCheckPermission("operationManagement:contractManage:add") + @Log(title = "合同管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增合同管理") + public R add(@Validated(AddGroup.class) @RequestBody HotContractManageBo bo) { + return toAjax(hotContractManageService.insertByBo(bo)); + } + + /** + * 修改合同管理 + */ + //@SaCheckPermission("operationManagement:contractManage:edit") + @Log(title = "合同管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改合同管理") + public R edit(@Validated(EditGroup.class) @RequestBody HotContractManageBo bo) { + return toAjax(hotContractManageService.updateByBo(bo)); + } + + /** + * 删除合同管理 + * + * @param ids 主键串 + */ + //@SaCheckPermission("operationManagement:contractManage:remove") + @Log(title = "合同管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除合同管理") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotContractManageService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/contractManage/domain/HotContractManage.java b/src/main/java/com/hotwj/platform/operationManagement/contractManage/domain/HotContractManage.java new file mode 100644 index 0000000..d4e8c98 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/contractManage/domain/HotContractManage.java @@ -0,0 +1,126 @@ +package com.hotwj.platform.operationManagement.contractManage.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 合同管理对象 hot_contract_manage + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_contract_manage") +public class HotContractManage extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 合同编号 + */ + private String contractNo; + + /** + * 合同名称 + */ + private String contractName; + + /** + * 合同金额(元) + */ + private Long contractAmount; + + /** + * 甲方名称 + */ + private String partyAName; + + /** + * 乙方名称 + */ + private String partyBName; + + /** + * 乙方责任人 + */ + private String partyBOwner; + + /** + * 联系电话 + */ + private String contactPhone; + + /** + * 合同签订时间 + */ + private Date contractSignTime; + + /** + * 合同生效时间 + */ + private Date contractEffectiveTime; + + /** + * 合同到期时间 + */ + private Date contractExpireTime; + + /** + * 合同状态:1=草稿 2=已签订 3=已生效 4=已终止 5=已作废 + */ + private Long contractStatus; + + /** + * 签约渠道:1=微信 2=支付宝 3=银行卡 4=余额 5=线下 + */ + private Long signChannel; + + /** + * 协议文件URL + */ + private String agreementUrl; + + /** + * 来源订单ID(支付订单) + */ + private Long sourceOrderId; + + /** + * 来源订单号(支付订单) + */ + private String sourceOrderNo; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/contractManage/domain/bo/HotContractManageBo.java b/src/main/java/com/hotwj/platform/operationManagement/contractManage/domain/bo/HotContractManageBo.java new file mode 100644 index 0000000..ce06961 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/contractManage/domain/bo/HotContractManageBo.java @@ -0,0 +1,124 @@ +package com.hotwj.platform.operationManagement.contractManage.domain.bo; + +import com.hotwj.platform.operationManagement.contractManage.domain.HotContractManage; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 合同管理业务对象 hot_contract_manage + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotContractManage.class, reverseConvertGenerate = false) +public class HotContractManageBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 合同编号 + */ + private String contractNo; + + /** + * 合同名称 + */ + private String contractName; + + /** + * 合同金额(元) + */ + private Long contractAmount; + + /** + * 甲方名称 + */ + private String partyAName; + + /** + * 乙方名称 + */ + private String partyBName; + + /** + * 乙方责任人 + */ + private String partyBOwner; + + /** + * 联系电话 + */ + private String contactPhone; + + /** + * 合同签订时间 + */ + private Date contractSignTime; + + /** + * 合同生效时间 + */ + private Date contractEffectiveTime; + + /** + * 合同到期时间 + */ + private Date contractExpireTime; + + /** + * 合同状态:1=草稿 2=已签订 3=已生效 4=已终止 5=已作废 + */ + private Long contractStatus; + + /** + * 签约渠道:1=微信 2=支付宝 3=银行卡 4=余额 5=线下 + */ + private Long signChannel; + + /** + * 协议文件URL + */ + private String agreementUrl; + + /** + * 来源订单ID(支付订单) + */ + private Long sourceOrderId; + + /** + * 来源订单号(支付订单) + */ + private String sourceOrderNo; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/contractManage/domain/vo/HotContractManageVo.java b/src/main/java/com/hotwj/platform/operationManagement/contractManage/domain/vo/HotContractManageVo.java new file mode 100644 index 0000000..70646c0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/contractManage/domain/vo/HotContractManageVo.java @@ -0,0 +1,164 @@ +package com.hotwj.platform.operationManagement.contractManage.domain.vo; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.hotwj.platform.operationManagement.contractManage.domain.HotContractManage; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 合同管理视图对象 hot_contract_manage + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotContractManage.class) +public class HotContractManageVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 合同编号 + */ + @ExcelProperty(value = "合同编号") + private String contractNo; + + /** + * 合同名称 + */ + @ExcelProperty(value = "合同名称") + private String contractName; + + /** + * 合同金额(元) + */ + @ExcelProperty(value = "合同金额", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long contractAmount; + + /** + * 甲方名称 + */ + @ExcelProperty(value = "甲方名称") + private String partyAName; + + /** + * 乙方名称 + */ + @ExcelProperty(value = "乙方名称") + private String partyBName; + + /** + * 乙方责任人 + */ + @ExcelProperty(value = "乙方责任人") + private String partyBOwner; + + /** + * 联系电话 + */ + @ExcelProperty(value = "联系电话") + private String contactPhone; + + /** + * 合同签订时间 + */ + @ExcelProperty(value = "合同签订时间") + private Date contractSignTime; + + /** + * 合同生效时间 + */ + @ExcelProperty(value = "合同生效时间") + private Date contractEffectiveTime; + + /** + * 合同到期时间 + */ + @ExcelProperty(value = "合同到期时间") + private Date contractExpireTime; + + /** + * 合同状态:1=草稿 2=已签订 3=已生效 4=已终止 5=已作废 + */ + @ExcelProperty(value = "合同状态:1=草稿 2=已签订 3=已生效 4=已终止 5=已作废") + private Long contractStatus; + + /** + * 签约渠道:1=微信 2=支付宝 3=银行卡 4=余额 5=线下 + */ + @ExcelProperty(value = "签约渠道:1=微信 2=支付宝 3=银行卡 4=余额 5=线下") + private Long signChannel; + + /** + * 协议文件URL + */ + @ExcelProperty(value = "协议文件URL") + private String agreementUrl; + + /** + * 来源订单ID(支付订单) + */ + @ExcelProperty(value = "来源订单ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "支=付订单") + private Long sourceOrderId; + + /** + * 来源订单号(支付订单) + */ + @ExcelProperty(value = "来源订单号", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "支=付订单") + private String sourceOrderNo; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/contractManage/mapper/HotContractManageMapper.java b/src/main/java/com/hotwj/platform/operationManagement/contractManage/mapper/HotContractManageMapper.java new file mode 100644 index 0000000..cc5ab12 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/contractManage/mapper/HotContractManageMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.contractManage.mapper; + +import com.hotwj.platform.operationManagement.contractManage.domain.HotContractManage; +import com.hotwj.platform.operationManagement.contractManage.domain.vo.HotContractManageVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 合同管理Mapper接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Mapper +public interface HotContractManageMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/contractManage/service/IHotContractManageService.java b/src/main/java/com/hotwj/platform/operationManagement/contractManage/service/IHotContractManageService.java new file mode 100644 index 0000000..292da34 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/contractManage/service/IHotContractManageService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.contractManage.service; + +import com.hotwj.platform.operationManagement.contractManage.domain.vo.HotContractManageVo; +import com.hotwj.platform.operationManagement.contractManage.domain.bo.HotContractManageBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 合同管理Service接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +public interface IHotContractManageService { + + /** + * 查询合同管理 + * + * @param id 主键 + * @return 合同管理 + */ + HotContractManageVo queryById(Long id); + + /** + * 分页查询合同管理列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 合同管理分页列表 + */ + TableDataInfo queryPageList(HotContractManageBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的合同管理列表 + * + * @param bo 查询条件 + * @return 合同管理列表 + */ + List queryList(HotContractManageBo bo); + + /** + * 新增合同管理 + * + * @param bo 合同管理 + * @return 是否新增成功 + */ + Boolean insertByBo(HotContractManageBo bo); + + /** + * 修改合同管理 + * + * @param bo 合同管理 + * @return 是否修改成功 + */ + Boolean updateByBo(HotContractManageBo bo); + + /** + * 校验并批量删除合同管理信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/contractManage/service/impl/HotContractManageServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/contractManage/service/impl/HotContractManageServiceImpl.java new file mode 100644 index 0000000..3ad1d5a --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/contractManage/service/impl/HotContractManageServiceImpl.java @@ -0,0 +1,150 @@ +package com.hotwj.platform.operationManagement.contractManage.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.hotwj.platform.operationManagement.contractManage.domain.bo.HotContractManageBo; +import com.hotwj.platform.operationManagement.contractManage.domain.vo.HotContractManageVo; +import com.hotwj.platform.operationManagement.contractManage.domain.HotContractManage; +import com.hotwj.platform.operationManagement.contractManage.mapper.HotContractManageMapper; +import com.hotwj.platform.operationManagement.contractManage.service.IHotContractManageService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 合同管理Service业务层处理 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotContractManageServiceImpl implements IHotContractManageService { + + private final HotContractManageMapper baseMapper; + + /** + * 查询合同管理 + * + * @param id 主键 + * @return 合同管理 + */ + @Override + public HotContractManageVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询合同管理列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 合同管理分页列表 + */ + @Override + public TableDataInfo queryPageList(HotContractManageBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的合同管理列表 + * + * @param bo 查询条件 + * @return 合同管理列表 + */ + @Override + public List queryList(HotContractManageBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotContractManageBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotContractManage::getId); + lqw.eq(bo.getCompanyId() != null, HotContractManage::getCompanyId, bo.getCompanyId()); + lqw.eq(StringUtils.isNotBlank(bo.getContractNo()), HotContractManage::getContractNo, bo.getContractNo()); + lqw.like(StringUtils.isNotBlank(bo.getContractName()), HotContractManage::getContractName, bo.getContractName()); + lqw.eq(bo.getContractAmount() != null, HotContractManage::getContractAmount, bo.getContractAmount()); + lqw.like(StringUtils.isNotBlank(bo.getPartyAName()), HotContractManage::getPartyAName, bo.getPartyAName()); + lqw.like(StringUtils.isNotBlank(bo.getPartyBName()), HotContractManage::getPartyBName, bo.getPartyBName()); + lqw.eq(StringUtils.isNotBlank(bo.getPartyBOwner()), HotContractManage::getPartyBOwner, bo.getPartyBOwner()); + lqw.eq(StringUtils.isNotBlank(bo.getContactPhone()), HotContractManage::getContactPhone, bo.getContactPhone()); + lqw.eq(bo.getContractSignTime() != null, HotContractManage::getContractSignTime, bo.getContractSignTime()); + lqw.eq(bo.getContractEffectiveTime() != null, HotContractManage::getContractEffectiveTime, bo.getContractEffectiveTime()); + lqw.eq(bo.getContractExpireTime() != null, HotContractManage::getContractExpireTime, bo.getContractExpireTime()); + lqw.eq(bo.getContractStatus() != null, HotContractManage::getContractStatus, bo.getContractStatus()); + lqw.eq(bo.getSignChannel() != null, HotContractManage::getSignChannel, bo.getSignChannel()); + lqw.eq(StringUtils.isNotBlank(bo.getAgreementUrl()), HotContractManage::getAgreementUrl, bo.getAgreementUrl()); + lqw.eq(bo.getSourceOrderId() != null, HotContractManage::getSourceOrderId, bo.getSourceOrderId()); + lqw.eq(StringUtils.isNotBlank(bo.getSourceOrderNo()), HotContractManage::getSourceOrderNo, bo.getSourceOrderNo()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotContractManage::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotContractManage::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotContractManage::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增合同管理 + * + * @param bo 合同管理 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotContractManageBo bo) { + HotContractManage add = MapstructUtils.convert(bo, HotContractManage.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改合同管理 + * + * @param bo 合同管理 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotContractManageBo bo) { + HotContractManage update = MapstructUtils.convert(bo, HotContractManage.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotContractManage entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除合同管理信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundAccount/controller/HotFundAccountController.java b/src/main/java/com/hotwj/platform/operationManagement/fundAccount/controller/HotFundAccountController.java new file mode 100644 index 0000000..9636865 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundAccount/controller/HotFundAccountController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.operationManagement.fundAccount.controller; + +import com.hotwj.platform.operationManagement.fundAccount.domain.bo.HotFundAccountBo; +import com.hotwj.platform.operationManagement.fundAccount.domain.vo.HotFundAccountVo; +import com.hotwj.platform.operationManagement.fundAccount.service.IHotFundAccountService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 资金账户 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/fundAccount") +@Tag(name = "资金账户", description = "资金账户管理") +public class HotFundAccountController extends BaseController { + + private final IHotFundAccountService hotFundAccountService; + + /** + * 查询资金账户列表 + */ + //@SaCheckPermission("operationManagement:fundAccount:list") + @GetMapping("/list") + @Operation(summary = "分页查询资金账户列表") + public TableDataInfo list(HotFundAccountBo bo, PageQuery pageQuery) { + return hotFundAccountService.queryPageList(bo, pageQuery); + } + + /** + * 导出资金账户列表 + */ + //@SaCheckPermission("operationManagement:fundAccount:export") + @Log(title = "资金账户", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出资金账户列表") + public void export(HotFundAccountBo bo, HttpServletResponse response) { + List list = hotFundAccountService.queryList(bo); + ExcelUtil.exportExcel(list, "资金账户", HotFundAccountVo.class, response); + } + + /** + * 获取资金账户详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("operationManagement:fundAccount:query") + @GetMapping("/{id}") + @Operation(summary = "获取资金账户详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotFundAccountService.queryById(id)); + } + + /** + * 新增资金账户 + */ + //@SaCheckPermission("operationManagement:fundAccount:add") + @Log(title = "资金账户", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增资金账户") + public R add(@Validated(AddGroup.class) @RequestBody HotFundAccountBo bo) { + return toAjax(hotFundAccountService.insertByBo(bo)); + } + + /** + * 修改资金账户 + */ + //@SaCheckPermission("operationManagement:fundAccount:edit") + @Log(title = "资金账户", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改资金账户") + public R edit(@Validated(EditGroup.class) @RequestBody HotFundAccountBo bo) { + return toAjax(hotFundAccountService.updateByBo(bo)); + } + + /** + * 删除资金账户 + * + * @param ids 主键串 + */ + //@SaCheckPermission("operationManagement:fundAccount:remove") + @Log(title = "资金账户", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除资金账户") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotFundAccountService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundAccount/domain/HotFundAccount.java b/src/main/java/com/hotwj/platform/operationManagement/fundAccount/domain/HotFundAccount.java new file mode 100644 index 0000000..65c2b5c --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundAccount/domain/HotFundAccount.java @@ -0,0 +1,74 @@ +package com.hotwj.platform.operationManagement.fundAccount.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 资金账户对象 hot_fund_account + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_fund_account") +public class HotFundAccount extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 账户编号 + */ + private String accountNo; + + /** + * 账户类型:1=个人 2=企业 + */ + private Long accountType; + + /** + * 可用余额 + */ + private BigDecimal balanceAmount; + + /** + * 状态:1=正常 0=禁用 + */ + private Long status; + + /** + * 乐观锁版本号 + */ + @Version + private Long version; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundAccount/domain/bo/HotFundAccountBo.java b/src/main/java/com/hotwj/platform/operationManagement/fundAccount/domain/bo/HotFundAccountBo.java new file mode 100644 index 0000000..ce4ca18 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundAccount/domain/bo/HotFundAccountBo.java @@ -0,0 +1,67 @@ +package com.hotwj.platform.operationManagement.fundAccount.domain.bo; + +import com.hotwj.platform.operationManagement.fundAccount.domain.HotFundAccount; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.math.BigDecimal; + +/** + * 资金账户业务对象 hot_fund_account + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotFundAccount.class, reverseConvertGenerate = false) +public class HotFundAccountBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 账户编号 + */ + private String accountNo; + + /** + * 账户类型:1=个人 2=企业 + */ + private Long accountType; + + /** + * 可用余额 + */ + private BigDecimal balanceAmount; + + /** + * 状态:1=正常 0=禁用 + */ + private Long status; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundAccount/domain/vo/HotFundAccountVo.java b/src/main/java/com/hotwj/platform/operationManagement/fundAccount/domain/vo/HotFundAccountVo.java new file mode 100644 index 0000000..c239b74 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundAccount/domain/vo/HotFundAccountVo.java @@ -0,0 +1,93 @@ +package com.hotwj.platform.operationManagement.fundAccount.domain.vo; + +import com.hotwj.platform.operationManagement.fundAccount.domain.HotFundAccount; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 资金账户视图对象 hot_fund_account + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotFundAccount.class) +public class HotFundAccountVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户ID") + private Long userId; + + /** + * 账户编号 + */ + @ExcelProperty(value = "账户编号") + private String accountNo; + + /** + * 账户类型:1=个人 2=企业 + */ + @ExcelProperty(value = "账户类型:1=个人 2=企业") + private Long accountType; + + /** + * 可用余额 + */ + @ExcelProperty(value = "可用余额") + private BigDecimal balanceAmount; + + /** + * 状态:1=正常 0=禁用 + */ + @ExcelProperty(value = "状态:1=正常 0=禁用") + private Long status; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundAccount/mapper/HotFundAccountMapper.java b/src/main/java/com/hotwj/platform/operationManagement/fundAccount/mapper/HotFundAccountMapper.java new file mode 100644 index 0000000..bce173e --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundAccount/mapper/HotFundAccountMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.fundAccount.mapper; + +import com.hotwj.platform.operationManagement.fundAccount.domain.HotFundAccount; +import com.hotwj.platform.operationManagement.fundAccount.domain.vo.HotFundAccountVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 资金账户Mapper接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Mapper +public interface HotFundAccountMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundAccount/service/IHotFundAccountService.java b/src/main/java/com/hotwj/platform/operationManagement/fundAccount/service/IHotFundAccountService.java new file mode 100644 index 0000000..b7c28fa --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundAccount/service/IHotFundAccountService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.fundAccount.service; + +import com.hotwj.platform.operationManagement.fundAccount.domain.vo.HotFundAccountVo; +import com.hotwj.platform.operationManagement.fundAccount.domain.bo.HotFundAccountBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 资金账户Service接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +public interface IHotFundAccountService { + + /** + * 查询资金账户 + * + * @param id 主键 + * @return 资金账户 + */ + HotFundAccountVo queryById(Long id); + + /** + * 分页查询资金账户列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 资金账户分页列表 + */ + TableDataInfo queryPageList(HotFundAccountBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的资金账户列表 + * + * @param bo 查询条件 + * @return 资金账户列表 + */ + List queryList(HotFundAccountBo bo); + + /** + * 新增资金账户 + * + * @param bo 资金账户 + * @return 是否新增成功 + */ + Boolean insertByBo(HotFundAccountBo bo); + + /** + * 修改资金账户 + * + * @param bo 资金账户 + * @return 是否修改成功 + */ + Boolean updateByBo(HotFundAccountBo bo); + + /** + * 校验并批量删除资金账户信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundAccount/service/impl/HotFundAccountServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/fundAccount/service/impl/HotFundAccountServiceImpl.java new file mode 100644 index 0000000..89bb4a2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundAccount/service/impl/HotFundAccountServiceImpl.java @@ -0,0 +1,140 @@ +package com.hotwj.platform.operationManagement.fundAccount.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.hotwj.platform.operationManagement.fundAccount.domain.bo.HotFundAccountBo; +import com.hotwj.platform.operationManagement.fundAccount.domain.vo.HotFundAccountVo; +import com.hotwj.platform.operationManagement.fundAccount.domain.HotFundAccount; +import com.hotwj.platform.operationManagement.fundAccount.mapper.HotFundAccountMapper; +import com.hotwj.platform.operationManagement.fundAccount.service.IHotFundAccountService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 资金账户Service业务层处理 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotFundAccountServiceImpl implements IHotFundAccountService { + + private final HotFundAccountMapper baseMapper; + + /** + * 查询资金账户 + * + * @param id 主键 + * @return 资金账户 + */ + @Override + public HotFundAccountVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询资金账户列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 资金账户分页列表 + */ + @Override + public TableDataInfo queryPageList(HotFundAccountBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的资金账户列表 + * + * @param bo 查询条件 + * @return 资金账户列表 + */ + @Override + public List queryList(HotFundAccountBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotFundAccountBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotFundAccount::getId); + lqw.eq(bo.getCompanyId() != null, HotFundAccount::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getUserId() != null, HotFundAccount::getUserId, bo.getUserId()); + lqw.eq(StringUtils.isNotBlank(bo.getAccountNo()), HotFundAccount::getAccountNo, bo.getAccountNo()); + lqw.eq(bo.getAccountType() != null, HotFundAccount::getAccountType, bo.getAccountType()); + lqw.eq(bo.getBalanceAmount() != null, HotFundAccount::getBalanceAmount, bo.getBalanceAmount()); + lqw.eq(bo.getStatus() != null, HotFundAccount::getStatus, bo.getStatus()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotFundAccount::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotFundAccount::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotFundAccount::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增资金账户 + * + * @param bo 资金账户 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotFundAccountBo bo) { + HotFundAccount add = MapstructUtils.convert(bo, HotFundAccount.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改资金账户 + * + * @param bo 资金账户 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotFundAccountBo bo) { + HotFundAccount update = MapstructUtils.convert(bo, HotFundAccount.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotFundAccount entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除资金账户信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/controller/HotFundRechargeOrderController.java b/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/controller/HotFundRechargeOrderController.java new file mode 100644 index 0000000..bda611d --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/controller/HotFundRechargeOrderController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.operationManagement.fundRechargeOrder.controller; + +import com.hotwj.platform.operationManagement.fundRechargeOrder.domain.bo.HotFundRechargeOrderBo; +import com.hotwj.platform.operationManagement.fundRechargeOrder.domain.vo.HotFundRechargeOrderVo; +import com.hotwj.platform.operationManagement.fundRechargeOrder.service.IHotFundRechargeOrderService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 充值订单 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/fundRechargeOrder") +@Tag(name = "充值订单", description = "充值订单管理") +public class HotFundRechargeOrderController extends BaseController { + + private final IHotFundRechargeOrderService hotFundRechargeOrderService; + + /** + * 查询充值订单列表 + */ + //@SaCheckPermission("operationManagement:fundRechargeOrder:list") + @GetMapping("/list") + @Operation(summary = "分页查询充值订单列表") + public TableDataInfo list(HotFundRechargeOrderBo bo, PageQuery pageQuery) { + return hotFundRechargeOrderService.queryPageList(bo, pageQuery); + } + + /** + * 导出充值订单列表 + */ + //@SaCheckPermission("operationManagement:fundRechargeOrder:export") + @Log(title = "充值订单", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出充值订单列表") + public void export(HotFundRechargeOrderBo bo, HttpServletResponse response) { + List list = hotFundRechargeOrderService.queryList(bo); + ExcelUtil.exportExcel(list, "充值订单", HotFundRechargeOrderVo.class, response); + } + + /** + * 获取充值订单详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("operationManagement:fundRechargeOrder:query") + @GetMapping("/{id}") + @Operation(summary = "获取充值订单详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotFundRechargeOrderService.queryById(id)); + } + + /** + * 新增充值订单 + */ + //@SaCheckPermission("operationManagement:fundRechargeOrder:add") + @Log(title = "充值订单", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增充值订单") + public R add(@Validated(AddGroup.class) @RequestBody HotFundRechargeOrderBo bo) { + return toAjax(hotFundRechargeOrderService.insertByBo(bo)); + } + + /** + * 修改充值订单 + */ + //@SaCheckPermission("operationManagement:fundRechargeOrder:edit") + @Log(title = "充值订单", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改充值订单") + public R edit(@Validated(EditGroup.class) @RequestBody HotFundRechargeOrderBo bo) { + return toAjax(hotFundRechargeOrderService.updateByBo(bo)); + } + + /** + * 删除充值订单 + * + * @param ids 主键串 + */ + //@SaCheckPermission("operationManagement:fundRechargeOrder:remove") + @Log(title = "充值订单", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除充值订单") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotFundRechargeOrderService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/domain/HotFundRechargeOrder.java b/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/domain/HotFundRechargeOrder.java new file mode 100644 index 0000000..7a7aded --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/domain/HotFundRechargeOrder.java @@ -0,0 +1,102 @@ +package com.hotwj.platform.operationManagement.fundRechargeOrder.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; +import java.math.BigDecimal; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 充值订单对象 hot_fund_recharge_order + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_fund_recharge_order") +public class HotFundRechargeOrder extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + private Long accountId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 充值订单号 + */ + private String rechargeOrderNo; + + /** + * 充值金额(如5.00) + */ + private BigDecimal rechargeAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 + */ + private Long payMethod; + + /** + * 支付场景:1=H5 2=小程序 3=APP 4=PC + */ + private Long payScene; + + /** + * 支付状态:1=待支付 2=支付中 3=支付成功 4=支付失败 5=已关闭 6=已退款 + */ + private Long payStatus; + + /** + * 三方支付流水号 + */ + private String thirdTradeNo; + + /** + * 支付成功时间 + */ + private Date paidTime; + + /** + * 失败原因 + */ + private String failReason; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/domain/bo/HotFundRechargeOrderBo.java b/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/domain/bo/HotFundRechargeOrderBo.java new file mode 100644 index 0000000..db5a8ae --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/domain/bo/HotFundRechargeOrderBo.java @@ -0,0 +1,100 @@ +package com.hotwj.platform.operationManagement.fundRechargeOrder.domain.bo; + +import com.hotwj.platform.operationManagement.fundRechargeOrder.domain.HotFundRechargeOrder; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.math.BigDecimal; +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 充值订单业务对象 hot_fund_recharge_order + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotFundRechargeOrder.class, reverseConvertGenerate = false) +public class HotFundRechargeOrderBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + private Long accountId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 充值订单号 + */ + private String rechargeOrderNo; + + /** + * 充值金额(如5.00) + */ + private BigDecimal rechargeAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 + */ + private Long payMethod; + + /** + * 支付场景:1=H5 2=小程序 3=APP 4=PC + */ + private Long payScene; + + /** + * 支付状态:1=待支付 2=支付中 3=支付成功 4=支付失败 5=已关闭 6=已退款 + */ + private Long payStatus; + + /** + * 三方支付流水号 + */ + private String thirdTradeNo; + + /** + * 支付成功时间 + */ + private Date paidTime; + + /** + * 失败原因 + */ + private String failReason; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/domain/vo/HotFundRechargeOrderVo.java b/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/domain/vo/HotFundRechargeOrderVo.java new file mode 100644 index 0000000..a9e1151 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/domain/vo/HotFundRechargeOrderVo.java @@ -0,0 +1,134 @@ +package com.hotwj.platform.operationManagement.fundRechargeOrder.domain.vo; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.hotwj.platform.operationManagement.fundRechargeOrder.domain.HotFundRechargeOrder; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 充值订单视图对象 hot_fund_recharge_order + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotFundRechargeOrder.class) +public class HotFundRechargeOrderVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + @ExcelProperty(value = "账户ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逻=辑外键") + private Long accountId; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户ID") + private Long userId; + + /** + * 充值订单号 + */ + @ExcelProperty(value = "充值订单号") + private String rechargeOrderNo; + + /** + * 充值金额(如5.00) + */ + @ExcelProperty(value = "充值金额", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "如=5.00") + private BigDecimal rechargeAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 + */ + @ExcelProperty(value = "支付方式:1=微信 2=支付宝 3=银行卡") + private Long payMethod; + + /** + * 支付场景:1=H5 2=小程序 3=APP 4=PC + */ + @ExcelProperty(value = "支付场景:1=H5 2=小程序 3=APP 4=PC") + private Long payScene; + + /** + * 支付状态:1=待支付 2=支付中 3=支付成功 4=支付失败 5=已关闭 6=已退款 + */ + @ExcelProperty(value = "支付状态:1=待支付 2=支付中 3=支付成功 4=支付失败 5=已关闭 6=已退款") + private Long payStatus; + + /** + * 三方支付流水号 + */ + @ExcelProperty(value = "三方支付流水号") + private String thirdTradeNo; + + /** + * 支付成功时间 + */ + @ExcelProperty(value = "支付成功时间") + private Date paidTime; + + /** + * 失败原因 + */ + @ExcelProperty(value = "失败原因") + private String failReason; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/mapper/HotFundRechargeOrderMapper.java b/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/mapper/HotFundRechargeOrderMapper.java new file mode 100644 index 0000000..227efff --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/mapper/HotFundRechargeOrderMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.fundRechargeOrder.mapper; + +import com.hotwj.platform.operationManagement.fundRechargeOrder.domain.HotFundRechargeOrder; +import com.hotwj.platform.operationManagement.fundRechargeOrder.domain.vo.HotFundRechargeOrderVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 充值订单Mapper接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Mapper +public interface HotFundRechargeOrderMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/service/IHotFundRechargeOrderService.java b/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/service/IHotFundRechargeOrderService.java new file mode 100644 index 0000000..80add55 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/service/IHotFundRechargeOrderService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.fundRechargeOrder.service; + +import com.hotwj.platform.operationManagement.fundRechargeOrder.domain.vo.HotFundRechargeOrderVo; +import com.hotwj.platform.operationManagement.fundRechargeOrder.domain.bo.HotFundRechargeOrderBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 充值订单Service接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +public interface IHotFundRechargeOrderService { + + /** + * 查询充值订单 + * + * @param id 主键 + * @return 充值订单 + */ + HotFundRechargeOrderVo queryById(Long id); + + /** + * 分页查询充值订单列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 充值订单分页列表 + */ + TableDataInfo queryPageList(HotFundRechargeOrderBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的充值订单列表 + * + * @param bo 查询条件 + * @return 充值订单列表 + */ + List queryList(HotFundRechargeOrderBo bo); + + /** + * 新增充值订单 + * + * @param bo 充值订单 + * @return 是否新增成功 + */ + Boolean insertByBo(HotFundRechargeOrderBo bo); + + /** + * 修改充值订单 + * + * @param bo 充值订单 + * @return 是否修改成功 + */ + Boolean updateByBo(HotFundRechargeOrderBo bo); + + /** + * 校验并批量删除充值订单信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/service/impl/HotFundRechargeOrderServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/service/impl/HotFundRechargeOrderServiceImpl.java new file mode 100644 index 0000000..257e51e --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundRechargeOrder/service/impl/HotFundRechargeOrderServiceImpl.java @@ -0,0 +1,145 @@ +package com.hotwj.platform.operationManagement.fundRechargeOrder.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.hotwj.platform.operationManagement.fundRechargeOrder.domain.bo.HotFundRechargeOrderBo; +import com.hotwj.platform.operationManagement.fundRechargeOrder.domain.vo.HotFundRechargeOrderVo; +import com.hotwj.platform.operationManagement.fundRechargeOrder.domain.HotFundRechargeOrder; +import com.hotwj.platform.operationManagement.fundRechargeOrder.mapper.HotFundRechargeOrderMapper; +import com.hotwj.platform.operationManagement.fundRechargeOrder.service.IHotFundRechargeOrderService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 充值订单Service业务层处理 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotFundRechargeOrderServiceImpl implements IHotFundRechargeOrderService { + + private final HotFundRechargeOrderMapper baseMapper; + + /** + * 查询充值订单 + * + * @param id 主键 + * @return 充值订单 + */ + @Override + public HotFundRechargeOrderVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询充值订单列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 充值订单分页列表 + */ + @Override + public TableDataInfo queryPageList(HotFundRechargeOrderBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的充值订单列表 + * + * @param bo 查询条件 + * @return 充值订单列表 + */ + @Override + public List queryList(HotFundRechargeOrderBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotFundRechargeOrderBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotFundRechargeOrder::getId); + lqw.eq(bo.getCompanyId() != null, HotFundRechargeOrder::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getAccountId() != null, HotFundRechargeOrder::getAccountId, bo.getAccountId()); + lqw.eq(bo.getUserId() != null, HotFundRechargeOrder::getUserId, bo.getUserId()); + lqw.eq(StringUtils.isNotBlank(bo.getRechargeOrderNo()), HotFundRechargeOrder::getRechargeOrderNo, bo.getRechargeOrderNo()); + lqw.eq(bo.getRechargeAmount() != null, HotFundRechargeOrder::getRechargeAmount, bo.getRechargeAmount()); + lqw.eq(bo.getPayMethod() != null, HotFundRechargeOrder::getPayMethod, bo.getPayMethod()); + lqw.eq(bo.getPayScene() != null, HotFundRechargeOrder::getPayScene, bo.getPayScene()); + lqw.eq(bo.getPayStatus() != null, HotFundRechargeOrder::getPayStatus, bo.getPayStatus()); + lqw.eq(StringUtils.isNotBlank(bo.getThirdTradeNo()), HotFundRechargeOrder::getThirdTradeNo, bo.getThirdTradeNo()); + lqw.eq(bo.getPaidTime() != null, HotFundRechargeOrder::getPaidTime, bo.getPaidTime()); + lqw.eq(StringUtils.isNotBlank(bo.getFailReason()), HotFundRechargeOrder::getFailReason, bo.getFailReason()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotFundRechargeOrder::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotFundRechargeOrder::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotFundRechargeOrder::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增充值订单 + * + * @param bo 充值订单 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotFundRechargeOrderBo bo) { + HotFundRechargeOrder add = MapstructUtils.convert(bo, HotFundRechargeOrder.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改充值订单 + * + * @param bo 充值订单 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotFundRechargeOrderBo bo) { + HotFundRechargeOrder update = MapstructUtils.convert(bo, HotFundRechargeOrder.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotFundRechargeOrder entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除充值订单信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/controller/HotFundTradeOrderController.java b/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/controller/HotFundTradeOrderController.java new file mode 100644 index 0000000..7551643 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/controller/HotFundTradeOrderController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.operationManagement.fundTradeOrder.controller; + +import com.hotwj.platform.operationManagement.fundTradeOrder.domain.bo.HotFundTradeOrderBo; +import com.hotwj.platform.operationManagement.fundTradeOrder.domain.vo.HotFundTradeOrderVo; +import com.hotwj.platform.operationManagement.fundTradeOrder.service.IHotFundTradeOrderService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 订单管理 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/platform/fundTradeOrder") +@Tag(name = "订单管理", description = "订单管理管理") +public class HotFundTradeOrderController extends BaseController { + + private final IHotFundTradeOrderService hotFundTradeOrderService; + + /** + * 查询订单管理列表 + */ + //@SaCheckPermission("platform:fundTradeOrder:list") + @GetMapping("/list") + @Operation(summary = "分页查询订单管理列表") + public TableDataInfo list(HotFundTradeOrderBo bo, PageQuery pageQuery) { + return hotFundTradeOrderService.queryPageList(bo, pageQuery); + } + + /** + * 导出订单管理列表 + */ + //@SaCheckPermission("platform:fundTradeOrder:export") + @Log(title = "订单管理", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出订单管理列表") + public void export(HotFundTradeOrderBo bo, HttpServletResponse response) { + List list = hotFundTradeOrderService.queryList(bo); + ExcelUtil.exportExcel(list, "订单管理", HotFundTradeOrderVo.class, response); + } + + /** + * 获取订单管理详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("platform:fundTradeOrder:query") + @GetMapping("/{id}") + @Operation(summary = "获取订单管理详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotFundTradeOrderService.queryById(id)); + } + + /** + * 新增订单管理 + */ + //@SaCheckPermission("platform:fundTradeOrder:add") + @Log(title = "订单管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增订单管理") + public R add(@Validated(AddGroup.class) @RequestBody HotFundTradeOrderBo bo) { + return toAjax(hotFundTradeOrderService.insertByBo(bo)); + } + + /** + * 修改订单管理 + */ + //@SaCheckPermission("platform:fundTradeOrder:edit") + @Log(title = "订单管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改订单管理") + public R edit(@Validated(EditGroup.class) @RequestBody HotFundTradeOrderBo bo) { + return toAjax(hotFundTradeOrderService.updateByBo(bo)); + } + + /** + * 删除订单管理 + * + * @param ids 主键串 + */ + //@SaCheckPermission("platform:fundTradeOrder:remove") + @Log(title = "订单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除订单管理") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotFundTradeOrderService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/domain/HotFundTradeOrder.java b/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/domain/HotFundTradeOrder.java new file mode 100644 index 0000000..ccbc73e --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/domain/HotFundTradeOrder.java @@ -0,0 +1,256 @@ +package com.hotwj.platform.operationManagement.fundTradeOrder.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 订单管理对象 hot_fund_trade_order + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_fund_trade_order") +public class HotFundTradeOrder extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + private Long accountId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 用户名 + */ + private String userName; + + /** + * 消费型企业ID + */ + private Long consumerCompanyId; + + /** + * 消费型企业名称 + */ + private String consumerCompanyName; + + /** + * 订单号 + */ + private String orderNo; + + /** + * 订单类型:1=拓客订单 2=学习订单 3=短信订单 4=岗前培训订单 5=其他 + */ + private Long orderType; + + /** + * 业务场景:1=学时套餐 2=短信套餐 3=岗前培训套餐 4=岗前活动套餐 5=充值 6=其他 + */ + private Long bizScene; + + /** + * 产品名称 + */ + private String productName; + + /** + * 学时套餐ID + */ + private Long packageId; + + /** + * 学时套餐名称(快照) + */ + private String packageName; + + /** + * 学时类型:1=通用学时 2=其他 + */ + private Long hourType; + + /** + * 学时数量(如1/50/100) + */ + private Long hourCount; + + /** + * 短信套餐ID + */ + private Long smsPackageId; + + /** + * 短信套餐名称(快照) + */ + private String smsPackageName; + + /** + * 短信条数(如1000/3000/5000) + */ + private Long smsCount; + + /** + * 岗前培训套餐ID + */ + private Long preJobPackageId; + + /** + * 岗前培训套餐名称(快照) + */ + private String preJobPackageName; + + /** + * 岗前活动ID + */ + private Long preJobActivityId; + + /** + * 岗前活动名称(快照) + */ + private String preJobActivityName; + + /** + * 单价(元) + */ + private Long unitPrice; + + /** + * 购买数量 + */ + private Long quantity; + + /** + * 应付金额(元) + */ + private Long payableAmount; + + /** + * 优惠金额(元) + */ + private Long discountAmount; + + /** + * 退款金额(元) + */ + private Long refundAmount; + + /** + * 实付金额(元) + */ + private Long paidAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 4=余额 + */ + private Long payMethod; + + /** + * 三方支付流水号 + */ + private String thirdTradeNo; + + /** + * 下单时间 + */ + private Date orderCreateTime; + + /** + * 支付时间 + */ + private Date payTime; + + /** + * 支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭 + */ + private Long payStatus; + + /** + * 退款状态:0=无退款 1=退款中 2=部分退款 3=全额退款 4=退款失败 + */ + private Long refundStatus; + + /** + * 退款完成时间 + */ + private Date refundTime; + + /** + * 取消时间 + */ + private Date cancelTime; + + /** + * 关闭时间 + */ + private Date closeTime; + + /** + * 合同ID + */ + private Long contractId; + + /** + * 合同编号 + */ + private String contractNo; + + /** + * 合同状态:1=草稿 2=已签订 3=已生效 4=已终止 5=已作废 + */ + private Long contractStatus; + + /** + * 合同签订时间 + */ + private Date contractSignTime; + + /** + * 订单来源(如渠道/来源ID) + */ + private String orderSource; + + /** + * 产品快照(JSON) + */ + private String productSnapshot; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/domain/bo/HotFundTradeOrderBo.java b/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/domain/bo/HotFundTradeOrderBo.java new file mode 100644 index 0000000..e42ac7f --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/domain/bo/HotFundTradeOrderBo.java @@ -0,0 +1,254 @@ +package com.hotwj.platform.operationManagement.fundTradeOrder.domain.bo; + +import com.hotwj.platform.operationManagement.fundTradeOrder.domain.HotFundTradeOrder; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 订单管理业务对象 hot_fund_trade_order + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotFundTradeOrder.class, reverseConvertGenerate = false) +public class HotFundTradeOrderBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + private Long accountId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 用户名 + */ + private String userName; + + /** + * 消费型企业ID + */ + private Long consumerCompanyId; + + /** + * 消费型企业名称 + */ + private String consumerCompanyName; + + /** + * 订单号 + */ + private String orderNo; + + /** + * 订单类型:1=拓客订单 2=学习订单 3=短信订单 4=岗前培训订单 5=其他 + */ + private Long orderType; + + /** + * 业务场景:1=学时套餐 2=短信套餐 3=岗前培训套餐 4=岗前活动套餐 5=充值 6=其他 + */ + private Long bizScene; + + /** + * 产品名称 + */ + private String productName; + + /** + * 学时套餐ID + */ + private Long packageId; + + /** + * 学时套餐名称(快照) + */ + private String packageName; + + /** + * 学时类型:1=通用学时 2=其他 + */ + private Long hourType; + + /** + * 学时数量(如1/50/100) + */ + private Long hourCount; + + /** + * 短信套餐ID + */ + private Long smsPackageId; + + /** + * 短信套餐名称(快照) + */ + private String smsPackageName; + + /** + * 短信条数(如1000/3000/5000) + */ + private Long smsCount; + + /** + * 岗前培训套餐ID + */ + private Long preJobPackageId; + + /** + * 岗前培训套餐名称(快照) + */ + private String preJobPackageName; + + /** + * 岗前活动ID + */ + private Long preJobActivityId; + + /** + * 岗前活动名称(快照) + */ + private String preJobActivityName; + + /** + * 单价(元) + */ + private Long unitPrice; + + /** + * 购买数量 + */ + private Long quantity; + + /** + * 应付金额(元) + */ + private Long payableAmount; + + /** + * 优惠金额(元) + */ + private Long discountAmount; + + /** + * 退款金额(元) + */ + private Long refundAmount; + + /** + * 实付金额(元) + */ + private Long paidAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 4=余额 + */ + private Long payMethod; + + /** + * 三方支付流水号 + */ + private String thirdTradeNo; + + /** + * 下单时间 + */ + private Date orderCreateTime; + + /** + * 支付时间 + */ + private Date payTime; + + /** + * 支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭 + */ + private Long payStatus; + + /** + * 退款状态:0=无退款 1=退款中 2=部分退款 3=全额退款 4=退款失败 + */ + private Long refundStatus; + + /** + * 退款完成时间 + */ + private Date refundTime; + + /** + * 取消时间 + */ + private Date cancelTime; + + /** + * 关闭时间 + */ + private Date closeTime; + + /** + * 合同ID + */ + private Long contractId; + + /** + * 合同编号 + */ + private String contractNo; + + /** + * 合同状态:1=草稿 2=已签订 3=已生效 4=已终止 5=已作废 + */ + private Long contractStatus; + + /** + * 合同签订时间 + */ + private Date contractSignTime; + + /** + * 订单来源(如渠道/来源ID) + */ + private String orderSource; + + /** + * 产品快照(JSON) + */ + private String productSnapshot; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/domain/vo/HotFundTradeOrderVo.java b/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/domain/vo/HotFundTradeOrderVo.java new file mode 100644 index 0000000..2129aa9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/domain/vo/HotFundTradeOrderVo.java @@ -0,0 +1,331 @@ +package com.hotwj.platform.operationManagement.fundTradeOrder.domain.vo; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.hotwj.platform.operationManagement.fundTradeOrder.domain.HotFundTradeOrder; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 订单管理视图对象 hot_fund_trade_order + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotFundTradeOrder.class) +public class HotFundTradeOrderVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + @ExcelProperty(value = "账户ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逻=辑外键") + private Long accountId; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户ID") + private Long userId; + + /** + * 用户名 + */ + @ExcelProperty(value = "用户名") + private String userName; + + /** + * 消费型企业ID + */ + @ExcelProperty(value = "消费型企业ID") + private Long consumerCompanyId; + + /** + * 消费型企业名称 + */ + @ExcelProperty(value = "消费型企业名称") + private String consumerCompanyName; + + /** + * 订单号 + */ + @ExcelProperty(value = "订单号") + private String orderNo; + + /** + * 订单类型:1=拓客订单 2=学习订单 3=短信订单 4=岗前培训订单 5=其他 + */ + @ExcelProperty(value = "订单类型:1=拓客订单 2=学习订单 3=短信订单 4=岗前培训订单 5=其他") + private Long orderType; + + /** + * 业务场景:1=学时套餐 2=短信套餐 3=岗前培训套餐 4=岗前活动套餐 5=充值 6=其他 + */ + @ExcelProperty(value = "业务场景:1=学时套餐 2=短信套餐 3=岗前培训套餐 4=岗前活动套餐 5=充值 6=其他") + private Long bizScene; + + /** + * 产品名称 + */ + @ExcelProperty(value = "产品名称") + private String productName; + + /** + * 学时套餐ID + */ + @ExcelProperty(value = "学时套餐ID") + private Long packageId; + + /** + * 学时套餐名称(快照) + */ + @ExcelProperty(value = "学时套餐名称", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "快=照") + private String packageName; + + /** + * 学时类型:1=通用学时 2=其他 + */ + @ExcelProperty(value = "学时类型:1=通用学时 2=其他") + private Long hourType; + + /** + * 学时数量(如1/50/100) + */ + @ExcelProperty(value = "学时数量", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "如=1/50/100") + private Long hourCount; + + /** + * 短信套餐ID + */ + @ExcelProperty(value = "短信套餐ID") + private Long smsPackageId; + + /** + * 短信套餐名称(快照) + */ + @ExcelProperty(value = "短信套餐名称", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "快=照") + private String smsPackageName; + + /** + * 短信条数(如1000/3000/5000) + */ + @ExcelProperty(value = "短信条数", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "如=1000/3000/5000") + private Long smsCount; + + /** + * 岗前培训套餐ID + */ + @ExcelProperty(value = "岗前培训套餐ID") + private Long preJobPackageId; + + /** + * 岗前培训套餐名称(快照) + */ + @ExcelProperty(value = "岗前培训套餐名称", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "快=照") + private String preJobPackageName; + + /** + * 岗前活动ID + */ + @ExcelProperty(value = "岗前活动ID") + private Long preJobActivityId; + + /** + * 岗前活动名称(快照) + */ + @ExcelProperty(value = "岗前活动名称", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "快=照") + private String preJobActivityName; + + /** + * 单价(元) + */ + @ExcelProperty(value = "单价", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long unitPrice; + + /** + * 购买数量 + */ + @ExcelProperty(value = "购买数量") + private Long quantity; + + /** + * 应付金额(元) + */ + @ExcelProperty(value = "应付金额", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long payableAmount; + + /** + * 优惠金额(元) + */ + @ExcelProperty(value = "优惠金额", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long discountAmount; + + /** + * 退款金额(元) + */ + @ExcelProperty(value = "退款金额", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long refundAmount; + + /** + * 实付金额(元) + */ + @ExcelProperty(value = "实付金额", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long paidAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 4=余额 + */ + @ExcelProperty(value = "支付方式:1=微信 2=支付宝 3=银行卡 4=余额") + private Long payMethod; + + /** + * 三方支付流水号 + */ + @ExcelProperty(value = "三方支付流水号") + private String thirdTradeNo; + + /** + * 下单时间 + */ + @ExcelProperty(value = "下单时间") + private Date orderCreateTime; + + /** + * 支付时间 + */ + @ExcelProperty(value = "支付时间") + private Date payTime; + + /** + * 支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭 + */ + @ExcelProperty(value = "支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭") + private Long payStatus; + + /** + * 退款状态:0=无退款 1=退款中 2=部分退款 3=全额退款 4=退款失败 + */ + @ExcelProperty(value = "退款状态:0=无退款 1=退款中 2=部分退款 3=全额退款 4=退款失败") + private Long refundStatus; + + /** + * 退款完成时间 + */ + @ExcelProperty(value = "退款完成时间") + private Date refundTime; + + /** + * 取消时间 + */ + @ExcelProperty(value = "取消时间") + private Date cancelTime; + + /** + * 关闭时间 + */ + @ExcelProperty(value = "关闭时间") + private Date closeTime; + + /** + * 合同ID + */ + @ExcelProperty(value = "合同ID") + private Long contractId; + + /** + * 合同编号 + */ + @ExcelProperty(value = "合同编号") + private String contractNo; + + /** + * 合同状态:1=草稿 2=已签订 3=已生效 4=已终止 5=已作废 + */ + @ExcelProperty(value = "合同状态:1=草稿 2=已签订 3=已生效 4=已终止 5=已作废") + private Long contractStatus; + + /** + * 合同签订时间 + */ + @ExcelProperty(value = "合同签订时间") + private Date contractSignTime; + + /** + * 订单来源(如渠道/来源ID) + */ + @ExcelProperty(value = "订单来源", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "如=渠道/来源ID") + private String orderSource; + + /** + * 产品快照(JSON) + */ + @ExcelProperty(value = "产品快照", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "J=SON") + private String productSnapshot; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/mapper/HotFundTradeOrderMapper.java b/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/mapper/HotFundTradeOrderMapper.java new file mode 100644 index 0000000..88c3a15 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/mapper/HotFundTradeOrderMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.fundTradeOrder.mapper; + +import com.hotwj.platform.operationManagement.fundTradeOrder.domain.HotFundTradeOrder; +import com.hotwj.platform.operationManagement.fundTradeOrder.domain.vo.HotFundTradeOrderVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 订单管理Mapper接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Mapper +public interface HotFundTradeOrderMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/service/IHotFundTradeOrderService.java b/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/service/IHotFundTradeOrderService.java new file mode 100644 index 0000000..b2c9bb7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/service/IHotFundTradeOrderService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.fundTradeOrder.service; + +import com.hotwj.platform.operationManagement.fundTradeOrder.domain.vo.HotFundTradeOrderVo; +import com.hotwj.platform.operationManagement.fundTradeOrder.domain.bo.HotFundTradeOrderBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 订单管理Service接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +public interface IHotFundTradeOrderService { + + /** + * 查询订单管理 + * + * @param id 主键 + * @return 订单管理 + */ + HotFundTradeOrderVo queryById(Long id); + + /** + * 分页查询订单管理列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 订单管理分页列表 + */ + TableDataInfo queryPageList(HotFundTradeOrderBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的订单管理列表 + * + * @param bo 查询条件 + * @return 订单管理列表 + */ + List queryList(HotFundTradeOrderBo bo); + + /** + * 新增订单管理 + * + * @param bo 订单管理 + * @return 是否新增成功 + */ + Boolean insertByBo(HotFundTradeOrderBo bo); + + /** + * 修改订单管理 + * + * @param bo 订单管理 + * @return 是否修改成功 + */ + Boolean updateByBo(HotFundTradeOrderBo bo); + + /** + * 校验并批量删除订单管理信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/service/impl/HotFundTradeOrderServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/service/impl/HotFundTradeOrderServiceImpl.java new file mode 100644 index 0000000..1b95d83 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/fundTradeOrder/service/impl/HotFundTradeOrderServiceImpl.java @@ -0,0 +1,176 @@ +package com.hotwj.platform.operationManagement.fundTradeOrder.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.hotwj.platform.operationManagement.fundTradeOrder.domain.bo.HotFundTradeOrderBo; +import com.hotwj.platform.operationManagement.fundTradeOrder.domain.vo.HotFundTradeOrderVo; +import com.hotwj.platform.operationManagement.fundTradeOrder.domain.HotFundTradeOrder; +import com.hotwj.platform.operationManagement.fundTradeOrder.mapper.HotFundTradeOrderMapper; +import com.hotwj.platform.operationManagement.fundTradeOrder.service.IHotFundTradeOrderService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 订单管理Service业务层处理 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotFundTradeOrderServiceImpl implements IHotFundTradeOrderService { + + private final HotFundTradeOrderMapper baseMapper; + + /** + * 查询订单管理 + * + * @param id 主键 + * @return 订单管理 + */ + @Override + public HotFundTradeOrderVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询订单管理列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 订单管理分页列表 + */ + @Override + public TableDataInfo queryPageList(HotFundTradeOrderBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的订单管理列表 + * + * @param bo 查询条件 + * @return 订单管理列表 + */ + @Override + public List queryList(HotFundTradeOrderBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotFundTradeOrderBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotFundTradeOrder::getId); + lqw.eq(bo.getCompanyId() != null, HotFundTradeOrder::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getAccountId() != null, HotFundTradeOrder::getAccountId, bo.getAccountId()); + lqw.eq(bo.getUserId() != null, HotFundTradeOrder::getUserId, bo.getUserId()); + lqw.like(StringUtils.isNotBlank(bo.getUserName()), HotFundTradeOrder::getUserName, bo.getUserName()); + lqw.eq(bo.getConsumerCompanyId() != null, HotFundTradeOrder::getConsumerCompanyId, bo.getConsumerCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getConsumerCompanyName()), HotFundTradeOrder::getConsumerCompanyName, bo.getConsumerCompanyName()); + lqw.eq(StringUtils.isNotBlank(bo.getOrderNo()), HotFundTradeOrder::getOrderNo, bo.getOrderNo()); + lqw.eq(bo.getOrderType() != null, HotFundTradeOrder::getOrderType, bo.getOrderType()); + lqw.eq(bo.getBizScene() != null, HotFundTradeOrder::getBizScene, bo.getBizScene()); + lqw.like(StringUtils.isNotBlank(bo.getProductName()), HotFundTradeOrder::getProductName, bo.getProductName()); + lqw.eq(bo.getPackageId() != null, HotFundTradeOrder::getPackageId, bo.getPackageId()); + lqw.like(StringUtils.isNotBlank(bo.getPackageName()), HotFundTradeOrder::getPackageName, bo.getPackageName()); + lqw.eq(bo.getHourType() != null, HotFundTradeOrder::getHourType, bo.getHourType()); + lqw.eq(bo.getHourCount() != null, HotFundTradeOrder::getHourCount, bo.getHourCount()); + lqw.eq(bo.getSmsPackageId() != null, HotFundTradeOrder::getSmsPackageId, bo.getSmsPackageId()); + lqw.like(StringUtils.isNotBlank(bo.getSmsPackageName()), HotFundTradeOrder::getSmsPackageName, bo.getSmsPackageName()); + lqw.eq(bo.getSmsCount() != null, HotFundTradeOrder::getSmsCount, bo.getSmsCount()); + lqw.eq(bo.getPreJobPackageId() != null, HotFundTradeOrder::getPreJobPackageId, bo.getPreJobPackageId()); + lqw.like(StringUtils.isNotBlank(bo.getPreJobPackageName()), HotFundTradeOrder::getPreJobPackageName, bo.getPreJobPackageName()); + lqw.eq(bo.getPreJobActivityId() != null, HotFundTradeOrder::getPreJobActivityId, bo.getPreJobActivityId()); + lqw.like(StringUtils.isNotBlank(bo.getPreJobActivityName()), HotFundTradeOrder::getPreJobActivityName, bo.getPreJobActivityName()); + lqw.eq(bo.getUnitPrice() != null, HotFundTradeOrder::getUnitPrice, bo.getUnitPrice()); + lqw.eq(bo.getQuantity() != null, HotFundTradeOrder::getQuantity, bo.getQuantity()); + lqw.eq(bo.getPayableAmount() != null, HotFundTradeOrder::getPayableAmount, bo.getPayableAmount()); + lqw.eq(bo.getDiscountAmount() != null, HotFundTradeOrder::getDiscountAmount, bo.getDiscountAmount()); + lqw.eq(bo.getRefundAmount() != null, HotFundTradeOrder::getRefundAmount, bo.getRefundAmount()); + lqw.eq(bo.getPaidAmount() != null, HotFundTradeOrder::getPaidAmount, bo.getPaidAmount()); + lqw.eq(bo.getPayMethod() != null, HotFundTradeOrder::getPayMethod, bo.getPayMethod()); + lqw.eq(StringUtils.isNotBlank(bo.getThirdTradeNo()), HotFundTradeOrder::getThirdTradeNo, bo.getThirdTradeNo()); + lqw.eq(bo.getOrderCreateTime() != null, HotFundTradeOrder::getOrderCreateTime, bo.getOrderCreateTime()); + lqw.eq(bo.getPayTime() != null, HotFundTradeOrder::getPayTime, bo.getPayTime()); + lqw.eq(bo.getPayStatus() != null, HotFundTradeOrder::getPayStatus, bo.getPayStatus()); + lqw.eq(bo.getRefundStatus() != null, HotFundTradeOrder::getRefundStatus, bo.getRefundStatus()); + lqw.eq(bo.getRefundTime() != null, HotFundTradeOrder::getRefundTime, bo.getRefundTime()); + lqw.eq(bo.getCancelTime() != null, HotFundTradeOrder::getCancelTime, bo.getCancelTime()); + lqw.eq(bo.getCloseTime() != null, HotFundTradeOrder::getCloseTime, bo.getCloseTime()); + lqw.eq(bo.getContractId() != null, HotFundTradeOrder::getContractId, bo.getContractId()); + lqw.eq(StringUtils.isNotBlank(bo.getContractNo()), HotFundTradeOrder::getContractNo, bo.getContractNo()); + lqw.eq(bo.getContractStatus() != null, HotFundTradeOrder::getContractStatus, bo.getContractStatus()); + lqw.eq(bo.getContractSignTime() != null, HotFundTradeOrder::getContractSignTime, bo.getContractSignTime()); + lqw.eq(StringUtils.isNotBlank(bo.getOrderSource()), HotFundTradeOrder::getOrderSource, bo.getOrderSource()); + lqw.eq(StringUtils.isNotBlank(bo.getProductSnapshot()), HotFundTradeOrder::getProductSnapshot, bo.getProductSnapshot()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotFundTradeOrder::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotFundTradeOrder::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotFundTradeOrder::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增订单管理 + * + * @param bo 订单管理 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotFundTradeOrderBo bo) { + HotFundTradeOrder add = MapstructUtils.convert(bo, HotFundTradeOrder.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改订单管理 + * + * @param bo 订单管理 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotFundTradeOrderBo bo) { + HotFundTradeOrder update = MapstructUtils.convert(bo, HotFundTradeOrder.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotFundTradeOrder entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除订单管理信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackage/controller/HotHourPackageController.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/controller/HotHourPackageController.java new file mode 100644 index 0000000..8b07706 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/controller/HotHourPackageController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.operationManagement.hourPackage.controller; + +import com.hotwj.platform.operationManagement.hourPackage.domain.bo.HotHourPackageBo; +import com.hotwj.platform.operationManagement.hourPackage.domain.vo.HotHourPackageVo; +import com.hotwj.platform.operationManagement.hourPackage.service.IHotHourPackageService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 学时套餐 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/hourPackage") +@Tag(name = "学时套餐", description = "学时套餐管理") +public class HotHourPackageController extends BaseController { + + private final IHotHourPackageService hotHourPackageService; + + /** + * 查询学时套餐列表 + */ + //@SaCheckPermission("operationManagement:hourPackage:list") + @GetMapping("/list") + @Operation(summary = "分页查询学时套餐列表") + public TableDataInfo list(HotHourPackageBo bo, PageQuery pageQuery) { + return hotHourPackageService.queryPageList(bo, pageQuery); + } + + /** + * 导出学时套餐列表 + */ + //@SaCheckPermission("operationManagement:hourPackage:export") + @Log(title = "学时套餐", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出学时套餐列表") + public void export(HotHourPackageBo bo, HttpServletResponse response) { + List list = hotHourPackageService.queryList(bo); + ExcelUtil.exportExcel(list, "学时套餐", HotHourPackageVo.class, response); + } + + /** + * 获取学时套餐详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("operationManagement:hourPackage:query") + @GetMapping("/{id}") + @Operation(summary = "获取学时套餐详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotHourPackageService.queryById(id)); + } + + /** + * 新增学时套餐 + */ + //@SaCheckPermission("operationManagement:hourPackage:add") + @Log(title = "学时套餐", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增学时套餐") + public R add(@Validated(AddGroup.class) @RequestBody HotHourPackageBo bo) { + return toAjax(hotHourPackageService.insertByBo(bo)); + } + + /** + * 修改学时套餐 + */ + //@SaCheckPermission("operationManagement:hourPackage:edit") + @Log(title = "学时套餐", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改学时套餐") + public R edit(@Validated(EditGroup.class) @RequestBody HotHourPackageBo bo) { + return toAjax(hotHourPackageService.updateByBo(bo)); + } + + /** + * 删除学时套餐 + * + * @param ids 主键串 + */ + //@SaCheckPermission("operationManagement:hourPackage:remove") + @Log(title = "学时套餐", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除学时套餐") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotHourPackageService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackage/domain/HotHourPackage.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/domain/HotHourPackage.java new file mode 100644 index 0000000..5210730 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/domain/HotHourPackage.java @@ -0,0 +1,105 @@ +package com.hotwj.platform.operationManagement.hourPackage.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 学时套餐对象 hot_hour_package + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_hour_package") +public class HotHourPackage extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 套餐编码 + */ + private String packageCode; + + /** + * 套餐名称 + */ + private String packageName; + + /** + * 学时类型:1=通用 + */ + private Long hourType; + + /** + * 学时数(如1/50/100) + */ + private Long hourCount; + + /** + * 原价格(元) + */ + private BigDecimal originPrice; + + /** + * 单价(元) + */ + private BigDecimal unitPrice; + + /** + * 状态:1=启用 0=停用 + */ + private Long status; + + /** + * 排序号 + */ + private Long sortNo; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 套餐活动价(元) + */ + private BigDecimal packagePrice; + + /** + * 适用类型(多选),逗号拼接 + */ + private String applicableTypes; + + /** + * 套餐说明 + */ + private String packageDesc; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackage/domain/bo/HotHourPackageBo.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/domain/bo/HotHourPackageBo.java new file mode 100644 index 0000000..18b6c46 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/domain/bo/HotHourPackageBo.java @@ -0,0 +1,111 @@ +package com.hotwj.platform.operationManagement.hourPackage.domain.bo; + +import com.hotwj.platform.operationManagement.hourPackage.domain.HotHourPackage; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.math.BigDecimal; + +/** + * 学时套餐业务对象 hot_hour_package + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotHourPackage.class, reverseConvertGenerate = false) +public class HotHourPackageBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 套餐编码 + */ + private String packageCode; + + /** + * 套餐名称 + */ + @NotBlank(message = "套餐名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String packageName; + + /** + * 学时类型:1=通用 + */ + private Long hourType; + + /** + * 学时数(如1/50/100) + */ + @NotNull(message = "学时不能为空", groups = {AddGroup.class, EditGroup.class}) + @Min(value = 1, message = "学时必须大于0", groups = {AddGroup.class, EditGroup.class}) + private Long hourCount; + + /** + * 原价格(元) + */ + @NotNull(message = "套餐原价格不能为空", groups = {AddGroup.class, EditGroup.class}) + @DecimalMin(value = "0.00", inclusive = false, message = "套餐原价格必须大于0", groups = {AddGroup.class, EditGroup.class}) + private BigDecimal originPrice; + + /** + * 单价(元) + */ + private BigDecimal unitPrice; + + /** + * 状态:1=启用 0=停用 + */ + @NotNull(message = "是否启用不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long status; + + /** + * 排序号 + */ + private Long sortNo; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + /** + * 套餐活动价(元) + */ + private BigDecimal packagePrice; + + /** + * 适用类型(多选),逗号拼接 + */ + private String applicableTypes; + + /** + * 套餐说明 + */ + private String packageDesc; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackage/domain/vo/HotHourPackageVo.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/domain/vo/HotHourPackageVo.java new file mode 100644 index 0000000..3ac0cad --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/domain/vo/HotHourPackageVo.java @@ -0,0 +1,135 @@ +package com.hotwj.platform.operationManagement.hourPackage.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.operationManagement.hourPackage.domain.HotHourPackage; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + + +/** + * 学时套餐视图对象 hot_hour_package + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotHourPackage.class) +public class HotHourPackageVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 套餐编码 + */ + @ExcelProperty(value = "套餐编码") + private String packageCode; + + /** + * 套餐名称 + */ + @ExcelProperty(value = "套餐名称") + private String packageName; + + /** + * 学时类型:1=通用 + */ + @ExcelProperty(value = "学时类型:1=通用") + private Long hourType; + + /** + * 学时数(如1/50/100) + */ + @ExcelProperty(value = "学时数", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "如=1/50/100") + private Long hourCount; + + /** + * 原价格(元) + */ + @ExcelProperty(value = "原价格(元)") + private BigDecimal originPrice; + + /** + * 学时单价(元) + */ + @ExcelProperty(value = "学时单价(元)") + private BigDecimal unitPrice; + + /** + * 状态:1=启用 0=停用 + */ + @ExcelProperty(value = "状态:1=启用 0=停用") + private Long status; + + /** + * 排序号 + */ + @ExcelProperty(value = "排序号") + private Long sortNo; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 套餐活动价(元) + */ + @ExcelProperty(value = "套餐活动价(元)") + private BigDecimal packagePrice; + + /** + * 适用类型(多选),逗号拼接 + */ + @ExcelProperty(value = "适用类型(多选),逗号拼接") + private String applicableTypes; + + /** + * 套餐说明 + */ + @ExcelProperty(value = "套餐说明") + private String packageDesc; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackage/enums/HotHourPackageTypeEnum.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/enums/HotHourPackageTypeEnum.java new file mode 100644 index 0000000..6340aca --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/enums/HotHourPackageTypeEnum.java @@ -0,0 +1,22 @@ +package com.hotwj.platform.operationManagement.hourPackage.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 学时套餐类型 + *

+ * 当前总部端仅支持通用套餐,后续如有新增类型可在此扩展。 + * + * @author shihongwei + * @date 2026-05-09 + */ +@Getter +@AllArgsConstructor +public enum HotHourPackageTypeEnum { + + GENERAL(1L, "通用"); + + private final Long code; + private final String desc; +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackage/hour-package.md b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/hour-package.md new file mode 100644 index 0000000..5eb2722 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/hour-package.md @@ -0,0 +1,295 @@ +# 学时套餐接口文档 + +## 0. 相关文档 + +- 学时套餐购买接口文档:`../hourPackagePurchase/hour-package-purchase.md` + +## 1. 模块说明 + +- 模块:总部端学时套餐管理 +- 控制器:`/operationManagement/hourPackage` +- 当前套餐类型固定为 `1=通用` +- 当前暂不需要前端传“适用类型”,后端会统一按通用套餐处理 + +## 2. 套餐字段说明 + +### 2.1 返回字段 + +| 字段名 | 类型 | 说明 | +|-------------------|--------------|---------------------------------| +| `id` | `Long` | 套餐ID | +| `packageCode` | `String` | 套餐编码,新增时后端自动生成 | +| `packageName` | `String` | 套餐名 | +| `hourType` | `Long` | 套餐类型,当前固定 `1` | +| `hourCount` | `Long` | 学时 | +| `originPrice` | `BigDecimal` | 套餐原价格 | +| `packagePrice` | `BigDecimal` | 套餐活动价,可为空 | +| `unitPrice` | `BigDecimal` | 学时单价;有活动价取 `活动价/学时`,否则取 `原价/学时` | +| `packageDesc` | `String` | 套餐说明 | +| `status` | `Long` | 是否启用:`1=启用`,`0=停用` | +| `remark` | `String` | 备注 | +| `sortNo` | `Long` | 排序号 | +| `applicableTypes` | `String` | 适用类型,当前固定为 `"1"` | +| `createByName` | `String` | 创建人姓名 | +| `updateByName` | `String` | 更新人姓名 | + +### 2.2 请求字段 + +| 字段名 | 类型 | 必填 | 说明 | +|----------------|--------------|-------|--------------------------| +| `id` | `Long` | 修改时必填 | 套餐ID | +| `packageName` | `String` | 是 | 套餐名 | +| `originPrice` | `BigDecimal` | 是 | 套餐原价格,必须大于 `0` | +| `packagePrice` | `BigDecimal` | 否 | 套餐活动价;传了必须大于 `0` 且不能大于原价 | +| `hourCount` | `Long` | 是 | 学时,必须大于 `0` | +| `packageDesc` | `String` | 否 | 套餐说明 | +| `status` | `Long` | 是 | 是否启用:`1=启用`,`0=停用` | +| `remark` | `String` | 否 | 备注 | +| `sortNo` | `Long` | 否 | 排序号 | + +说明: + +- `hourType` 不需要前端传,后端固定写入 `1=通用` +- `applicableTypes` 不需要前端传,后端固定写入 `"1"` +- 修改接口如果未传 `packagePrice`、`packageDesc`、`remark`、`sortNo`,后端会保留原值 + +## 3. 接口1:分页查询学时套餐列表 + +- 地址:`GET /operationManagement/hourPackage/list` +- 说明:分页返回学时套餐列表 + +### 3.1 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|----------------|--------------|----|--------------------| +| `pageNum` | `Integer` | 是 | 页码 | +| `pageSize` | `Integer` | 是 | 每页条数 | +| `packageName` | `String` | 否 | 套餐名,模糊查询 | +| `hourCount` | `Long` | 否 | 学时,精确查询 | +| `originPrice` | `BigDecimal` | 否 | 原价格,精确查询 | +| `packagePrice` | `BigDecimal` | 否 | 活动价,精确查询 | +| `status` | `Long` | 否 | 是否启用:`1=启用`,`0=停用` | +| `packageDesc` | `String` | 否 | 套餐说明,模糊查询 | + +### 3.2 返回示例 + +```json +{ + "code": 200, + "rows": [ + { + "id": 1, + "packageCode": "HP1931512345678901234", + "packageName": "基础学时包", + "hourType": 1, + "hourCount": 40, + "originPrice": 1000.00, + "packagePrice": 800.00, + "unitPrice": 20.00, + "packageDesc": "总部通用学时套餐", + "status": 1, + "remark": "活动套餐", + "sortNo": 0, + "applicableTypes": "1" + } + ], + "total": 1, + "msg": "查询成功" +} +``` + +## 4. 接口2:修改指定套餐 + +- 地址:`PUT /operationManagement/hourPackage` +- 说明:修改套餐名、套餐原价格、学时、是否启用;活动价和套餐说明如果不传则保留原值 + +### 4.1 请求示例 + +```json +{ + "id": 1, + "packageName": "基础学时包", + "originPrice": 1000.00, + "hourCount": 40, + "status": 1 +} +``` + +### 4.2 返回示例 + +```json +{ + "code": 200, + "msg": "操作成功", + "data": null +} +``` + +## 5. 接口3:删除套餐 + +- 地址:`DELETE /operationManagement/hourPackage/{ids}` +- 说明:支持单个或批量删除 + +### 5.1 请求示例 + +```text +DELETE /operationManagement/hourPackage/1 +``` + +或 + +```text +DELETE /operationManagement/hourPackage/1,2,3 +``` + +### 5.2 返回示例 + +```json +{ + "code": 200, + "msg": "操作成功", + "data": null +} +``` + +## 6. 接口4:新增套餐 + +- 地址:`POST /operationManagement/hourPackage` +- 说明:新增总部端学时套餐 + +### 6.1 请求示例 + +```json +{ + "packageName": "高级学时包", + "originPrice": 2000.00, + "packagePrice": 1500.00, + "hourCount": 80, + "packageDesc": "适用于总部统一活动投放", + "status": 1, + "remark": "新客活动", + "sortNo": 1 +} +``` + +### 6.2 返回示例 + +```json +{ + "code": 200, + "msg": "操作成功", + "data": null +} +``` + +## 7. 业务规则 + +- 套餐类型固定:`1=通用` +- 套餐名不能为空,且不能重复 +- 学时必须大于 `0` +- 套餐原价格必须大于 `0` +- 套餐活动价如果传入,必须大于 `0`,且不能大于原价格 +- 学时单价由后端计算,前端不需要传 +- 新增时如果未传 `packageCode`,由后端自动生成 + +## 8. 前端表格字段对照清单 + +### 8.1 列表页字段 + +| 列表列名 | 对应字段 | 展示建议 | 说明 | +|-------|----------------|-------------|----------------| +| 套餐名 | `packageName` | 文本 | 必显 | +| 套餐原价格 | `originPrice` | 金额,保留两位小数 | 可显示为 `1000.00` | +| 学时 | `hourCount` | 数字 | 建议后缀显示“学时” | +| 套餐活动价 | `packagePrice` | 金额,空值显示 `-` | 没有活动价时为空 | +| 学时单价 | `unitPrice` | 金额,保留两位小数 | 后端已计算好,直接展示 | +| 套餐说明 | `packageDesc` | 文本,过长省略 | 鼠标悬停可看完整内容 | +| 是否启用 | `status` | 开关/标签 | `1=启用`,`0=停用` | +| 操作 | `id` | 按钮组 | `编辑`、`删除` | + +### 8.2 列表页筛选项 + +| 筛选项 | 对应参数 | 组件建议 | 说明 | +|------|----------------|-------|----------------| +| 套餐名 | `packageName` | 输入框 | 模糊查询 | +| 学时 | `hourCount` | 数字输入框 | 精确查询 | +| 原价格 | `originPrice` | 金额输入框 | 精确查询 | +| 活动价 | `packagePrice` | 金额输入框 | 精确查询 | +| 是否启用 | `status` | 下拉框 | `全部 / 启用 / 停用` | +| 套餐说明 | `packageDesc` | 输入框 | 模糊查询 | + +### 8.3 列表页接口对接 + +- 请求地址:`GET /operationManagement/hourPackage/list` +- 分页参数:`pageNum`、`pageSize` +- 表格数据源:响应中的 `rows` +- 总数:响应中的 `total` + +## 9. 前端弹窗表单字段对照清单 + +### 9.1 新增/编辑弹窗字段 + +| 表单项 | 对应字段 | 必填 | 组件建议 | 说明 | +|-------|----------------|----|-------|-------------------------| +| 套餐名 | `packageName` | 是 | 输入框 | 最长建议前端限制 `100` 字 | +| 套餐原价格 | `originPrice` | 是 | 金额输入框 | 必须大于 `0` | +| 学时 | `hourCount` | 是 | 数字输入框 | 必须大于 `0` | +| 套餐活动价 | `packagePrice` | 否 | 金额输入框 | 可为空;传了必须大于 `0` 且不能大于原价格 | +| 套餐说明 | `packageDesc` | 否 | 多行文本框 | 建议前端限制 `500` 字 | +| 是否启用 | `status` | 是 | 开关/单选 | `1=启用`,`0=停用` | +| 备注 | `remark` | 否 | 输入框 | 内部管理备注 | +| 排序号 | `sortNo` | 否 | 数字输入框 | 可选 | + +### 9.2 表单中不需要前端传的字段 + +| 字段 | 原因 | +|-------------------|--------------| +| `id` | 新增时不用传,编辑时才传 | +| `packageCode` | 新增时后端自动生成 | +| `hourType` | 后端固定为 `1=通用` | +| `applicableTypes` | 后端固定为 `"1"` | +| `unitPrice` | 后端自动计算 | +| `createByName` | 后端返回字段 | +| `updateByName` | 后端返回字段 | + +### 9.3 新增弹窗提交示例 + +```json +{ + "packageName": "标准学时包", + "originPrice": 1200.00, + "hourCount": 60, + "packagePrice": 999.00, + "packageDesc": "用于总部常规投放", + "status": 1, + "remark": "默认套餐", + "sortNo": 1 +} +``` + +### 9.4 编辑弹窗回显建议 + +- 先调 `GET /operationManagement/hourPackage/{id}` 获取详情 +- 弹窗回显字段建议: + - `packageName` + - `originPrice` + - `hourCount` + - `packagePrice` + - `packageDesc` + - `status` + - `remark` + - `sortNo` +- `unitPrice` 可只读展示,不参与提交 + +## 10. 前端实现建议 + +- 列表页的“是否启用”建议直接渲染成标签: + - `status=1` -> `启用` + - `status=0` -> `停用` +- 活动价为空时,列表页显示 `-` +- 学时单价直接使用后端返回值,不要前端自行计算 +- 新增/编辑提交前,前端可先做基础校验: + - 套餐名不能为空 + - 原价格必须大于 `0` + - 学时必须大于 `0` + - 活动价若填写,不能大于原价格 diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackage/mapper/HotHourPackageMapper.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/mapper/HotHourPackageMapper.java new file mode 100644 index 0000000..6ac6f2b --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/mapper/HotHourPackageMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.hourPackage.mapper; + +import com.hotwj.platform.operationManagement.hourPackage.domain.HotHourPackage; +import com.hotwj.platform.operationManagement.hourPackage.domain.vo.HotHourPackageVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 学时套餐Mapper接口 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Mapper +public interface HotHourPackageMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackage/service/IHotHourPackageService.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/service/IHotHourPackageService.java new file mode 100644 index 0000000..e1c51a1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/service/IHotHourPackageService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.hourPackage.service; + +import com.hotwj.platform.operationManagement.hourPackage.domain.bo.HotHourPackageBo; +import com.hotwj.platform.operationManagement.hourPackage.domain.vo.HotHourPackageVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 学时套餐Service接口 + * + * @author shihongwei + * @date 2026-05-08 + */ +public interface IHotHourPackageService { + + /** + * 查询学时套餐 + * + * @param id 主键 + * @return 学时套餐 + */ + HotHourPackageVo queryById(Long id); + + /** + * 分页查询学时套餐列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 学时套餐分页列表 + */ + TableDataInfo queryPageList(HotHourPackageBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的学时套餐列表 + * + * @param bo 查询条件 + * @return 学时套餐列表 + */ + List queryList(HotHourPackageBo bo); + + /** + * 新增学时套餐 + * + * @param bo 学时套餐 + * @return 是否新增成功 + */ + Boolean insertByBo(HotHourPackageBo bo); + + /** + * 修改学时套餐 + * + * @param bo 学时套餐 + * @return 是否修改成功 + */ + Boolean updateByBo(HotHourPackageBo bo); + + /** + * 校验并批量删除学时套餐信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackage/service/impl/HotHourPackageServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/service/impl/HotHourPackageServiceImpl.java new file mode 100644 index 0000000..8117493 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackage/service/impl/HotHourPackageServiceImpl.java @@ -0,0 +1,245 @@ +package com.hotwj.platform.operationManagement.hourPackage.service.impl; + +import cn.hutool.core.util.IdUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.operationManagement.hourPackage.domain.HotHourPackage; +import com.hotwj.platform.operationManagement.hourPackage.domain.bo.HotHourPackageBo; +import com.hotwj.platform.operationManagement.hourPackage.domain.vo.HotHourPackageVo; +import com.hotwj.platform.operationManagement.hourPackage.enums.HotHourPackageTypeEnum; +import com.hotwj.platform.operationManagement.hourPackage.mapper.HotHourPackageMapper; +import com.hotwj.platform.operationManagement.hourPackage.service.IHotHourPackageService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 学时套餐Service业务层处理 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotHourPackageServiceImpl implements IHotHourPackageService { + + private final HotHourPackageMapper baseMapper; + + /** + * 查询学时套餐 + * + * @param id 主键 + * @return 学时套餐 + */ + @Override + public HotHourPackageVo queryById(Long id) { + HotHourPackageVo vo = baseMapper.selectVoById(id); + if (vo != null) { + fillDerivedFields(vo); + } + return vo; + } + + /** + * 分页查询学时套餐列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 学时套餐分页列表 + */ + @Override + public TableDataInfo queryPageList(HotHourPackageBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + if (result.getRecords() != null) { + result.getRecords().forEach(this::fillDerivedFields); + } + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的学时套餐列表 + * + * @param bo 查询条件 + * @return 学时套餐列表 + */ + @Override + public List queryList(HotHourPackageBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + List list = baseMapper.selectVoList(lqw); + list.forEach(this::fillDerivedFields); + return list; + } + + private LambdaQueryWrapper buildQueryWrapper(HotHourPackageBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotHourPackage::getId); + lqw.eq(bo.getCompanyId() != null, HotHourPackage::getCompanyId, bo.getCompanyId()); + lqw.eq(StringUtils.isNotBlank(bo.getPackageCode()), HotHourPackage::getPackageCode, bo.getPackageCode()); + lqw.like(StringUtils.isNotBlank(bo.getPackageName()), HotHourPackage::getPackageName, bo.getPackageName()); + lqw.eq(HotHourPackage::getHourType, bo.getHourType() != null ? bo.getHourType() : HotHourPackageTypeEnum.GENERAL.getCode()); + lqw.eq(bo.getHourCount() != null, HotHourPackage::getHourCount, bo.getHourCount()); + lqw.eq(bo.getOriginPrice() != null, HotHourPackage::getOriginPrice, bo.getOriginPrice()); + lqw.eq(bo.getStatus() != null, HotHourPackage::getStatus, bo.getStatus()); + lqw.eq(bo.getSortNo() != null, HotHourPackage::getSortNo, bo.getSortNo()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotHourPackage::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotHourPackage::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotHourPackage::getIsDeleted, bo.getIsDeleted()); + lqw.eq(bo.getPackagePrice() != null, HotHourPackage::getPackagePrice, bo.getPackagePrice()); + lqw.like(StringUtils.isNotBlank(bo.getPackageDesc()), HotHourPackage::getPackageDesc, bo.getPackageDesc()); + return lqw; + } + + /** + * 新增学时套餐 + * + * @param bo 学时套餐 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotHourPackageBo bo) { + HotHourPackage add = MapstructUtils.convert(bo, HotHourPackage.class); + prepareEntityForSave(add, true); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改学时套餐 + * + * @param bo 学时套餐 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotHourPackageBo bo) { + HotHourPackage update = MapstructUtils.convert(bo, HotHourPackage.class); + HotHourPackage current = baseMapper.selectById(update.getId()); + if (current == null) { + throw new ServiceException("学时套餐不存在"); + } + if (update.getPackageCode() == null) { + update.setPackageCode(current.getPackageCode()); + } + if (update.getCompanyId() == null) { + update.setCompanyId(current.getCompanyId()); + } + if (update.getPackagePrice() == null) { + update.setPackagePrice(current.getPackagePrice()); + } + if (update.getPackageDesc() == null) { + update.setPackageDesc(current.getPackageDesc()); + } + if (update.getSortNo() == null) { + update.setSortNo(current.getSortNo()); + } + if (update.getRemark() == null) { + update.setRemark(current.getRemark()); + } + prepareEntityForSave(update, false); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotHourPackage entity) { + if (StringUtils.isBlank(entity.getPackageName())) { + throw new ServiceException("套餐名称不能为空"); + } + if (entity.getHourCount() == null || entity.getHourCount() <= 0) { + throw new ServiceException("学时必须大于0"); + } + if (entity.getOriginPrice() == null || entity.getOriginPrice().compareTo(BigDecimal.ZERO) <= 0) { + throw new ServiceException("套餐原价格必须大于0"); + } + if (entity.getPackagePrice() != null && entity.getPackagePrice().compareTo(BigDecimal.ZERO) <= 0) { + throw new ServiceException("套餐活动价必须大于0"); + } + if (entity.getPackagePrice() != null && entity.getPackagePrice().compareTo(entity.getOriginPrice()) > 0) { + throw new ServiceException("套餐活动价不能大于原价格"); + } + if (entity.getStatus() == null || (entity.getStatus() != 0L && entity.getStatus() != 1L)) { + throw new ServiceException("是否启用取值不正确"); + } + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(HotHourPackage::getPackageName, entity.getPackageName()); + lqw.eq(HotHourPackage::getHourType, HotHourPackageTypeEnum.GENERAL.getCode()); + lqw.ne(entity.getId() != null, HotHourPackage::getId, entity.getId()); + if (baseMapper.selectCount(lqw) > 0) { + throw new ServiceException("套餐名称已存在,请勿重复添加"); + } + } + + private void prepareEntityForSave(HotHourPackage entity, boolean create) { + entity.setHourType(HotHourPackageTypeEnum.GENERAL.getCode()); + entity.setApplicableTypes(String.valueOf(HotHourPackageTypeEnum.GENERAL.getCode())); + entity.setOriginPrice(normalizeMoney(entity.getOriginPrice())); + entity.setPackagePrice(normalizeOptionalMoney(entity.getPackagePrice())); + entity.setUnitPrice(calculateUnitPrice(entity.getOriginPrice(), entity.getPackagePrice(), entity.getHourCount())); + if (entity.getStatus() == null) { + entity.setStatus(1L); + } + if (create && StringUtils.isBlank(entity.getPackageCode())) { + entity.setPackageCode("HP" + IdUtil.getSnowflakeNextIdStr()); + } + } + + private void fillDerivedFields(HotHourPackageVo vo) { + vo.setHourType(HotHourPackageTypeEnum.GENERAL.getCode()); + vo.setApplicableTypes(String.valueOf(HotHourPackageTypeEnum.GENERAL.getCode())); + vo.setUnitPrice(calculateUnitPrice(vo.getOriginPrice(), vo.getPackagePrice(), vo.getHourCount())); + } + + private BigDecimal calculateUnitPrice(BigDecimal originPrice, BigDecimal packagePrice, Long hourCount) { + if (hourCount == null || hourCount <= 0) { + return BigDecimal.ZERO; + } + BigDecimal effectivePrice = packagePrice != null ? packagePrice : originPrice; + if (effectivePrice == null) { + return BigDecimal.ZERO; + } + return effectivePrice.divide(BigDecimal.valueOf(hourCount), 2, RoundingMode.HALF_UP); + } + + private BigDecimal normalizeMoney(BigDecimal amount) { + return amount == null ? null : amount.setScale(2, RoundingMode.HALF_UP); + } + + private BigDecimal normalizeOptionalMoney(BigDecimal amount) { + return amount == null ? null : amount.setScale(2, RoundingMode.HALF_UP); + } + + /** + * 校验并批量删除学时套餐信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/controller/HotHourPackagePurchaseController.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/controller/HotHourPackagePurchaseController.java new file mode 100644 index 0000000..3a0a7ad --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/controller/HotHourPackagePurchaseController.java @@ -0,0 +1,150 @@ +package com.hotwj.platform.operationManagement.hourPackagePurchase.controller; + +import com.hotwj.platform.operationManagement.hourPackage.domain.bo.HotHourPackageBo; +import com.hotwj.platform.operationManagement.hourPackage.domain.vo.HotHourPackageVo; +import com.hotwj.platform.operationManagement.hourPackagePurchase.controller.vo.HotHourPackageWalletPurchaseReqVO; +import com.hotwj.platform.operationManagement.hourPackagePurchase.controller.vo.HotHourPackageWalletPurchaseRespVO; +import com.hotwj.platform.operationManagement.hourPackagePurchase.domain.bo.HotHourPackagePurchaseBo; +import com.hotwj.platform.operationManagement.hourPackagePurchase.domain.vo.HotHourPackagePurchaseVo; +import com.hotwj.platform.operationManagement.hourPackagePurchase.service.IHotHourPackagePurchaseService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 学时套餐购买记录 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/hourPackagePurchase") +@Tag(name = "学时套餐购买记录", description = "学时套餐购买记录管理") +public class HotHourPackagePurchaseController extends BaseController { + + private final IHotHourPackagePurchaseService hotHourPackagePurchaseService; + + /** + * 查询学时套餐购买记录列表 + */ + //@SaCheckPermission("operationManagement:hourPackagePurchase:list") + @GetMapping("/list") + @Operation(summary = "分页查询学时套餐购买记录列表") + public TableDataInfo list(HotHourPackagePurchaseBo bo, PageQuery pageQuery) { + return hotHourPackagePurchaseService.queryPageList(bo, pageQuery); + } + + /** + * 查询当前登录用户可购买的学时套餐列表 + */ + @GetMapping("/available/list") + @Operation(summary = "分页查询当前登录用户可购买的学时套餐列表") + public TableDataInfo availableList(HotHourPackageBo bo, PageQuery pageQuery) { + return hotHourPackagePurchaseService.queryAvailablePageList(bo, pageQuery); + } + + /** + * 查询当前登录用户购买记录 + */ + @GetMapping("/current/list") + @Operation(summary = "分页查询当前登录用户学时套餐购买记录") + public TableDataInfo currentList(HotHourPackagePurchaseBo bo, PageQuery pageQuery) { + return hotHourPackagePurchaseService.queryCurrentUserPageList(bo, pageQuery); + } + + /** + * 使用钱包余额购买学时套餐 + */ + @Log(title = "学时套餐购买记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/wallet/purchase") + @Operation(summary = "使用钱包余额购买学时套餐") + public R walletPurchase(@Validated @RequestBody HotHourPackageWalletPurchaseReqVO reqVO) { + return R.ok(hotHourPackagePurchaseService.purchaseByWallet(reqVO)); + } + + /** + * 导出学时套餐购买记录列表 + */ + //@SaCheckPermission("operationManagement:hourPackagePurchase:export") + @Log(title = "学时套餐购买记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出学时套餐购买记录列表") + public void export(HotHourPackagePurchaseBo bo, HttpServletResponse response) { + List list = hotHourPackagePurchaseService.queryList(bo); + ExcelUtil.exportExcel(list, "学时套餐购买记录", HotHourPackagePurchaseVo.class, response); + } + + /** + * 获取学时套餐购买记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("operationManagement:hourPackagePurchase:query") + @GetMapping("/{id}") + @Operation(summary = "获取学时套餐购买记录详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotHourPackagePurchaseService.queryById(id)); + } + + /** + * 新增学时套餐购买记录 + */ + //@SaCheckPermission("operationManagement:hourPackagePurchase:add") + @Log(title = "学时套餐购买记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增学时套餐购买记录") + public R add(@Validated(AddGroup.class) @RequestBody HotHourPackagePurchaseBo bo) { + return toAjax(hotHourPackagePurchaseService.insertByBo(bo)); + } + + /** + * 修改学时套餐购买记录 + */ + //@SaCheckPermission("operationManagement:hourPackagePurchase:edit") + @Log(title = "学时套餐购买记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改学时套餐购买记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotHourPackagePurchaseBo bo) { + return toAjax(hotHourPackagePurchaseService.updateByBo(bo)); + } + + /** + * 删除学时套餐购买记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("operationManagement:hourPackagePurchase:remove") + @Log(title = "学时套餐购买记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除学时套餐购买记录") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotHourPackagePurchaseService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/controller/vo/HotHourPackageWalletPurchaseReqVO.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/controller/vo/HotHourPackageWalletPurchaseReqVO.java new file mode 100644 index 0000000..ad5e525 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/controller/vo/HotHourPackageWalletPurchaseReqVO.java @@ -0,0 +1,30 @@ +package com.hotwj.platform.operationManagement.hourPackagePurchase.controller.vo; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * 学时套餐钱包购买请求 + */ +@Data +public class HotHourPackageWalletPurchaseReqVO { + + /** + * 套餐ID + */ + @NotNull(message = "套餐ID不能为空") + private Long packageId; + + /** + * 购买数量 + */ + @NotNull(message = "购买数量不能为空") + @Min(value = 1, message = "购买数量必须大于0") + private Long quantity = 1L; + + /** + * 备注 + */ + private String remark; +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/controller/vo/HotHourPackageWalletPurchaseRespVO.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/controller/vo/HotHourPackageWalletPurchaseRespVO.java new file mode 100644 index 0000000..2827e35 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/controller/vo/HotHourPackageWalletPurchaseRespVO.java @@ -0,0 +1,36 @@ +package com.hotwj.platform.operationManagement.hourPackagePurchase.controller.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 学时套餐钱包购买结果 + */ +@Data +public class HotHourPackageWalletPurchaseRespVO { + + private Long purchaseId; + + private Long orderId; + + private String orderNo; + + private Long payOrderId; + + private String payOrderNo; + + private Long walletType; + + private Long walletId; + + private Long walletTransactionId; + + private String walletTransactionNo; + + private Long totalHours; + + private BigDecimal totalAmount; + + private BigDecimal walletBalance; +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/domain/HotHourPackagePurchase.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/domain/HotHourPackagePurchase.java new file mode 100644 index 0000000..3b9d35f --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/domain/HotHourPackagePurchase.java @@ -0,0 +1,165 @@ +package com.hotwj.platform.operationManagement.hourPackagePurchase.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.util.Date; + +import java.io.Serial; + +/** + * 学时套餐购买记录对象 hot_hour_package_purchase + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_hour_package_purchase") +public class HotHourPackagePurchase extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + private Long accountId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 订单管理表ID(逻辑外键) + */ + private Long orderId; + + /** + * 订单号 + */ + private String orderNo; + + /** + * 学时套餐ID + */ + private Long packageId; + + /** + * 学时套餐名称(快照) + */ + private String packageName; + + /** + * 学时类型:1=通用学时 2=其他 + */ + private Long hourType; + + /** + * 学时数 + */ + private Long hourCount; + + /** + * 原价格(元) + */ + private BigDecimal originPrice; + + /** + * 套餐价格(元) + */ + private BigDecimal packagePrice; + + /** + * 单价(元) + */ + private BigDecimal unitPrice; + + /** + * 购买数量 + */ + private Long quantity; + + /** + * 总课时数 + */ + private Long totalHours; + + /** + * 总金额(元) + */ + private BigDecimal totalAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 4=余额 + */ + private Long payMethod; + + /** + * 钱包类型:1=个人钱包 2=企业钱包 + */ + private Long walletType; + + /** + * 钱包ID + */ + private Long walletId; + + /** + * 钱包流水ID + */ + private Long walletTransactionId; + + /** + * 钱包流水号 + */ + private String walletTransactionNo; + + /** + * 支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭 + */ + private Long payStatus; + + /** + * 下单时间 + */ + private Date orderCreateTime; + + /** + * 支付时间 + */ + private Date payTime; + + /** + * 套餐快照(JSON) + */ + private String packageSnapshot; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/domain/bo/HotHourPackagePurchaseBo.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/domain/bo/HotHourPackagePurchaseBo.java new file mode 100644 index 0000000..b5f120a --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/domain/bo/HotHourPackagePurchaseBo.java @@ -0,0 +1,163 @@ +package com.hotwj.platform.operationManagement.hourPackagePurchase.domain.bo; + +import com.hotwj.platform.operationManagement.hourPackagePurchase.domain.HotHourPackagePurchase; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 学时套餐购买记录业务对象 hot_hour_package_purchase + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotHourPackagePurchase.class, reverseConvertGenerate = false) +public class HotHourPackagePurchaseBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + private Long accountId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 订单管理表ID(逻辑外键) + */ + private Long orderId; + + /** + * 订单号 + */ + private String orderNo; + + /** + * 学时套餐ID + */ + private Long packageId; + + /** + * 学时套餐名称(快照) + */ + private String packageName; + + /** + * 学时类型:1=通用学时 2=其他 + */ + private Long hourType; + + /** + * 学时数 + */ + private Long hourCount; + + /** + * 原价格(元) + */ + private BigDecimal originPrice; + + /** + * 套餐价格(元) + */ + private BigDecimal packagePrice; + + /** + * 单价(元) + */ + private BigDecimal unitPrice; + + /** + * 购买数量 + */ + private Long quantity; + + /** + * 总课时数 + */ + private Long totalHours; + + /** + * 总金额(元) + */ + private BigDecimal totalAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 4=余额 + */ + private Long payMethod; + + /** + * 钱包类型:1=个人钱包 2=企业钱包 + */ + private Long walletType; + + /** + * 钱包ID + */ + private Long walletId; + + /** + * 钱包流水ID + */ + private Long walletTransactionId; + + /** + * 钱包流水号 + */ + private String walletTransactionNo; + + /** + * 支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭 + */ + private Long payStatus; + + /** + * 下单时间 + */ + private Date orderCreateTime; + + /** + * 支付时间 + */ + private Date payTime; + + /** + * 套餐快照(JSON) + */ + private String packageSnapshot; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/domain/vo/HotHourPackagePurchaseVo.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/domain/vo/HotHourPackagePurchaseVo.java new file mode 100644 index 0000000..29398a6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/domain/vo/HotHourPackagePurchaseVo.java @@ -0,0 +1,210 @@ +package com.hotwj.platform.operationManagement.hourPackagePurchase.domain.vo; + +import com.hotwj.platform.operationManagement.hourPackagePurchase.domain.HotHourPackagePurchase; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 学时套餐购买记录视图对象 hot_hour_package_purchase + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotHourPackagePurchase.class) +public class HotHourPackagePurchaseVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + @ExcelProperty(value = "账户ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逻=辑外键") + private Long accountId; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户ID") + private Long userId; + + /** + * 订单管理表ID(逻辑外键) + */ + @ExcelProperty(value = "订单管理表ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逻=辑外键") + private Long orderId; + + /** + * 订单号 + */ + @ExcelProperty(value = "订单号") + private String orderNo; + + /** + * 学时套餐ID + */ + @ExcelProperty(value = "学时套餐ID") + private Long packageId; + + /** + * 学时套餐名称(快照) + */ + @ExcelProperty(value = "学时套餐名称", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "快=照") + private String packageName; + + /** + * 学时类型:1=通用学时 2=其他 + */ + @ExcelProperty(value = "学时类型:1=通用学时 2=其他") + private Long hourType; + + /** + * 学时数 + */ + @ExcelProperty(value = "学时数") + private Long hourCount; + + /** + * 原价格(元) + */ + @ExcelProperty(value = "原价格(元)") + private BigDecimal originPrice; + + /** + * 套餐价格(元) + */ + @ExcelProperty(value = "套餐价格(元)") + private BigDecimal packagePrice; + + /** + * 单价(元) + */ + @ExcelProperty(value = "单价(元)") + private BigDecimal unitPrice; + + /** + * 购买数量 + */ + @ExcelProperty(value = "购买数量") + private Long quantity; + + /** + * 总课时数 + */ + @ExcelProperty(value = "总课时数") + private Long totalHours; + + /** + * 总金额(元) + */ + @ExcelProperty(value = "总金额(元)") + private BigDecimal totalAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 4=余额 + */ + @ExcelProperty(value = "支付方式:1=微信 2=支付宝 3=银行卡 4=余额") + private Long payMethod; + + /** + * 钱包类型:1=个人钱包 2=企业钱包 + */ + @ExcelProperty(value = "钱包类型:1=个人钱包 2=企业钱包") + private Long walletType; + + /** + * 钱包ID + */ + @ExcelProperty(value = "钱包ID") + private Long walletId; + + /** + * 钱包流水ID + */ + @ExcelProperty(value = "钱包流水ID") + private Long walletTransactionId; + + /** + * 钱包流水号 + */ + @ExcelProperty(value = "钱包流水号") + private String walletTransactionNo; + + /** + * 支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭 + */ + @ExcelProperty(value = "支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭") + private Long payStatus; + + /** + * 下单时间 + */ + @ExcelProperty(value = "下单时间") + private Date orderCreateTime; + + /** + * 支付时间 + */ + @ExcelProperty(value = "支付时间") + private Date payTime; + + /** + * 套餐快照(JSON) + */ + @ExcelProperty(value = "套餐快照") + private String packageSnapshot; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/hour-package-purchase.md b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/hour-package-purchase.md new file mode 100644 index 0000000..1873c5b --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/hour-package-purchase.md @@ -0,0 +1,389 @@ +# 学时套餐购买接口文档 + +## 1. 模块说明 + +- 模块:企业端 / 移动端学时套餐购买 +- 控制器:`/operationManagement/hourPackagePurchase` +- 适用端口: + - 企业端:`port-company_admin` + - 移动端:`port-driver` +- 钱包规则: + - 企业端登录:默认使用企业钱包 `2` + - 移动端登录:默认使用个人钱包 `1` +- 支付方式:当前仅支持钱包余额支付,固定为 `4=余额` + +说明: + +- 可购买套餐来自总部端已启用的学时套餐 +- 购买成功后,后端会同时生成: + - 学时套餐购买记录 + - 支付订单 + - 钱包流水 + - 课时购买明细 +- 当前接口不需要前端传 `walletType`,后端会按登录端口自动判定 + +## 2. 字段说明 + +### 2.1 可购买套餐返回字段 + +接口 `GET /operationManagement/hourPackagePurchase/available/list` 返回字段与学时套餐列表保持一致: + +| 字段名 | 类型 | 说明 | +|-------------------|--------------|------------------| +| `id` | `Long` | 套餐ID | +| `packageCode` | `String` | 套餐编码 | +| `packageName` | `String` | 套餐名称 | +| `hourType` | `Long` | 学时类型,当前固定 `1` | +| `hourCount` | `Long` | 套餐学时数 | +| `originPrice` | `BigDecimal` | 原价格 | +| `packagePrice` | `BigDecimal` | 活动价,可为空 | +| `unitPrice` | `BigDecimal` | 学时单价 | +| `packageDesc` | `String` | 套餐说明 | +| `status` | `Long` | 状态:`1=启用`,`0=停用` | +| `sortNo` | `Long` | 排序号 | +| `applicableTypes` | `String` | 适用类型 | + +### 2.2 购买请求字段 + +| 字段名 | 类型 | 必填 | 说明 | +|-------------|----------|----|---------------| +| `packageId` | `Long` | 是 | 购买的套餐ID | +| `quantity` | `Long` | 是 | 购买数量,必须大于 `0` | +| `remark` | `String` | 否 | 备注 | + +### 2.3 购买成功返回字段 + +| 字段名 | 类型 | 说明 | +|-----------------------|--------------|------------------------| +| `purchaseId` | `Long` | 学时套餐购买记录ID | +| `orderId` | `Long` | 支付订单ID | +| `orderNo` | `String` | 业务订单号 | +| `payOrderId` | `Long` | 支付订单ID | +| `payOrderNo` | `String` | 平台支付订单号 | +| `walletType` | `Long` | 钱包类型:`1=个人钱包`,`2=企业钱包` | +| `walletId` | `Long` | 钱包ID | +| `walletTransactionId` | `Long` | 钱包流水ID | +| `walletTransactionNo` | `String` | 钱包流水号 | +| `totalHours` | `Long` | 本次购买总课时数 | +| `totalAmount` | `BigDecimal` | 本次支付总金额 | +| `walletBalance` | `BigDecimal` | 扣款后的钱包余额 | + +### 2.4 购买记录返回字段 + +接口 `GET /operationManagement/hourPackagePurchase/current/list` 主要返回以下字段: + +| 字段名 | 类型 | 说明 | +|-----------------------|--------------|------------------------| +| `id` | `Long` | 购买记录ID | +| `orderId` | `Long` | 支付订单ID | +| `orderNo` | `String` | 业务订单号 | +| `packageId` | `Long` | 套餐ID | +| `packageName` | `String` | 套餐名称 | +| `hourType` | `Long` | 学时类型 | +| `hourCount` | `Long` | 单份套餐学时数 | +| `originPrice` | `BigDecimal` | 原价格 | +| `packagePrice` | `BigDecimal` | 套餐价格 | +| `unitPrice` | `BigDecimal` | 学时单价 | +| `quantity` | `Long` | 购买数量 | +| `totalHours` | `Long` | 总课时数 | +| `totalAmount` | `BigDecimal` | 总金额 | +| `payMethod` | `Long` | 支付方式,当前固定 `4=余额` | +| `walletType` | `Long` | 钱包类型:`1=个人钱包`,`2=企业钱包` | +| `walletId` | `Long` | 钱包ID | +| `walletTransactionId` | `Long` | 钱包流水ID | +| `walletTransactionNo` | `String` | 钱包流水号 | +| `payStatus` | `Long` | 支付状态:`2=已支付` | +| `orderCreateTime` | `Date` | 下单时间 | +| `payTime` | `Date` | 支付时间 | +| `packageSnapshot` | `String` | 套餐快照JSON | +| `remark` | `String` | 备注 | + +## 3. 接口1:分页查询当前登录用户可购买的学时套餐列表 + +- 地址:`GET /operationManagement/hourPackagePurchase/available/list` +- 说明:返回当前登录用户可购买的已启用学时套餐 + +### 3.1 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|----------------|--------------|----|-----------| +| `pageNum` | `Integer` | 是 | 页码 | +| `pageSize` | `Integer` | 是 | 每页条数 | +| `packageName` | `String` | 否 | 套餐名称,模糊查询 | +| `hourCount` | `Long` | 否 | 学时数,精确查询 | +| `originPrice` | `BigDecimal` | 否 | 原价格,精确查询 | +| `packagePrice` | `BigDecimal` | 否 | 活动价,精确查询 | +| `packageDesc` | `String` | 否 | 套餐说明,模糊查询 | + +### 3.2 返回示例 + +```json +{ + "code": 200, + "rows": [ + { + "id": 1, + "packageCode": "HP1931512345678901234", + "packageName": "基础学时包", + "hourType": 1, + "hourCount": 40, + "originPrice": 1000.00, + "packagePrice": 800.00, + "unitPrice": 20.00, + "packageDesc": "总部通用学时套餐", + "status": 1, + "sortNo": 0, + "applicableTypes": "1" + } + ], + "total": 1, + "msg": "查询成功" +} +``` + +## 4. 接口2:使用钱包余额购买学时套餐 + +- 地址:`POST /operationManagement/hourPackagePurchase/wallet/purchase` +- Content-Type:`application/json` +- 说明:按当前登录端口自动选择企业钱包或个人钱包扣款 + +### 4.1 请求示例 + +```json +{ + "packageId": 1, + "quantity": 2, + "remark": "购买2026年培训课时" +} +``` + +### 4.2 返回示例 + +```json +{ + "code": 200, + "msg": "操作成功", + "data": { + "purchaseId": 10, + "orderId": 23, + "orderNo": "HPO1931512345678901234", + "payOrderId": 23, + "payOrderNo": "PO1931512345678905678", + "walletType": 2, + "walletId": 8, + "walletTransactionId": 66, + "walletTransactionNo": "WT1931512345678909999", + "totalHours": 80, + "totalAmount": 1600.00, + "walletBalance": 3400.00 + } +} +``` + +### 4.3 失败示例 + +```json +{ + "code": 500, + "msg": "钱包余额不足", + "data": null +} +``` + +常见失败原因: + +- 套餐不存在 +- 套餐未启用 +- 套餐学时配置异常 +- 钱包不存在 +- 钱包已冻结 +- 钱包余额不足 +- 当前登录端口不支持购买学时套餐 + +## 5. 接口3:分页查询当前登录用户购买记录 + +- 地址:`GET /operationManagement/hourPackagePurchase/current/list` +- 说明:按当前登录身份自动隔离购买记录 + +### 5.1 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|-------------------|-----------|----|---------------------| +| `pageNum` | `Integer` | 是 | 页码 | +| `pageSize` | `Integer` | 是 | 每页条数 | +| `packageName` | `String` | 否 | 套餐名称,模糊查询 | +| `orderNo` | `String` | 否 | 订单号,精确查询 | +| `payStatus` | `Long` | 否 | 支付状态 | +| `walletType` | `Long` | 否 | 钱包类型;建议前端不传,由后端自动锁定 | +| `orderCreateTime` | `Date` | 否 | 下单时间,精确查询 | + +### 5.2 返回示例 + +```json +{ + "code": 200, + "rows": [ + { + "id": 10, + "orderId": 23, + "orderNo": "HPO1931512345678901234", + "packageId": 1, + "packageName": "基础学时包", + "hourType": 1, + "hourCount": 40, + "originPrice": 1000.00, + "packagePrice": 800.00, + "unitPrice": 20.00, + "quantity": 2, + "totalHours": 80, + "totalAmount": 1600.00, + "payMethod": 4, + "walletType": 2, + "walletId": 8, + "walletTransactionId": 66, + "walletTransactionNo": "WT1931512345678909999", + "payStatus": 2, + "orderCreateTime": "2026-05-12 10:20:30", + "payTime": "2026-05-12 10:20:30", + "remark": "购买2026年培训课时" + } + ], + "total": 1, + "msg": "查询成功" +} +``` + +## 6. 业务规则 + +- 仅企业端与移动端可调用购买能力 +- 企业端固定使用企业钱包,移动端固定使用个人钱包 +- 钱包必须存在且未冻结 +- 钱包余额必须大于等于购买金额 +- 套餐必须是启用状态 +- 购买数量必须大于 `0` +- 总金额计算规则: + - 有活动价:`活动价 * 数量` + - 无活动价:`原价格 * 数量` +- 总课时数计算规则:`单份学时 * 数量` +- 支付方式当前固定为 `4=余额` +- 购买成功后支付状态固定记为 `2=已支付` + +## 7. 前端表格字段对照清单 + +### 7.1 可购买套餐列表页字段 + +| 列表列名 | 对应字段 | 展示建议 | 说明 | +|------|----------------|------|--------------| +| 套餐名称 | `packageName` | 文本 | 必显 | +| 学时数 | `hourCount` | 数字 | 建议展示为 `40学时` | +| 原价格 | `originPrice` | 金额 | 保留两位小数 | +| 活动价 | `packagePrice` | 金额 | 为空显示 `-` | +| 学时单价 | `unitPrice` | 金额 | 后端已算好 | +| 套餐说明 | `packageDesc` | 文本 | 过长省略 | +| 操作 | `id` | 按钮 | `立即购买` | + +### 7.2 可购买套餐列表页筛选项 + +| 筛选项 | 对应参数 | 组件建议 | 说明 | +|------|----------------|-------|------| +| 套餐名称 | `packageName` | 输入框 | 模糊查询 | +| 学时数 | `hourCount` | 数字输入框 | 精确查询 | +| 原价格 | `originPrice` | 金额输入框 | 精确查询 | +| 活动价 | `packagePrice` | 金额输入框 | 精确查询 | +| 套餐说明 | `packageDesc` | 输入框 | 模糊查询 | + +### 7.3 购买记录列表页字段 + +| 列表列名 | 对应字段 | 展示建议 | 说明 | +|-------|-----------------------|------|-------------------| +| 订单号 | `orderNo` | 文本 | 可复制 | +| 套餐名称 | `packageName` | 文本 | 必显 | +| 单份学时 | `hourCount` | 数字 | 建议加后缀“学时” | +| 购买数量 | `quantity` | 数字 | 必显 | +| 总课时数 | `totalHours` | 数字 | 必显 | +| 总金额 | `totalAmount` | 金额 | 保留两位小数 | +| 支付方式 | `payMethod` | 标签 | 当前固定显示“余额支付” | +| 钱包类型 | `walletType` | 标签 | `1=个人钱包`,`2=企业钱包` | +| 支付状态 | `payStatus` | 标签 | 当前主要显示“已支付” | +| 支付时间 | `payTime` | 日期时间 | 建议格式化展示 | +| 钱包流水号 | `walletTransactionNo` | 文本 | 可复制 | +| 备注 | `remark` | 文本 | 为空显示 `-` | + +### 7.4 购买记录列表页筛选项 + +| 筛选项 | 对应参数 | 组件建议 | 说明 | +|------|---------------|------|------------| +| 套餐名称 | `packageName` | 输入框 | 模糊查询 | +| 订单号 | `orderNo` | 输入框 | 精确查询 | +| 支付状态 | `payStatus` | 下拉框 | 当前可保留“已支付” | + +### 7.5 列表页接口对接 + +- 可购买套餐列表: + - 地址:`GET /operationManagement/hourPackagePurchase/available/list` + - 数据源:响应中的 `rows` + - 总数:响应中的 `total` +- 购买记录列表: + - 地址:`GET /operationManagement/hourPackagePurchase/current/list` + - 数据源:响应中的 `rows` + - 总数:响应中的 `total` + +## 8. 前端购买弹窗字段对照清单 + +### 8.1 购买弹窗字段 + +| 表单项 | 对应字段 | 必填 | 组件建议 | 说明 | +|------|----------------------------|----|---------|-------------------| +| 套餐名称 | `packageName` | 否 | 只读文本 | 从列表带入 | +| 单份学时 | `hourCount` | 否 | 只读文本 | 从列表带入 | +| 套餐价格 | `packagePrice/originPrice` | 否 | 只读文本 | 有活动价优先展示活动价 | +| 购买数量 | `quantity` | 是 | 数字输入框 | 最小值 `1` | +| 总金额 | 无 | 否 | 只读文本 | 前端可实时计算,后端以实际计算为准 | +| 备注 | `remark` | 否 | 输入框/文本域 | 可选 | + +### 8.2 弹窗提交字段 + +前端最终只需要提交: + +```json +{ + "packageId": 1, + "quantity": 1, + "remark": "购买课时套餐" +} +``` + +### 8.3 弹窗中不需要前端传的字段 + +| 字段 | 原因 | +|---------------|--------------| +| `walletType` | 后端按登录端口自动判断 | +| `walletId` | 后端自动获取 | +| `totalAmount` | 后端按套餐价格和数量计算 | +| `totalHours` | 后端按学时和数量计算 | +| `payMethod` | 当前固定为余额支付 | + +## 9. 前端实现建议 + +- 进入购买页时先调可购买套餐列表接口 +- 点击“立即购买”后,弹窗内建议同时展示: + - 套餐名称 + - 单份学时 + - 单份价格 + - 购买数量 + - 实时总金额 +- 购买成功后建议: + - 关闭弹窗 + - 刷新购买记录列表 + - 刷新钱包余额 +- 购买记录页建议把 `payMethod` 映射为中文: + - `4` -> `余额支付` +- 钱包类型建议映射为中文: + - `1` -> `个人钱包` + - `2` -> `企业钱包` +- 支付状态建议映射为中文: + - `1` -> `待支付` + - `2` -> `已支付` + - `3` -> `已退款` + - `4` -> `已取消` + - `5` -> `已关闭` diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/mapper/HotHourPackagePurchaseMapper.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/mapper/HotHourPackagePurchaseMapper.java new file mode 100644 index 0000000..b938607 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/mapper/HotHourPackagePurchaseMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.hourPackagePurchase.mapper; + +import com.hotwj.platform.operationManagement.hourPackagePurchase.domain.HotHourPackagePurchase; +import com.hotwj.platform.operationManagement.hourPackagePurchase.domain.vo.HotHourPackagePurchaseVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 学时套餐购买记录Mapper接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Mapper +public interface HotHourPackagePurchaseMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/service/IHotHourPackagePurchaseService.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/service/IHotHourPackagePurchaseService.java new file mode 100644 index 0000000..453542e --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/service/IHotHourPackagePurchaseService.java @@ -0,0 +1,98 @@ +package com.hotwj.platform.operationManagement.hourPackagePurchase.service; + +import com.hotwj.platform.operationManagement.hourPackage.domain.bo.HotHourPackageBo; +import com.hotwj.platform.operationManagement.hourPackage.domain.vo.HotHourPackageVo; +import com.hotwj.platform.operationManagement.hourPackagePurchase.controller.vo.HotHourPackageWalletPurchaseReqVO; +import com.hotwj.platform.operationManagement.hourPackagePurchase.controller.vo.HotHourPackageWalletPurchaseRespVO; +import com.hotwj.platform.operationManagement.hourPackagePurchase.domain.vo.HotHourPackagePurchaseVo; +import com.hotwj.platform.operationManagement.hourPackagePurchase.domain.bo.HotHourPackagePurchaseBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 学时套餐购买记录Service接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +public interface IHotHourPackagePurchaseService { + + /** + * 查询学时套餐购买记录 + * + * @param id 主键 + * @return 学时套餐购买记录 + */ + HotHourPackagePurchaseVo queryById(Long id); + + /** + * 分页查询学时套餐购买记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 学时套餐购买记录分页列表 + */ + TableDataInfo queryPageList(HotHourPackagePurchaseBo bo, PageQuery pageQuery); + + /** + * 查询当前登录用户可购买的学时套餐列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 可购买学时套餐分页列表 + */ + TableDataInfo queryAvailablePageList(HotHourPackageBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的学时套餐购买记录列表 + * + * @param bo 查询条件 + * @return 学时套餐购买记录列表 + */ + List queryList(HotHourPackagePurchaseBo bo); + + /** + * 查询当前登录用户的购买记录 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 当前登录用户购买记录 + */ + TableDataInfo queryCurrentUserPageList(HotHourPackagePurchaseBo bo, PageQuery pageQuery); + + /** + * 使用钱包余额购买学时套餐 + * + * @param reqVO 请求参数 + * @return 购买结果 + */ + HotHourPackageWalletPurchaseRespVO purchaseByWallet(HotHourPackageWalletPurchaseReqVO reqVO); + + /** + * 新增学时套餐购买记录 + * + * @param bo 学时套餐购买记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotHourPackagePurchaseBo bo); + + /** + * 修改学时套餐购买记录 + * + * @param bo 学时套餐购买记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotHourPackagePurchaseBo bo); + + /** + * 校验并批量删除学时套餐购买记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/service/impl/HotHourPackagePurchaseServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/service/impl/HotHourPackagePurchaseServiceImpl.java new file mode 100644 index 0000000..2e32196 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPackagePurchase/service/impl/HotHourPackagePurchaseServiceImpl.java @@ -0,0 +1,540 @@ +package com.hotwj.platform.operationManagement.hourPackagePurchase.service.impl; + +import cn.hutool.core.util.IdUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.common.helper.DriverLoginContextHelper; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.operationManagement.hourPackage.domain.HotHourPackage; +import com.hotwj.platform.operationManagement.hourPackage.domain.bo.HotHourPackageBo; +import com.hotwj.platform.operationManagement.hourPackage.domain.vo.HotHourPackageVo; +import com.hotwj.platform.operationManagement.hourPackage.mapper.HotHourPackageMapper; +import com.hotwj.platform.operationManagement.hourPackage.service.IHotHourPackageService; +import com.hotwj.platform.operationManagement.hourPackagePurchase.controller.vo.HotHourPackageWalletPurchaseReqVO; +import com.hotwj.platform.operationManagement.hourPackagePurchase.controller.vo.HotHourPackageWalletPurchaseRespVO; +import com.hotwj.platform.operationManagement.hourPackagePurchase.domain.HotHourPackagePurchase; +import com.hotwj.platform.operationManagement.hourPackagePurchase.domain.bo.HotHourPackagePurchaseBo; +import com.hotwj.platform.operationManagement.hourPackagePurchase.domain.vo.HotHourPackagePurchaseVo; +import com.hotwj.platform.operationManagement.hourPackagePurchase.mapper.HotHourPackagePurchaseMapper; +import com.hotwj.platform.operationManagement.hourPackagePurchase.service.IHotHourPackagePurchaseService; +import com.hotwj.platform.operationManagement.hourPurchaseDetail.domain.HotHourPurchaseDetail; +import com.hotwj.platform.operationManagement.hourPurchaseDetail.mapper.HotHourPurchaseDetailMapper; +import com.hotwj.platform.pay.domain.PayApp; +import com.hotwj.platform.pay.domain.PayOrder; +import com.hotwj.platform.pay.domain.PayWallet; +import com.hotwj.platform.pay.domain.PayWalletTransaction; +import com.hotwj.platform.pay.enums.PayOrderBizTypeEnum; +import com.hotwj.platform.pay.enums.PayOrderStatusEnum; +import com.hotwj.platform.pay.mapper.PayAppMapper; +import com.hotwj.platform.pay.mapper.PayOrderMapper; +import com.hotwj.platform.pay.mapper.PayWalletMapper; +import com.hotwj.platform.pay.mapper.PayWalletTransactionMapper; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.ServletUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.*; + +/** + * 学时套餐购买记录Service业务层处理 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotHourPackagePurchaseServiceImpl implements IHotHourPackagePurchaseService { + + private static final Long PERSONAL_WALLET_TYPE = 1L; + private static final Long COMPANY_WALLET_TYPE = 2L; + private static final Long PAY_METHOD_WALLET = 4L; + private static final Long PURCHASE_PAY_STATUS_PAID = 2L; + private static final Long WALLET_FREEZE_STATUS_NORMAL = 0L; + private static final Long WALLET_FREEZE_STATUS_FROZEN = 1L; + private static final Long WALLET_BIZ_TYPE_HOUR_PACKAGE = 3L; + private static final Long PURCHASE_DETAIL_STATUS_EFFECTIVE = 1L; + + private final HotHourPackagePurchaseMapper baseMapper; + private final IHotHourPackageService hotHourPackageService; + private final HotHourPackageMapper hotHourPackageMapper; + private final HotHourPurchaseDetailMapper hotHourPurchaseDetailMapper; + private final PayWalletMapper payWalletMapper; + private final PayWalletTransactionMapper payWalletTransactionMapper; + private final PayOrderMapper payOrderMapper; + private final PayAppMapper payAppMapper; + + /** + * 查询学时套餐购买记录 + * + * @param id 主键 + * @return 学时套餐购买记录 + */ + @Override + public HotHourPackagePurchaseVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询学时套餐购买记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 学时套餐购买记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotHourPackagePurchaseBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + @Override + public TableDataInfo queryAvailablePageList(HotHourPackageBo bo, PageQuery pageQuery) { + assertSupportedBuyer(LoginHelper.getLoginUser()); + HotHourPackageBo queryBo = bo == null ? new HotHourPackageBo() : bo; + queryBo.setStatus(1L); + return hotHourPackageService.queryPageList(queryBo, pageQuery); + } + + /** + * 查询符合条件的学时套餐购买记录列表 + * + * @param bo 查询条件 + * @return 学时套餐购买记录列表 + */ + @Override + public List queryList(HotHourPackagePurchaseBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + @Override + public TableDataInfo queryCurrentUserPageList(HotHourPackagePurchaseBo bo, PageQuery pageQuery) { + LoginUser loginUser = assertValidLoginUser(LoginHelper.getLoginUser()); + HotHourPackagePurchaseBo queryBo = bo == null ? new HotHourPackagePurchaseBo() : bo; + if (isDriverPort(loginUser)) { + queryBo.setUserId(loginUser.getUserId()); + queryBo.setWalletType(PERSONAL_WALLET_TYPE); + } else { + queryBo.setCompanyId(loginUser.getCompanyId()); + queryBo.setWalletType(COMPANY_WALLET_TYPE); + } + return queryPageList(queryBo, pageQuery); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public HotHourPackageWalletPurchaseRespVO purchaseByWallet(HotHourPackageWalletPurchaseReqVO reqVO) { + LoginUser loginUser = assertSupportedBuyer(LoginHelper.getLoginUser()); + Long quantity = reqVO.getQuantity() == null ? 1L : reqVO.getQuantity(); + if (quantity <= 0) { + throw new ServiceException("购买数量必须大于0"); + } + + HotHourPackage hourPackage = hotHourPackageMapper.selectById(reqVO.getPackageId()); + if (hourPackage == null) { + throw new ServiceException("学时套餐不存在"); + } + if (!Objects.equals(hourPackage.getStatus(), 1L)) { + throw new ServiceException("学时套餐未启用,暂不可购买"); + } + if (hourPackage.getHourCount() == null || hourPackage.getHourCount() <= 0) { + throw new ServiceException("学时套餐学时配置异常"); + } + + PurchaseContext context = resolvePurchaseContext(loginUser); + PayWallet wallet = getWallet(context); + ensureWalletAvailable(wallet); + + BigDecimal effectivePrice = resolveEffectivePrice(hourPackage); + BigDecimal packageUnitPrice = resolvePackageUnitPrice(hourPackage, effectivePrice); + BigDecimal totalAmount = effectivePrice.multiply(BigDecimal.valueOf(quantity)).setScale(2, RoundingMode.HALF_UP); + Long totalHours = hourPackage.getHourCount() * quantity; + BigDecimal balance = defaultAmount(wallet.getBalance()); + if (balance.compareTo(totalAmount) < 0) { + throw new ServiceException("钱包余额不足"); + } + + Date now = new Date(); + String orderNo = buildNo("HPO"); + + HotHourPackagePurchase purchase = new HotHourPackagePurchase(); + purchase.setCompanyId(loginUser.getCompanyId()); + purchase.setAccountId(wallet.getId()); + purchase.setUserId(loginUser.getUserId()); + purchase.setOrderNo(orderNo); + purchase.setPackageId(hourPackage.getId()); + purchase.setPackageName(hourPackage.getPackageName()); + purchase.setHourType(hourPackage.getHourType()); + purchase.setHourCount(hourPackage.getHourCount()); + purchase.setOriginPrice(normalizeMoney(hourPackage.getOriginPrice())); + purchase.setPackagePrice(normalizeNullableMoney(hourPackage.getPackagePrice())); + purchase.setUnitPrice(packageUnitPrice); + purchase.setQuantity(quantity); + purchase.setTotalHours(totalHours); + purchase.setTotalAmount(totalAmount); + purchase.setPayMethod(PAY_METHOD_WALLET); + purchase.setWalletType(context.walletType()); + purchase.setWalletId(wallet.getId()); + purchase.setPayStatus(PURCHASE_PAY_STATUS_PAID); + purchase.setOrderCreateTime(now); + purchase.setPayTime(now); + purchase.setPackageSnapshot(buildPackageSnapshot(hourPackage)); + purchase.setRemark(reqVO.getRemark()); + if (baseMapper.insert(purchase) <= 0) { + throw new ServiceException("创建学时套餐购买记录失败"); + } + + PayOrder payOrder = buildSuccessPayOrder(purchase, totalAmount, now, hourPackage.getPackageName()); + if (payOrderMapper.insert(payOrder) <= 0) { + throw new ServiceException("创建支付订单失败"); + } + + HotHourPackagePurchase purchaseUpdate = new HotHourPackagePurchase(); + purchaseUpdate.setId(purchase.getId()); + purchaseUpdate.setOrderId(payOrder.getId()); + if (baseMapper.updateById(purchaseUpdate) <= 0) { + throw new ServiceException("回写支付订单失败"); + } + + BigDecimal newBalance = balance.subtract(totalAmount).setScale(2, RoundingMode.HALF_UP); + if (!deductWalletBalance(wallet, balance, totalAmount, newBalance)) { + throw new ServiceException("钱包余额已变更,请刷新后重试"); + } + + PayWalletTransaction walletTransaction = buildWalletTransaction(wallet.getId(), orderNo, totalAmount, newBalance, reqVO.getRemark()); + if (payWalletTransactionMapper.insert(walletTransaction) <= 0) { + throw new ServiceException("写入钱包流水失败"); + } + + HotHourPackagePurchase transactionUpdate = new HotHourPackagePurchase(); + transactionUpdate.setId(purchase.getId()); + transactionUpdate.setWalletTransactionId(walletTransaction.getId()); + transactionUpdate.setWalletTransactionNo(walletTransaction.getNo()); + if (baseMapper.updateById(transactionUpdate) <= 0) { + throw new ServiceException("回写钱包流水失败"); + } + + HotHourPurchaseDetail purchaseDetail = buildPurchaseDetail(purchase, payOrder, wallet, hourPackage, context, quantity, totalHours, reqVO.getRemark()); + if (hotHourPurchaseDetailMapper.insert(purchaseDetail) <= 0) { + throw new ServiceException("创建课时购买明细失败"); + } + + HotHourPackageWalletPurchaseRespVO respVO = new HotHourPackageWalletPurchaseRespVO(); + respVO.setPurchaseId(purchase.getId()); + respVO.setOrderId(payOrder.getId()); + respVO.setOrderNo(orderNo); + respVO.setPayOrderId(payOrder.getId()); + respVO.setPayOrderNo(payOrder.getNo()); + respVO.setWalletType(context.walletType()); + respVO.setWalletId(wallet.getId()); + respVO.setWalletTransactionId(walletTransaction.getId()); + respVO.setWalletTransactionNo(walletTransaction.getNo()); + respVO.setTotalHours(totalHours); + respVO.setTotalAmount(totalAmount); + respVO.setWalletBalance(newBalance); + return respVO; + } + + private LambdaQueryWrapper buildQueryWrapper(HotHourPackagePurchaseBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotHourPackagePurchase::getId); + lqw.eq(bo.getCompanyId() != null, HotHourPackagePurchase::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getAccountId() != null, HotHourPackagePurchase::getAccountId, bo.getAccountId()); + lqw.eq(bo.getUserId() != null, HotHourPackagePurchase::getUserId, bo.getUserId()); + lqw.eq(bo.getOrderId() != null, HotHourPackagePurchase::getOrderId, bo.getOrderId()); + lqw.eq(StringUtils.isNotBlank(bo.getOrderNo()), HotHourPackagePurchase::getOrderNo, bo.getOrderNo()); + lqw.eq(bo.getPackageId() != null, HotHourPackagePurchase::getPackageId, bo.getPackageId()); + lqw.like(StringUtils.isNotBlank(bo.getPackageName()), HotHourPackagePurchase::getPackageName, bo.getPackageName()); + lqw.eq(bo.getHourType() != null, HotHourPackagePurchase::getHourType, bo.getHourType()); + lqw.eq(bo.getHourCount() != null, HotHourPackagePurchase::getHourCount, bo.getHourCount()); + lqw.eq(bo.getOriginPrice() != null, HotHourPackagePurchase::getOriginPrice, bo.getOriginPrice()); + lqw.eq(bo.getPackagePrice() != null, HotHourPackagePurchase::getPackagePrice, bo.getPackagePrice()); + lqw.eq(bo.getUnitPrice() != null, HotHourPackagePurchase::getUnitPrice, bo.getUnitPrice()); + lqw.eq(bo.getQuantity() != null, HotHourPackagePurchase::getQuantity, bo.getQuantity()); + lqw.eq(bo.getTotalHours() != null, HotHourPackagePurchase::getTotalHours, bo.getTotalHours()); + lqw.eq(bo.getTotalAmount() != null, HotHourPackagePurchase::getTotalAmount, bo.getTotalAmount()); + lqw.eq(bo.getPayMethod() != null, HotHourPackagePurchase::getPayMethod, bo.getPayMethod()); + lqw.eq(bo.getWalletType() != null, HotHourPackagePurchase::getWalletType, bo.getWalletType()); + lqw.eq(bo.getWalletId() != null, HotHourPackagePurchase::getWalletId, bo.getWalletId()); + lqw.eq(bo.getWalletTransactionId() != null, HotHourPackagePurchase::getWalletTransactionId, bo.getWalletTransactionId()); + lqw.eq(StringUtils.isNotBlank(bo.getWalletTransactionNo()), HotHourPackagePurchase::getWalletTransactionNo, bo.getWalletTransactionNo()); + lqw.eq(bo.getPayStatus() != null, HotHourPackagePurchase::getPayStatus, bo.getPayStatus()); + lqw.eq(bo.getOrderCreateTime() != null, HotHourPackagePurchase::getOrderCreateTime, bo.getOrderCreateTime()); + lqw.eq(bo.getPayTime() != null, HotHourPackagePurchase::getPayTime, bo.getPayTime()); + lqw.like(StringUtils.isNotBlank(bo.getPackageSnapshot()), HotHourPackagePurchase::getPackageSnapshot, bo.getPackageSnapshot()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotHourPackagePurchase::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotHourPackagePurchase::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotHourPackagePurchase::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + private LoginUser assertSupportedBuyer(LoginUser loginUser) { + loginUser = assertValidLoginUser(loginUser); + if (isEnterprisePort(loginUser) || isDriverPort(loginUser)) { + return loginUser; + } + throw new ServiceException("当前登录端口不支持购买学时套餐"); + } + + private LoginUser assertValidLoginUser(LoginUser loginUser) { + if (loginUser == null || loginUser.getUserId() == null || loginUser.getCompanyId() == null) { + throw new ServiceException("登录状态异常,请重新登录后再操作"); + } + return loginUser; + } + + private boolean isEnterprisePort(LoginUser loginUser) { + return loginUser != null && StringUtils.equals(loginUser.getLoginPort(), ISysUserLoginPortService.ENTERPRISE_PORT); + } + + private boolean isDriverPort(LoginUser loginUser) { + return loginUser != null && StringUtils.equals(loginUser.getLoginPort(), ISysUserLoginPortService.DRIVER_PORT); + } + + private PurchaseContext resolvePurchaseContext(LoginUser loginUser) { + if (isEnterprisePort(loginUser)) { + return new PurchaseContext( + COMPANY_WALLET_TYPE, + loginUser.getCompanyId(), + StringUtils.defaultIfBlank(loginUser.getNickname(), loginUser.getUsername()), + loginUser.getCompanyId(), + loginUser.getCompanyName() + ); + } + HotDriver driver = DriverLoginContextHelper.getCurrentDriver(); + return new PurchaseContext( + PERSONAL_WALLET_TYPE, + loginUser.getUserId(), + StringUtils.defaultIfBlank(driver.getName(), StringUtils.defaultIfBlank(loginUser.getNickname(), loginUser.getUsername())), + loginUser.getCompanyId(), + loginUser.getCompanyName() + ); + } + + private PayWallet getWallet(PurchaseContext context) { + PayWallet wallet = payWalletMapper.selectOne(Wrappers.lambdaQuery() + .eq(PayWallet::getUserId, context.walletOwnerId()) + .eq(PayWallet::getWalletType, context.walletType()) + .last("limit 1")); + if (wallet == null) { + throw new ServiceException("钱包不存在,请先充值后再购买"); + } + return wallet; + } + + private void ensureWalletAvailable(PayWallet wallet) { + if (wallet == null) { + throw new ServiceException("钱包不存在"); + } + if (Objects.equals(wallet.getFreezeStatus(), WALLET_FREEZE_STATUS_FROZEN)) { + throw new ServiceException("钱包已冻结,无法购买"); + } + if (!Objects.equals(wallet.getFreezeStatus(), WALLET_FREEZE_STATUS_NORMAL)) { + throw new ServiceException("钱包状态异常,无法购买"); + } + } + + private BigDecimal resolveEffectivePrice(HotHourPackage hourPackage) { + BigDecimal price = hourPackage.getPackagePrice() != null ? hourPackage.getPackagePrice() : hourPackage.getOriginPrice(); + if (price == null || price.compareTo(BigDecimal.ZERO) <= 0) { + throw new ServiceException("学时套餐价格配置异常"); + } + return normalizeMoney(price); + } + + private BigDecimal resolvePackageUnitPrice(HotHourPackage hourPackage, BigDecimal effectivePrice) { + if (hourPackage.getUnitPrice() != null && hourPackage.getUnitPrice().compareTo(BigDecimal.ZERO) > 0) { + return normalizeMoney(hourPackage.getUnitPrice()); + } + return effectivePrice.divide(BigDecimal.valueOf(hourPackage.getHourCount()), 2, RoundingMode.HALF_UP); + } + + private PayOrder buildSuccessPayOrder(HotHourPackagePurchase purchase, BigDecimal totalAmount, Date now, String packageName) { + PayApp payApp = getActivePayApp(); + PayOrder payOrder = new PayOrder(); + payOrder.setAppId(payApp.getId()); + payOrder.setMerchantOrderNo(purchase.getOrderNo()); + payOrder.setBizOrderType(PayOrderBizTypeEnum.HOUR_PACKAGE.getCode()); + payOrder.setBizOrderId(purchase.getId()); + payOrder.setSubject("课时套餐购买"); + payOrder.setBody("钱包购买课时套餐:" + packageName); + // 钱包直购为同步成功单,不走第三方支付回调,但仍需满足支付订单表的非空约束。 + payOrder.setNotifyUrl(""); + payOrder.setReturnUrl(""); + payOrder.setUserIp(StringUtils.isNotBlank(ServletUtils.getClientIP()) ? ServletUtils.getClientIP() : "127.0.0.1"); + payOrder.setPrice(totalAmount); + payOrder.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); + payOrder.setSuccessTime(now); + payOrder.setNo(buildNo("PO")); + return payOrder; + } + + private boolean deductWalletBalance(PayWallet wallet, BigDecimal oldBalance, BigDecimal expenseAmount, BigDecimal newBalance) { + PayWallet updateWallet = new PayWallet(); + updateWallet.setBalance(newBalance); + updateWallet.setTotalExpense(defaultAmount(wallet.getTotalExpense()).add(expenseAmount).setScale(2, RoundingMode.HALF_UP)); + return payWalletMapper.update(updateWallet, Wrappers.lambdaUpdate() + .eq(PayWallet::getId, wallet.getId()) + .eq(PayWallet::getFreezeStatus, WALLET_FREEZE_STATUS_NORMAL) + .eq(PayWallet::getBalance, oldBalance)) > 0; + } + + private PayWalletTransaction buildWalletTransaction(Long walletId, String bizId, BigDecimal totalAmount, BigDecimal balance, String remark) { + PayWalletTransaction walletTransaction = new PayWalletTransaction(); + walletTransaction.setWalletId(walletId); + walletTransaction.setBizType(WALLET_BIZ_TYPE_HOUR_PACKAGE); + walletTransaction.setBizId(bizId); + walletTransaction.setNo(buildNo("WT")); + walletTransaction.setTitle("购买课时套餐"); + walletTransaction.setPrice(totalAmount.negate().setScale(2, RoundingMode.HALF_UP)); + walletTransaction.setBalance(balance); + walletTransaction.setRemark(remark); + return walletTransaction; + } + + private HotHourPurchaseDetail buildPurchaseDetail(HotHourPackagePurchase purchase, PayOrder payOrder, PayWallet wallet, + HotHourPackage hourPackage, PurchaseContext context, Long quantity, + Long totalHours, String remark) { + HotHourPurchaseDetail detail = new HotHourPurchaseDetail(); + detail.setCompanyId(purchase.getCompanyId()); + detail.setAccountId(wallet.getId()); + detail.setUserId(purchase.getUserId()); + detail.setBuyerName(context.buyerName()); + detail.setBuyerCompanyId(context.buyerCompanyId()); + detail.setBuyerCompanyName(context.buyerCompanyName()); + detail.setPurchaseId(purchase.getId()); + detail.setOrderId(payOrder.getId()); + detail.setOrderNo(purchase.getOrderNo()); + detail.setPackageId(hourPackage.getId()); + detail.setPackageName(hourPackage.getPackageName()); + detail.setApplicableTypes(hourPackage.getApplicableTypes()); + detail.setPurchaseCount(quantity); + detail.setTotalHours(totalHours); + detail.setUsedHours(0L); + detail.setRemainingHours(totalHours); + detail.setWalletType(purchase.getWalletType()); + detail.setWalletId(wallet.getId()); + detail.setVersion(0L); + detail.setStatus(PURCHASE_DETAIL_STATUS_EFFECTIVE); + detail.setRemark(remark); + return detail; + } + + private String buildPackageSnapshot(HotHourPackage hourPackage) { + Map snapshot = new LinkedHashMap<>(); + snapshot.put("packageId", hourPackage.getId()); + snapshot.put("packageCode", hourPackage.getPackageCode()); + snapshot.put("packageName", hourPackage.getPackageName()); + snapshot.put("hourType", hourPackage.getHourType()); + snapshot.put("hourCount", hourPackage.getHourCount()); + snapshot.put("originPrice", normalizeNullableMoney(hourPackage.getOriginPrice())); + snapshot.put("packagePrice", normalizeNullableMoney(hourPackage.getPackagePrice())); + snapshot.put("unitPrice", normalizeNullableMoney(hourPackage.getUnitPrice())); + snapshot.put("applicableTypes", hourPackage.getApplicableTypes()); + snapshot.put("packageDesc", hourPackage.getPackageDesc()); + return JsonUtils.toJsonString(snapshot); + } + + private PayApp getActivePayApp() { + PayApp app = payAppMapper.selectOne(Wrappers.lambdaQuery() + .eq(PayApp::getStatus, 1L) + .orderByAsc(PayApp::getId) + .last("limit 1")); + if (app == null) { + throw new ServiceException("未配置可用支付应用,请先维护支付应用"); + } + return app; + } + + private BigDecimal defaultAmount(BigDecimal amount) { + return amount == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : amount.setScale(2, RoundingMode.HALF_UP); + } + + private BigDecimal normalizeMoney(BigDecimal amount) { + if (amount == null) { + throw new ServiceException("金额不能为空"); + } + return amount.setScale(2, RoundingMode.HALF_UP); + } + + private BigDecimal normalizeNullableMoney(BigDecimal amount) { + return amount == null ? null : amount.setScale(2, RoundingMode.HALF_UP); + } + + private String buildNo(String prefix) { + return prefix + IdUtil.getSnowflakeNextIdStr(); + } + + private record PurchaseContext(Long walletType, Long walletOwnerId, String buyerName, + Long buyerCompanyId, String buyerCompanyName) { + } + + /** + * 新增学时套餐购买记录 + * + * @param bo 学时套餐购买记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotHourPackagePurchaseBo bo) { + HotHourPackagePurchase add = MapstructUtils.convert(bo, HotHourPackagePurchase.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改学时套餐购买记录 + * + * @param bo 学时套餐购买记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotHourPackagePurchaseBo bo) { + HotHourPackagePurchase update = MapstructUtils.convert(bo, HotHourPackagePurchase.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotHourPackagePurchase entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除学时套餐购买记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/controller/HotHourPurchaseDetailController.java b/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/controller/HotHourPurchaseDetailController.java new file mode 100644 index 0000000..d597cc8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/controller/HotHourPurchaseDetailController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.operationManagement.hourPurchaseDetail.controller; + +import com.hotwj.platform.operationManagement.hourPurchaseDetail.domain.bo.HotHourPurchaseDetailBo; +import com.hotwj.platform.operationManagement.hourPurchaseDetail.domain.vo.HotHourPurchaseDetailVo; +import com.hotwj.platform.operationManagement.hourPurchaseDetail.service.IHotHourPurchaseDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 培训学时购买明细 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/hourPurchaseDetail") +@Tag(name = "培训学时购买明细", description = "培训学时购买明细管理") +public class HotHourPurchaseDetailController extends BaseController { + + private final IHotHourPurchaseDetailService hotHourPurchaseDetailService; + + /** + * 查询培训学时购买明细列表 + */ + //@SaCheckPermission("operationManagement:hourPurchaseDetail:list") + @GetMapping("/list") + @Operation(summary = "分页查询培训学时购买明细列表") + public TableDataInfo list(HotHourPurchaseDetailBo bo, PageQuery pageQuery) { + return hotHourPurchaseDetailService.queryPageList(bo, pageQuery); + } + + /** + * 导出培训学时购买明细列表 + */ + //@SaCheckPermission("operationManagement:hourPurchaseDetail:export") + @Log(title = "培训学时购买明细", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出培训学时购买明细列表") + public void export(HotHourPurchaseDetailBo bo, HttpServletResponse response) { + List list = hotHourPurchaseDetailService.queryList(bo); + ExcelUtil.exportExcel(list, "培训学时购买明细", HotHourPurchaseDetailVo.class, response); + } + + /** + * 获取培训学时购买明细详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("operationManagement:hourPurchaseDetail:query") + @GetMapping("/{id}") + @Operation(summary = "获取培训学时购买明细详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotHourPurchaseDetailService.queryById(id)); + } + + /** + * 新增培训学时购买明细 + */ + //@SaCheckPermission("operationManagement:hourPurchaseDetail:add") + @Log(title = "培训学时购买明细", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增培训学时购买明细") + public R add(@Validated(AddGroup.class) @RequestBody HotHourPurchaseDetailBo bo) { + return toAjax(hotHourPurchaseDetailService.insertByBo(bo)); + } + + /** + * 修改培训学时购买明细 + */ + //@SaCheckPermission("operationManagement:hourPurchaseDetail:edit") + @Log(title = "培训学时购买明细", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改培训学时购买明细") + public R edit(@Validated(EditGroup.class) @RequestBody HotHourPurchaseDetailBo bo) { + return toAjax(hotHourPurchaseDetailService.updateByBo(bo)); + } + + /** + * 删除培训学时购买明细 + * + * @param ids 主键串 + */ + //@SaCheckPermission("operationManagement:hourPurchaseDetail:remove") + @Log(title = "培训学时购买明细", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除培训学时购买明细") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotHourPurchaseDetailService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/domain/HotHourPurchaseDetail.java b/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/domain/HotHourPurchaseDetail.java new file mode 100644 index 0000000..d948010 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/domain/HotHourPurchaseDetail.java @@ -0,0 +1,154 @@ +package com.hotwj.platform.operationManagement.hourPurchaseDetail.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +import java.io.Serial; + +/** + * 培训学时购买明细对象 hot_hour_purchase_detail + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_hour_purchase_detail") +public class HotHourPurchaseDetail extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + private Long accountId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 购买人 + */ + private String buyerName; + + /** + * 购买企业ID + */ + private Long buyerCompanyId; + + /** + * 购买企业名称 + */ + private String buyerCompanyName; + + /** + * 学时套餐购买记录ID + */ + private Long purchaseId; + + /** + * 订单管理表ID(逻辑外键) + */ + private Long orderId; + + /** + * 订单号 + */ + private String orderNo; + + /** + * 学时套餐ID + */ + private Long packageId; + + /** + * 套餐名称(快照) + */ + private String packageName; + + /** + * 适用类型(如日常培训、事故培训等) + */ + private String applicableTypes; + + /** + * 购买数量 + */ + private Long purchaseCount; + + /** + * 总课时数 + */ + private Long totalHours; + + /** + * 已使用学时数 + */ + private Long usedHours; + + /** + * 剩余学时数 + */ + private Long remainingHours; + + /** + * 钱包类型:1=个人钱包 2=企业钱包 + */ + private Long walletType; + + /** + * 钱包ID + */ + private Long walletId; + + /** + * 乐观锁版本号 + */ + private Long version; + + /** + * 状态:1=生效 2=已用完 3=已过期 4=已作废 + */ + private Long status; + + /** + * 最近使用时间 + */ + private Date lastUseTime; + + /** + * 到期时间 + */ + private Date expireTime; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/domain/bo/HotHourPurchaseDetailBo.java b/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/domain/bo/HotHourPurchaseDetailBo.java new file mode 100644 index 0000000..789cc49 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/domain/bo/HotHourPurchaseDetailBo.java @@ -0,0 +1,152 @@ +package com.hotwj.platform.operationManagement.hourPurchaseDetail.domain.bo; + +import com.hotwj.platform.operationManagement.hourPurchaseDetail.domain.HotHourPurchaseDetail; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.util.Date; + +/** + * 培训学时购买明细业务对象 hot_hour_purchase_detail + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotHourPurchaseDetail.class, reverseConvertGenerate = false) +public class HotHourPurchaseDetailBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + private Long accountId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 购买人 + */ + private String buyerName; + + /** + * 购买企业ID + */ + private Long buyerCompanyId; + + /** + * 购买企业名称 + */ + private String buyerCompanyName; + + /** + * 学时套餐购买记录ID + */ + private Long purchaseId; + + /** + * 订单管理表ID(逻辑外键) + */ + private Long orderId; + + /** + * 订单号 + */ + private String orderNo; + + /** + * 学时套餐ID + */ + private Long packageId; + + /** + * 套餐名称(快照) + */ + private String packageName; + + /** + * 适用类型(如日常培训、事故培训等) + */ + private String applicableTypes; + + /** + * 购买数量 + */ + private Long purchaseCount; + + /** + * 总课时数 + */ + private Long totalHours; + + /** + * 已使用学时数 + */ + private Long usedHours; + + /** + * 剩余学时数 + */ + private Long remainingHours; + + /** + * 钱包类型:1=个人钱包 2=企业钱包 + */ + private Long walletType; + + /** + * 钱包ID + */ + private Long walletId; + + /** + * 乐观锁版本号 + */ + private Long version; + + /** + * 状态:1=生效 2=已用完 3=已过期 4=已作废 + */ + private Long status; + + /** + * 最近使用时间 + */ + private Date lastUseTime; + + /** + * 到期时间 + */ + private Date expireTime; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/domain/vo/HotHourPurchaseDetailVo.java b/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/domain/vo/HotHourPurchaseDetailVo.java new file mode 100644 index 0000000..e0f0e1e --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/domain/vo/HotHourPurchaseDetailVo.java @@ -0,0 +1,198 @@ +package com.hotwj.platform.operationManagement.hourPurchaseDetail.domain.vo; + +import com.hotwj.platform.operationManagement.hourPurchaseDetail.domain.HotHourPurchaseDetail; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 培训学时购买明细视图对象 hot_hour_purchase_detail + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotHourPurchaseDetail.class) +public class HotHourPurchaseDetailVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + @ExcelProperty(value = "账户ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逻=辑外键") + private Long accountId; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户ID") + private Long userId; + + /** + * 购买人 + */ + @ExcelProperty(value = "购买人") + private String buyerName; + + /** + * 购买企业ID + */ + @ExcelProperty(value = "购买企业ID") + private Long buyerCompanyId; + + /** + * 购买企业名称 + */ + @ExcelProperty(value = "购买企业名称") + private String buyerCompanyName; + + /** + * 学时套餐购买记录ID + */ + @ExcelProperty(value = "购买记录ID") + private Long purchaseId; + + /** + * 订单管理表ID(逻辑外键) + */ + @ExcelProperty(value = "订单管理表ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逻=辑外键") + private Long orderId; + + /** + * 订单号 + */ + @ExcelProperty(value = "订单号") + private String orderNo; + + /** + * 学时套餐ID + */ + @ExcelProperty(value = "学时套餐ID") + private Long packageId; + + /** + * 套餐名称(快照) + */ + @ExcelProperty(value = "套餐名称", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "快=照") + private String packageName; + + /** + * 适用类型(如日常培训、事故培训等) + */ + @ExcelProperty(value = "适用类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "如=日常培训、事故培训等") + private String applicableTypes; + + /** + * 购买数量 + */ + @ExcelProperty(value = "购买数量") + private Long purchaseCount; + + /** + * 总课时数 + */ + @ExcelProperty(value = "总课时数") + private Long totalHours; + + /** + * 已使用学时数 + */ + @ExcelProperty(value = "已使用学时数") + private Long usedHours; + + /** + * 剩余学时数 + */ + @ExcelProperty(value = "剩余学时数") + private Long remainingHours; + + /** + * 钱包类型:1=个人钱包 2=企业钱包 + */ + @ExcelProperty(value = "钱包类型:1=个人钱包 2=企业钱包") + private Long walletType; + + /** + * 钱包ID + */ + @ExcelProperty(value = "钱包ID") + private Long walletId; + + /** + * 乐观锁版本号 + */ + @ExcelProperty(value = "版本号") + private Long version; + + /** + * 状态:1=生效 2=已用完 3=已过期 4=已作废 + */ + @ExcelProperty(value = "状态:1=生效 2=已用完 3=已过期 4=已作废") + private Long status; + + /** + * 最近使用时间 + */ + @ExcelProperty(value = "最近使用时间") + private Date lastUseTime; + + /** + * 到期时间 + */ + @ExcelProperty(value = "到期时间") + private Date expireTime; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/mapper/HotHourPurchaseDetailMapper.java b/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/mapper/HotHourPurchaseDetailMapper.java new file mode 100644 index 0000000..9e40343 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/mapper/HotHourPurchaseDetailMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.hourPurchaseDetail.mapper; + +import com.hotwj.platform.operationManagement.hourPurchaseDetail.domain.HotHourPurchaseDetail; +import com.hotwj.platform.operationManagement.hourPurchaseDetail.domain.vo.HotHourPurchaseDetailVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 培训学时购买明细Mapper接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Mapper +public interface HotHourPurchaseDetailMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/service/IHotHourPurchaseDetailService.java b/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/service/IHotHourPurchaseDetailService.java new file mode 100644 index 0000000..c8486d3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/service/IHotHourPurchaseDetailService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.hourPurchaseDetail.service; + +import com.hotwj.platform.operationManagement.hourPurchaseDetail.domain.vo.HotHourPurchaseDetailVo; +import com.hotwj.platform.operationManagement.hourPurchaseDetail.domain.bo.HotHourPurchaseDetailBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 培训学时购买明细Service接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +public interface IHotHourPurchaseDetailService { + + /** + * 查询培训学时购买明细 + * + * @param id 主键 + * @return 培训学时购买明细 + */ + HotHourPurchaseDetailVo queryById(Long id); + + /** + * 分页查询培训学时购买明细列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训学时购买明细分页列表 + */ + TableDataInfo queryPageList(HotHourPurchaseDetailBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的培训学时购买明细列表 + * + * @param bo 查询条件 + * @return 培训学时购买明细列表 + */ + List queryList(HotHourPurchaseDetailBo bo); + + /** + * 新增培训学时购买明细 + * + * @param bo 培训学时购买明细 + * @return 是否新增成功 + */ + Boolean insertByBo(HotHourPurchaseDetailBo bo); + + /** + * 修改培训学时购买明细 + * + * @param bo 培训学时购买明细 + * @return 是否修改成功 + */ + Boolean updateByBo(HotHourPurchaseDetailBo bo); + + /** + * 校验并批量删除培训学时购买明细信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/service/impl/HotHourPurchaseDetailServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/service/impl/HotHourPurchaseDetailServiceImpl.java new file mode 100644 index 0000000..9661410 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourPurchaseDetail/service/impl/HotHourPurchaseDetailServiceImpl.java @@ -0,0 +1,156 @@ +package com.hotwj.platform.operationManagement.hourPurchaseDetail.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.hotwj.platform.operationManagement.hourPurchaseDetail.domain.bo.HotHourPurchaseDetailBo; +import com.hotwj.platform.operationManagement.hourPurchaseDetail.domain.vo.HotHourPurchaseDetailVo; +import com.hotwj.platform.operationManagement.hourPurchaseDetail.domain.HotHourPurchaseDetail; +import com.hotwj.platform.operationManagement.hourPurchaseDetail.mapper.HotHourPurchaseDetailMapper; +import com.hotwj.platform.operationManagement.hourPurchaseDetail.service.IHotHourPurchaseDetailService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 培训学时购买明细Service业务层处理 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotHourPurchaseDetailServiceImpl implements IHotHourPurchaseDetailService { + + private final HotHourPurchaseDetailMapper baseMapper; + + /** + * 查询培训学时购买明细 + * + * @param id 主键 + * @return 培训学时购买明细 + */ + @Override + public HotHourPurchaseDetailVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询培训学时购买明细列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训学时购买明细分页列表 + */ + @Override + public TableDataInfo queryPageList(HotHourPurchaseDetailBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的培训学时购买明细列表 + * + * @param bo 查询条件 + * @return 培训学时购买明细列表 + */ + @Override + public List queryList(HotHourPurchaseDetailBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotHourPurchaseDetailBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotHourPurchaseDetail::getId); + lqw.eq(bo.getCompanyId() != null, HotHourPurchaseDetail::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getAccountId() != null, HotHourPurchaseDetail::getAccountId, bo.getAccountId()); + lqw.eq(bo.getUserId() != null, HotHourPurchaseDetail::getUserId, bo.getUserId()); + lqw.like(StringUtils.isNotBlank(bo.getBuyerName()), HotHourPurchaseDetail::getBuyerName, bo.getBuyerName()); + lqw.eq(bo.getBuyerCompanyId() != null, HotHourPurchaseDetail::getBuyerCompanyId, bo.getBuyerCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getBuyerCompanyName()), HotHourPurchaseDetail::getBuyerCompanyName, bo.getBuyerCompanyName()); + lqw.eq(bo.getPurchaseId() != null, HotHourPurchaseDetail::getPurchaseId, bo.getPurchaseId()); + lqw.eq(bo.getOrderId() != null, HotHourPurchaseDetail::getOrderId, bo.getOrderId()); + lqw.eq(StringUtils.isNotBlank(bo.getOrderNo()), HotHourPurchaseDetail::getOrderNo, bo.getOrderNo()); + lqw.eq(bo.getPackageId() != null, HotHourPurchaseDetail::getPackageId, bo.getPackageId()); + lqw.like(StringUtils.isNotBlank(bo.getPackageName()), HotHourPurchaseDetail::getPackageName, bo.getPackageName()); + lqw.eq(StringUtils.isNotBlank(bo.getApplicableTypes()), HotHourPurchaseDetail::getApplicableTypes, bo.getApplicableTypes()); + lqw.eq(bo.getPurchaseCount() != null, HotHourPurchaseDetail::getPurchaseCount, bo.getPurchaseCount()); + lqw.eq(bo.getTotalHours() != null, HotHourPurchaseDetail::getTotalHours, bo.getTotalHours()); + lqw.eq(bo.getUsedHours() != null, HotHourPurchaseDetail::getUsedHours, bo.getUsedHours()); + lqw.eq(bo.getRemainingHours() != null, HotHourPurchaseDetail::getRemainingHours, bo.getRemainingHours()); + lqw.eq(bo.getWalletType() != null, HotHourPurchaseDetail::getWalletType, bo.getWalletType()); + lqw.eq(bo.getWalletId() != null, HotHourPurchaseDetail::getWalletId, bo.getWalletId()); + lqw.eq(bo.getVersion() != null, HotHourPurchaseDetail::getVersion, bo.getVersion()); + lqw.eq(bo.getStatus() != null, HotHourPurchaseDetail::getStatus, bo.getStatus()); + lqw.eq(bo.getLastUseTime() != null, HotHourPurchaseDetail::getLastUseTime, bo.getLastUseTime()); + lqw.eq(bo.getExpireTime() != null, HotHourPurchaseDetail::getExpireTime, bo.getExpireTime()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotHourPurchaseDetail::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotHourPurchaseDetail::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotHourPurchaseDetail::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增培训学时购买明细 + * + * @param bo 培训学时购买明细 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotHourPurchaseDetailBo bo) { + HotHourPurchaseDetail add = MapstructUtils.convert(bo, HotHourPurchaseDetail.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改培训学时购买明细 + * + * @param bo 培训学时购买明细 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotHourPurchaseDetailBo bo) { + HotHourPurchaseDetail update = MapstructUtils.convert(bo, HotHourPurchaseDetail.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotHourPurchaseDetail entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除培训学时购买明细信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/controller/HotHourUsageDetailController.java b/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/controller/HotHourUsageDetailController.java new file mode 100644 index 0000000..3ef548e --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/controller/HotHourUsageDetailController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.operationManagement.hourUsageDetail.controller; + +import com.hotwj.platform.operationManagement.hourUsageDetail.domain.bo.HotHourUsageDetailBo; +import com.hotwj.platform.operationManagement.hourUsageDetail.domain.vo.HotHourUsageDetailVo; +import com.hotwj.platform.operationManagement.hourUsageDetail.service.IHotHourUsageDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 培训学时使用明细 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/hourUsageDetail") +@Tag(name = "培训学时使用明细", description = "培训学时使用明细管理") +public class HotHourUsageDetailController extends BaseController { + + private final IHotHourUsageDetailService hotHourUsageDetailService; + + /** + * 查询培训学时使用明细列表 + */ + //@SaCheckPermission("operationManagement:hourUsageDetail:list") + @GetMapping("/list") + @Operation(summary = "分页查询培训学时使用明细列表") + public TableDataInfo list(HotHourUsageDetailBo bo, PageQuery pageQuery) { + return hotHourUsageDetailService.queryPageList(bo, pageQuery); + } + + /** + * 导出培训学时使用明细列表 + */ + //@SaCheckPermission("operationManagement:hourUsageDetail:export") + @Log(title = "培训学时使用明细", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出培训学时使用明细列表") + public void export(HotHourUsageDetailBo bo, HttpServletResponse response) { + List list = hotHourUsageDetailService.queryList(bo); + ExcelUtil.exportExcel(list, "培训学时使用明细", HotHourUsageDetailVo.class, response); + } + + /** + * 获取培训学时使用明细详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("operationManagement:hourUsageDetail:query") + @GetMapping("/{id}") + @Operation(summary = "获取培训学时使用明细详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotHourUsageDetailService.queryById(id)); + } + + /** + * 新增培训学时使用明细 + */ + //@SaCheckPermission("operationManagement:hourUsageDetail:add") + @Log(title = "培训学时使用明细", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增培训学时使用明细") + public R add(@Validated(AddGroup.class) @RequestBody HotHourUsageDetailBo bo) { + return toAjax(hotHourUsageDetailService.insertByBo(bo)); + } + + /** + * 修改培训学时使用明细 + */ + //@SaCheckPermission("operationManagement:hourUsageDetail:edit") + @Log(title = "培训学时使用明细", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改培训学时使用明细") + public R edit(@Validated(EditGroup.class) @RequestBody HotHourUsageDetailBo bo) { + return toAjax(hotHourUsageDetailService.updateByBo(bo)); + } + + /** + * 删除培训学时使用明细 + * + * @param ids 主键串 + */ + //@SaCheckPermission("operationManagement:hourUsageDetail:remove") + @Log(title = "培训学时使用明细", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除培训学时使用明细") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotHourUsageDetailService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/domain/HotHourUsageDetail.java b/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/domain/HotHourUsageDetail.java new file mode 100644 index 0000000..7dfcdfa --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/domain/HotHourUsageDetail.java @@ -0,0 +1,129 @@ +package com.hotwj.platform.operationManagement.hourUsageDetail.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +import java.io.Serial; + +/** + * 培训学时使用明细对象 hot_hour_usage_detail + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_hour_usage_detail") +public class HotHourUsageDetail extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 购买明细ID(逻辑外键) + */ + private Long purchaseDetailId; + + /** + * 账户ID(逻辑外键) + */ + private Long accountId; + + /** + * 使用人员ID + */ + private Long userId; + + /** + * 使用人员 + */ + private String userName; + + /** + * 操作人ID + */ + private Long operatorId; + + /** + * 操作人 + */ + private String operatorName; + + /** + * 使用类型(如日常培训/违章培训) + */ + private String useType; + + /** + * 套餐ID + */ + private Long packageId; + + /** + * 套餐名称(快照) + */ + private String packageName; + + /** + * 使用学时数量(正数存储,展示可加负号) + */ + private Long usedHours; + + /** + * 扣减前剩余学时 + */ + private Long beforeRemainingHours; + + /** + * 扣减后剩余学时 + */ + private Long afterRemainingHours; + + /** + * 使用后剩余学时 + */ + private Long remainingHours; + + /** + * 使用日期 + */ + private Date useDate; + + /** + * 业务单据ID + */ + private Long bizOrderId; + + /** + * 业务单号 + */ + private String bizOrderNo; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/domain/bo/HotHourUsageDetailBo.java b/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/domain/bo/HotHourUsageDetailBo.java new file mode 100644 index 0000000..47863df --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/domain/bo/HotHourUsageDetailBo.java @@ -0,0 +1,127 @@ +package com.hotwj.platform.operationManagement.hourUsageDetail.domain.bo; + +import com.hotwj.platform.operationManagement.hourUsageDetail.domain.HotHourUsageDetail; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.util.Date; + +/** + * 培训学时使用明细业务对象 hot_hour_usage_detail + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotHourUsageDetail.class, reverseConvertGenerate = false) +public class HotHourUsageDetailBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 购买明细ID(逻辑外键) + */ + private Long purchaseDetailId; + + /** + * 账户ID(逻辑外键) + */ + private Long accountId; + + /** + * 使用人员ID + */ + private Long userId; + + /** + * 使用人员 + */ + private String userName; + + /** + * 操作人ID + */ + private Long operatorId; + + /** + * 操作人 + */ + private String operatorName; + + /** + * 使用类型(如日常培训/违章培训) + */ + private String useType; + + /** + * 套餐ID + */ + private Long packageId; + + /** + * 套餐名称(快照) + */ + private String packageName; + + /** + * 使用学时数量(正数存储,展示可加负号) + */ + private Long usedHours; + + /** + * 扣减前剩余学时 + */ + private Long beforeRemainingHours; + + /** + * 扣减后剩余学时 + */ + private Long afterRemainingHours; + + /** + * 使用后剩余学时 + */ + private Long remainingHours; + + /** + * 使用日期 + */ + private Date useDate; + + /** + * 业务单据ID + */ + private Long bizOrderId; + + /** + * 业务单号 + */ + private String bizOrderNo; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/domain/vo/HotHourUsageDetailVo.java b/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/domain/vo/HotHourUsageDetailVo.java new file mode 100644 index 0000000..a1621d8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/domain/vo/HotHourUsageDetailVo.java @@ -0,0 +1,169 @@ +package com.hotwj.platform.operationManagement.hourUsageDetail.domain.vo; + +import com.hotwj.platform.operationManagement.hourUsageDetail.domain.HotHourUsageDetail; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 培训学时使用明细视图对象 hot_hour_usage_detail + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotHourUsageDetail.class) +public class HotHourUsageDetailVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 购买明细ID(逻辑外键) + */ + @ExcelProperty(value = "购买明细ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逻=辑外键") + private Long purchaseDetailId; + + /** + * 账户ID(逻辑外键) + */ + @ExcelProperty(value = "账户ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逻=辑外键") + private Long accountId; + + /** + * 使用人员ID + */ + @ExcelProperty(value = "使用人员ID") + private Long userId; + + /** + * 使用人员 + */ + @ExcelProperty(value = "使用人员") + private String userName; + + /** + * 操作人ID + */ + @ExcelProperty(value = "操作人ID") + private Long operatorId; + + /** + * 操作人 + */ + @ExcelProperty(value = "操作人") + private String operatorName; + + /** + * 使用类型(如日常培训/违章培训) + */ + @ExcelProperty(value = "使用类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "如=日常培训/违章培训") + private String useType; + + /** + * 套餐ID + */ + @ExcelProperty(value = "套餐ID") + private Long packageId; + + /** + * 套餐名称(快照) + */ + @ExcelProperty(value = "套餐名称", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "快=照") + private String packageName; + + /** + * 使用学时数量(正数存储,展示可加负号) + */ + @ExcelProperty(value = "使用学时数量", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "正=数存储,展示可加负号") + private Long usedHours; + + /** + * 扣减前剩余学时 + */ + @ExcelProperty(value = "扣减前剩余学时") + private Long beforeRemainingHours; + + /** + * 扣减后剩余学时 + */ + @ExcelProperty(value = "扣减后剩余学时") + private Long afterRemainingHours; + + /** + * 使用后剩余学时 + */ + @ExcelProperty(value = "使用后剩余学时") + private Long remainingHours; + + /** + * 使用日期 + */ + @ExcelProperty(value = "使用日期") + private Date useDate; + + /** + * 业务单据ID + */ + @ExcelProperty(value = "业务单据ID") + private Long bizOrderId; + + /** + * 业务单号 + */ + @ExcelProperty(value = "业务单号") + private String bizOrderNo; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/mapper/HotHourUsageDetailMapper.java b/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/mapper/HotHourUsageDetailMapper.java new file mode 100644 index 0000000..7b82554 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/mapper/HotHourUsageDetailMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.hourUsageDetail.mapper; + +import com.hotwj.platform.operationManagement.hourUsageDetail.domain.HotHourUsageDetail; +import com.hotwj.platform.operationManagement.hourUsageDetail.domain.vo.HotHourUsageDetailVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 培训学时使用明细Mapper接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Mapper +public interface HotHourUsageDetailMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/service/IHotHourUsageDetailService.java b/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/service/IHotHourUsageDetailService.java new file mode 100644 index 0000000..50a89d4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/service/IHotHourUsageDetailService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.hourUsageDetail.service; + +import com.hotwj.platform.operationManagement.hourUsageDetail.domain.vo.HotHourUsageDetailVo; +import com.hotwj.platform.operationManagement.hourUsageDetail.domain.bo.HotHourUsageDetailBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 培训学时使用明细Service接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +public interface IHotHourUsageDetailService { + + /** + * 查询培训学时使用明细 + * + * @param id 主键 + * @return 培训学时使用明细 + */ + HotHourUsageDetailVo queryById(Long id); + + /** + * 分页查询培训学时使用明细列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训学时使用明细分页列表 + */ + TableDataInfo queryPageList(HotHourUsageDetailBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的培训学时使用明细列表 + * + * @param bo 查询条件 + * @return 培训学时使用明细列表 + */ + List queryList(HotHourUsageDetailBo bo); + + /** + * 新增培训学时使用明细 + * + * @param bo 培训学时使用明细 + * @return 是否新增成功 + */ + Boolean insertByBo(HotHourUsageDetailBo bo); + + /** + * 修改培训学时使用明细 + * + * @param bo 培训学时使用明细 + * @return 是否修改成功 + */ + Boolean updateByBo(HotHourUsageDetailBo bo); + + /** + * 校验并批量删除培训学时使用明细信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/service/impl/HotHourUsageDetailServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/service/impl/HotHourUsageDetailServiceImpl.java new file mode 100644 index 0000000..81e6534 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/hourUsageDetail/service/impl/HotHourUsageDetailServiceImpl.java @@ -0,0 +1,151 @@ +package com.hotwj.platform.operationManagement.hourUsageDetail.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.hotwj.platform.operationManagement.hourUsageDetail.domain.bo.HotHourUsageDetailBo; +import com.hotwj.platform.operationManagement.hourUsageDetail.domain.vo.HotHourUsageDetailVo; +import com.hotwj.platform.operationManagement.hourUsageDetail.domain.HotHourUsageDetail; +import com.hotwj.platform.operationManagement.hourUsageDetail.mapper.HotHourUsageDetailMapper; +import com.hotwj.platform.operationManagement.hourUsageDetail.service.IHotHourUsageDetailService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 培训学时使用明细Service业务层处理 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotHourUsageDetailServiceImpl implements IHotHourUsageDetailService { + + private final HotHourUsageDetailMapper baseMapper; + + /** + * 查询培训学时使用明细 + * + * @param id 主键 + * @return 培训学时使用明细 + */ + @Override + public HotHourUsageDetailVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询培训学时使用明细列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训学时使用明细分页列表 + */ + @Override + public TableDataInfo queryPageList(HotHourUsageDetailBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的培训学时使用明细列表 + * + * @param bo 查询条件 + * @return 培训学时使用明细列表 + */ + @Override + public List queryList(HotHourUsageDetailBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotHourUsageDetailBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotHourUsageDetail::getId); + lqw.eq(bo.getCompanyId() != null, HotHourUsageDetail::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getPurchaseDetailId() != null, HotHourUsageDetail::getPurchaseDetailId, bo.getPurchaseDetailId()); + lqw.eq(bo.getAccountId() != null, HotHourUsageDetail::getAccountId, bo.getAccountId()); + lqw.eq(bo.getUserId() != null, HotHourUsageDetail::getUserId, bo.getUserId()); + lqw.like(StringUtils.isNotBlank(bo.getUserName()), HotHourUsageDetail::getUserName, bo.getUserName()); + lqw.eq(bo.getOperatorId() != null, HotHourUsageDetail::getOperatorId, bo.getOperatorId()); + lqw.like(StringUtils.isNotBlank(bo.getOperatorName()), HotHourUsageDetail::getOperatorName, bo.getOperatorName()); + lqw.eq(StringUtils.isNotBlank(bo.getUseType()), HotHourUsageDetail::getUseType, bo.getUseType()); + lqw.eq(bo.getPackageId() != null, HotHourUsageDetail::getPackageId, bo.getPackageId()); + lqw.like(StringUtils.isNotBlank(bo.getPackageName()), HotHourUsageDetail::getPackageName, bo.getPackageName()); + lqw.eq(bo.getUsedHours() != null, HotHourUsageDetail::getUsedHours, bo.getUsedHours()); + lqw.eq(bo.getBeforeRemainingHours() != null, HotHourUsageDetail::getBeforeRemainingHours, bo.getBeforeRemainingHours()); + lqw.eq(bo.getAfterRemainingHours() != null, HotHourUsageDetail::getAfterRemainingHours, bo.getAfterRemainingHours()); + lqw.eq(bo.getRemainingHours() != null, HotHourUsageDetail::getRemainingHours, bo.getRemainingHours()); + lqw.eq(bo.getUseDate() != null, HotHourUsageDetail::getUseDate, bo.getUseDate()); + lqw.eq(bo.getBizOrderId() != null, HotHourUsageDetail::getBizOrderId, bo.getBizOrderId()); + lqw.eq(StringUtils.isNotBlank(bo.getBizOrderNo()), HotHourUsageDetail::getBizOrderNo, bo.getBizOrderNo()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotHourUsageDetail::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotHourUsageDetail::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotHourUsageDetail::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增培训学时使用明细 + * + * @param bo 培训学时使用明细 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotHourUsageDetailBo bo) { + HotHourUsageDetail add = MapstructUtils.convert(bo, HotHourUsageDetail.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改培训学时使用明细 + * + * @param bo 培训学时使用明细 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotHourUsageDetailBo bo) { + HotHourUsageDetail update = MapstructUtils.convert(bo, HotHourUsageDetail.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotHourUsageDetail entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除培训学时使用明细信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/controller/HotInvoiceManageController.java b/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/controller/HotInvoiceManageController.java new file mode 100644 index 0000000..34142f2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/controller/HotInvoiceManageController.java @@ -0,0 +1,118 @@ +package com.hotwj.platform.operationManagement.invoiceManage.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.hotwj.platform.operationManagement.invoiceManage.domain.bo.HotInvoiceManageBo; +import com.hotwj.platform.operationManagement.invoiceManage.domain.vo.HotInvoiceManageVo; +import com.hotwj.platform.operationManagement.invoiceManage.service.IHotInvoiceManageService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 发票管理 + * + * @author shihongwei + * @date 2026-05-06 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/invoiceManage") +@Tag(name = "发票管理", description = "发票管理管理") +public class HotInvoiceManageController extends BaseController { + + private final IHotInvoiceManageService hotInvoiceManageService; + + /** + * 查询发票管理列表 + */ + @SaCheckPermission("operationManagement:invoiceManage:list") + @GetMapping("/list") + @Operation(summary = "分页查询发票管理列表") + public TableDataInfo list(HotInvoiceManageBo bo, PageQuery pageQuery) { + return hotInvoiceManageService.queryPageList(bo, pageQuery); + } + + /** + * 导出发票管理列表 + */ + @SaCheckPermission("operationManagement:invoiceManage:export") + @Log(title = "发票管理", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出发票管理列表") + public void export(HotInvoiceManageBo bo, HttpServletResponse response) { + List list = hotInvoiceManageService.queryList(bo); + ExcelUtil.exportExcel(list, "发票管理", HotInvoiceManageVo.class, response); + } + + /** + * 获取发票管理详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("operationManagement:invoiceManage:query") + @GetMapping("/{id}") + @Operation(summary = "获取发票管理详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotInvoiceManageService.queryById(id)); + } + + /** + * 新增发票管理 + */ + @SaCheckPermission("operationManagement:invoiceManage:add") + @Log(title = "发票管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增发票管理") + public R add(@Validated(AddGroup.class) @RequestBody HotInvoiceManageBo bo) { + return toAjax(hotInvoiceManageService.insertByBo(bo)); + } + + /** + * 修改发票管理 + */ + @SaCheckPermission("operationManagement:invoiceManage:edit") + @Log(title = "发票管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改发票管理") + public R edit(@Validated(EditGroup.class) @RequestBody HotInvoiceManageBo bo) { + return toAjax(hotInvoiceManageService.updateByBo(bo)); + } + + /** + * 删除发票管理 + * + * @param ids 主键串 + */ + @SaCheckPermission("operationManagement:invoiceManage:remove") + @Log(title = "发票管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除发票管理") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotInvoiceManageService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/domain/HotInvoiceManage.java b/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/domain/HotInvoiceManage.java new file mode 100644 index 0000000..98d89c4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/domain/HotInvoiceManage.java @@ -0,0 +1,165 @@ +package com.hotwj.platform.operationManagement.invoiceManage.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 发票管理对象 hot_invoice_manage + * + * @author shihongwei + * @date 2026-05-06 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_invoice_manage") +public class HotInvoiceManage extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 订单ID(逻辑外键) + */ + private Long orderId; + + /** + * 订单号 + */ + private String orderNo; + + /** + * 购买者企业ID + */ + private Long buyerCompanyId; + + /** + * 购买者(企业名称) + */ + private String buyerCompanyName; + + /** + * 订单类型:1=拓客订单 2=学习订单 3=短信订单 4=岗前培训订单 5=其他 + */ + private Long orderType; + + /** + * 订单金额(元) + */ + private Long orderAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 4=余额 + */ + private Long payMethod; + + /** + * 支付时间 + */ + private Date payTime; + + /** + * 开票金额(元) + */ + private Long invoiceAmount; + + /** + * 发票类型:1=增值税电子普通发票 2=增值税电子专用发票 + */ + private Long invoiceType; + + /** + * 发票抬头 + */ + private String invoiceTitle; + + /** + * 税号 + */ + private String taxNo; + + /** + * 开户银行 + */ + private String bankName; + + /** + * 银行账号 + */ + private String bankAccount; + + /** + * 企业地址 + */ + private String companyAddress; + + /** + * 企业电话 + */ + private String companyPhone; + + /** + * 开票状态:1=未开票 2=申请开票 3=已开票 4=驳回 5=已作废 + */ + private Long invoiceStatus; + + /** + * 申请时间 + */ + private Date applyTime; + + /** + * 开票时间 + */ + private Date invoiceTime; + + /** + * 发票代码 + */ + private String invoiceCode; + + /** + * 发票号码(可按年月+随机号) + */ + private String invoiceNo; + + /** + * 电子发票文件URL + */ + private String invoiceUrl; + + /** + * 驳回原因 + */ + private String rejectReason; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/domain/bo/HotInvoiceManageBo.java b/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/domain/bo/HotInvoiceManageBo.java new file mode 100644 index 0000000..46a12e0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/domain/bo/HotInvoiceManageBo.java @@ -0,0 +1,161 @@ +package com.hotwj.platform.operationManagement.invoiceManage.domain.bo; + +import com.hotwj.platform.operationManagement.invoiceManage.domain.HotInvoiceManage; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 发票管理业务对象 hot_invoice_manage + * + * @author shihongwei + * @date 2026-05-06 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotInvoiceManage.class, reverseConvertGenerate = false) +public class HotInvoiceManageBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 订单ID(逻辑外键) + */ + private Long orderId; + + /** + * 订单号 + */ + private String orderNo; + + /** + * 购买者企业ID + */ + private Long buyerCompanyId; + + /** + * 购买者(企业名称) + */ + private String buyerCompanyName; + + /** + * 订单类型:1=拓客订单 2=学习订单 3=短信订单 4=岗前培训订单 5=其他 + */ + private Long orderType; + + /** + * 订单金额(元) + */ + private Long orderAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 4=余额 + */ + private Long payMethod; + + /** + * 支付时间 + */ + private Date payTime; + + /** + * 开票金额(元) + */ + private Long invoiceAmount; + + /** + * 发票类型:1=增值税电子普通发票 2=增值税电子专用发票 + */ + private Long invoiceType; + + /** + * 发票抬头 + */ + private String invoiceTitle; + + /** + * 税号 + */ + private String taxNo; + + /** + * 开户银行 + */ + private String bankName; + + /** + * 银行账号 + */ + private String bankAccount; + + /** + * 企业地址 + */ + private String companyAddress; + + /** + * 企业电话 + */ + private String companyPhone; + + /** + * 开票状态:1=未开票 2=申请开票 3=已开票 4=驳回 5=已作废 + */ + private Long invoiceStatus; + + /** + * 申请时间 + */ + private Date applyTime; + + /** + * 开票时间 + */ + private Date invoiceTime; + + /** + * 发票代码 + */ + private String invoiceCode; + + /** + * 发票号码(可按年月+随机号) + */ + private String invoiceNo; + + /** + * 电子发票文件URL + */ + private String invoiceUrl; + + /** + * 驳回原因 + */ + private String rejectReason; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/domain/vo/HotInvoiceManageVo.java b/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/domain/vo/HotInvoiceManageVo.java new file mode 100644 index 0000000..662eec1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/domain/vo/HotInvoiceManageVo.java @@ -0,0 +1,211 @@ +package com.hotwj.platform.operationManagement.invoiceManage.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.operationManagement.invoiceManage.domain.HotInvoiceManage; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 发票管理视图对象 hot_invoice_manage + * + * @author shihongwei + * @date 2026-05-06 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotInvoiceManage.class) +public class HotInvoiceManageVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 订单ID(逻辑外键) + */ + @ExcelProperty(value = "订单ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逻=辑外键") + private Long orderId; + + /** + * 订单号 + */ + @ExcelProperty(value = "订单号") + private String orderNo; + + /** + * 购买者企业ID + */ + @ExcelProperty(value = "购买者企业ID") + private Long buyerCompanyId; + + /** + * 购买者(企业名称) + */ + @ExcelProperty(value = "购买者", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "企=业名称") + private String buyerCompanyName; + + /** + * 订单类型:1=拓客订单 2=学习订单 3=短信订单 4=岗前培训订单 5=其他 + */ + @ExcelProperty(value = "订单类型:1=拓客订单 2=学习订单 3=短信订单 4=岗前培训订单 5=其他") + private Long orderType; + + /** + * 订单金额(元) + */ + @ExcelProperty(value = "订单金额", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long orderAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 4=余额 + */ + @ExcelProperty(value = "支付方式:1=微信 2=支付宝 3=银行卡 4=余额") + private Long payMethod; + + /** + * 支付时间 + */ + @ExcelProperty(value = "支付时间") + private Date payTime; + + /** + * 开票金额(元) + */ + @ExcelProperty(value = "开票金额", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long invoiceAmount; + + /** + * 发票类型:1=增值税电子普通发票 2=增值税电子专用发票 + */ + @ExcelProperty(value = "发票类型:1=增值税电子普通发票 2=增值税电子专用发票") + private Long invoiceType; + + /** + * 发票抬头 + */ + @ExcelProperty(value = "发票抬头") + private String invoiceTitle; + + /** + * 税号 + */ + @ExcelProperty(value = "税号") + private String taxNo; + + /** + * 开户银行 + */ + @ExcelProperty(value = "开户银行") + private String bankName; + + /** + * 银行账号 + */ + @ExcelProperty(value = "银行账号") + private String bankAccount; + + /** + * 企业地址 + */ + @ExcelProperty(value = "企业地址") + private String companyAddress; + + /** + * 企业电话 + */ + @ExcelProperty(value = "企业电话") + private String companyPhone; + + /** + * 开票状态:1=未开票 2=申请开票 3=已开票 4=驳回 5=已作废 + */ + @ExcelProperty(value = "开票状态:1=未开票 2=申请开票 3=已开票 4=驳回 5=已作废") + private Long invoiceStatus; + + /** + * 申请时间 + */ + @ExcelProperty(value = "申请时间") + private Date applyTime; + + /** + * 开票时间 + */ + @ExcelProperty(value = "开票时间") + private Date invoiceTime; + + /** + * 发票代码 + */ + @ExcelProperty(value = "发票代码") + private String invoiceCode; + + /** + * 发票号码(可按年月+随机号) + */ + @ExcelProperty(value = "发票号码", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "可=按年月+随机号") + private String invoiceNo; + + /** + * 电子发票文件URL + */ + @ExcelProperty(value = "电子发票文件URL") + private String invoiceUrl; + + /** + * 驳回原因 + */ + @ExcelProperty(value = "驳回原因") + private String rejectReason; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/mapper/HotInvoiceManageMapper.java b/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/mapper/HotInvoiceManageMapper.java new file mode 100644 index 0000000..5e1485b --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/mapper/HotInvoiceManageMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.invoiceManage.mapper; + +import com.hotwj.platform.operationManagement.invoiceManage.domain.HotInvoiceManage; +import com.hotwj.platform.operationManagement.invoiceManage.domain.vo.HotInvoiceManageVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 发票管理Mapper接口 + * + * @author shihongwei + * @date 2026-05-06 + */ +@Mapper +public interface HotInvoiceManageMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/service/IHotInvoiceManageService.java b/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/service/IHotInvoiceManageService.java new file mode 100644 index 0000000..6c09e25 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/service/IHotInvoiceManageService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.invoiceManage.service; + +import com.hotwj.platform.operationManagement.invoiceManage.domain.bo.HotInvoiceManageBo; +import com.hotwj.platform.operationManagement.invoiceManage.domain.vo.HotInvoiceManageVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 发票管理Service接口 + * + * @author shihongwei + * @date 2026-05-06 + */ +public interface IHotInvoiceManageService { + + /** + * 查询发票管理 + * + * @param id 主键 + * @return 发票管理 + */ + HotInvoiceManageVo queryById(Long id); + + /** + * 分页查询发票管理列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 发票管理分页列表 + */ + TableDataInfo queryPageList(HotInvoiceManageBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的发票管理列表 + * + * @param bo 查询条件 + * @return 发票管理列表 + */ + List queryList(HotInvoiceManageBo bo); + + /** + * 新增发票管理 + * + * @param bo 发票管理 + * @return 是否新增成功 + */ + Boolean insertByBo(HotInvoiceManageBo bo); + + /** + * 修改发票管理 + * + * @param bo 发票管理 + * @return 是否修改成功 + */ + Boolean updateByBo(HotInvoiceManageBo bo); + + /** + * 校验并批量删除发票管理信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/service/impl/HotInvoiceManageServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/service/impl/HotInvoiceManageServiceImpl.java new file mode 100644 index 0000000..74302aa --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/invoiceManage/service/impl/HotInvoiceManageServiceImpl.java @@ -0,0 +1,158 @@ +package com.hotwj.platform.operationManagement.invoiceManage.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.operationManagement.invoiceManage.domain.HotInvoiceManage; +import com.hotwj.platform.operationManagement.invoiceManage.domain.bo.HotInvoiceManageBo; +import com.hotwj.platform.operationManagement.invoiceManage.domain.vo.HotInvoiceManageVo; +import com.hotwj.platform.operationManagement.invoiceManage.mapper.HotInvoiceManageMapper; +import com.hotwj.platform.operationManagement.invoiceManage.service.IHotInvoiceManageService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 发票管理Service业务层处理 + * + * @author shihongwei + * @date 2026-05-06 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotInvoiceManageServiceImpl implements IHotInvoiceManageService { + + private final HotInvoiceManageMapper baseMapper; + + /** + * 查询发票管理 + * + * @param id 主键 + * @return 发票管理 + */ + @Override + public HotInvoiceManageVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询发票管理列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 发票管理分页列表 + */ + @Override + public TableDataInfo queryPageList(HotInvoiceManageBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的发票管理列表 + * + * @param bo 查询条件 + * @return 发票管理列表 + */ + @Override + public List queryList(HotInvoiceManageBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotInvoiceManageBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotInvoiceManage::getId); + lqw.eq(bo.getCompanyId() != null, HotInvoiceManage::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getOrderId() != null, HotInvoiceManage::getOrderId, bo.getOrderId()); + lqw.eq(StringUtils.isNotBlank(bo.getOrderNo()), HotInvoiceManage::getOrderNo, bo.getOrderNo()); + lqw.eq(bo.getBuyerCompanyId() != null, HotInvoiceManage::getBuyerCompanyId, bo.getBuyerCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getBuyerCompanyName()), HotInvoiceManage::getBuyerCompanyName, bo.getBuyerCompanyName()); + lqw.eq(bo.getOrderType() != null, HotInvoiceManage::getOrderType, bo.getOrderType()); + lqw.eq(bo.getOrderAmount() != null, HotInvoiceManage::getOrderAmount, bo.getOrderAmount()); + lqw.eq(bo.getPayMethod() != null, HotInvoiceManage::getPayMethod, bo.getPayMethod()); + lqw.eq(bo.getPayTime() != null, HotInvoiceManage::getPayTime, bo.getPayTime()); + lqw.eq(bo.getInvoiceAmount() != null, HotInvoiceManage::getInvoiceAmount, bo.getInvoiceAmount()); + lqw.eq(bo.getInvoiceType() != null, HotInvoiceManage::getInvoiceType, bo.getInvoiceType()); + lqw.eq(StringUtils.isNotBlank(bo.getInvoiceTitle()), HotInvoiceManage::getInvoiceTitle, bo.getInvoiceTitle()); + lqw.eq(StringUtils.isNotBlank(bo.getTaxNo()), HotInvoiceManage::getTaxNo, bo.getTaxNo()); + lqw.like(StringUtils.isNotBlank(bo.getBankName()), HotInvoiceManage::getBankName, bo.getBankName()); + lqw.eq(StringUtils.isNotBlank(bo.getBankAccount()), HotInvoiceManage::getBankAccount, bo.getBankAccount()); + lqw.eq(StringUtils.isNotBlank(bo.getCompanyAddress()), HotInvoiceManage::getCompanyAddress, bo.getCompanyAddress()); + lqw.eq(StringUtils.isNotBlank(bo.getCompanyPhone()), HotInvoiceManage::getCompanyPhone, bo.getCompanyPhone()); + lqw.eq(bo.getInvoiceStatus() != null, HotInvoiceManage::getInvoiceStatus, bo.getInvoiceStatus()); + lqw.eq(bo.getApplyTime() != null, HotInvoiceManage::getApplyTime, bo.getApplyTime()); + lqw.eq(bo.getInvoiceTime() != null, HotInvoiceManage::getInvoiceTime, bo.getInvoiceTime()); + lqw.eq(StringUtils.isNotBlank(bo.getInvoiceCode()), HotInvoiceManage::getInvoiceCode, bo.getInvoiceCode()); + lqw.eq(StringUtils.isNotBlank(bo.getInvoiceNo()), HotInvoiceManage::getInvoiceNo, bo.getInvoiceNo()); + lqw.eq(StringUtils.isNotBlank(bo.getInvoiceUrl()), HotInvoiceManage::getInvoiceUrl, bo.getInvoiceUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getRejectReason()), HotInvoiceManage::getRejectReason, bo.getRejectReason()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotInvoiceManage::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotInvoiceManage::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotInvoiceManage::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增发票管理 + * + * @param bo 发票管理 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotInvoiceManageBo bo) { + HotInvoiceManage add = MapstructUtils.convert(bo, HotInvoiceManage.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改发票管理 + * + * @param bo 发票管理 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotInvoiceManageBo bo) { + HotInvoiceManage update = MapstructUtils.convert(bo, HotInvoiceManage.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotInvoiceManage entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除发票管理信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/controller/HotPreJobPackageController.java b/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/controller/HotPreJobPackageController.java new file mode 100644 index 0000000..6a82773 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/controller/HotPreJobPackageController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.operationManagement.preJobPackage.controller; + +import com.hotwj.platform.operationManagement.preJobPackage.domain.bo.HotPreJobPackageBo; +import com.hotwj.platform.operationManagement.preJobPackage.domain.vo.HotPreJobPackageVo; +import com.hotwj.platform.operationManagement.preJobPackage.service.IHotPreJobPackageService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 岗前培训套餐 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/preJobPackage") +@Tag(name = "岗前培训套餐", description = "岗前培训套餐管理") +public class HotPreJobPackageController extends BaseController { + + private final IHotPreJobPackageService hotPreJobPackageService; + + /** + * 查询岗前培训套餐列表 + */ + //@SaCheckPermission("operationManagement:preJobPackage:list") + @GetMapping("/list") + @Operation(summary = "分页查询岗前培训套餐列表") + public TableDataInfo list(HotPreJobPackageBo bo, PageQuery pageQuery) { + return hotPreJobPackageService.queryPageList(bo, pageQuery); + } + + /** + * 导出岗前培训套餐列表 + */ + //@SaCheckPermission("operationManagement:preJobPackage:export") + @Log(title = "岗前培训套餐", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出岗前培训套餐列表") + public void export(HotPreJobPackageBo bo, HttpServletResponse response) { + List list = hotPreJobPackageService.queryList(bo); + ExcelUtil.exportExcel(list, "岗前培训套餐", HotPreJobPackageVo.class, response); + } + + /** + * 获取岗前培训套餐详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("operationManagement:preJobPackage:query") + @GetMapping("/{id}") + @Operation(summary = "获取岗前培训套餐详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotPreJobPackageService.queryById(id)); + } + + /** + * 新增岗前培训套餐 + */ + //@SaCheckPermission("operationManagement:preJobPackage:add") + @Log(title = "岗前培训套餐", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增岗前培训套餐") + public R add(@Validated(AddGroup.class) @RequestBody HotPreJobPackageBo bo) { + return toAjax(hotPreJobPackageService.insertByBo(bo)); + } + + /** + * 修改岗前培训套餐 + */ + //@SaCheckPermission("operationManagement:preJobPackage:edit") + @Log(title = "岗前培训套餐", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改岗前培训套餐") + public R edit(@Validated(EditGroup.class) @RequestBody HotPreJobPackageBo bo) { + return toAjax(hotPreJobPackageService.updateByBo(bo)); + } + + /** + * 删除岗前培训套餐 + * + * @param ids 主键串 + */ + //@SaCheckPermission("operationManagement:preJobPackage:remove") + @Log(title = "岗前培训套餐", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除岗前培训套餐") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotPreJobPackageService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/domain/HotPreJobPackage.java b/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/domain/HotPreJobPackage.java new file mode 100644 index 0000000..2632623 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/domain/HotPreJobPackage.java @@ -0,0 +1,116 @@ +package com.hotwj.platform.operationManagement.preJobPackage.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 岗前培训套餐对象 hot_pre_job_package + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_pre_job_package") +public class HotPreJobPackage extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 套餐编码 + */ + private String packageCode; + + /** + * 套餐名称 + */ + private String packageName; + + /** + * 套餐内容 + */ + private String packageContent; + + /** + * 套餐类型:1=常规套餐 2=活动套餐 + */ + private Long packageType; + + /** + * 活动ID + */ + private Long activityId; + + /** + * 活动名称 + */ + private String activityName; + + /** + * 原价(元) + */ + private Long originPrice; + + /** + * 活动价(元) + */ + private Long activityPrice; + + /** + * 生效单价(元) + */ + private Long unitPrice; + + /** + * 活动有效开始时间 + */ + private Date validStartTime; + + /** + * 活动有效结束时间 + */ + private Date validEndTime; + + /** + * 状态:1=启用 0=停用 + */ + private Long status; + + /** + * 排序号 + */ + private Long sortNo; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/domain/bo/HotPreJobPackageBo.java b/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/domain/bo/HotPreJobPackageBo.java new file mode 100644 index 0000000..be7ebd3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/domain/bo/HotPreJobPackageBo.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.operationManagement.preJobPackage.domain.bo; + +import com.hotwj.platform.operationManagement.preJobPackage.domain.HotPreJobPackage; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 岗前培训套餐业务对象 hot_pre_job_package + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotPreJobPackage.class, reverseConvertGenerate = false) +public class HotPreJobPackageBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 套餐编码 + */ + private String packageCode; + + /** + * 套餐名称 + */ + private String packageName; + + /** + * 套餐内容 + */ + private String packageContent; + + /** + * 套餐类型:1=常规套餐 2=活动套餐 + */ + private Long packageType; + + /** + * 活动ID + */ + private Long activityId; + + /** + * 活动名称 + */ + private String activityName; + + /** + * 原价(元) + */ + private Long originPrice; + + /** + * 活动价(元) + */ + private Long activityPrice; + + /** + * 生效单价(元) + */ + private Long unitPrice; + + /** + * 活动有效开始时间 + */ + private Date validStartTime; + + /** + * 活动有效结束时间 + */ + private Date validEndTime; + + /** + * 状态:1=启用 0=停用 + */ + private Long status; + + /** + * 排序号 + */ + private Long sortNo; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/domain/vo/HotPreJobPackageVo.java b/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/domain/vo/HotPreJobPackageVo.java new file mode 100644 index 0000000..8a17ac1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/domain/vo/HotPreJobPackageVo.java @@ -0,0 +1,152 @@ +package com.hotwj.platform.operationManagement.preJobPackage.domain.vo; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.hotwj.platform.operationManagement.preJobPackage.domain.HotPreJobPackage; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 岗前培训套餐视图对象 hot_pre_job_package + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotPreJobPackage.class) +public class HotPreJobPackageVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 套餐编码 + */ + @ExcelProperty(value = "套餐编码") + private String packageCode; + + /** + * 套餐名称 + */ + @ExcelProperty(value = "套餐名称") + private String packageName; + + /** + * 套餐内容 + */ + @ExcelProperty(value = "套餐内容") + private String packageContent; + + /** + * 套餐类型:1=常规套餐 2=活动套餐 + */ + @ExcelProperty(value = "套餐类型:1=常规套餐 2=活动套餐") + private Long packageType; + + /** + * 活动ID + */ + @ExcelProperty(value = "活动ID") + private Long activityId; + + /** + * 活动名称 + */ + @ExcelProperty(value = "活动名称") + private String activityName; + + /** + * 原价(元) + */ + @ExcelProperty(value = "原价", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long originPrice; + + /** + * 活动价(元) + */ + @ExcelProperty(value = "活动价", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long activityPrice; + + /** + * 生效单价(元) + */ + @ExcelProperty(value = "生效单价", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long unitPrice; + + /** + * 活动有效开始时间 + */ + @ExcelProperty(value = "活动有效开始时间") + private Date validStartTime; + + /** + * 活动有效结束时间 + */ + @ExcelProperty(value = "活动有效结束时间") + private Date validEndTime; + + /** + * 状态:1=启用 0=停用 + */ + @ExcelProperty(value = "状态:1=启用 0=停用") + private Long status; + + /** + * 排序号 + */ + @ExcelProperty(value = "排序号") + private Long sortNo; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/mapper/HotPreJobPackageMapper.java b/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/mapper/HotPreJobPackageMapper.java new file mode 100644 index 0000000..5b97be8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/mapper/HotPreJobPackageMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.preJobPackage.mapper; + +import com.hotwj.platform.operationManagement.preJobPackage.domain.HotPreJobPackage; +import com.hotwj.platform.operationManagement.preJobPackage.domain.vo.HotPreJobPackageVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 岗前培训套餐Mapper接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Mapper +public interface HotPreJobPackageMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/service/IHotPreJobPackageService.java b/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/service/IHotPreJobPackageService.java new file mode 100644 index 0000000..8288f5e --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/service/IHotPreJobPackageService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.preJobPackage.service; + +import com.hotwj.platform.operationManagement.preJobPackage.domain.vo.HotPreJobPackageVo; +import com.hotwj.platform.operationManagement.preJobPackage.domain.bo.HotPreJobPackageBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 岗前培训套餐Service接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +public interface IHotPreJobPackageService { + + /** + * 查询岗前培训套餐 + * + * @param id 主键 + * @return 岗前培训套餐 + */ + HotPreJobPackageVo queryById(Long id); + + /** + * 分页查询岗前培训套餐列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 岗前培训套餐分页列表 + */ + TableDataInfo queryPageList(HotPreJobPackageBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的岗前培训套餐列表 + * + * @param bo 查询条件 + * @return 岗前培训套餐列表 + */ + List queryList(HotPreJobPackageBo bo); + + /** + * 新增岗前培训套餐 + * + * @param bo 岗前培训套餐 + * @return 是否新增成功 + */ + Boolean insertByBo(HotPreJobPackageBo bo); + + /** + * 修改岗前培训套餐 + * + * @param bo 岗前培训套餐 + * @return 是否修改成功 + */ + Boolean updateByBo(HotPreJobPackageBo bo); + + /** + * 校验并批量删除岗前培训套餐信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/service/impl/HotPreJobPackageServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/service/impl/HotPreJobPackageServiceImpl.java new file mode 100644 index 0000000..0d623a8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/preJobPackage/service/impl/HotPreJobPackageServiceImpl.java @@ -0,0 +1,148 @@ +package com.hotwj.platform.operationManagement.preJobPackage.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.hotwj.platform.operationManagement.preJobPackage.domain.bo.HotPreJobPackageBo; +import com.hotwj.platform.operationManagement.preJobPackage.domain.vo.HotPreJobPackageVo; +import com.hotwj.platform.operationManagement.preJobPackage.domain.HotPreJobPackage; +import com.hotwj.platform.operationManagement.preJobPackage.mapper.HotPreJobPackageMapper; +import com.hotwj.platform.operationManagement.preJobPackage.service.IHotPreJobPackageService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 岗前培训套餐Service业务层处理 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotPreJobPackageServiceImpl implements IHotPreJobPackageService { + + private final HotPreJobPackageMapper baseMapper; + + /** + * 查询岗前培训套餐 + * + * @param id 主键 + * @return 岗前培训套餐 + */ + @Override + public HotPreJobPackageVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询岗前培训套餐列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 岗前培训套餐分页列表 + */ + @Override + public TableDataInfo queryPageList(HotPreJobPackageBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的岗前培训套餐列表 + * + * @param bo 查询条件 + * @return 岗前培训套餐列表 + */ + @Override + public List queryList(HotPreJobPackageBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotPreJobPackageBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotPreJobPackage::getId); + lqw.eq(bo.getCompanyId() != null, HotPreJobPackage::getCompanyId, bo.getCompanyId()); + lqw.eq(StringUtils.isNotBlank(bo.getPackageCode()), HotPreJobPackage::getPackageCode, bo.getPackageCode()); + lqw.like(StringUtils.isNotBlank(bo.getPackageName()), HotPreJobPackage::getPackageName, bo.getPackageName()); + lqw.eq(StringUtils.isNotBlank(bo.getPackageContent()), HotPreJobPackage::getPackageContent, bo.getPackageContent()); + lqw.eq(bo.getPackageType() != null, HotPreJobPackage::getPackageType, bo.getPackageType()); + lqw.eq(bo.getActivityId() != null, HotPreJobPackage::getActivityId, bo.getActivityId()); + lqw.like(StringUtils.isNotBlank(bo.getActivityName()), HotPreJobPackage::getActivityName, bo.getActivityName()); + lqw.eq(bo.getOriginPrice() != null, HotPreJobPackage::getOriginPrice, bo.getOriginPrice()); + lqw.eq(bo.getActivityPrice() != null, HotPreJobPackage::getActivityPrice, bo.getActivityPrice()); + lqw.eq(bo.getUnitPrice() != null, HotPreJobPackage::getUnitPrice, bo.getUnitPrice()); + lqw.eq(bo.getValidStartTime() != null, HotPreJobPackage::getValidStartTime, bo.getValidStartTime()); + lqw.eq(bo.getValidEndTime() != null, HotPreJobPackage::getValidEndTime, bo.getValidEndTime()); + lqw.eq(bo.getStatus() != null, HotPreJobPackage::getStatus, bo.getStatus()); + lqw.eq(bo.getSortNo() != null, HotPreJobPackage::getSortNo, bo.getSortNo()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotPreJobPackage::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotPreJobPackage::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotPreJobPackage::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增岗前培训套餐 + * + * @param bo 岗前培训套餐 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotPreJobPackageBo bo) { + HotPreJobPackage add = MapstructUtils.convert(bo, HotPreJobPackage.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改岗前培训套餐 + * + * @param bo 岗前培训套餐 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotPreJobPackageBo bo) { + HotPreJobPackage update = MapstructUtils.convert(bo, HotPreJobPackage.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotPreJobPackage entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除岗前培训套餐信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/controller/HotPreJobPackagePurchaseController.java b/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/controller/HotPreJobPackagePurchaseController.java new file mode 100644 index 0000000..a5c52db --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/controller/HotPreJobPackagePurchaseController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.operationManagement.preJobPackagePurchase.controller; + +import com.hotwj.platform.operationManagement.preJobPackagePurchase.domain.bo.HotPreJobPackagePurchaseBo; +import com.hotwj.platform.operationManagement.preJobPackagePurchase.domain.vo.HotPreJobPackagePurchaseVo; +import com.hotwj.platform.operationManagement.preJobPackagePurchase.service.IHotPreJobPackagePurchaseService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 岗前培训套餐购买记录 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/preJobPackagePurchase") +@Tag(name = "岗前培训套餐购买记录", description = "岗前培训套餐购买记录管理") +public class HotPreJobPackagePurchaseController extends BaseController { + + private final IHotPreJobPackagePurchaseService hotPreJobPackagePurchaseService; + + /** + * 查询岗前培训套餐购买记录列表 + */ + //@SaCheckPermission("operationManagement:preJobPackagePurchase:list") + @GetMapping("/list") + @Operation(summary = "分页查询岗前培训套餐购买记录列表") + public TableDataInfo list(HotPreJobPackagePurchaseBo bo, PageQuery pageQuery) { + return hotPreJobPackagePurchaseService.queryPageList(bo, pageQuery); + } + + /** + * 导出岗前培训套餐购买记录列表 + */ + //@SaCheckPermission("operationManagement:preJobPackagePurchase:export") + @Log(title = "岗前培训套餐购买记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出岗前培训套餐购买记录列表") + public void export(HotPreJobPackagePurchaseBo bo, HttpServletResponse response) { + List list = hotPreJobPackagePurchaseService.queryList(bo); + ExcelUtil.exportExcel(list, "岗前培训套餐购买记录", HotPreJobPackagePurchaseVo.class, response); + } + + /** + * 获取岗前培训套餐购买记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("operationManagement:preJobPackagePurchase:query") + @GetMapping("/{id}") + @Operation(summary = "获取岗前培训套餐购买记录详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotPreJobPackagePurchaseService.queryById(id)); + } + + /** + * 新增岗前培训套餐购买记录 + */ + //@SaCheckPermission("operationManagement:preJobPackagePurchase:add") + @Log(title = "岗前培训套餐购买记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增岗前培训套餐购买记录") + public R add(@Validated(AddGroup.class) @RequestBody HotPreJobPackagePurchaseBo bo) { + return toAjax(hotPreJobPackagePurchaseService.insertByBo(bo)); + } + + /** + * 修改岗前培训套餐购买记录 + */ + //@SaCheckPermission("operationManagement:preJobPackagePurchase:edit") + @Log(title = "岗前培训套餐购买记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改岗前培训套餐购买记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotPreJobPackagePurchaseBo bo) { + return toAjax(hotPreJobPackagePurchaseService.updateByBo(bo)); + } + + /** + * 删除岗前培训套餐购买记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("operationManagement:preJobPackagePurchase:remove") + @Log(title = "岗前培训套餐购买记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除岗前培训套餐购买记录") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotPreJobPackagePurchaseService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/domain/HotPreJobPackagePurchase.java b/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/domain/HotPreJobPackagePurchase.java new file mode 100644 index 0000000..e4dee29 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/domain/HotPreJobPackagePurchase.java @@ -0,0 +1,156 @@ +package com.hotwj.platform.operationManagement.preJobPackagePurchase.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 岗前培训套餐购买记录对象 hot_pre_job_package_purchase + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_pre_job_package_purchase") +public class HotPreJobPackagePurchase extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + private Long accountId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 消费型企业ID + */ + private Long consumerCompanyId; + + /** + * 消费型企业名称 + */ + private String consumerCompanyName; + + /** + * 订单管理表ID(逻辑外键) + */ + private Long orderId; + + /** + * 订单号 + */ + private String orderNo; + + /** + * 岗前培训套餐ID + */ + private Long packageId; + + /** + * 岗前培训套餐名称(快照) + */ + private String packageName; + + /** + * 套餐内容(快照) + */ + private String packageContent; + + /** + * 套餐类型:1=常规套餐 2=活动套餐 + */ + private Long packageType; + + /** + * 活动ID + */ + private Long activityId; + + /** + * 活动名称(快照) + */ + private String activityName; + + /** + * 原价(元) + */ + private Long originPrice; + + /** + * 活动价(元) + */ + private Long activityPrice; + + /** + * 成交单价(元) + */ + private Long unitPrice; + + /** + * 购买数量 + */ + private Long quantity; + + /** + * 总金额(元) + */ + private Long totalAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 4=余额 + */ + private Long payMethod; + + /** + * 支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭 + */ + private Long payStatus; + + /** + * 下单时间 + */ + private Date orderCreateTime; + + /** + * 支付时间 + */ + private Date payTime; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/domain/bo/HotPreJobPackagePurchaseBo.java b/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/domain/bo/HotPreJobPackagePurchaseBo.java new file mode 100644 index 0000000..23352d3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/domain/bo/HotPreJobPackagePurchaseBo.java @@ -0,0 +1,154 @@ +package com.hotwj.platform.operationManagement.preJobPackagePurchase.domain.bo; + +import com.hotwj.platform.operationManagement.preJobPackagePurchase.domain.HotPreJobPackagePurchase; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 岗前培训套餐购买记录业务对象 hot_pre_job_package_purchase + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotPreJobPackagePurchase.class, reverseConvertGenerate = false) +public class HotPreJobPackagePurchaseBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + private Long accountId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 消费型企业ID + */ + private Long consumerCompanyId; + + /** + * 消费型企业名称 + */ + private String consumerCompanyName; + + /** + * 订单管理表ID(逻辑外键) + */ + private Long orderId; + + /** + * 订单号 + */ + private String orderNo; + + /** + * 岗前培训套餐ID + */ + private Long packageId; + + /** + * 岗前培训套餐名称(快照) + */ + private String packageName; + + /** + * 套餐内容(快照) + */ + private String packageContent; + + /** + * 套餐类型:1=常规套餐 2=活动套餐 + */ + private Long packageType; + + /** + * 活动ID + */ + private Long activityId; + + /** + * 活动名称(快照) + */ + private String activityName; + + /** + * 原价(元) + */ + private Long originPrice; + + /** + * 活动价(元) + */ + private Long activityPrice; + + /** + * 成交单价(元) + */ + private Long unitPrice; + + /** + * 购买数量 + */ + private Long quantity; + + /** + * 总金额(元) + */ + private Long totalAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 4=余额 + */ + private Long payMethod; + + /** + * 支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭 + */ + private Long payStatus; + + /** + * 下单时间 + */ + private Date orderCreateTime; + + /** + * 支付时间 + */ + private Date payTime; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/domain/vo/HotPreJobPackagePurchaseVo.java b/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/domain/vo/HotPreJobPackagePurchaseVo.java new file mode 100644 index 0000000..eb8032d --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/domain/vo/HotPreJobPackagePurchaseVo.java @@ -0,0 +1,206 @@ +package com.hotwj.platform.operationManagement.preJobPackagePurchase.domain.vo; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.hotwj.platform.operationManagement.preJobPackagePurchase.domain.HotPreJobPackagePurchase; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 岗前培训套餐购买记录视图对象 hot_pre_job_package_purchase + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotPreJobPackagePurchase.class) +public class HotPreJobPackagePurchaseVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + @ExcelProperty(value = "账户ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逻=辑外键") + private Long accountId; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户ID") + private Long userId; + + /** + * 消费型企业ID + */ + @ExcelProperty(value = "消费型企业ID") + private Long consumerCompanyId; + + /** + * 消费型企业名称 + */ + @ExcelProperty(value = "消费型企业名称") + private String consumerCompanyName; + + /** + * 订单管理表ID(逻辑外键) + */ + @ExcelProperty(value = "订单管理表ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逻=辑外键") + private Long orderId; + + /** + * 订单号 + */ + @ExcelProperty(value = "订单号") + private String orderNo; + + /** + * 岗前培训套餐ID + */ + @ExcelProperty(value = "岗前培训套餐ID") + private Long packageId; + + /** + * 岗前培训套餐名称(快照) + */ + @ExcelProperty(value = "岗前培训套餐名称", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "快=照") + private String packageName; + + /** + * 套餐内容(快照) + */ + @ExcelProperty(value = "套餐内容", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "快=照") + private String packageContent; + + /** + * 套餐类型:1=常规套餐 2=活动套餐 + */ + @ExcelProperty(value = "套餐类型:1=常规套餐 2=活动套餐") + private Long packageType; + + /** + * 活动ID + */ + @ExcelProperty(value = "活动ID") + private Long activityId; + + /** + * 活动名称(快照) + */ + @ExcelProperty(value = "活动名称", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "快=照") + private String activityName; + + /** + * 原价(元) + */ + @ExcelProperty(value = "原价", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long originPrice; + + /** + * 活动价(元) + */ + @ExcelProperty(value = "活动价", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long activityPrice; + + /** + * 成交单价(元) + */ + @ExcelProperty(value = "成交单价", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long unitPrice; + + /** + * 购买数量 + */ + @ExcelProperty(value = "购买数量") + private Long quantity; + + /** + * 总金额(元) + */ + @ExcelProperty(value = "总金额", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long totalAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 4=余额 + */ + @ExcelProperty(value = "支付方式:1=微信 2=支付宝 3=银行卡 4=余额") + private Long payMethod; + + /** + * 支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭 + */ + @ExcelProperty(value = "支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭") + private Long payStatus; + + /** + * 下单时间 + */ + @ExcelProperty(value = "下单时间") + private Date orderCreateTime; + + /** + * 支付时间 + */ + @ExcelProperty(value = "支付时间") + private Date payTime; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/mapper/HotPreJobPackagePurchaseMapper.java b/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/mapper/HotPreJobPackagePurchaseMapper.java new file mode 100644 index 0000000..574a876 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/mapper/HotPreJobPackagePurchaseMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.preJobPackagePurchase.mapper; + +import com.hotwj.platform.operationManagement.preJobPackagePurchase.domain.HotPreJobPackagePurchase; +import com.hotwj.platform.operationManagement.preJobPackagePurchase.domain.vo.HotPreJobPackagePurchaseVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 岗前培训套餐购买记录Mapper接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Mapper +public interface HotPreJobPackagePurchaseMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/service/IHotPreJobPackagePurchaseService.java b/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/service/IHotPreJobPackagePurchaseService.java new file mode 100644 index 0000000..66bd4a8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/service/IHotPreJobPackagePurchaseService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.preJobPackagePurchase.service; + +import com.hotwj.platform.operationManagement.preJobPackagePurchase.domain.vo.HotPreJobPackagePurchaseVo; +import com.hotwj.platform.operationManagement.preJobPackagePurchase.domain.bo.HotPreJobPackagePurchaseBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 岗前培训套餐购买记录Service接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +public interface IHotPreJobPackagePurchaseService { + + /** + * 查询岗前培训套餐购买记录 + * + * @param id 主键 + * @return 岗前培训套餐购买记录 + */ + HotPreJobPackagePurchaseVo queryById(Long id); + + /** + * 分页查询岗前培训套餐购买记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 岗前培训套餐购买记录分页列表 + */ + TableDataInfo queryPageList(HotPreJobPackagePurchaseBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的岗前培训套餐购买记录列表 + * + * @param bo 查询条件 + * @return 岗前培训套餐购买记录列表 + */ + List queryList(HotPreJobPackagePurchaseBo bo); + + /** + * 新增岗前培训套餐购买记录 + * + * @param bo 岗前培训套餐购买记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotPreJobPackagePurchaseBo bo); + + /** + * 修改岗前培训套餐购买记录 + * + * @param bo 岗前培训套餐购买记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotPreJobPackagePurchaseBo bo); + + /** + * 校验并批量删除岗前培训套餐购买记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/service/impl/HotPreJobPackagePurchaseServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/service/impl/HotPreJobPackagePurchaseServiceImpl.java new file mode 100644 index 0000000..ac924a7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/preJobPackagePurchase/service/impl/HotPreJobPackagePurchaseServiceImpl.java @@ -0,0 +1,156 @@ +package com.hotwj.platform.operationManagement.preJobPackagePurchase.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.hotwj.platform.operationManagement.preJobPackagePurchase.domain.bo.HotPreJobPackagePurchaseBo; +import com.hotwj.platform.operationManagement.preJobPackagePurchase.domain.vo.HotPreJobPackagePurchaseVo; +import com.hotwj.platform.operationManagement.preJobPackagePurchase.domain.HotPreJobPackagePurchase; +import com.hotwj.platform.operationManagement.preJobPackagePurchase.mapper.HotPreJobPackagePurchaseMapper; +import com.hotwj.platform.operationManagement.preJobPackagePurchase.service.IHotPreJobPackagePurchaseService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 岗前培训套餐购买记录Service业务层处理 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotPreJobPackagePurchaseServiceImpl implements IHotPreJobPackagePurchaseService { + + private final HotPreJobPackagePurchaseMapper baseMapper; + + /** + * 查询岗前培训套餐购买记录 + * + * @param id 主键 + * @return 岗前培训套餐购买记录 + */ + @Override + public HotPreJobPackagePurchaseVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询岗前培训套餐购买记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 岗前培训套餐购买记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotPreJobPackagePurchaseBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的岗前培训套餐购买记录列表 + * + * @param bo 查询条件 + * @return 岗前培训套餐购买记录列表 + */ + @Override + public List queryList(HotPreJobPackagePurchaseBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotPreJobPackagePurchaseBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotPreJobPackagePurchase::getId); + lqw.eq(bo.getCompanyId() != null, HotPreJobPackagePurchase::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getAccountId() != null, HotPreJobPackagePurchase::getAccountId, bo.getAccountId()); + lqw.eq(bo.getUserId() != null, HotPreJobPackagePurchase::getUserId, bo.getUserId()); + lqw.eq(bo.getConsumerCompanyId() != null, HotPreJobPackagePurchase::getConsumerCompanyId, bo.getConsumerCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getConsumerCompanyName()), HotPreJobPackagePurchase::getConsumerCompanyName, bo.getConsumerCompanyName()); + lqw.eq(bo.getOrderId() != null, HotPreJobPackagePurchase::getOrderId, bo.getOrderId()); + lqw.eq(StringUtils.isNotBlank(bo.getOrderNo()), HotPreJobPackagePurchase::getOrderNo, bo.getOrderNo()); + lqw.eq(bo.getPackageId() != null, HotPreJobPackagePurchase::getPackageId, bo.getPackageId()); + lqw.like(StringUtils.isNotBlank(bo.getPackageName()), HotPreJobPackagePurchase::getPackageName, bo.getPackageName()); + lqw.eq(StringUtils.isNotBlank(bo.getPackageContent()), HotPreJobPackagePurchase::getPackageContent, bo.getPackageContent()); + lqw.eq(bo.getPackageType() != null, HotPreJobPackagePurchase::getPackageType, bo.getPackageType()); + lqw.eq(bo.getActivityId() != null, HotPreJobPackagePurchase::getActivityId, bo.getActivityId()); + lqw.like(StringUtils.isNotBlank(bo.getActivityName()), HotPreJobPackagePurchase::getActivityName, bo.getActivityName()); + lqw.eq(bo.getOriginPrice() != null, HotPreJobPackagePurchase::getOriginPrice, bo.getOriginPrice()); + lqw.eq(bo.getActivityPrice() != null, HotPreJobPackagePurchase::getActivityPrice, bo.getActivityPrice()); + lqw.eq(bo.getUnitPrice() != null, HotPreJobPackagePurchase::getUnitPrice, bo.getUnitPrice()); + lqw.eq(bo.getQuantity() != null, HotPreJobPackagePurchase::getQuantity, bo.getQuantity()); + lqw.eq(bo.getTotalAmount() != null, HotPreJobPackagePurchase::getTotalAmount, bo.getTotalAmount()); + lqw.eq(bo.getPayMethod() != null, HotPreJobPackagePurchase::getPayMethod, bo.getPayMethod()); + lqw.eq(bo.getPayStatus() != null, HotPreJobPackagePurchase::getPayStatus, bo.getPayStatus()); + lqw.eq(bo.getOrderCreateTime() != null, HotPreJobPackagePurchase::getOrderCreateTime, bo.getOrderCreateTime()); + lqw.eq(bo.getPayTime() != null, HotPreJobPackagePurchase::getPayTime, bo.getPayTime()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotPreJobPackagePurchase::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotPreJobPackagePurchase::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotPreJobPackagePurchase::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增岗前培训套餐购买记录 + * + * @param bo 岗前培训套餐购买记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotPreJobPackagePurchaseBo bo) { + HotPreJobPackagePurchase add = MapstructUtils.convert(bo, HotPreJobPackagePurchase.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改岗前培训套餐购买记录 + * + * @param bo 岗前培训套餐购买记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotPreJobPackagePurchaseBo bo) { + HotPreJobPackagePurchase update = MapstructUtils.convert(bo, HotPreJobPackagePurchase.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotPreJobPackagePurchase entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除岗前培训套餐购买记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/controller/HotSmsCompanyStatController.java b/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/controller/HotSmsCompanyStatController.java new file mode 100644 index 0000000..e0d9c42 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/controller/HotSmsCompanyStatController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.operationManagement.smsCompanyStat.controller; + +import com.hotwj.platform.operationManagement.smsCompanyStat.domain.bo.HotSmsCompanyStatBo; +import com.hotwj.platform.operationManagement.smsCompanyStat.domain.vo.HotSmsCompanyStatVo; +import com.hotwj.platform.operationManagement.smsCompanyStat.service.IHotSmsCompanyStatService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 短信企业统计 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/smsCompanyStat") +@Tag(name = "短信企业统计", description = "短信企业统计管理") +public class HotSmsCompanyStatController extends BaseController { + + private final IHotSmsCompanyStatService hotSmsCompanyStatService; + + /** + * 查询短信企业统计列表 + */ + //@SaCheckPermission("operationManagement:smsCompanyStat:list") + @GetMapping("/list") + @Operation(summary = "分页查询短信企业统计列表") + public TableDataInfo list(HotSmsCompanyStatBo bo, PageQuery pageQuery) { + return hotSmsCompanyStatService.queryPageList(bo, pageQuery); + } + + /** + * 导出短信企业统计列表 + */ + //@SaCheckPermission("operationManagement:smsCompanyStat:export") + @Log(title = "短信企业统计", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出短信企业统计列表") + public void export(HotSmsCompanyStatBo bo, HttpServletResponse response) { + List list = hotSmsCompanyStatService.queryList(bo); + ExcelUtil.exportExcel(list, "短信企业统计", HotSmsCompanyStatVo.class, response); + } + + /** + * 获取短信企业统计详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("operationManagement:smsCompanyStat:query") + @GetMapping("/{id}") + @Operation(summary = "获取短信企业统计详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotSmsCompanyStatService.queryById(id)); + } + + /** + * 新增短信企业统计 + */ + //@SaCheckPermission("operationManagement:smsCompanyStat:add") + @Log(title = "短信企业统计", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增短信企业统计") + public R add(@Validated(AddGroup.class) @RequestBody HotSmsCompanyStatBo bo) { + return toAjax(hotSmsCompanyStatService.insertByBo(bo)); + } + + /** + * 修改短信企业统计 + */ + //@SaCheckPermission("operationManagement:smsCompanyStat:edit") + @Log(title = "短信企业统计", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改短信企业统计") + public R edit(@Validated(EditGroup.class) @RequestBody HotSmsCompanyStatBo bo) { + return toAjax(hotSmsCompanyStatService.updateByBo(bo)); + } + + /** + * 删除短信企业统计 + * + * @param ids 主键串 + */ + //@SaCheckPermission("operationManagement:smsCompanyStat:remove") + @Log(title = "短信企业统计", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除短信企业统计") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotSmsCompanyStatService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/domain/HotSmsCompanyStat.java b/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/domain/HotSmsCompanyStat.java new file mode 100644 index 0000000..16f9cdf --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/domain/HotSmsCompanyStat.java @@ -0,0 +1,96 @@ +package com.hotwj.platform.operationManagement.smsCompanyStat.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 短信企业统计对象 hot_sms_company_stat + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_sms_company_stat") +public class HotSmsCompanyStat extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 企业ID + */ + private Long enterpriseId; + + /** + * 企业名称 + */ + private String enterpriseName; + + /** + * 总充值条数 + */ + private Long totalRechargeCount; + + /** + * 总发送条数 + */ + private Long totalSendCount; + + /** + * 总成功到达量 + */ + private Long totalSuccessCount; + + /** + * 总计费量 + */ + private Long totalBillableCount; + + /** + * 总充值金额(元) + */ + private Long totalRechargeAmount; + + /** + * 剩余短信 + */ + private Long remainingSmsCount; + + /** + * 最近统计时间 + */ + private Date lastStatTime; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/domain/bo/HotSmsCompanyStatBo.java b/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/domain/bo/HotSmsCompanyStatBo.java new file mode 100644 index 0000000..c24f751 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/domain/bo/HotSmsCompanyStatBo.java @@ -0,0 +1,94 @@ +package com.hotwj.platform.operationManagement.smsCompanyStat.domain.bo; + +import com.hotwj.platform.operationManagement.smsCompanyStat.domain.HotSmsCompanyStat; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 短信企业统计业务对象 hot_sms_company_stat + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotSmsCompanyStat.class, reverseConvertGenerate = false) +public class HotSmsCompanyStatBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 企业ID + */ + private Long enterpriseId; + + /** + * 企业名称 + */ + private String enterpriseName; + + /** + * 总充值条数 + */ + private Long totalRechargeCount; + + /** + * 总发送条数 + */ + private Long totalSendCount; + + /** + * 总成功到达量 + */ + private Long totalSuccessCount; + + /** + * 总计费量 + */ + private Long totalBillableCount; + + /** + * 总充值金额(元) + */ + private Long totalRechargeAmount; + + /** + * 剩余短信 + */ + private Long remainingSmsCount; + + /** + * 最近统计时间 + */ + private Date lastStatTime; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/domain/vo/HotSmsCompanyStatVo.java b/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/domain/vo/HotSmsCompanyStatVo.java new file mode 100644 index 0000000..bbdc4b1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/domain/vo/HotSmsCompanyStatVo.java @@ -0,0 +1,126 @@ +package com.hotwj.platform.operationManagement.smsCompanyStat.domain.vo; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.hotwj.platform.operationManagement.smsCompanyStat.domain.HotSmsCompanyStat; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 短信企业统计视图对象 hot_sms_company_stat + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotSmsCompanyStat.class) +public class HotSmsCompanyStatVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 企业ID + */ + @ExcelProperty(value = "企业ID") + private Long enterpriseId; + + /** + * 企业名称 + */ + @ExcelProperty(value = "企业名称") + private String enterpriseName; + + /** + * 总充值条数 + */ + @ExcelProperty(value = "总充值条数") + private Long totalRechargeCount; + + /** + * 总发送条数 + */ + @ExcelProperty(value = "总发送条数") + private Long totalSendCount; + + /** + * 总成功到达量 + */ + @ExcelProperty(value = "总成功到达量") + private Long totalSuccessCount; + + /** + * 总计费量 + */ + @ExcelProperty(value = "总计费量") + private Long totalBillableCount; + + /** + * 总充值金额(元) + */ + @ExcelProperty(value = "总充值金额", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long totalRechargeAmount; + + /** + * 剩余短信 + */ + @ExcelProperty(value = "剩余短信") + private Long remainingSmsCount; + + /** + * 最近统计时间 + */ + @ExcelProperty(value = "最近统计时间") + private Date lastStatTime; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/mapper/HotSmsCompanyStatMapper.java b/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/mapper/HotSmsCompanyStatMapper.java new file mode 100644 index 0000000..9567d00 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/mapper/HotSmsCompanyStatMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.smsCompanyStat.mapper; + +import com.hotwj.platform.operationManagement.smsCompanyStat.domain.HotSmsCompanyStat; +import com.hotwj.platform.operationManagement.smsCompanyStat.domain.vo.HotSmsCompanyStatVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 短信企业统计Mapper接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Mapper +public interface HotSmsCompanyStatMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/service/IHotSmsCompanyStatService.java b/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/service/IHotSmsCompanyStatService.java new file mode 100644 index 0000000..ab140bb --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/service/IHotSmsCompanyStatService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.smsCompanyStat.service; + +import com.hotwj.platform.operationManagement.smsCompanyStat.domain.vo.HotSmsCompanyStatVo; +import com.hotwj.platform.operationManagement.smsCompanyStat.domain.bo.HotSmsCompanyStatBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 短信企业统计Service接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +public interface IHotSmsCompanyStatService { + + /** + * 查询短信企业统计 + * + * @param id 主键 + * @return 短信企业统计 + */ + HotSmsCompanyStatVo queryById(Long id); + + /** + * 分页查询短信企业统计列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 短信企业统计分页列表 + */ + TableDataInfo queryPageList(HotSmsCompanyStatBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的短信企业统计列表 + * + * @param bo 查询条件 + * @return 短信企业统计列表 + */ + List queryList(HotSmsCompanyStatBo bo); + + /** + * 新增短信企业统计 + * + * @param bo 短信企业统计 + * @return 是否新增成功 + */ + Boolean insertByBo(HotSmsCompanyStatBo bo); + + /** + * 修改短信企业统计 + * + * @param bo 短信企业统计 + * @return 是否修改成功 + */ + Boolean updateByBo(HotSmsCompanyStatBo bo); + + /** + * 校验并批量删除短信企业统计信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/service/impl/HotSmsCompanyStatServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/service/impl/HotSmsCompanyStatServiceImpl.java new file mode 100644 index 0000000..ca28d46 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsCompanyStat/service/impl/HotSmsCompanyStatServiceImpl.java @@ -0,0 +1,144 @@ +package com.hotwj.platform.operationManagement.smsCompanyStat.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.hotwj.platform.operationManagement.smsCompanyStat.domain.bo.HotSmsCompanyStatBo; +import com.hotwj.platform.operationManagement.smsCompanyStat.domain.vo.HotSmsCompanyStatVo; +import com.hotwj.platform.operationManagement.smsCompanyStat.domain.HotSmsCompanyStat; +import com.hotwj.platform.operationManagement.smsCompanyStat.mapper.HotSmsCompanyStatMapper; +import com.hotwj.platform.operationManagement.smsCompanyStat.service.IHotSmsCompanyStatService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 短信企业统计Service业务层处理 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotSmsCompanyStatServiceImpl implements IHotSmsCompanyStatService { + + private final HotSmsCompanyStatMapper baseMapper; + + /** + * 查询短信企业统计 + * + * @param id 主键 + * @return 短信企业统计 + */ + @Override + public HotSmsCompanyStatVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询短信企业统计列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 短信企业统计分页列表 + */ + @Override + public TableDataInfo queryPageList(HotSmsCompanyStatBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的短信企业统计列表 + * + * @param bo 查询条件 + * @return 短信企业统计列表 + */ + @Override + public List queryList(HotSmsCompanyStatBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotSmsCompanyStatBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotSmsCompanyStat::getId); + lqw.eq(bo.getCompanyId() != null, HotSmsCompanyStat::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getEnterpriseId() != null, HotSmsCompanyStat::getEnterpriseId, bo.getEnterpriseId()); + lqw.like(StringUtils.isNotBlank(bo.getEnterpriseName()), HotSmsCompanyStat::getEnterpriseName, bo.getEnterpriseName()); + lqw.eq(bo.getTotalRechargeCount() != null, HotSmsCompanyStat::getTotalRechargeCount, bo.getTotalRechargeCount()); + lqw.eq(bo.getTotalSendCount() != null, HotSmsCompanyStat::getTotalSendCount, bo.getTotalSendCount()); + lqw.eq(bo.getTotalSuccessCount() != null, HotSmsCompanyStat::getTotalSuccessCount, bo.getTotalSuccessCount()); + lqw.eq(bo.getTotalBillableCount() != null, HotSmsCompanyStat::getTotalBillableCount, bo.getTotalBillableCount()); + lqw.eq(bo.getTotalRechargeAmount() != null, HotSmsCompanyStat::getTotalRechargeAmount, bo.getTotalRechargeAmount()); + lqw.eq(bo.getRemainingSmsCount() != null, HotSmsCompanyStat::getRemainingSmsCount, bo.getRemainingSmsCount()); + lqw.eq(bo.getLastStatTime() != null, HotSmsCompanyStat::getLastStatTime, bo.getLastStatTime()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotSmsCompanyStat::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotSmsCompanyStat::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotSmsCompanyStat::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增短信企业统计 + * + * @param bo 短信企业统计 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotSmsCompanyStatBo bo) { + HotSmsCompanyStat add = MapstructUtils.convert(bo, HotSmsCompanyStat.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改短信企业统计 + * + * @param bo 短信企业统计 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotSmsCompanyStatBo bo) { + HotSmsCompanyStat update = MapstructUtils.convert(bo, HotSmsCompanyStat.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotSmsCompanyStat entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除短信企业统计信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/controller/HotSmsDailyStatController.java b/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/controller/HotSmsDailyStatController.java new file mode 100644 index 0000000..c32be29 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/controller/HotSmsDailyStatController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.operationManagement.smsDailyStat.controller; + +import com.hotwj.platform.operationManagement.smsDailyStat.domain.bo.HotSmsDailyStatBo; +import com.hotwj.platform.operationManagement.smsDailyStat.domain.vo.HotSmsDailyStatVo; +import com.hotwj.platform.operationManagement.smsDailyStat.service.IHotSmsDailyStatService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 短信按日统计 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/smsDailyStat") +@Tag(name = "短信按日统计", description = "短信按日统计管理") +public class HotSmsDailyStatController extends BaseController { + + private final IHotSmsDailyStatService hotSmsDailyStatService; + + /** + * 查询短信按日统计列表 + */ + //@SaCheckPermission("operationManagement:smsDailyStat:list") + @GetMapping("/list") + @Operation(summary = "分页查询短信按日统计列表") + public TableDataInfo list(HotSmsDailyStatBo bo, PageQuery pageQuery) { + return hotSmsDailyStatService.queryPageList(bo, pageQuery); + } + + /** + * 导出短信按日统计列表 + */ + //@SaCheckPermission("operationManagement:smsDailyStat:export") + @Log(title = "短信按日统计", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出短信按日统计列表") + public void export(HotSmsDailyStatBo bo, HttpServletResponse response) { + List list = hotSmsDailyStatService.queryList(bo); + ExcelUtil.exportExcel(list, "短信按日统计", HotSmsDailyStatVo.class, response); + } + + /** + * 获取短信按日统计详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("operationManagement:smsDailyStat:query") + @GetMapping("/{id}") + @Operation(summary = "获取短信按日统计详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotSmsDailyStatService.queryById(id)); + } + + /** + * 新增短信按日统计 + */ + //@SaCheckPermission("operationManagement:smsDailyStat:add") + @Log(title = "短信按日统计", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增短信按日统计") + public R add(@Validated(AddGroup.class) @RequestBody HotSmsDailyStatBo bo) { + return toAjax(hotSmsDailyStatService.insertByBo(bo)); + } + + /** + * 修改短信按日统计 + */ + //@SaCheckPermission("operationManagement:smsDailyStat:edit") + @Log(title = "短信按日统计", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改短信按日统计") + public R edit(@Validated(EditGroup.class) @RequestBody HotSmsDailyStatBo bo) { + return toAjax(hotSmsDailyStatService.updateByBo(bo)); + } + + /** + * 删除短信按日统计 + * + * @param ids 主键串 + */ + //@SaCheckPermission("operationManagement:smsDailyStat:remove") + @Log(title = "短信按日统计", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除短信按日统计") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotSmsDailyStatService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/domain/HotSmsDailyStat.java b/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/domain/HotSmsDailyStat.java new file mode 100644 index 0000000..72dfccf --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/domain/HotSmsDailyStat.java @@ -0,0 +1,86 @@ +package com.hotwj.platform.operationManagement.smsDailyStat.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 短信按日统计对象 hot_sms_daily_stat + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_sms_daily_stat") +public class HotSmsDailyStat extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 企业ID + */ + private Long enterpriseId; + + /** + * 企业名称 + */ + private String enterpriseName; + + /** + * 统计日期 + */ + private Date statDate; + + /** + * 总发送量 + */ + private Long totalSendCount; + + /** + * 成功到达量 + */ + private Long successCount; + + /** + * 计费量 + */ + private Long billableCount; + + /** + * 失败量 + */ + private Long failCount; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/domain/bo/HotSmsDailyStatBo.java b/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/domain/bo/HotSmsDailyStatBo.java new file mode 100644 index 0000000..55fe7df --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/domain/bo/HotSmsDailyStatBo.java @@ -0,0 +1,84 @@ +package com.hotwj.platform.operationManagement.smsDailyStat.domain.bo; + +import com.hotwj.platform.operationManagement.smsDailyStat.domain.HotSmsDailyStat; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 短信按日统计业务对象 hot_sms_daily_stat + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotSmsDailyStat.class, reverseConvertGenerate = false) +public class HotSmsDailyStatBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 企业ID + */ + private Long enterpriseId; + + /** + * 企业名称 + */ + private String enterpriseName; + + /** + * 统计日期 + */ + private Date statDate; + + /** + * 总发送量 + */ + private Long totalSendCount; + + /** + * 成功到达量 + */ + private Long successCount; + + /** + * 计费量 + */ + private Long billableCount; + + /** + * 失败量 + */ + private Long failCount; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/domain/vo/HotSmsDailyStatVo.java b/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/domain/vo/HotSmsDailyStatVo.java new file mode 100644 index 0000000..835a419 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/domain/vo/HotSmsDailyStatVo.java @@ -0,0 +1,113 @@ +package com.hotwj.platform.operationManagement.smsDailyStat.domain.vo; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.hotwj.platform.operationManagement.smsDailyStat.domain.HotSmsDailyStat; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 短信按日统计视图对象 hot_sms_daily_stat + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotSmsDailyStat.class) +public class HotSmsDailyStatVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 企业ID + */ + @ExcelProperty(value = "企业ID") + private Long enterpriseId; + + /** + * 企业名称 + */ + @ExcelProperty(value = "企业名称") + private String enterpriseName; + + /** + * 统计日期 + */ + @ExcelProperty(value = "统计日期") + private Date statDate; + + /** + * 总发送量 + */ + @ExcelProperty(value = "总发送量") + private Long totalSendCount; + + /** + * 成功到达量 + */ + @ExcelProperty(value = "成功到达量") + private Long successCount; + + /** + * 计费量 + */ + @ExcelProperty(value = "计费量") + private Long billableCount; + + /** + * 失败量 + */ + @ExcelProperty(value = "失败量") + private Long failCount; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/mapper/HotSmsDailyStatMapper.java b/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/mapper/HotSmsDailyStatMapper.java new file mode 100644 index 0000000..b396f00 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/mapper/HotSmsDailyStatMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.smsDailyStat.mapper; + +import com.hotwj.platform.operationManagement.smsDailyStat.domain.HotSmsDailyStat; +import com.hotwj.platform.operationManagement.smsDailyStat.domain.vo.HotSmsDailyStatVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 短信按日统计Mapper接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Mapper +public interface HotSmsDailyStatMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/service/IHotSmsDailyStatService.java b/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/service/IHotSmsDailyStatService.java new file mode 100644 index 0000000..a09d992 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/service/IHotSmsDailyStatService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.smsDailyStat.service; + +import com.hotwj.platform.operationManagement.smsDailyStat.domain.vo.HotSmsDailyStatVo; +import com.hotwj.platform.operationManagement.smsDailyStat.domain.bo.HotSmsDailyStatBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 短信按日统计Service接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +public interface IHotSmsDailyStatService { + + /** + * 查询短信按日统计 + * + * @param id 主键 + * @return 短信按日统计 + */ + HotSmsDailyStatVo queryById(Long id); + + /** + * 分页查询短信按日统计列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 短信按日统计分页列表 + */ + TableDataInfo queryPageList(HotSmsDailyStatBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的短信按日统计列表 + * + * @param bo 查询条件 + * @return 短信按日统计列表 + */ + List queryList(HotSmsDailyStatBo bo); + + /** + * 新增短信按日统计 + * + * @param bo 短信按日统计 + * @return 是否新增成功 + */ + Boolean insertByBo(HotSmsDailyStatBo bo); + + /** + * 修改短信按日统计 + * + * @param bo 短信按日统计 + * @return 是否修改成功 + */ + Boolean updateByBo(HotSmsDailyStatBo bo); + + /** + * 校验并批量删除短信按日统计信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/service/impl/HotSmsDailyStatServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/service/impl/HotSmsDailyStatServiceImpl.java new file mode 100644 index 0000000..675188a --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsDailyStat/service/impl/HotSmsDailyStatServiceImpl.java @@ -0,0 +1,142 @@ +package com.hotwj.platform.operationManagement.smsDailyStat.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.hotwj.platform.operationManagement.smsDailyStat.domain.bo.HotSmsDailyStatBo; +import com.hotwj.platform.operationManagement.smsDailyStat.domain.vo.HotSmsDailyStatVo; +import com.hotwj.platform.operationManagement.smsDailyStat.domain.HotSmsDailyStat; +import com.hotwj.platform.operationManagement.smsDailyStat.mapper.HotSmsDailyStatMapper; +import com.hotwj.platform.operationManagement.smsDailyStat.service.IHotSmsDailyStatService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 短信按日统计Service业务层处理 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotSmsDailyStatServiceImpl implements IHotSmsDailyStatService { + + private final HotSmsDailyStatMapper baseMapper; + + /** + * 查询短信按日统计 + * + * @param id 主键 + * @return 短信按日统计 + */ + @Override + public HotSmsDailyStatVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询短信按日统计列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 短信按日统计分页列表 + */ + @Override + public TableDataInfo queryPageList(HotSmsDailyStatBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的短信按日统计列表 + * + * @param bo 查询条件 + * @return 短信按日统计列表 + */ + @Override + public List queryList(HotSmsDailyStatBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotSmsDailyStatBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotSmsDailyStat::getId); + lqw.eq(bo.getCompanyId() != null, HotSmsDailyStat::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getEnterpriseId() != null, HotSmsDailyStat::getEnterpriseId, bo.getEnterpriseId()); + lqw.like(StringUtils.isNotBlank(bo.getEnterpriseName()), HotSmsDailyStat::getEnterpriseName, bo.getEnterpriseName()); + lqw.eq(bo.getStatDate() != null, HotSmsDailyStat::getStatDate, bo.getStatDate()); + lqw.eq(bo.getTotalSendCount() != null, HotSmsDailyStat::getTotalSendCount, bo.getTotalSendCount()); + lqw.eq(bo.getSuccessCount() != null, HotSmsDailyStat::getSuccessCount, bo.getSuccessCount()); + lqw.eq(bo.getBillableCount() != null, HotSmsDailyStat::getBillableCount, bo.getBillableCount()); + lqw.eq(bo.getFailCount() != null, HotSmsDailyStat::getFailCount, bo.getFailCount()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotSmsDailyStat::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotSmsDailyStat::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotSmsDailyStat::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增短信按日统计 + * + * @param bo 短信按日统计 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotSmsDailyStatBo bo) { + HotSmsDailyStat add = MapstructUtils.convert(bo, HotSmsDailyStat.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改短信按日统计 + * + * @param bo 短信按日统计 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotSmsDailyStatBo bo) { + HotSmsDailyStat update = MapstructUtils.convert(bo, HotSmsDailyStat.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotSmsDailyStat entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除短信按日统计信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsPackage/controller/HotSmsPackageController.java b/src/main/java/com/hotwj/platform/operationManagement/smsPackage/controller/HotSmsPackageController.java new file mode 100644 index 0000000..cb481b4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsPackage/controller/HotSmsPackageController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.operationManagement.smsPackage.controller; + +import com.hotwj.platform.operationManagement.smsPackage.domain.bo.HotSmsPackageBo; +import com.hotwj.platform.operationManagement.smsPackage.domain.vo.HotSmsPackageVo; +import com.hotwj.platform.operationManagement.smsPackage.service.IHotSmsPackageService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 短信套餐 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/smsPackage") +@Tag(name = "短信套餐", description = "短信套餐管理") +public class HotSmsPackageController extends BaseController { + + private final IHotSmsPackageService hotSmsPackageService; + + /** + * 查询短信套餐列表 + */ + //@SaCheckPermission("operationManagement:smsPackage:list") + @GetMapping("/list") + @Operation(summary = "分页查询短信套餐列表") + public TableDataInfo list(HotSmsPackageBo bo, PageQuery pageQuery) { + return hotSmsPackageService.queryPageList(bo, pageQuery); + } + + /** + * 导出短信套餐列表 + */ + //@SaCheckPermission("operationManagement:smsPackage:export") + @Log(title = "短信套餐", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出短信套餐列表") + public void export(HotSmsPackageBo bo, HttpServletResponse response) { + List list = hotSmsPackageService.queryList(bo); + ExcelUtil.exportExcel(list, "短信套餐", HotSmsPackageVo.class, response); + } + + /** + * 获取短信套餐详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("operationManagement:smsPackage:query") + @GetMapping("/{id}") + @Operation(summary = "获取短信套餐详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotSmsPackageService.queryById(id)); + } + + /** + * 新增短信套餐 + */ + //@SaCheckPermission("operationManagement:smsPackage:add") + @Log(title = "短信套餐", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增短信套餐") + public R add(@Validated(AddGroup.class) @RequestBody HotSmsPackageBo bo) { + return toAjax(hotSmsPackageService.insertByBo(bo)); + } + + /** + * 修改短信套餐 + */ + //@SaCheckPermission("operationManagement:smsPackage:edit") + @Log(title = "短信套餐", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改短信套餐") + public R edit(@Validated(EditGroup.class) @RequestBody HotSmsPackageBo bo) { + return toAjax(hotSmsPackageService.updateByBo(bo)); + } + + /** + * 删除短信套餐 + * + * @param ids 主键串 + */ + //@SaCheckPermission("operationManagement:smsPackage:remove") + @Log(title = "短信套餐", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除短信套餐") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotSmsPackageService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsPackage/domain/HotSmsPackage.java b/src/main/java/com/hotwj/platform/operationManagement/smsPackage/domain/HotSmsPackage.java new file mode 100644 index 0000000..6c70d1a --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsPackage/domain/HotSmsPackage.java @@ -0,0 +1,90 @@ +package com.hotwj.platform.operationManagement.smsPackage.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 短信套餐对象 hot_sms_package + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_sms_package") +public class HotSmsPackage extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 套餐编码 + */ + private String packageCode; + + /** + * 套餐名称 + */ + private String packageName; + + /** + * 短信条数(如1000/3000/5000) + */ + private Long smsCount; + + /** + * 套餐价(元) + */ + private BigDecimal packagePrice; + + /** + * 单条单价(元) + */ + private BigDecimal unitPrice; + + /** + * 状态:1=启用 0=停用 + */ + private Long status; + + /** + * 排序号 + */ + private Long sortNo; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 活动价格(元) + */ + private BigDecimal activityPrice; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsPackage/domain/bo/HotSmsPackageBo.java b/src/main/java/com/hotwj/platform/operationManagement/smsPackage/domain/bo/HotSmsPackageBo.java new file mode 100644 index 0000000..8b07f36 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsPackage/domain/bo/HotSmsPackageBo.java @@ -0,0 +1,86 @@ +package com.hotwj.platform.operationManagement.smsPackage.domain.bo; + +import com.hotwj.platform.operationManagement.smsPackage.domain.HotSmsPackage; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.math.BigDecimal; + +/** + * 短信套餐业务对象 hot_sms_package + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotSmsPackage.class, reverseConvertGenerate = false) +public class HotSmsPackageBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 套餐编码 + */ + private String packageCode; + + /** + * 套餐名称 + */ + private String packageName; + + /** + * 短信条数(如1000/3000/5000) + */ + private Long smsCount; + + /** + * 套餐价(元) + */ + private BigDecimal packagePrice; + + /** + * 单条单价(元) + */ + private BigDecimal unitPrice; + + /** + * 状态:1=启用 0=停用 + */ + private Long status; + + /** + * 排序号 + */ + private Long sortNo; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + /** + * 活动价格(元) + */ + private BigDecimal activityPrice; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsPackage/domain/vo/HotSmsPackageVo.java b/src/main/java/com/hotwj/platform/operationManagement/smsPackage/domain/vo/HotSmsPackageVo.java new file mode 100644 index 0000000..4231a85 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsPackage/domain/vo/HotSmsPackageVo.java @@ -0,0 +1,119 @@ +package com.hotwj.platform.operationManagement.smsPackage.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.operationManagement.smsPackage.domain.HotSmsPackage; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + + +/** + * 短信套餐视图对象 hot_sms_package + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotSmsPackage.class) +public class HotSmsPackageVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 套餐编码 + */ + @ExcelProperty(value = "套餐编码") + private String packageCode; + + /** + * 套餐名称 + */ + @ExcelProperty(value = "套餐名称") + private String packageName; + + /** + * 短信条数(如1000/3000/5000) + */ + @ExcelProperty(value = "短信条数", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "如=1000/3000/5000") + private Long smsCount; + + /** + * 套餐价(元) + */ + @ExcelProperty(value = "套餐价", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private BigDecimal packagePrice; + + /** + * 单条单价(元) + */ + @ExcelProperty(value = "单条单价", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private BigDecimal unitPrice; + + /** + * 状态:1=启用 0=停用 + */ + @ExcelProperty(value = "状态:1=启用 0=停用") + private Long status; + + /** + * 排序号 + */ + @ExcelProperty(value = "排序号") + private Long sortNo; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 活动价格(元) + */ + @ExcelProperty(value = "活动价格(元)") + private BigDecimal activityPrice; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsPackage/mapper/HotSmsPackageMapper.java b/src/main/java/com/hotwj/platform/operationManagement/smsPackage/mapper/HotSmsPackageMapper.java new file mode 100644 index 0000000..a52c9bf --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsPackage/mapper/HotSmsPackageMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.smsPackage.mapper; + +import com.hotwj.platform.operationManagement.smsPackage.domain.HotSmsPackage; +import com.hotwj.platform.operationManagement.smsPackage.domain.vo.HotSmsPackageVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 短信套餐Mapper接口 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Mapper +public interface HotSmsPackageMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsPackage/service/IHotSmsPackageService.java b/src/main/java/com/hotwj/platform/operationManagement/smsPackage/service/IHotSmsPackageService.java new file mode 100644 index 0000000..f0d97d4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsPackage/service/IHotSmsPackageService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.smsPackage.service; + +import com.hotwj.platform.operationManagement.smsPackage.domain.bo.HotSmsPackageBo; +import com.hotwj.platform.operationManagement.smsPackage.domain.vo.HotSmsPackageVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 短信套餐Service接口 + * + * @author shihongwei + * @date 2026-05-08 + */ +public interface IHotSmsPackageService { + + /** + * 查询短信套餐 + * + * @param id 主键 + * @return 短信套餐 + */ + HotSmsPackageVo queryById(Long id); + + /** + * 分页查询短信套餐列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 短信套餐分页列表 + */ + TableDataInfo queryPageList(HotSmsPackageBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的短信套餐列表 + * + * @param bo 查询条件 + * @return 短信套餐列表 + */ + List queryList(HotSmsPackageBo bo); + + /** + * 新增短信套餐 + * + * @param bo 短信套餐 + * @return 是否新增成功 + */ + Boolean insertByBo(HotSmsPackageBo bo); + + /** + * 修改短信套餐 + * + * @param bo 短信套餐 + * @return 是否修改成功 + */ + Boolean updateByBo(HotSmsPackageBo bo); + + /** + * 校验并批量删除短信套餐信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsPackage/service/impl/HotSmsPackageServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/smsPackage/service/impl/HotSmsPackageServiceImpl.java new file mode 100644 index 0000000..0e517fc --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsPackage/service/impl/HotSmsPackageServiceImpl.java @@ -0,0 +1,143 @@ +package com.hotwj.platform.operationManagement.smsPackage.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.operationManagement.smsPackage.domain.HotSmsPackage; +import com.hotwj.platform.operationManagement.smsPackage.domain.bo.HotSmsPackageBo; +import com.hotwj.platform.operationManagement.smsPackage.domain.vo.HotSmsPackageVo; +import com.hotwj.platform.operationManagement.smsPackage.mapper.HotSmsPackageMapper; +import com.hotwj.platform.operationManagement.smsPackage.service.IHotSmsPackageService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 短信套餐Service业务层处理 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotSmsPackageServiceImpl implements IHotSmsPackageService { + + private final HotSmsPackageMapper baseMapper; + + /** + * 查询短信套餐 + * + * @param id 主键 + * @return 短信套餐 + */ + @Override + public HotSmsPackageVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询短信套餐列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 短信套餐分页列表 + */ + @Override + public TableDataInfo queryPageList(HotSmsPackageBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的短信套餐列表 + * + * @param bo 查询条件 + * @return 短信套餐列表 + */ + @Override + public List queryList(HotSmsPackageBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotSmsPackageBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotSmsPackage::getId); + lqw.eq(bo.getCompanyId() != null, HotSmsPackage::getCompanyId, bo.getCompanyId()); + lqw.eq(StringUtils.isNotBlank(bo.getPackageCode()), HotSmsPackage::getPackageCode, bo.getPackageCode()); + lqw.like(StringUtils.isNotBlank(bo.getPackageName()), HotSmsPackage::getPackageName, bo.getPackageName()); + lqw.eq(bo.getSmsCount() != null, HotSmsPackage::getSmsCount, bo.getSmsCount()); + lqw.eq(bo.getPackagePrice() != null, HotSmsPackage::getPackagePrice, bo.getPackagePrice()); + lqw.eq(bo.getUnitPrice() != null, HotSmsPackage::getUnitPrice, bo.getUnitPrice()); + lqw.eq(bo.getStatus() != null, HotSmsPackage::getStatus, bo.getStatus()); + lqw.eq(bo.getSortNo() != null, HotSmsPackage::getSortNo, bo.getSortNo()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotSmsPackage::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotSmsPackage::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotSmsPackage::getIsDeleted, bo.getIsDeleted()); + lqw.eq(bo.getActivityPrice() != null, HotSmsPackage::getActivityPrice, bo.getActivityPrice()); + return lqw; + } + + /** + * 新增短信套餐 + * + * @param bo 短信套餐 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotSmsPackageBo bo) { + HotSmsPackage add = MapstructUtils.convert(bo, HotSmsPackage.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改短信套餐 + * + * @param bo 短信套餐 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotSmsPackageBo bo) { + HotSmsPackage update = MapstructUtils.convert(bo, HotSmsPackage.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotSmsPackage entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除短信套餐信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/controller/HotSmsPackagePurchaseController.java b/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/controller/HotSmsPackagePurchaseController.java new file mode 100644 index 0000000..1b2701e --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/controller/HotSmsPackagePurchaseController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.operationManagement.smsPackagePurchase.controller; + +import com.hotwj.platform.operationManagement.smsPackagePurchase.domain.bo.HotSmsPackagePurchaseBo; +import com.hotwj.platform.operationManagement.smsPackagePurchase.domain.vo.HotSmsPackagePurchaseVo; +import com.hotwj.platform.operationManagement.smsPackagePurchase.service.IHotSmsPackagePurchaseService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 短信套餐购买记录 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/smsPackagePurchase") +@Tag(name = "短信套餐购买记录", description = "短信套餐购买记录管理") +public class HotSmsPackagePurchaseController extends BaseController { + + private final IHotSmsPackagePurchaseService hotSmsPackagePurchaseService; + + /** + * 查询短信套餐购买记录列表 + */ + //@SaCheckPermission("operationManagement:smsPackagePurchase:list") + @GetMapping("/list") + @Operation(summary = "分页查询短信套餐购买记录列表") + public TableDataInfo list(HotSmsPackagePurchaseBo bo, PageQuery pageQuery) { + return hotSmsPackagePurchaseService.queryPageList(bo, pageQuery); + } + + /** + * 导出短信套餐购买记录列表 + */ + //@SaCheckPermission("operationManagement:smsPackagePurchase:export") + @Log(title = "短信套餐购买记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出短信套餐购买记录列表") + public void export(HotSmsPackagePurchaseBo bo, HttpServletResponse response) { + List list = hotSmsPackagePurchaseService.queryList(bo); + ExcelUtil.exportExcel(list, "短信套餐购买记录", HotSmsPackagePurchaseVo.class, response); + } + + /** + * 获取短信套餐购买记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("operationManagement:smsPackagePurchase:query") + @GetMapping("/{id}") + @Operation(summary = "获取短信套餐购买记录详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotSmsPackagePurchaseService.queryById(id)); + } + + /** + * 新增短信套餐购买记录 + */ + //@SaCheckPermission("operationManagement:smsPackagePurchase:add") + @Log(title = "短信套餐购买记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增短信套餐购买记录") + public R add(@Validated(AddGroup.class) @RequestBody HotSmsPackagePurchaseBo bo) { + return toAjax(hotSmsPackagePurchaseService.insertByBo(bo)); + } + + /** + * 修改短信套餐购买记录 + */ + //@SaCheckPermission("operationManagement:smsPackagePurchase:edit") + @Log(title = "短信套餐购买记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改短信套餐购买记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotSmsPackagePurchaseBo bo) { + return toAjax(hotSmsPackagePurchaseService.updateByBo(bo)); + } + + /** + * 删除短信套餐购买记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("operationManagement:smsPackagePurchase:remove") + @Log(title = "短信套餐购买记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除短信套餐购买记录") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotSmsPackagePurchaseService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/domain/HotSmsPackagePurchase.java b/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/domain/HotSmsPackagePurchase.java new file mode 100644 index 0000000..278a8b9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/domain/HotSmsPackagePurchase.java @@ -0,0 +1,131 @@ +package com.hotwj.platform.operationManagement.smsPackagePurchase.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 短信套餐购买记录对象 hot_sms_package_purchase + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_sms_package_purchase") +public class HotSmsPackagePurchase extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + private Long accountId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 充值企业ID + */ + private Long consumerCompanyId; + + /** + * 充值企业名称 + */ + private String consumerCompanyName; + + /** + * 订单管理表ID(逻辑外键) + */ + private Long orderId; + + /** + * 订单号 + */ + private String orderNo; + + /** + * 短信套餐ID + */ + private Long packageId; + + /** + * 短信套餐名称(快照) + */ + private String packageName; + + /** + * 短信条数 + */ + private Long smsCount; + + /** + * 单条单价(元) + */ + private Long unitPrice; + + /** + * 购买数量 + */ + private Long quantity; + + /** + * 总金额(元) + */ + private Long totalAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 4=余额 + */ + private Long payMethod; + + /** + * 支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭 + */ + private Long payStatus; + + /** + * 下单时间 + */ + private Date orderCreateTime; + + /** + * 支付时间 + */ + private Date payTime; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/domain/bo/HotSmsPackagePurchaseBo.java b/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/domain/bo/HotSmsPackagePurchaseBo.java new file mode 100644 index 0000000..71847c9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/domain/bo/HotSmsPackagePurchaseBo.java @@ -0,0 +1,129 @@ +package com.hotwj.platform.operationManagement.smsPackagePurchase.domain.bo; + +import com.hotwj.platform.operationManagement.smsPackagePurchase.domain.HotSmsPackagePurchase; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 短信套餐购买记录业务对象 hot_sms_package_purchase + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotSmsPackagePurchase.class, reverseConvertGenerate = false) +public class HotSmsPackagePurchaseBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + private Long accountId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 充值企业ID + */ + private Long consumerCompanyId; + + /** + * 充值企业名称 + */ + private String consumerCompanyName; + + /** + * 订单管理表ID(逻辑外键) + */ + private Long orderId; + + /** + * 订单号 + */ + private String orderNo; + + /** + * 短信套餐ID + */ + private Long packageId; + + /** + * 短信套餐名称(快照) + */ + private String packageName; + + /** + * 短信条数 + */ + private Long smsCount; + + /** + * 单条单价(元) + */ + private Long unitPrice; + + /** + * 购买数量 + */ + private Long quantity; + + /** + * 总金额(元) + */ + private Long totalAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 4=余额 + */ + private Long payMethod; + + /** + * 支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭 + */ + private Long payStatus; + + /** + * 下单时间 + */ + private Date orderCreateTime; + + /** + * 支付时间 + */ + private Date payTime; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/domain/vo/HotSmsPackagePurchaseVo.java b/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/domain/vo/HotSmsPackagePurchaseVo.java new file mode 100644 index 0000000..33f0f1c --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/domain/vo/HotSmsPackagePurchaseVo.java @@ -0,0 +1,172 @@ +package com.hotwj.platform.operationManagement.smsPackagePurchase.domain.vo; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.hotwj.platform.operationManagement.smsPackagePurchase.domain.HotSmsPackagePurchase; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 短信套餐购买记录视图对象 hot_sms_package_purchase + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotSmsPackagePurchase.class) +public class HotSmsPackagePurchaseVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 账户ID(逻辑外键) + */ + @ExcelProperty(value = "账户ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逻=辑外键") + private Long accountId; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户ID") + private Long userId; + + /** + * 充值企业ID + */ + @ExcelProperty(value = "充值企业ID") + private Long consumerCompanyId; + + /** + * 充值企业名称 + */ + @ExcelProperty(value = "充值企业名称") + private String consumerCompanyName; + + /** + * 订单管理表ID(逻辑外键) + */ + @ExcelProperty(value = "订单管理表ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逻=辑外键") + private Long orderId; + + /** + * 订单号 + */ + @ExcelProperty(value = "订单号") + private String orderNo; + + /** + * 短信套餐ID + */ + @ExcelProperty(value = "短信套餐ID") + private Long packageId; + + /** + * 短信套餐名称(快照) + */ + @ExcelProperty(value = "短信套餐名称", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "快=照") + private String packageName; + + /** + * 短信条数 + */ + @ExcelProperty(value = "短信条数") + private Long smsCount; + + /** + * 单条单价(元) + */ + @ExcelProperty(value = "单条单价", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long unitPrice; + + /** + * 购买数量 + */ + @ExcelProperty(value = "购买数量") + private Long quantity; + + /** + * 总金额(元) + */ + @ExcelProperty(value = "总金额", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long totalAmount; + + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 4=余额 + */ + @ExcelProperty(value = "支付方式:1=微信 2=支付宝 3=银行卡 4=余额") + private Long payMethod; + + /** + * 支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭 + */ + @ExcelProperty(value = "支付状态:1=待支付 2=已支付 3=已退款 4=已取消 5=已关闭") + private Long payStatus; + + /** + * 下单时间 + */ + @ExcelProperty(value = "下单时间") + private Date orderCreateTime; + + /** + * 支付时间 + */ + @ExcelProperty(value = "支付时间") + private Date payTime; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/mapper/HotSmsPackagePurchaseMapper.java b/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/mapper/HotSmsPackagePurchaseMapper.java new file mode 100644 index 0000000..2c4cfeb --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/mapper/HotSmsPackagePurchaseMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.smsPackagePurchase.mapper; + +import com.hotwj.platform.operationManagement.smsPackagePurchase.domain.HotSmsPackagePurchase; +import com.hotwj.platform.operationManagement.smsPackagePurchase.domain.vo.HotSmsPackagePurchaseVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 短信套餐购买记录Mapper接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Mapper +public interface HotSmsPackagePurchaseMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/service/IHotSmsPackagePurchaseService.java b/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/service/IHotSmsPackagePurchaseService.java new file mode 100644 index 0000000..71a634a --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/service/IHotSmsPackagePurchaseService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.smsPackagePurchase.service; + +import com.hotwj.platform.operationManagement.smsPackagePurchase.domain.vo.HotSmsPackagePurchaseVo; +import com.hotwj.platform.operationManagement.smsPackagePurchase.domain.bo.HotSmsPackagePurchaseBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 短信套餐购买记录Service接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +public interface IHotSmsPackagePurchaseService { + + /** + * 查询短信套餐购买记录 + * + * @param id 主键 + * @return 短信套餐购买记录 + */ + HotSmsPackagePurchaseVo queryById(Long id); + + /** + * 分页查询短信套餐购买记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 短信套餐购买记录分页列表 + */ + TableDataInfo queryPageList(HotSmsPackagePurchaseBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的短信套餐购买记录列表 + * + * @param bo 查询条件 + * @return 短信套餐购买记录列表 + */ + List queryList(HotSmsPackagePurchaseBo bo); + + /** + * 新增短信套餐购买记录 + * + * @param bo 短信套餐购买记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotSmsPackagePurchaseBo bo); + + /** + * 修改短信套餐购买记录 + * + * @param bo 短信套餐购买记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotSmsPackagePurchaseBo bo); + + /** + * 校验并批量删除短信套餐购买记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/service/impl/HotSmsPackagePurchaseServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/service/impl/HotSmsPackagePurchaseServiceImpl.java new file mode 100644 index 0000000..1b53ee3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsPackagePurchase/service/impl/HotSmsPackagePurchaseServiceImpl.java @@ -0,0 +1,151 @@ +package com.hotwj.platform.operationManagement.smsPackagePurchase.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.hotwj.platform.operationManagement.smsPackagePurchase.domain.bo.HotSmsPackagePurchaseBo; +import com.hotwj.platform.operationManagement.smsPackagePurchase.domain.vo.HotSmsPackagePurchaseVo; +import com.hotwj.platform.operationManagement.smsPackagePurchase.domain.HotSmsPackagePurchase; +import com.hotwj.platform.operationManagement.smsPackagePurchase.mapper.HotSmsPackagePurchaseMapper; +import com.hotwj.platform.operationManagement.smsPackagePurchase.service.IHotSmsPackagePurchaseService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 短信套餐购买记录Service业务层处理 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotSmsPackagePurchaseServiceImpl implements IHotSmsPackagePurchaseService { + + private final HotSmsPackagePurchaseMapper baseMapper; + + /** + * 查询短信套餐购买记录 + * + * @param id 主键 + * @return 短信套餐购买记录 + */ + @Override + public HotSmsPackagePurchaseVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询短信套餐购买记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 短信套餐购买记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotSmsPackagePurchaseBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的短信套餐购买记录列表 + * + * @param bo 查询条件 + * @return 短信套餐购买记录列表 + */ + @Override + public List queryList(HotSmsPackagePurchaseBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotSmsPackagePurchaseBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotSmsPackagePurchase::getId); + lqw.eq(bo.getCompanyId() != null, HotSmsPackagePurchase::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getAccountId() != null, HotSmsPackagePurchase::getAccountId, bo.getAccountId()); + lqw.eq(bo.getUserId() != null, HotSmsPackagePurchase::getUserId, bo.getUserId()); + lqw.eq(bo.getConsumerCompanyId() != null, HotSmsPackagePurchase::getConsumerCompanyId, bo.getConsumerCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getConsumerCompanyName()), HotSmsPackagePurchase::getConsumerCompanyName, bo.getConsumerCompanyName()); + lqw.eq(bo.getOrderId() != null, HotSmsPackagePurchase::getOrderId, bo.getOrderId()); + lqw.eq(StringUtils.isNotBlank(bo.getOrderNo()), HotSmsPackagePurchase::getOrderNo, bo.getOrderNo()); + lqw.eq(bo.getPackageId() != null, HotSmsPackagePurchase::getPackageId, bo.getPackageId()); + lqw.like(StringUtils.isNotBlank(bo.getPackageName()), HotSmsPackagePurchase::getPackageName, bo.getPackageName()); + lqw.eq(bo.getSmsCount() != null, HotSmsPackagePurchase::getSmsCount, bo.getSmsCount()); + lqw.eq(bo.getUnitPrice() != null, HotSmsPackagePurchase::getUnitPrice, bo.getUnitPrice()); + lqw.eq(bo.getQuantity() != null, HotSmsPackagePurchase::getQuantity, bo.getQuantity()); + lqw.eq(bo.getTotalAmount() != null, HotSmsPackagePurchase::getTotalAmount, bo.getTotalAmount()); + lqw.eq(bo.getPayMethod() != null, HotSmsPackagePurchase::getPayMethod, bo.getPayMethod()); + lqw.eq(bo.getPayStatus() != null, HotSmsPackagePurchase::getPayStatus, bo.getPayStatus()); + lqw.eq(bo.getOrderCreateTime() != null, HotSmsPackagePurchase::getOrderCreateTime, bo.getOrderCreateTime()); + lqw.eq(bo.getPayTime() != null, HotSmsPackagePurchase::getPayTime, bo.getPayTime()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotSmsPackagePurchase::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotSmsPackagePurchase::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotSmsPackagePurchase::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增短信套餐购买记录 + * + * @param bo 短信套餐购买记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotSmsPackagePurchaseBo bo) { + HotSmsPackagePurchase add = MapstructUtils.convert(bo, HotSmsPackagePurchase.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改短信套餐购买记录 + * + * @param bo 短信套餐购买记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotSmsPackagePurchaseBo bo) { + HotSmsPackagePurchase update = MapstructUtils.convert(bo, HotSmsPackagePurchase.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotSmsPackagePurchase entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除短信套餐购买记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/controller/HotSmsRechargeRecordController.java b/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/controller/HotSmsRechargeRecordController.java new file mode 100644 index 0000000..d86f012 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/controller/HotSmsRechargeRecordController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.operationManagement.smsRechargeRecord.controller; + +import com.hotwj.platform.operationManagement.smsRechargeRecord.domain.bo.HotSmsRechargeRecordBo; +import com.hotwj.platform.operationManagement.smsRechargeRecord.domain.vo.HotSmsRechargeRecordVo; +import com.hotwj.platform.operationManagement.smsRechargeRecord.service.IHotSmsRechargeRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 短信充值记录 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/smsRechargeRecord") +@Tag(name = "短信充值记录", description = "短信充值记录管理") +public class HotSmsRechargeRecordController extends BaseController { + + private final IHotSmsRechargeRecordService hotSmsRechargeRecordService; + + /** + * 查询短信充值记录列表 + */ + //@SaCheckPermission("operationManagement:smsRechargeRecord:list") + @GetMapping("/list") + @Operation(summary = "分页查询短信充值记录列表") + public TableDataInfo list(HotSmsRechargeRecordBo bo, PageQuery pageQuery) { + return hotSmsRechargeRecordService.queryPageList(bo, pageQuery); + } + + /** + * 导出短信充值记录列表 + */ + //@SaCheckPermission("operationManagement:smsRechargeRecord:export") + @Log(title = "短信充值记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出短信充值记录列表") + public void export(HotSmsRechargeRecordBo bo, HttpServletResponse response) { + List list = hotSmsRechargeRecordService.queryList(bo); + ExcelUtil.exportExcel(list, "短信充值记录", HotSmsRechargeRecordVo.class, response); + } + + /** + * 获取短信充值记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("operationManagement:smsRechargeRecord:query") + @GetMapping("/{id}") + @Operation(summary = "获取短信充值记录详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotSmsRechargeRecordService.queryById(id)); + } + + /** + * 新增短信充值记录 + */ + //@SaCheckPermission("operationManagement:smsRechargeRecord:add") + @Log(title = "短信充值记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增短信充值记录") + public R add(@Validated(AddGroup.class) @RequestBody HotSmsRechargeRecordBo bo) { + return toAjax(hotSmsRechargeRecordService.insertByBo(bo)); + } + + /** + * 修改短信充值记录 + */ + //@SaCheckPermission("operationManagement:smsRechargeRecord:edit") + @Log(title = "短信充值记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改短信充值记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotSmsRechargeRecordBo bo) { + return toAjax(hotSmsRechargeRecordService.updateByBo(bo)); + } + + /** + * 删除短信充值记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("operationManagement:smsRechargeRecord:remove") + @Log(title = "短信充值记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除短信充值记录") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotSmsRechargeRecordService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/domain/HotSmsRechargeRecord.java b/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/domain/HotSmsRechargeRecord.java new file mode 100644 index 0000000..496d36c --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/domain/HotSmsRechargeRecord.java @@ -0,0 +1,106 @@ +package com.hotwj.platform.operationManagement.smsRechargeRecord.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 短信充值记录对象 hot_sms_recharge_record + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_sms_recharge_record") +public class HotSmsRechargeRecord extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 充值企业ID + */ + private Long enterpriseId; + + /** + * 充值企业 + */ + private String enterpriseName; + + /** + * 短信购买记录ID(逻辑外键) + */ + private Long rechargeOrderId; + + /** + * 充值订单号 + */ + private String rechargeOrderNo; + + /** + * 充值时间 + */ + private Date rechargeTime; + + /** + * 充值金额(元) + */ + private Long rechargeAmount; + + /** + * 充值条数 + */ + private Long rechargeSmsCount; + + /** + * 过期时间 + */ + private Date expireTime; + + /** + * 操作人ID + */ + private Long operatorId; + + /** + * 操作人 + */ + private String operatorName; + + /** + * 状态:1=生效 2=已过期 3=已作废 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/domain/bo/HotSmsRechargeRecordBo.java b/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/domain/bo/HotSmsRechargeRecordBo.java new file mode 100644 index 0000000..72bcf8e --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/domain/bo/HotSmsRechargeRecordBo.java @@ -0,0 +1,104 @@ +package com.hotwj.platform.operationManagement.smsRechargeRecord.domain.bo; + +import com.hotwj.platform.operationManagement.smsRechargeRecord.domain.HotSmsRechargeRecord; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 短信充值记录业务对象 hot_sms_recharge_record + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotSmsRechargeRecord.class, reverseConvertGenerate = false) +public class HotSmsRechargeRecordBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 充值企业ID + */ + private Long enterpriseId; + + /** + * 充值企业 + */ + private String enterpriseName; + + /** + * 短信购买记录ID(逻辑外键) + */ + private Long rechargeOrderId; + + /** + * 充值订单号 + */ + private String rechargeOrderNo; + + /** + * 充值时间 + */ + private Date rechargeTime; + + /** + * 充值金额(元) + */ + private Long rechargeAmount; + + /** + * 充值条数 + */ + private Long rechargeSmsCount; + + /** + * 过期时间 + */ + private Date expireTime; + + /** + * 操作人ID + */ + private Long operatorId; + + /** + * 操作人 + */ + private String operatorName; + + /** + * 状态:1=生效 2=已过期 3=已作废 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/domain/vo/HotSmsRechargeRecordVo.java b/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/domain/vo/HotSmsRechargeRecordVo.java new file mode 100644 index 0000000..4a686ff --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/domain/vo/HotSmsRechargeRecordVo.java @@ -0,0 +1,139 @@ +package com.hotwj.platform.operationManagement.smsRechargeRecord.domain.vo; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.hotwj.platform.operationManagement.smsRechargeRecord.domain.HotSmsRechargeRecord; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 短信充值记录视图对象 hot_sms_recharge_record + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotSmsRechargeRecord.class) +public class HotSmsRechargeRecordVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 充值企业ID + */ + @ExcelProperty(value = "充值企业ID") + private Long enterpriseId; + + /** + * 充值企业 + */ + @ExcelProperty(value = "充值企业") + private String enterpriseName; + + /** + * 短信购买记录ID(逻辑外键) + */ + @ExcelProperty(value = "短信购买记录ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逻=辑外键") + private Long rechargeOrderId; + + /** + * 充值订单号 + */ + @ExcelProperty(value = "充值订单号") + private String rechargeOrderNo; + + /** + * 充值时间 + */ + @ExcelProperty(value = "充值时间") + private Date rechargeTime; + + /** + * 充值金额(元) + */ + @ExcelProperty(value = "充值金额", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "元=") + private Long rechargeAmount; + + /** + * 充值条数 + */ + @ExcelProperty(value = "充值条数") + private Long rechargeSmsCount; + + /** + * 过期时间 + */ + @ExcelProperty(value = "过期时间") + private Date expireTime; + + /** + * 操作人ID + */ + @ExcelProperty(value = "操作人ID") + private Long operatorId; + + /** + * 操作人 + */ + @ExcelProperty(value = "操作人") + private String operatorName; + + /** + * 状态:1=生效 2=已过期 3=已作废 + */ + @ExcelProperty(value = "状态:1=生效 2=已过期 3=已作废") + private Long status; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/mapper/HotSmsRechargeRecordMapper.java b/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/mapper/HotSmsRechargeRecordMapper.java new file mode 100644 index 0000000..a02e407 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/mapper/HotSmsRechargeRecordMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.smsRechargeRecord.mapper; + +import com.hotwj.platform.operationManagement.smsRechargeRecord.domain.HotSmsRechargeRecord; +import com.hotwj.platform.operationManagement.smsRechargeRecord.domain.vo.HotSmsRechargeRecordVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 短信充值记录Mapper接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Mapper +public interface HotSmsRechargeRecordMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/service/IHotSmsRechargeRecordService.java b/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/service/IHotSmsRechargeRecordService.java new file mode 100644 index 0000000..614d262 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/service/IHotSmsRechargeRecordService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.smsRechargeRecord.service; + +import com.hotwj.platform.operationManagement.smsRechargeRecord.domain.vo.HotSmsRechargeRecordVo; +import com.hotwj.platform.operationManagement.smsRechargeRecord.domain.bo.HotSmsRechargeRecordBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 短信充值记录Service接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +public interface IHotSmsRechargeRecordService { + + /** + * 查询短信充值记录 + * + * @param id 主键 + * @return 短信充值记录 + */ + HotSmsRechargeRecordVo queryById(Long id); + + /** + * 分页查询短信充值记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 短信充值记录分页列表 + */ + TableDataInfo queryPageList(HotSmsRechargeRecordBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的短信充值记录列表 + * + * @param bo 查询条件 + * @return 短信充值记录列表 + */ + List queryList(HotSmsRechargeRecordBo bo); + + /** + * 新增短信充值记录 + * + * @param bo 短信充值记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotSmsRechargeRecordBo bo); + + /** + * 修改短信充值记录 + * + * @param bo 短信充值记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotSmsRechargeRecordBo bo); + + /** + * 校验并批量删除短信充值记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/service/impl/HotSmsRechargeRecordServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/service/impl/HotSmsRechargeRecordServiceImpl.java new file mode 100644 index 0000000..eac37ec --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsRechargeRecord/service/impl/HotSmsRechargeRecordServiceImpl.java @@ -0,0 +1,146 @@ +package com.hotwj.platform.operationManagement.smsRechargeRecord.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.hotwj.platform.operationManagement.smsRechargeRecord.domain.bo.HotSmsRechargeRecordBo; +import com.hotwj.platform.operationManagement.smsRechargeRecord.domain.vo.HotSmsRechargeRecordVo; +import com.hotwj.platform.operationManagement.smsRechargeRecord.domain.HotSmsRechargeRecord; +import com.hotwj.platform.operationManagement.smsRechargeRecord.mapper.HotSmsRechargeRecordMapper; +import com.hotwj.platform.operationManagement.smsRechargeRecord.service.IHotSmsRechargeRecordService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 短信充值记录Service业务层处理 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotSmsRechargeRecordServiceImpl implements IHotSmsRechargeRecordService { + + private final HotSmsRechargeRecordMapper baseMapper; + + /** + * 查询短信充值记录 + * + * @param id 主键 + * @return 短信充值记录 + */ + @Override + public HotSmsRechargeRecordVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询短信充值记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 短信充值记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotSmsRechargeRecordBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的短信充值记录列表 + * + * @param bo 查询条件 + * @return 短信充值记录列表 + */ + @Override + public List queryList(HotSmsRechargeRecordBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotSmsRechargeRecordBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotSmsRechargeRecord::getId); + lqw.eq(bo.getCompanyId() != null, HotSmsRechargeRecord::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getEnterpriseId() != null, HotSmsRechargeRecord::getEnterpriseId, bo.getEnterpriseId()); + lqw.like(StringUtils.isNotBlank(bo.getEnterpriseName()), HotSmsRechargeRecord::getEnterpriseName, bo.getEnterpriseName()); + lqw.eq(bo.getRechargeOrderId() != null, HotSmsRechargeRecord::getRechargeOrderId, bo.getRechargeOrderId()); + lqw.eq(StringUtils.isNotBlank(bo.getRechargeOrderNo()), HotSmsRechargeRecord::getRechargeOrderNo, bo.getRechargeOrderNo()); + lqw.eq(bo.getRechargeTime() != null, HotSmsRechargeRecord::getRechargeTime, bo.getRechargeTime()); + lqw.eq(bo.getRechargeAmount() != null, HotSmsRechargeRecord::getRechargeAmount, bo.getRechargeAmount()); + lqw.eq(bo.getRechargeSmsCount() != null, HotSmsRechargeRecord::getRechargeSmsCount, bo.getRechargeSmsCount()); + lqw.eq(bo.getExpireTime() != null, HotSmsRechargeRecord::getExpireTime, bo.getExpireTime()); + lqw.eq(bo.getOperatorId() != null, HotSmsRechargeRecord::getOperatorId, bo.getOperatorId()); + lqw.like(StringUtils.isNotBlank(bo.getOperatorName()), HotSmsRechargeRecord::getOperatorName, bo.getOperatorName()); + lqw.eq(bo.getStatus() != null, HotSmsRechargeRecord::getStatus, bo.getStatus()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotSmsRechargeRecord::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotSmsRechargeRecord::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotSmsRechargeRecord::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增短信充值记录 + * + * @param bo 短信充值记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotSmsRechargeRecordBo bo) { + HotSmsRechargeRecord add = MapstructUtils.convert(bo, HotSmsRechargeRecord.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改短信充值记录 + * + * @param bo 短信充值记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotSmsRechargeRecordBo bo) { + HotSmsRechargeRecord update = MapstructUtils.convert(bo, HotSmsRechargeRecord.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotSmsRechargeRecord entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除短信充值记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/controller/HotSmsSendDetailController.java b/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/controller/HotSmsSendDetailController.java new file mode 100644 index 0000000..0d7cdc5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/controller/HotSmsSendDetailController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.operationManagement.smsSendDetail.controller; + +import com.hotwj.platform.operationManagement.smsSendDetail.domain.bo.HotSmsSendDetailBo; +import com.hotwj.platform.operationManagement.smsSendDetail.domain.vo.HotSmsSendDetailVo; +import com.hotwj.platform.operationManagement.smsSendDetail.service.IHotSmsSendDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 短信发送明细 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/smsSendDetail") +@Tag(name = "短信发送明细", description = "短信发送明细管理") +public class HotSmsSendDetailController extends BaseController { + + private final IHotSmsSendDetailService hotSmsSendDetailService; + + /** + * 查询短信发送明细列表 + */ + //@SaCheckPermission("operationManagement:smsSendDetail:list") + @GetMapping("/list") + @Operation(summary = "分页查询短信发送明细列表") + public TableDataInfo list(HotSmsSendDetailBo bo, PageQuery pageQuery) { + return hotSmsSendDetailService.queryPageList(bo, pageQuery); + } + + /** + * 导出短信发送明细列表 + */ + //@SaCheckPermission("operationManagement:smsSendDetail:export") + @Log(title = "短信发送明细", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出短信发送明细列表") + public void export(HotSmsSendDetailBo bo, HttpServletResponse response) { + List list = hotSmsSendDetailService.queryList(bo); + ExcelUtil.exportExcel(list, "短信发送明细", HotSmsSendDetailVo.class, response); + } + + /** + * 获取短信发送明细详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("operationManagement:smsSendDetail:query") + @GetMapping("/{id}") + @Operation(summary = "获取短信发送明细详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotSmsSendDetailService.queryById(id)); + } + + /** + * 新增短信发送明细 + */ + //@SaCheckPermission("operationManagement:smsSendDetail:add") + @Log(title = "短信发送明细", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增短信发送明细") + public R add(@Validated(AddGroup.class) @RequestBody HotSmsSendDetailBo bo) { + return toAjax(hotSmsSendDetailService.insertByBo(bo)); + } + + /** + * 修改短信发送明细 + */ + //@SaCheckPermission("operationManagement:smsSendDetail:edit") + @Log(title = "短信发送明细", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改短信发送明细") + public R edit(@Validated(EditGroup.class) @RequestBody HotSmsSendDetailBo bo) { + return toAjax(hotSmsSendDetailService.updateByBo(bo)); + } + + /** + * 删除短信发送明细 + * + * @param ids 主键串 + */ + //@SaCheckPermission("operationManagement:smsSendDetail:remove") + @Log(title = "短信发送明细", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除短信发送明细") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotSmsSendDetailService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/domain/HotSmsSendDetail.java b/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/domain/HotSmsSendDetail.java new file mode 100644 index 0000000..604420f --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/domain/HotSmsSendDetail.java @@ -0,0 +1,136 @@ +package com.hotwj.platform.operationManagement.smsSendDetail.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 短信发送明细对象 hot_sms_send_detail + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_sms_send_detail") +public class HotSmsSendDetail extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 企业ID + */ + private Long enterpriseId; + + /** + * 企业名称 + */ + private String enterpriseName; + + /** + * 所属类型(如驾驶员管理) + */ + private String bizType; + + /** + * 短信类型 + */ + private String smsType; + + /** + * 接收人ID + */ + private Long receiverId; + + /** + * 姓名 + */ + private String receiverName; + + /** + * 手机号 + */ + private String mobile; + + /** + * 岗位/接收人类型 + */ + private String receiverRole; + + /** + * 通知内容 + */ + private String noticeContent; + + /** + * 通知时间 + */ + private Date notifyTime; + + /** + * 通知状态:1=待发送 2=已发送 3=发送失败 + */ + private Long notifyStatus; + + /** + * 是否收到:0=否 1=是 + */ + private Long isReceived; + + /** + * 收到时间 + */ + private Date receiveTime; + + /** + * 发送渠道:1=短信 2=站内信 + */ + private Long sendChannel; + + /** + * 消耗短信条数 + */ + private Long smsCount; + + /** + * 计费条数 + */ + private Long billableCount; + + /** + * 供应商消息ID + */ + private String providerMsgId; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/domain/bo/HotSmsSendDetailBo.java b/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/domain/bo/HotSmsSendDetailBo.java new file mode 100644 index 0000000..706727e --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/domain/bo/HotSmsSendDetailBo.java @@ -0,0 +1,134 @@ +package com.hotwj.platform.operationManagement.smsSendDetail.domain.bo; + +import com.hotwj.platform.operationManagement.smsSendDetail.domain.HotSmsSendDetail; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 短信发送明细业务对象 hot_sms_send_detail + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotSmsSendDetail.class, reverseConvertGenerate = false) +public class HotSmsSendDetailBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 企业ID + */ + private Long enterpriseId; + + /** + * 企业名称 + */ + private String enterpriseName; + + /** + * 所属类型(如驾驶员管理) + */ + private String bizType; + + /** + * 短信类型 + */ + private String smsType; + + /** + * 接收人ID + */ + private Long receiverId; + + /** + * 姓名 + */ + private String receiverName; + + /** + * 手机号 + */ + private String mobile; + + /** + * 岗位/接收人类型 + */ + private String receiverRole; + + /** + * 通知内容 + */ + private String noticeContent; + + /** + * 通知时间 + */ + private Date notifyTime; + + /** + * 通知状态:1=待发送 2=已发送 3=发送失败 + */ + private Long notifyStatus; + + /** + * 是否收到:0=否 1=是 + */ + private Long isReceived; + + /** + * 收到时间 + */ + private Date receiveTime; + + /** + * 发送渠道:1=短信 2=站内信 + */ + private Long sendChannel; + + /** + * 消耗短信条数 + */ + private Long smsCount; + + /** + * 计费条数 + */ + private Long billableCount; + + /** + * 供应商消息ID + */ + private String providerMsgId; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/domain/vo/HotSmsSendDetailVo.java b/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/domain/vo/HotSmsSendDetailVo.java new file mode 100644 index 0000000..7f6fb49 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/domain/vo/HotSmsSendDetailVo.java @@ -0,0 +1,174 @@ +package com.hotwj.platform.operationManagement.smsSendDetail.domain.vo; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.hotwj.platform.operationManagement.smsSendDetail.domain.HotSmsSendDetail; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 短信发送明细视图对象 hot_sms_send_detail + * + * @author shihongwei + * @date 2026-04-28 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotSmsSendDetail.class) +public class HotSmsSendDetailVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 企业ID + */ + @ExcelProperty(value = "企业ID") + private Long enterpriseId; + + /** + * 企业名称 + */ + @ExcelProperty(value = "企业名称") + private String enterpriseName; + + /** + * 所属类型(如驾驶员管理) + */ + @ExcelProperty(value = "所属类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "如=驾驶员管理") + private String bizType; + + /** + * 短信类型 + */ + @ExcelProperty(value = "短信类型") + private String smsType; + + /** + * 接收人ID + */ + @ExcelProperty(value = "接收人ID") + private Long receiverId; + + /** + * 姓名 + */ + @ExcelProperty(value = "姓名") + private String receiverName; + + /** + * 手机号 + */ + @ExcelProperty(value = "手机号") + private String mobile; + + /** + * 岗位/接收人类型 + */ + @ExcelProperty(value = "岗位/接收人类型") + private String receiverRole; + + /** + * 通知内容 + */ + @ExcelProperty(value = "通知内容") + private String noticeContent; + + /** + * 通知时间 + */ + @ExcelProperty(value = "通知时间") + private Date notifyTime; + + /** + * 通知状态:1=待发送 2=已发送 3=发送失败 + */ + @ExcelProperty(value = "通知状态:1=待发送 2=已发送 3=发送失败") + private Long notifyStatus; + + /** + * 是否收到:0=否 1=是 + */ + @ExcelProperty(value = "是否收到:0=否 1=是") + private Long isReceived; + + /** + * 收到时间 + */ + @ExcelProperty(value = "收到时间") + private Date receiveTime; + + /** + * 发送渠道:1=短信 2=站内信 + */ + @ExcelProperty(value = "发送渠道:1=短信 2=站内信") + private Long sendChannel; + + /** + * 消耗短信条数 + */ + @ExcelProperty(value = "消耗短信条数") + private Long smsCount; + + /** + * 计费条数 + */ + @ExcelProperty(value = "计费条数") + private Long billableCount; + + /** + * 供应商消息ID + */ + @ExcelProperty(value = "供应商消息ID") + private String providerMsgId; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/mapper/HotSmsSendDetailMapper.java b/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/mapper/HotSmsSendDetailMapper.java new file mode 100644 index 0000000..e5b2003 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/mapper/HotSmsSendDetailMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.smsSendDetail.mapper; + +import com.hotwj.platform.operationManagement.smsSendDetail.domain.HotSmsSendDetail; +import com.hotwj.platform.operationManagement.smsSendDetail.domain.vo.HotSmsSendDetailVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 短信发送明细Mapper接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Mapper +public interface HotSmsSendDetailMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/service/IHotSmsSendDetailService.java b/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/service/IHotSmsSendDetailService.java new file mode 100644 index 0000000..a42315b --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/service/IHotSmsSendDetailService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.smsSendDetail.service; + +import com.hotwj.platform.operationManagement.smsSendDetail.domain.vo.HotSmsSendDetailVo; +import com.hotwj.platform.operationManagement.smsSendDetail.domain.bo.HotSmsSendDetailBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 短信发送明细Service接口 + * + * @author shihongwei + * @date 2026-04-28 + */ +public interface IHotSmsSendDetailService { + + /** + * 查询短信发送明细 + * + * @param id 主键 + * @return 短信发送明细 + */ + HotSmsSendDetailVo queryById(Long id); + + /** + * 分页查询短信发送明细列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 短信发送明细分页列表 + */ + TableDataInfo queryPageList(HotSmsSendDetailBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的短信发送明细列表 + * + * @param bo 查询条件 + * @return 短信发送明细列表 + */ + List queryList(HotSmsSendDetailBo bo); + + /** + * 新增短信发送明细 + * + * @param bo 短信发送明细 + * @return 是否新增成功 + */ + Boolean insertByBo(HotSmsSendDetailBo bo); + + /** + * 修改短信发送明细 + * + * @param bo 短信发送明细 + * @return 是否修改成功 + */ + Boolean updateByBo(HotSmsSendDetailBo bo); + + /** + * 校验并批量删除短信发送明细信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/service/impl/HotSmsSendDetailServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/service/impl/HotSmsSendDetailServiceImpl.java new file mode 100644 index 0000000..6668340 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/smsSendDetail/service/impl/HotSmsSendDetailServiceImpl.java @@ -0,0 +1,152 @@ +package com.hotwj.platform.operationManagement.smsSendDetail.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import com.hotwj.platform.operationManagement.smsSendDetail.domain.bo.HotSmsSendDetailBo; +import com.hotwj.platform.operationManagement.smsSendDetail.domain.vo.HotSmsSendDetailVo; +import com.hotwj.platform.operationManagement.smsSendDetail.domain.HotSmsSendDetail; +import com.hotwj.platform.operationManagement.smsSendDetail.mapper.HotSmsSendDetailMapper; +import com.hotwj.platform.operationManagement.smsSendDetail.service.IHotSmsSendDetailService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 短信发送明细Service业务层处理 + * + * @author shihongwei + * @date 2026-04-28 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotSmsSendDetailServiceImpl implements IHotSmsSendDetailService { + + private final HotSmsSendDetailMapper baseMapper; + + /** + * 查询短信发送明细 + * + * @param id 主键 + * @return 短信发送明细 + */ + @Override + public HotSmsSendDetailVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询短信发送明细列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 短信发送明细分页列表 + */ + @Override + public TableDataInfo queryPageList(HotSmsSendDetailBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的短信发送明细列表 + * + * @param bo 查询条件 + * @return 短信发送明细列表 + */ + @Override + public List queryList(HotSmsSendDetailBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotSmsSendDetailBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotSmsSendDetail::getId); + lqw.eq(bo.getCompanyId() != null, HotSmsSendDetail::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getEnterpriseId() != null, HotSmsSendDetail::getEnterpriseId, bo.getEnterpriseId()); + lqw.like(StringUtils.isNotBlank(bo.getEnterpriseName()), HotSmsSendDetail::getEnterpriseName, bo.getEnterpriseName()); + lqw.eq(StringUtils.isNotBlank(bo.getBizType()), HotSmsSendDetail::getBizType, bo.getBizType()); + lqw.eq(StringUtils.isNotBlank(bo.getSmsType()), HotSmsSendDetail::getSmsType, bo.getSmsType()); + lqw.eq(bo.getReceiverId() != null, HotSmsSendDetail::getReceiverId, bo.getReceiverId()); + lqw.like(StringUtils.isNotBlank(bo.getReceiverName()), HotSmsSendDetail::getReceiverName, bo.getReceiverName()); + lqw.eq(StringUtils.isNotBlank(bo.getMobile()), HotSmsSendDetail::getMobile, bo.getMobile()); + lqw.eq(StringUtils.isNotBlank(bo.getReceiverRole()), HotSmsSendDetail::getReceiverRole, bo.getReceiverRole()); + lqw.eq(StringUtils.isNotBlank(bo.getNoticeContent()), HotSmsSendDetail::getNoticeContent, bo.getNoticeContent()); + lqw.eq(bo.getNotifyTime() != null, HotSmsSendDetail::getNotifyTime, bo.getNotifyTime()); + lqw.eq(bo.getNotifyStatus() != null, HotSmsSendDetail::getNotifyStatus, bo.getNotifyStatus()); + lqw.eq(bo.getIsReceived() != null, HotSmsSendDetail::getIsReceived, bo.getIsReceived()); + lqw.eq(bo.getReceiveTime() != null, HotSmsSendDetail::getReceiveTime, bo.getReceiveTime()); + lqw.eq(bo.getSendChannel() != null, HotSmsSendDetail::getSendChannel, bo.getSendChannel()); + lqw.eq(bo.getSmsCount() != null, HotSmsSendDetail::getSmsCount, bo.getSmsCount()); + lqw.eq(bo.getBillableCount() != null, HotSmsSendDetail::getBillableCount, bo.getBillableCount()); + lqw.eq(StringUtils.isNotBlank(bo.getProviderMsgId()), HotSmsSendDetail::getProviderMsgId, bo.getProviderMsgId()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotSmsSendDetail::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotSmsSendDetail::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotSmsSendDetail::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增短信发送明细 + * + * @param bo 短信发送明细 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotSmsSendDetailBo bo) { + HotSmsSendDetail add = MapstructUtils.convert(bo, HotSmsSendDetail.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改短信发送明细 + * + * @param bo 短信发送明细 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotSmsSendDetailBo bo) { + HotSmsSendDetail update = MapstructUtils.convert(bo, HotSmsSendDetail.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotSmsSendDetail entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除短信发送明细信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/controller/HotTrainingFeedbackController.java b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/controller/HotTrainingFeedbackController.java new file mode 100644 index 0000000..5cde240 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/controller/HotTrainingFeedbackController.java @@ -0,0 +1,118 @@ +package com.hotwj.platform.operationManagement.trainingFeedback.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.hotwj.platform.operationManagement.trainingFeedback.domain.bo.HotTrainingFeedbackBo; +import com.hotwj.platform.operationManagement.trainingFeedback.domain.vo.HotTrainingFeedbackVo; +import com.hotwj.platform.operationManagement.trainingFeedback.service.IHotTrainingFeedbackService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 培训反馈主表 + * + * @author shihongwei + * @date 2026-05-07 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/trainingFeedback") +@Tag(name = "培训反馈主表", description = "培训反馈主表管理") +public class HotTrainingFeedbackController extends BaseController { + + private final IHotTrainingFeedbackService hotTrainingFeedbackService; + + /** + * 查询培训反馈主表列表 + */ + @SaCheckPermission("operationManagement:trainingFeedback:list") + @GetMapping("/list") + @Operation(summary = "分页查询培训反馈主表列表") + public TableDataInfo list(HotTrainingFeedbackBo bo, PageQuery pageQuery) { + return hotTrainingFeedbackService.queryPageList(bo, pageQuery); + } + + /** + * 导出培训反馈主表列表 + */ + @SaCheckPermission("operationManagement:trainingFeedback:export") + @Log(title = "培训反馈主表", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出培训反馈主表列表") + public void export(HotTrainingFeedbackBo bo, HttpServletResponse response) { + List list = hotTrainingFeedbackService.queryList(bo); + ExcelUtil.exportExcel(list, "培训反馈主表", HotTrainingFeedbackVo.class, response); + } + + /** + * 获取培训反馈主表详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("operationManagement:trainingFeedback:query") + @GetMapping("/{id}") + @Operation(summary = "获取培训反馈主表详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotTrainingFeedbackService.queryById(id)); + } + + /** + * 新增培训反馈主表 + */ + @SaCheckPermission("operationManagement:trainingFeedback:add") + @Log(title = "培训反馈主表", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增培训反馈主表") + public R add(@Validated(AddGroup.class) @RequestBody HotTrainingFeedbackBo bo) { + return toAjax(hotTrainingFeedbackService.insertByBo(bo)); + } + + /** + * 修改培训反馈主表 + */ + @SaCheckPermission("operationManagement:trainingFeedback:edit") + @Log(title = "培训反馈主表", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改培训反馈主表") + public R edit(@Validated(EditGroup.class) @RequestBody HotTrainingFeedbackBo bo) { + return toAjax(hotTrainingFeedbackService.updateByBo(bo)); + } + + /** + * 删除培训反馈主表 + * + * @param ids 主键串 + */ + @SaCheckPermission("operationManagement:trainingFeedback:remove") + @Log(title = "培训反馈主表", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除培训反馈主表") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotTrainingFeedbackService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/domain/HotTrainingFeedback.java b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/domain/HotTrainingFeedback.java new file mode 100644 index 0000000..0d953e9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/domain/HotTrainingFeedback.java @@ -0,0 +1,105 @@ +package com.hotwj.platform.operationManagement.trainingFeedback.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 培训反馈主表对象 hot_training_feedback + * + * @author shihongwei + * @date 2026-05-07 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_training_feedback") +public class HotTrainingFeedback extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 反馈单号 + */ + private String feedbackNo; + + /** + * 反馈人ID + */ + private Long feedbackUserId; + + /** + * 反馈人姓名 + */ + private String feedbackUserName; + + /** + * 反馈关键词 + */ + private String feedbackKeyword; + + /** + * 反馈内容 + */ + private String feedbackContent; + + /** + * 附件URL列表(逗号分隔,对应sys_oss) + */ + private String attachmentUrls; + + /** + * 反馈时间 + */ + private Date feedbackTime; + + /** + * 处理状态:1=待处理 2=处理中 3=已回复 4=已关闭 + */ + private Long status; + + /** + * 最新回复时间 + */ + private Date replyTime; + + /** + * 回复条数 + */ + private Long replyCount; + + /** + * 最新回复内容摘要 + */ + private String lastReplyContent; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/domain/bo/HotTrainingFeedbackBo.java b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/domain/bo/HotTrainingFeedbackBo.java new file mode 100644 index 0000000..258afe2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/domain/bo/HotTrainingFeedbackBo.java @@ -0,0 +1,101 @@ +package com.hotwj.platform.operationManagement.trainingFeedback.domain.bo; + +import com.hotwj.platform.operationManagement.trainingFeedback.domain.HotTrainingFeedback; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 培训反馈主表业务对象 hot_training_feedback + * + * @author shihongwei + * @date 2026-05-07 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotTrainingFeedback.class, reverseConvertGenerate = false) +public class HotTrainingFeedbackBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 反馈单号 + */ + private String feedbackNo; + + /** + * 反馈人ID + */ + private Long feedbackUserId; + + /** + * 反馈人姓名 + */ + private String feedbackUserName; + + /** + * 反馈关键词 + */ + private String feedbackKeyword; + + /** + * 反馈内容 + */ + private String feedbackContent; + + /** + * 附件URL列表(逗号分隔,对应sys_oss) + */ + private String attachmentUrls; + + /** + * 反馈时间 + */ + private Date feedbackTime; + + /** + * 处理状态:1=待处理 2=处理中 3=已回复 4=已关闭 + */ + private Long status; + + /** + * 最新回复时间 + */ + private Date replyTime; + + /** + * 回复条数 + */ + private Long replyCount; + + /** + * 最新回复内容摘要 + */ + private String lastReplyContent; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/domain/vo/HotTrainingFeedbackVo.java b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/domain/vo/HotTrainingFeedbackVo.java new file mode 100644 index 0000000..1c2b245 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/domain/vo/HotTrainingFeedbackVo.java @@ -0,0 +1,135 @@ +package com.hotwj.platform.operationManagement.trainingFeedback.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.operationManagement.trainingFeedback.domain.HotTrainingFeedback; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 培训反馈主表视图对象 hot_training_feedback + * + * @author shihongwei + * @date 2026-05-07 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotTrainingFeedback.class) +public class HotTrainingFeedbackVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 反馈单号 + */ + @ExcelProperty(value = "反馈单号") + private String feedbackNo; + + /** + * 反馈人ID + */ + @ExcelProperty(value = "反馈人ID") + private Long feedbackUserId; + + /** + * 反馈人姓名 + */ + @ExcelProperty(value = "反馈人姓名") + private String feedbackUserName; + + /** + * 反馈关键词 + */ + @ExcelProperty(value = "反馈关键词") + private String feedbackKeyword; + + /** + * 反馈内容 + */ + @ExcelProperty(value = "反馈内容") + private String feedbackContent; + + /** + * 附件URL列表(逗号分隔,对应sys_oss) + */ + @ExcelProperty(value = "附件URL列表", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逗=号分隔,对应sys_oss") + private String attachmentUrls; + + /** + * 反馈时间 + */ + @ExcelProperty(value = "反馈时间") + private Date feedbackTime; + + /** + * 处理状态:1=待处理 2=处理中 3=已回复 4=已关闭 + */ + @ExcelProperty(value = "处理状态:1=待处理 2=处理中 3=已回复 4=已关闭") + private Long status; + + /** + * 最新回复时间 + */ + @ExcelProperty(value = "最新回复时间") + private Date replyTime; + + /** + * 回复条数 + */ + @ExcelProperty(value = "回复条数") + private Long replyCount; + + /** + * 最新回复内容摘要 + */ + @ExcelProperty(value = "最新回复内容摘要") + private String lastReplyContent; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/mapper/HotTrainingFeedbackMapper.java b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/mapper/HotTrainingFeedbackMapper.java new file mode 100644 index 0000000..457d23f --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/mapper/HotTrainingFeedbackMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.trainingFeedback.mapper; + +import com.hotwj.platform.operationManagement.trainingFeedback.domain.HotTrainingFeedback; +import com.hotwj.platform.operationManagement.trainingFeedback.domain.vo.HotTrainingFeedbackVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 培训反馈主表Mapper接口 + * + * @author shihongwei + * @date 2026-05-07 + */ +@Mapper +public interface HotTrainingFeedbackMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/service/IHotTrainingFeedbackService.java b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/service/IHotTrainingFeedbackService.java new file mode 100644 index 0000000..70880c0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/service/IHotTrainingFeedbackService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.trainingFeedback.service; + +import com.hotwj.platform.operationManagement.trainingFeedback.domain.bo.HotTrainingFeedbackBo; +import com.hotwj.platform.operationManagement.trainingFeedback.domain.vo.HotTrainingFeedbackVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 培训反馈主表Service接口 + * + * @author shihongwei + * @date 2026-05-07 + */ +public interface IHotTrainingFeedbackService { + + /** + * 查询培训反馈主表 + * + * @param id 主键 + * @return 培训反馈主表 + */ + HotTrainingFeedbackVo queryById(Long id); + + /** + * 分页查询培训反馈主表列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训反馈主表分页列表 + */ + TableDataInfo queryPageList(HotTrainingFeedbackBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的培训反馈主表列表 + * + * @param bo 查询条件 + * @return 培训反馈主表列表 + */ + List queryList(HotTrainingFeedbackBo bo); + + /** + * 新增培训反馈主表 + * + * @param bo 培训反馈主表 + * @return 是否新增成功 + */ + Boolean insertByBo(HotTrainingFeedbackBo bo); + + /** + * 修改培训反馈主表 + * + * @param bo 培训反馈主表 + * @return 是否修改成功 + */ + Boolean updateByBo(HotTrainingFeedbackBo bo); + + /** + * 校验并批量删除培训反馈主表信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/service/impl/HotTrainingFeedbackServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/service/impl/HotTrainingFeedbackServiceImpl.java new file mode 100644 index 0000000..12905cb --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedback/service/impl/HotTrainingFeedbackServiceImpl.java @@ -0,0 +1,146 @@ +package com.hotwj.platform.operationManagement.trainingFeedback.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.operationManagement.trainingFeedback.domain.HotTrainingFeedback; +import com.hotwj.platform.operationManagement.trainingFeedback.domain.bo.HotTrainingFeedbackBo; +import com.hotwj.platform.operationManagement.trainingFeedback.domain.vo.HotTrainingFeedbackVo; +import com.hotwj.platform.operationManagement.trainingFeedback.mapper.HotTrainingFeedbackMapper; +import com.hotwj.platform.operationManagement.trainingFeedback.service.IHotTrainingFeedbackService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 培训反馈主表Service业务层处理 + * + * @author shihongwei + * @date 2026-05-07 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotTrainingFeedbackServiceImpl implements IHotTrainingFeedbackService { + + private final HotTrainingFeedbackMapper baseMapper; + + /** + * 查询培训反馈主表 + * + * @param id 主键 + * @return 培训反馈主表 + */ + @Override + public HotTrainingFeedbackVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询培训反馈主表列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训反馈主表分页列表 + */ + @Override + public TableDataInfo queryPageList(HotTrainingFeedbackBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的培训反馈主表列表 + * + * @param bo 查询条件 + * @return 培训反馈主表列表 + */ + @Override + public List queryList(HotTrainingFeedbackBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotTrainingFeedbackBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotTrainingFeedback::getId); + lqw.eq(bo.getCompanyId() != null, HotTrainingFeedback::getCompanyId, bo.getCompanyId()); + lqw.eq(StringUtils.isNotBlank(bo.getFeedbackNo()), HotTrainingFeedback::getFeedbackNo, bo.getFeedbackNo()); + lqw.eq(bo.getFeedbackUserId() != null, HotTrainingFeedback::getFeedbackUserId, bo.getFeedbackUserId()); + lqw.like(StringUtils.isNotBlank(bo.getFeedbackUserName()), HotTrainingFeedback::getFeedbackUserName, bo.getFeedbackUserName()); + lqw.eq(StringUtils.isNotBlank(bo.getFeedbackKeyword()), HotTrainingFeedback::getFeedbackKeyword, bo.getFeedbackKeyword()); + lqw.eq(StringUtils.isNotBlank(bo.getFeedbackContent()), HotTrainingFeedback::getFeedbackContent, bo.getFeedbackContent()); + lqw.eq(StringUtils.isNotBlank(bo.getAttachmentUrls()), HotTrainingFeedback::getAttachmentUrls, bo.getAttachmentUrls()); + lqw.eq(bo.getFeedbackTime() != null, HotTrainingFeedback::getFeedbackTime, bo.getFeedbackTime()); + lqw.eq(bo.getStatus() != null, HotTrainingFeedback::getStatus, bo.getStatus()); + lqw.eq(bo.getReplyTime() != null, HotTrainingFeedback::getReplyTime, bo.getReplyTime()); + lqw.eq(bo.getReplyCount() != null, HotTrainingFeedback::getReplyCount, bo.getReplyCount()); + lqw.eq(StringUtils.isNotBlank(bo.getLastReplyContent()), HotTrainingFeedback::getLastReplyContent, bo.getLastReplyContent()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotTrainingFeedback::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotTrainingFeedback::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotTrainingFeedback::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增培训反馈主表 + * + * @param bo 培训反馈主表 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotTrainingFeedbackBo bo) { + HotTrainingFeedback add = MapstructUtils.convert(bo, HotTrainingFeedback.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改培训反馈主表 + * + * @param bo 培训反馈主表 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotTrainingFeedbackBo bo) { + HotTrainingFeedback update = MapstructUtils.convert(bo, HotTrainingFeedback.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotTrainingFeedback entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除培训反馈主表信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/controller/HotTrainingFeedbackReplyController.java b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/controller/HotTrainingFeedbackReplyController.java new file mode 100644 index 0000000..a6fc1af --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/controller/HotTrainingFeedbackReplyController.java @@ -0,0 +1,118 @@ +package com.hotwj.platform.operationManagement.trainingFeedbackReply.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.hotwj.platform.operationManagement.trainingFeedbackReply.domain.bo.HotTrainingFeedbackReplyBo; +import com.hotwj.platform.operationManagement.trainingFeedbackReply.domain.vo.HotTrainingFeedbackReplyVo; +import com.hotwj.platform.operationManagement.trainingFeedbackReply.service.IHotTrainingFeedbackReplyService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 培训反馈回复 + * + * @author shihongwei + * @date 2026-05-07 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/operationManagement/trainingFeedbackReply") +@Tag(name = "培训反馈回复", description = "培训反馈回复管理") +public class HotTrainingFeedbackReplyController extends BaseController { + + private final IHotTrainingFeedbackReplyService hotTrainingFeedbackReplyService; + + /** + * 查询培训反馈回复列表 + */ + @SaCheckPermission("operationManagement:trainingFeedbackReply:list") + @GetMapping("/list") + @Operation(summary = "分页查询培训反馈回复列表") + public TableDataInfo list(HotTrainingFeedbackReplyBo bo, PageQuery pageQuery) { + return hotTrainingFeedbackReplyService.queryPageList(bo, pageQuery); + } + + /** + * 导出培训反馈回复列表 + */ + @SaCheckPermission("operationManagement:trainingFeedbackReply:export") + @Log(title = "培训反馈回复", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出培训反馈回复列表") + public void export(HotTrainingFeedbackReplyBo bo, HttpServletResponse response) { + List list = hotTrainingFeedbackReplyService.queryList(bo); + ExcelUtil.exportExcel(list, "培训反馈回复", HotTrainingFeedbackReplyVo.class, response); + } + + /** + * 获取培训反馈回复详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("operationManagement:trainingFeedbackReply:query") + @GetMapping("/{id}") + @Operation(summary = "获取培训反馈回复详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotTrainingFeedbackReplyService.queryById(id)); + } + + /** + * 新增培训反馈回复 + */ + @SaCheckPermission("operationManagement:trainingFeedbackReply:add") + @Log(title = "培训反馈回复", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增培训反馈回复") + public R add(@Validated(AddGroup.class) @RequestBody HotTrainingFeedbackReplyBo bo) { + return toAjax(hotTrainingFeedbackReplyService.insertByBo(bo)); + } + + /** + * 修改培训反馈回复 + */ + @SaCheckPermission("operationManagement:trainingFeedbackReply:edit") + @Log(title = "培训反馈回复", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改培训反馈回复") + public R edit(@Validated(EditGroup.class) @RequestBody HotTrainingFeedbackReplyBo bo) { + return toAjax(hotTrainingFeedbackReplyService.updateByBo(bo)); + } + + /** + * 删除培训反馈回复 + * + * @param ids 主键串 + */ + @SaCheckPermission("operationManagement:trainingFeedbackReply:remove") + @Log(title = "培训反馈回复", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除培训反馈回复") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotTrainingFeedbackReplyService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/domain/HotTrainingFeedbackReply.java b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/domain/HotTrainingFeedbackReply.java new file mode 100644 index 0000000..7241c67 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/domain/HotTrainingFeedbackReply.java @@ -0,0 +1,90 @@ +package com.hotwj.platform.operationManagement.trainingFeedbackReply.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 培训反馈回复对象 hot_training_feedback_reply + * + * @author shihongwei + * @date 2026-05-07 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_training_feedback_reply") +public class HotTrainingFeedbackReply extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 反馈主表ID(逻辑外键) + */ + private Long feedbackId; + + /** + * 反馈单号 + */ + private String feedbackNo; + + /** + * 回复角色:1=反馈人 2=处理人 + */ + private Long replyRole; + + /** + * 回复人ID + */ + private Long replyUserId; + + /** + * 回复人姓名 + */ + private String replyUserName; + + /** + * 回复内容 + */ + private String replyContent; + + /** + * 回复时间 + */ + private Date replyTime; + + /** + * 状态:1=有效 0=无效 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/domain/bo/HotTrainingFeedbackReplyBo.java b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/domain/bo/HotTrainingFeedbackReplyBo.java new file mode 100644 index 0000000..2aa209b --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/domain/bo/HotTrainingFeedbackReplyBo.java @@ -0,0 +1,86 @@ +package com.hotwj.platform.operationManagement.trainingFeedbackReply.domain.bo; + +import com.hotwj.platform.operationManagement.trainingFeedbackReply.domain.HotTrainingFeedbackReply; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 培训反馈回复业务对象 hot_training_feedback_reply + * + * @author shihongwei + * @date 2026-05-07 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotTrainingFeedbackReply.class, reverseConvertGenerate = false) +public class HotTrainingFeedbackReplyBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + + /** + * 反馈主表ID(逻辑外键) + */ + private Long feedbackId; + + /** + * 反馈单号 + */ + private String feedbackNo; + + /** + * 回复角色:1=反馈人 2=处理人 + */ + private Long replyRole; + + /** + * 回复人ID + */ + private Long replyUserId; + + /** + * 回复人姓名 + */ + private String replyUserName; + + /** + * 回复内容 + */ + private String replyContent; + + /** + * 回复时间 + */ + private Date replyTime; + + /** + * 状态:1=有效 0=无效 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/domain/vo/HotTrainingFeedbackReplyVo.java b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/domain/vo/HotTrainingFeedbackReplyVo.java new file mode 100644 index 0000000..83450a0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/domain/vo/HotTrainingFeedbackReplyVo.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.operationManagement.trainingFeedbackReply.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.operationManagement.trainingFeedbackReply.domain.HotTrainingFeedbackReply; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 培训反馈回复视图对象 hot_training_feedback_reply + * + * @author shihongwei + * @date 2026-05-07 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotTrainingFeedbackReply.class) +public class HotTrainingFeedbackReplyVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "应=用层保证必填") + private Long companyId; + + /** + * 反馈主表ID(逻辑外键) + */ + @ExcelProperty(value = "反馈主表ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逻=辑外键") + private Long feedbackId; + + /** + * 反馈单号 + */ + @ExcelProperty(value = "反馈单号") + private String feedbackNo; + + /** + * 回复角色:1=反馈人 2=处理人 + */ + @ExcelProperty(value = "回复角色:1=反馈人 2=处理人") + private Long replyRole; + + /** + * 回复人ID + */ + @ExcelProperty(value = "回复人ID") + private Long replyUserId; + + /** + * 回复人姓名 + */ + @ExcelProperty(value = "回复人姓名") + private String replyUserName; + + /** + * 回复内容 + */ + @ExcelProperty(value = "回复内容") + private String replyContent; + + /** + * 回复时间 + */ + @ExcelProperty(value = "回复时间") + private Date replyTime; + + /** + * 状态:1=有效 0=无效 + */ + @ExcelProperty(value = "状态:1=有效 0=无效") + private Long status; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/mapper/HotTrainingFeedbackReplyMapper.java b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/mapper/HotTrainingFeedbackReplyMapper.java new file mode 100644 index 0000000..8925fdb --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/mapper/HotTrainingFeedbackReplyMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.operationManagement.trainingFeedbackReply.mapper; + +import com.hotwj.platform.operationManagement.trainingFeedbackReply.domain.HotTrainingFeedbackReply; +import com.hotwj.platform.operationManagement.trainingFeedbackReply.domain.vo.HotTrainingFeedbackReplyVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 培训反馈回复Mapper接口 + * + * @author shihongwei + * @date 2026-05-07 + */ +@Mapper +public interface HotTrainingFeedbackReplyMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/service/IHotTrainingFeedbackReplyService.java b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/service/IHotTrainingFeedbackReplyService.java new file mode 100644 index 0000000..d136fd7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/service/IHotTrainingFeedbackReplyService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.operationManagement.trainingFeedbackReply.service; + +import com.hotwj.platform.operationManagement.trainingFeedbackReply.domain.bo.HotTrainingFeedbackReplyBo; +import com.hotwj.platform.operationManagement.trainingFeedbackReply.domain.vo.HotTrainingFeedbackReplyVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 培训反馈回复Service接口 + * + * @author shihongwei + * @date 2026-05-07 + */ +public interface IHotTrainingFeedbackReplyService { + + /** + * 查询培训反馈回复 + * + * @param id 主键 + * @return 培训反馈回复 + */ + HotTrainingFeedbackReplyVo queryById(Long id); + + /** + * 分页查询培训反馈回复列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训反馈回复分页列表 + */ + TableDataInfo queryPageList(HotTrainingFeedbackReplyBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的培训反馈回复列表 + * + * @param bo 查询条件 + * @return 培训反馈回复列表 + */ + List queryList(HotTrainingFeedbackReplyBo bo); + + /** + * 新增培训反馈回复 + * + * @param bo 培训反馈回复 + * @return 是否新增成功 + */ + Boolean insertByBo(HotTrainingFeedbackReplyBo bo); + + /** + * 修改培训反馈回复 + * + * @param bo 培训反馈回复 + * @return 是否修改成功 + */ + Boolean updateByBo(HotTrainingFeedbackReplyBo bo); + + /** + * 校验并批量删除培训反馈回复信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/service/impl/HotTrainingFeedbackReplyServiceImpl.java b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/service/impl/HotTrainingFeedbackReplyServiceImpl.java new file mode 100644 index 0000000..1dd4530 --- /dev/null +++ b/src/main/java/com/hotwj/platform/operationManagement/trainingFeedbackReply/service/impl/HotTrainingFeedbackReplyServiceImpl.java @@ -0,0 +1,143 @@ +package com.hotwj.platform.operationManagement.trainingFeedbackReply.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.operationManagement.trainingFeedbackReply.domain.HotTrainingFeedbackReply; +import com.hotwj.platform.operationManagement.trainingFeedbackReply.domain.bo.HotTrainingFeedbackReplyBo; +import com.hotwj.platform.operationManagement.trainingFeedbackReply.domain.vo.HotTrainingFeedbackReplyVo; +import com.hotwj.platform.operationManagement.trainingFeedbackReply.mapper.HotTrainingFeedbackReplyMapper; +import com.hotwj.platform.operationManagement.trainingFeedbackReply.service.IHotTrainingFeedbackReplyService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 培训反馈回复Service业务层处理 + * + * @author shihongwei + * @date 2026-05-07 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotTrainingFeedbackReplyServiceImpl implements IHotTrainingFeedbackReplyService { + + private final HotTrainingFeedbackReplyMapper baseMapper; + + /** + * 查询培训反馈回复 + * + * @param id 主键 + * @return 培训反馈回复 + */ + @Override + public HotTrainingFeedbackReplyVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询培训反馈回复列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训反馈回复分页列表 + */ + @Override + public TableDataInfo queryPageList(HotTrainingFeedbackReplyBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的培训反馈回复列表 + * + * @param bo 查询条件 + * @return 培训反馈回复列表 + */ + @Override + public List queryList(HotTrainingFeedbackReplyBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotTrainingFeedbackReplyBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotTrainingFeedbackReply::getId); + lqw.eq(bo.getCompanyId() != null, HotTrainingFeedbackReply::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getFeedbackId() != null, HotTrainingFeedbackReply::getFeedbackId, bo.getFeedbackId()); + lqw.eq(StringUtils.isNotBlank(bo.getFeedbackNo()), HotTrainingFeedbackReply::getFeedbackNo, bo.getFeedbackNo()); + lqw.eq(bo.getReplyRole() != null, HotTrainingFeedbackReply::getReplyRole, bo.getReplyRole()); + lqw.eq(bo.getReplyUserId() != null, HotTrainingFeedbackReply::getReplyUserId, bo.getReplyUserId()); + lqw.like(StringUtils.isNotBlank(bo.getReplyUserName()), HotTrainingFeedbackReply::getReplyUserName, bo.getReplyUserName()); + lqw.eq(StringUtils.isNotBlank(bo.getReplyContent()), HotTrainingFeedbackReply::getReplyContent, bo.getReplyContent()); + lqw.eq(bo.getReplyTime() != null, HotTrainingFeedbackReply::getReplyTime, bo.getReplyTime()); + lqw.eq(bo.getStatus() != null, HotTrainingFeedbackReply::getStatus, bo.getStatus()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotTrainingFeedbackReply::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotTrainingFeedbackReply::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotTrainingFeedbackReply::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增培训反馈回复 + * + * @param bo 培训反馈回复 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotTrainingFeedbackReplyBo bo) { + HotTrainingFeedbackReply add = MapstructUtils.convert(bo, HotTrainingFeedbackReply.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改培训反馈回复 + * + * @param bo 培训反馈回复 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotTrainingFeedbackReplyBo bo) { + HotTrainingFeedbackReply update = MapstructUtils.convert(bo, HotTrainingFeedbackReply.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotTrainingFeedbackReply entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除培训反馈回复信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/pay/controller/PayAppController.java b/src/main/java/com/hotwj/platform/pay/controller/PayAppController.java new file mode 100644 index 0000000..1c82f4a --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/controller/PayAppController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.pay.controller; + +import java.util.List; + +import com.hotwj.platform.pay.domain.bo.PayAppBo; +import com.hotwj.platform.pay.domain.vo.PayAppVo; +import com.hotwj.platform.pay.service.IPayAppService; +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; + +/** + * 支付应用信息 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/platform/app") +@Tag(name = "支付应用信息", description = "支付应用信息管理") +public class PayAppController extends BaseController { + + private final IPayAppService payAppService; + + /** + * 查询支付应用信息列表 + */ + @SaCheckPermission("platform:app:list") + @GetMapping("/list") + @Operation(summary = "分页查询支付应用信息列表") + public TableDataInfo list(PayAppBo bo, PageQuery pageQuery) { + return payAppService.queryPageList(bo, pageQuery); + } + + /** + * 导出支付应用信息列表 + */ + @SaCheckPermission("platform:app:export") + @Log(title = "支付应用信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出支付应用信息列表") + public void export(PayAppBo bo, HttpServletResponse response) { + List list = payAppService.queryList(bo); + ExcelUtil.exportExcel(list, "支付应用信息", PayAppVo.class, response); + } + + /** + * 获取支付应用信息详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("platform:app:query") + @GetMapping("/{id}") + @Operation(summary = "获取支付应用信息详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(payAppService.queryById(id)); + } + + /** + * 新增支付应用信息 + */ + @SaCheckPermission("platform:app:add") + @Log(title = "支付应用信息", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增支付应用信息") + public R add(@Validated(AddGroup.class) @RequestBody PayAppBo bo) { + return toAjax(payAppService.insertByBo(bo)); + } + + /** + * 修改支付应用信息 + */ + @SaCheckPermission("platform:app:edit") + @Log(title = "支付应用信息", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改支付应用信息") + public R edit(@Validated(EditGroup.class) @RequestBody PayAppBo bo) { + return toAjax(payAppService.updateByBo(bo)); + } + + /** + * 删除支付应用信息 + * + * @param ids 主键串 + */ + @SaCheckPermission("platform:app:remove") + @Log(title = "支付应用信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除支付应用信息") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(payAppService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/pay/controller/PayChannelController.java b/src/main/java/com/hotwj/platform/pay/controller/PayChannelController.java new file mode 100644 index 0000000..c0d7318 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/controller/PayChannelController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.pay.controller; + +import java.util.List; + +import com.hotwj.platform.pay.domain.bo.PayChannelBo; +import com.hotwj.platform.pay.domain.vo.PayChannelVo; +import com.hotwj.platform.pay.service.IPayChannelService; +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; + +/** + * 支付渠道 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/platform/channel") +@Tag(name = "支付渠道", description = "支付渠道管理") +public class PayChannelController extends BaseController { + + private final IPayChannelService payChannelService; + + /** + * 查询支付渠道列表 + */ + @SaCheckPermission("platform:channel:list") + @GetMapping("/list") + @Operation(summary = "分页查询支付渠道列表") + public TableDataInfo list(PayChannelBo bo, PageQuery pageQuery) { + return payChannelService.queryPageList(bo, pageQuery); + } + + /** + * 导出支付渠道列表 + */ + @SaCheckPermission("platform:channel:export") + @Log(title = "支付渠道 ", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出支付渠道列表") + public void export(PayChannelBo bo, HttpServletResponse response) { + List list = payChannelService.queryList(bo); + ExcelUtil.exportExcel(list, "支付渠道", PayChannelVo.class, response); + } + + /** + * 获取支付渠道详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("platform:channel:query") + @GetMapping("/{id}") + @Operation(summary = "获取支付渠道详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(payChannelService.queryById(id)); + } + + /** + * 新增支付渠道 + */ + @SaCheckPermission("platform:channel:add") + @Log(title = "支付渠道", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增支付渠道") + public R add(@Validated(AddGroup.class) @RequestBody PayChannelBo bo) { + return toAjax(payChannelService.insertByBo(bo)); + } + + /** + * 修改支付渠道 + */ + @SaCheckPermission("platform:channel:edit") + @Log(title = "支付渠道", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改支付渠道") + public R edit(@Validated(EditGroup.class) @RequestBody PayChannelBo bo) { + return toAjax(payChannelService.updateByBo(bo)); + } + + /** + * 删除支付渠道 + * + * @param ids 主键串 + */ + @SaCheckPermission("platform:channel:remove") + @Log(title = "支付渠道", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除支付渠道") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(payChannelService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/pay/controller/PayOpenController.java b/src/main/java/com/hotwj/platform/pay/controller/PayOpenController.java new file mode 100644 index 0000000..0cd5760 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/controller/PayOpenController.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.pay.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import com.hotwj.platform.pay.controller.vo.PayRechargeChannelRespVO; +import com.hotwj.platform.pay.controller.vo.PayRechargeStatusRespVO; +import com.hotwj.platform.pay.controller.vo.PayRechargeUnifiedOrderReqVO; +import com.hotwj.platform.pay.controller.vo.PayRechargeUnifiedOrderRespVO; +import com.hotwj.platform.pay.controller.vo.PayWalletBalanceRespVO; +import com.hotwj.platform.pay.service.PayRechargeOpenService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; + +@Validated +@RestController +@RequiredArgsConstructor +@RequestMapping("/pay/open") +@Tag(name = "开放支付", description = "钱包充值支付能力") +public class PayOpenController { + + private final PayRechargeOpenService payRechargeOpenService; + + @GetMapping("/recharge/channels") + @Operation(summary = "查询钱包充值可用支付方式") + public R> listRechargeChannels() { + return R.ok(payRechargeOpenService.listRechargeChannels()); + } + + @GetMapping("/wallet/balance") + @Operation(summary = "查询钱包余额") + public R queryWalletBalance(@RequestParam(value = "walletType", required = false) Long walletType) { + return R.ok(payRechargeOpenService.queryWalletBalance(walletType)); + } + + @PostMapping("/recharge/unified-order") + @Operation(summary = "钱包充值统一下单") + public R unifiedOrder(@Valid @RequestBody PayRechargeUnifiedOrderReqVO reqVO, + HttpServletRequest request) { + return R.ok(payRechargeOpenService.unifiedOrder(reqVO, request)); + } + + @GetMapping("/recharge/status/{rechargeOrderNo}") + @Operation(summary = "查询钱包充值状态") + public R queryRechargeStatus(@PathVariable String rechargeOrderNo) { + return R.ok(payRechargeOpenService.queryRechargeStatus(rechargeOrderNo)); + } + + @SaIgnore + @PostMapping(value = "/notify/alipay/{channelId}", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + @Operation(summary = "支付宝支付回调") + public String alipayNotify(@PathVariable Long channelId, @RequestParam Map params) { + return payRechargeOpenService.handleAlipayNotify(channelId, params); + } + + @SaIgnore + @PostMapping(value = "/notify/wechat/{channelId}", consumes = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "微信支付回调") + public String wechatNotify(@PathVariable Long channelId, @RequestBody String body, HttpServletRequest request) { + return payRechargeOpenService.handleWechatNotify(channelId, body, request); + } +} diff --git a/src/main/java/com/hotwj/platform/pay/controller/PayOrderController.java b/src/main/java/com/hotwj/platform/pay/controller/PayOrderController.java new file mode 100644 index 0000000..e7cd0df --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/controller/PayOrderController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.pay.controller; + +import java.util.List; + +import com.hotwj.platform.pay.domain.bo.PayOrderBo; +import com.hotwj.platform.pay.domain.vo.PayOrderVo; +import com.hotwj.platform.pay.service.IPayOrderService; +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; + +/** + * 支付订单 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/platform/order") +@Tag(name = "支付订单", description = "支付订单管理") +public class PayOrderController extends BaseController { + + private final IPayOrderService payOrderService; + + /** + * 查询支付订单列表 + */ + @SaCheckPermission("platform:order:list") + @GetMapping("/list") + @Operation(summary = "分页查询支付订单列表") + public TableDataInfo list(PayOrderBo bo, PageQuery pageQuery) { + return payOrderService.queryPageList(bo, pageQuery); + } + + /** + * 导出支付订单列表 + */ + @SaCheckPermission("platform:order:export") + @Log(title = "支付订单", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出支付订单列表") + public void export(PayOrderBo bo, HttpServletResponse response) { + List list = payOrderService.queryList(bo); + ExcelUtil.exportExcel(list, "支付订单", PayOrderVo.class, response); + } + + /** + * 获取支付订单详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("platform:order:query") + @GetMapping("/{id}") + @Operation(summary = "获取支付订单详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(payOrderService.queryById(id)); + } + + /** + * 新增支付订单 + */ + @SaCheckPermission("platform:order:add") + @Log(title = "支付订单", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增支付订单") + public R add(@Validated(AddGroup.class) @RequestBody PayOrderBo bo) { + return toAjax(payOrderService.insertByBo(bo)); + } + + /** + * 修改支付订单 + */ + @SaCheckPermission("platform:order:edit") + @Log(title = "支付订单", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改支付订单") + public R edit(@Validated(EditGroup.class) @RequestBody PayOrderBo bo) { + return toAjax(payOrderService.updateByBo(bo)); + } + + /** + * 删除支付订单 + * + * @param ids 主键串 + */ + @SaCheckPermission("platform:order:remove") + @Log(title = "支付订单", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除支付订单") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(payOrderService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/pay/controller/PayOrderExtensionController.java b/src/main/java/com/hotwj/platform/pay/controller/PayOrderExtensionController.java new file mode 100644 index 0000000..0876963 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/controller/PayOrderExtensionController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.pay.controller; + +import java.util.List; + +import com.hotwj.platform.pay.domain.bo.PayOrderExtensionBo; +import com.hotwj.platform.pay.domain.vo.PayOrderExtensionVo; +import com.hotwj.platform.pay.service.IPayOrderExtensionService; +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; + +/** + * 支付订单 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/platform/orderExtension") +@Tag(name = "支付订单", description = "支付订单管理") +public class PayOrderExtensionController extends BaseController { + + private final IPayOrderExtensionService payOrderExtensionService; + + /** + * 查询支付订单列表 + */ + @SaCheckPermission("platform:orderExtension:list") + @GetMapping("/list") + @Operation(summary = "分页查询支付订单列表") + public TableDataInfo list(PayOrderExtensionBo bo, PageQuery pageQuery) { + return payOrderExtensionService.queryPageList(bo, pageQuery); + } + + /** + * 导出支付订单列表 + */ + @SaCheckPermission("platform:orderExtension:export") + @Log(title = "支付订单", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出支付订单列表") + public void export(PayOrderExtensionBo bo, HttpServletResponse response) { + List list = payOrderExtensionService.queryList(bo); + ExcelUtil.exportExcel(list, "支付订单", PayOrderExtensionVo.class, response); + } + + /** + * 获取支付订单详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("platform:orderExtension:query") + @GetMapping("/{id}") + @Operation(summary = "获取支付订单详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(payOrderExtensionService.queryById(id)); + } + + /** + * 新增支付订单 + */ + @SaCheckPermission("platform:orderExtension:add") + @Log(title = "支付订单", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增支付订单") + public R add(@Validated(AddGroup.class) @RequestBody PayOrderExtensionBo bo) { + return toAjax(payOrderExtensionService.insertByBo(bo)); + } + + /** + * 修改支付订单 + */ + @SaCheckPermission("platform:orderExtension:edit") + @Log(title = "支付订单", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改支付订单") + public R edit(@Validated(EditGroup.class) @RequestBody PayOrderExtensionBo bo) { + return toAjax(payOrderExtensionService.updateByBo(bo)); + } + + /** + * 删除支付订单 + * + * @param ids 主键串 + */ + @SaCheckPermission("platform:orderExtension:remove") + @Log(title = "支付订单", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除支付订单") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(payOrderExtensionService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/pay/controller/PayWalletController.java b/src/main/java/com/hotwj/platform/pay/controller/PayWalletController.java new file mode 100644 index 0000000..2f4de51 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/controller/PayWalletController.java @@ -0,0 +1,185 @@ +package com.hotwj.platform.pay.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.hotwj.platform.pay.domain.bo.PayWalletBatchRechargeBo; +import com.hotwj.platform.pay.domain.bo.PayWalletBo; +import com.hotwj.platform.pay.domain.bo.PayWalletFreezeBo; +import com.hotwj.platform.pay.domain.vo.PayCompanyWalletBalanceVo; +import com.hotwj.platform.pay.domain.vo.PayWalletVo; +import com.hotwj.platform.pay.service.IPayWalletService; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 会员钱包 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/platform/wallet") +@Tag(name = "会员钱包", description = "会员钱包管理") +public class PayWalletController extends BaseController { + + private final IPayWalletService payWalletService; + + /** + * 查询会员钱包列表 + */ + @SaCheckPermission("platform:wallet:list") + @GetMapping("/list") + @Operation(summary = "分页查询会员钱包列表") + public TableDataInfo list(PayWalletBo bo, PageQuery pageQuery) { + return payWalletService.queryPageList(bo, pageQuery); + } + + /** + * 分页查询企业钱包余额列表 + */ + @SaCheckPermission("platform:wallet:list") + @GetMapping("/companyBalanceList") + @Operation(summary = "分页查询企业钱包余额列表") + public TableDataInfo companyBalanceList( + @Parameter(name = "keyword", description = "企业名/负责人/手机号关键词") + @RequestParam(required = false) String keyword, + PageQuery pageQuery) { + return payWalletService.queryCompanyWalletPage(keyword, pageQuery); + } + + /** + * 总部端按钱包ID查询钱包余额 + */ + @SaCheckPermission("platform:wallet:query") + @GetMapping("/headquarters/balance") + @Operation(summary = "总部端按钱包ID查询钱包余额") + public R headquartersBalance( + @NotNull(message = "钱包ID不能为空") + @Parameter(name = "walletId", description = "钱包ID", required = true, example = "1") + @RequestParam Long walletId) { + ensureHeadquartersAccess(); + return R.ok(payWalletService.queryById(walletId)); + } + + /** + * 导出会员钱包列表 + */ + @SaCheckPermission("platform:wallet:export") + @Log(title = "会员钱包", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出会员钱包列表") + public void export(PayWalletBo bo, HttpServletResponse response) { + List list = payWalletService.queryList(bo); + ExcelUtil.exportExcel(list, "会员钱包", PayWalletVo.class, response); + } + + /** + * 获取会员钱包详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("platform:wallet:query") + @GetMapping("/{id}") + @Operation(summary = "获取会员钱包详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(payWalletService.queryById(id)); + } + + /** + * 新增会员钱包 + */ + @SaCheckPermission("platform:wallet:add") + @Log(title = "会员钱包", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增会员钱包") + public R add(@Validated(AddGroup.class) @RequestBody PayWalletBo bo) { + return toAjax(payWalletService.insertByBo(bo)); + } + + /** + * 修改会员钱包 + */ + @SaCheckPermission("platform:wallet:edit") + @Log(title = "会员钱包", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改会员钱包") + public R edit(@Validated(EditGroup.class) @RequestBody PayWalletBo bo) { + return toAjax(payWalletService.updateByBo(bo)); + } + + /** + * 删除会员钱包 + * + * @param ids 主键串 + */ + @SaCheckPermission("platform:wallet:remove") + @Log(title = "会员钱包", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除会员钱包") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(payWalletService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 总部批量充值企业钱包 + */ + @SaCheckPermission("platform:wallet:recharge") + @Log(title = "会员钱包", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/batchRechargeCompany") + @Operation(summary = "总部批量充值企业钱包") + public R batchRechargeCompany(@Validated @RequestBody PayWalletBatchRechargeBo bo) { + return R.ok(payWalletService.batchRechargeCompanyWallet(bo)); + } + + /** + * 冻结/解冻钱包 + */ + @SaCheckPermission("platform:wallet:edit") + @Log(title = "会员钱包", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/freeze") + @Operation(summary = "冻结/解冻钱包") + public R freeze(@Validated @RequestBody PayWalletFreezeBo bo) { + return toAjax(payWalletService.changeFreezeStatus(bo)); + } + + private void ensureHeadquartersAccess() { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHeadquarters = loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isHeadquarters && !isAdmin) { + throw new ServiceException("非总部端用户无权访问"); + } + } +} diff --git a/src/main/java/com/hotwj/platform/pay/controller/PayWalletTransactionController.java b/src/main/java/com/hotwj/platform/pay/controller/PayWalletTransactionController.java new file mode 100644 index 0000000..d4a380d --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/controller/PayWalletTransactionController.java @@ -0,0 +1,186 @@ +package com.hotwj.platform.pay.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.hotwj.platform.pay.domain.bo.PayWalletTransactionBo; +import com.hotwj.platform.pay.domain.vo.PayWalletTransactionVo; +import com.hotwj.platform.pay.service.IPayWalletTransactionService; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 会员钱包流水 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/platform/walletTransaction") +@Tag(name = "会员钱包流水", description = "会员钱包流水管理") +public class PayWalletTransactionController extends BaseController { + + private final IPayWalletTransactionService payWalletTransactionService; + + /** + * 查询会员钱包流水列表 + */ + @SaCheckPermission("platform:walletTransaction:list") + @GetMapping("/list") + @Operation(summary = "分页查询会员钱包流水列表") + public TableDataInfo list(PayWalletTransactionBo bo, PageQuery pageQuery) { + return payWalletTransactionService.queryPageList(bo, pageQuery); + } + + /** + * 企业端查询当前企业钱包流水明细 + */ + @GetMapping("/company/current/list") + @Operation(summary = "企业端分页查询当前企业钱包流水明细") + public TableDataInfo currentCompanyList(PayWalletTransactionBo bo, PageQuery pageQuery) { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isEnterprise = loginUser != null && ISysUserLoginPortService.ENTERPRISE_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isEnterprise && !isAdmin) { + throw new ServiceException("非企业端用户无权访问"); + } + Long companyId = loginUser == null ? null : loginUser.getCompanyId(); + if (companyId == null) { + throw new ServiceException("当前登录企业信息不存在"); + } + return payWalletTransactionService.queryCompanyWalletPage(companyId, bo, pageQuery); + } + + /** + * 总部端查询指定企业钱包流水明细 + */ + @GetMapping("/company/list") + @Operation(summary = "总部端分页查询指定企业钱包流水明细") + public TableDataInfo companyList( + @NotNull(message = "企业ID不能为空") + @Parameter(name = "companyId", description = "企业ID", required = true, example = "1") + @RequestParam Long companyId, + PayWalletTransactionBo bo, PageQuery pageQuery) { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHeadquarters = loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isHeadquarters && !isAdmin) { + throw new ServiceException("非总部端用户无权访问"); + } + return payWalletTransactionService.queryCompanyWalletPage(companyId, bo, pageQuery); + } + + /** + * 总部端按钱包ID分页查询钱包流水 + */ + @SaCheckPermission("platform:walletTransaction:list") + @GetMapping("/headquarters/list") + @Operation(summary = "总部端按钱包ID分页查询钱包流水") + public TableDataInfo headquartersList( + @NotNull(message = "钱包ID不能为空") + @Parameter(name = "walletId", description = "钱包ID", required = true, example = "1") + @RequestParam Long walletId, + PayWalletTransactionBo bo, PageQuery pageQuery) { + ensureHeadquartersAccess(); + PayWalletTransactionBo queryBo = bo == null ? new PayWalletTransactionBo() : bo; + queryBo.setWalletId(walletId); + return payWalletTransactionService.queryPageList(queryBo, pageQuery); + } + + /** + * 导出会员钱包流水列表 + */ + @SaCheckPermission("platform:walletTransaction:export") + @Log(title = "会员钱包流水", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出会员钱包流水列表") + public void export(PayWalletTransactionBo bo, HttpServletResponse response) { + List list = payWalletTransactionService.queryList(bo); + ExcelUtil.exportExcel(list, "会员钱包流水", PayWalletTransactionVo.class, response); + } + + /** + * 获取会员钱包流水详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("platform:walletTransaction:query") + @GetMapping("/{id}") + @Operation(summary = "获取会员钱包流水详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(payWalletTransactionService.queryById(id)); + } + + /** + * 新增会员钱包流水 + */ + @SaCheckPermission("platform:walletTransaction:add") + @Log(title = "会员钱包流水", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增会员钱包流水") + public R add(@Validated(AddGroup.class) @RequestBody PayWalletTransactionBo bo) { + return toAjax(payWalletTransactionService.insertByBo(bo)); + } + + /** + * 修改会员钱包流水 + */ + @SaCheckPermission("platform:walletTransaction:edit") + @Log(title = "会员钱包流水", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改会员钱包流水") + public R edit(@Validated(EditGroup.class) @RequestBody PayWalletTransactionBo bo) { + return toAjax(payWalletTransactionService.updateByBo(bo)); + } + + /** + * 删除会员钱包流水 + * + * @param ids 主键串 + */ + @SaCheckPermission("platform:walletTransaction:remove") + @Log(title = "会员钱包流水", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除会员钱包流水") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(payWalletTransactionService.deleteWithValidByIds(List.of(ids), true)); + } + + private void ensureHeadquartersAccess() { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHeadquarters = loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isHeadquarters && !isAdmin) { + throw new ServiceException("非总部端用户无权访问"); + } + } +} diff --git a/src/main/java/com/hotwj/platform/pay/controller/vo/PayRechargeChannelRespVO.java b/src/main/java/com/hotwj/platform/pay/controller/vo/PayRechargeChannelRespVO.java new file mode 100644 index 0000000..b4bb690 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/controller/vo/PayRechargeChannelRespVO.java @@ -0,0 +1,19 @@ +package com.hotwj.platform.pay.controller.vo; + +import lombok.Data; + +@Data +public class PayRechargeChannelRespVO { + + private String provider; + + private String scene; + + private String channelCode; + + private Long payMethod; + + private Long payScene; + + private String displayName; +} diff --git a/src/main/java/com/hotwj/platform/pay/controller/vo/PayRechargeStatusRespVO.java b/src/main/java/com/hotwj/platform/pay/controller/vo/PayRechargeStatusRespVO.java new file mode 100644 index 0000000..29503ec --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/controller/vo/PayRechargeStatusRespVO.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.pay.controller.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class PayRechargeStatusRespVO { + + private String rechargeOrderNo; + private String payOrderNo; + private Long payStatus; + private BigDecimal amount; + private String thirdTradeNo; + private Date paidTime; +} diff --git a/src/main/java/com/hotwj/platform/pay/controller/vo/PayRechargeUnifiedOrderReqVO.java b/src/main/java/com/hotwj/platform/pay/controller/vo/PayRechargeUnifiedOrderReqVO.java new file mode 100644 index 0000000..682d66a --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/controller/vo/PayRechargeUnifiedOrderReqVO.java @@ -0,0 +1,26 @@ +package com.hotwj.platform.pay.controller.vo; + +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class PayRechargeUnifiedOrderReqVO { + + @NotNull(message = "充值金额不能为空") + @DecimalMin(value = "0.01", message = "充值金额必须大于 0") + private BigDecimal amount; + + @NotBlank(message = "支付渠道不能为空") + private String provider; + + @NotBlank(message = "支付场景不能为空") + private String scene; + + private Long walletType = 2L; + + private String returnUrl; +} diff --git a/src/main/java/com/hotwj/platform/pay/controller/vo/PayRechargeUnifiedOrderRespVO.java b/src/main/java/com/hotwj/platform/pay/controller/vo/PayRechargeUnifiedOrderRespVO.java new file mode 100644 index 0000000..0197352 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/controller/vo/PayRechargeUnifiedOrderRespVO.java @@ -0,0 +1,20 @@ +package com.hotwj.platform.pay.controller.vo; + +import lombok.Data; + +import java.util.Date; +import java.util.Map; + +@Data +public class PayRechargeUnifiedOrderRespVO { + + private String rechargeOrderNo; + private String payOrderNo; + private String channelCode; + private String displayMode; + private String codeUrl; + private String payUrl; + private String orderString; + private Map appPayParams; + private Date expireTime; +} diff --git a/src/main/java/com/hotwj/platform/pay/controller/vo/PayWalletBalanceRespVO.java b/src/main/java/com/hotwj/platform/pay/controller/vo/PayWalletBalanceRespVO.java new file mode 100644 index 0000000..d9e2007 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/controller/vo/PayWalletBalanceRespVO.java @@ -0,0 +1,18 @@ +package com.hotwj.platform.pay.controller.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class PayWalletBalanceRespVO { + + private Long walletType; + + private BigDecimal balance; + + /** + * 冻结状态:0=正常 1=冻结 + */ + private Long freezeStatus; +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/PayApp.java b/src/main/java/com/hotwj/platform/pay/domain/PayApp.java new file mode 100644 index 0000000..18898d0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/PayApp.java @@ -0,0 +1,62 @@ +package com.hotwj.platform.pay.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 支付应用信息对象 pay_app + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pay_app") +public class PayApp extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 应用编号 + */ + @TableId(value = "id") + private Long id; + + /** + * 应用标识 + */ + private String appKey; + + /** + * 应用名 + */ + private String name; + + /** + * 开启状态 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 支付结果的回调地址 + */ + private String orderNotifyUrl; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/PayChannel.java b/src/main/java/com/hotwj/platform/pay/domain/PayChannel.java new file mode 100644 index 0000000..65984e9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/PayChannel.java @@ -0,0 +1,63 @@ +package com.hotwj.platform.pay.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 支付渠道 + * 对象 pay_channel + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pay_channel") +public class PayChannel extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 商户编号 + */ + @TableId(value = "id") + private Long id; + + /** + * 渠道编码 + */ + private String code; + + /** + * 开启状态 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 应用编号 + */ + private Long appId; + + /** + * 支付渠道配置 + */ + private String config; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/PayOrder.java b/src/main/java/com/hotwj/platform/pay/domain/PayOrder.java new file mode 100644 index 0000000..e15cabf --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/PayOrder.java @@ -0,0 +1,138 @@ +package com.hotwj.platform.pay.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 支付订单 + * 对象 pay_order + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pay_order") +public class PayOrder extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 支付订单编号 + */ + @TableId(value = "id") + private Long id; + + /** + * 应用编号 + */ + private Long appId; + + /** + * 业务订单号 + */ + private String merchantOrderNo; + + /** + * 业务类型 + */ + private String bizOrderType; + + /** + * 业务主键 + */ + private Long bizOrderId; + + /** + * 订单标题 + */ + private String subject; + + /** + * 订单描述 + */ + private String body; + + /** + * 渠道编号 + */ + private Long channelId; + + /** + * 渠道编码 + */ + private String channelCode; + + /** + * 异步通知地址 + */ + private String notifyUrl; + + /** + * 同步跳转地址 + */ + private String returnUrl; + + /** + * 支付金额 + */ + private BigDecimal price; + + /** + * 支付状态 + */ + private Long status; + + /** + * 用户 IP + */ + private String userIp; + + /** + * 订单失效时间 + */ + private Date expireTime; + + /** + * 订单支付成功时间 + */ + private Date successTime; + + /** + * 支付成功的订单拓展单编号 + */ + private Long extensionId; + + /** + * 支付订单号 + */ + private String no; + + /** + * 渠道用户编号 + */ + private String channelUserId; + + /** + * 渠道订单号 + */ + private String channelOrderNo; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/PayOrderExtension.java b/src/main/java/com/hotwj/platform/pay/domain/PayOrderExtension.java new file mode 100644 index 0000000..56eec0d --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/PayOrderExtension.java @@ -0,0 +1,88 @@ +package com.hotwj.platform.pay.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 支付订单 + * 对象 pay_order_extension + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pay_order_extension") +public class PayOrderExtension extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 支付订单编号 + */ + @TableId(value = "id") + private Long id; + + /** + * 支付订单号 + */ + private String no; + + /** + * 支付订单编号 + */ + private Long orderId; + + /** + * 渠道编号 + */ + private Long channelId; + + /** + * 渠道编码 + */ + private String channelCode; + + /** + * 用户 IP + */ + private String userIp; + + /** + * 支付状态 + */ + private Long status; + + /** + * 支付渠道的额外参数 + */ + private String channelExtras; + + /** + * 渠道调用报错时,错误码 + */ + private String channelErrorCode; + + /** + * 渠道调用报错时,错误信息 + */ + private String channelErrorMsg; + + /** + * 支付渠道异步通知的内容 + */ + private String channelNotifyData; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/PayRechargeOrder.java b/src/main/java/com/hotwj/platform/pay/domain/PayRechargeOrder.java new file mode 100644 index 0000000..4d464bf --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/PayRechargeOrder.java @@ -0,0 +1,78 @@ +package com.hotwj.platform.pay.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 充值订单对象 hot_fund_recharge_order + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pay_recharge_order") +public class PayRechargeOrder extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @TableId(value = "id") + private Long id; + /** + * 公司ID(应用层保证必填) + */ + private Long companyId; + /** + * 账户ID(逻辑外键) + */ + private Long accountId; + /** + * 用户ID + */ + private Long userId; + /** + * 充值订单号 + */ + private String rechargeOrderNo; + /** + * 充值金额(如5.00) + */ + private BigDecimal rechargeAmount; + /** + * 支付方式:1=微信 2=支付宝 3=银行卡 + */ + private Long payMethod; + /** + * 支付场景:1=H5 2=小程序 3=APP 4=PC + */ + private Long payScene; + /** + * 支付状态:1=待支付 2=支付中 3=支付成功 4=支付失败 5=已关闭 6=已退款 + */ + private Long payStatus; + /** + * 三方支付流水号 + */ + private String thirdTradeNo; + /** + * 支付成功时间 + */ + private Date paidTime; + /** + * 失败原因 + */ + private String failReason; + /** + * 备注 + */ + private String remark; + + @TableLogic + private Long isDeleted; +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/PayWallet.java b/src/main/java/com/hotwj/platform/pay/domain/PayWallet.java new file mode 100644 index 0000000..b8ba312 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/PayWallet.java @@ -0,0 +1,73 @@ +package com.hotwj.platform.pay.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 会员钱包对象 pay_wallet + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pay_wallet") +public class PayWallet extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 编号 + */ + @TableId(value = "id") + private Long id; + + /** + * 用户编号(企业则为企业id) + */ + private Long userId; + + /** + * 余额 + */ + private BigDecimal balance; + + /** + * 累计支出 + */ + private BigDecimal totalExpense; + + /** + * 累计充值 + */ + private BigDecimal totalRecharge; + + /** + * 冻结金额 + */ + private BigDecimal freezePrice; + + /** + * 1.个人账户 2.企业账户 + */ + private Long walletType; + + /** + * 冻结状态:0=正常 1=冻结 + */ + private Long freezeStatus; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/PayWalletTransaction.java b/src/main/java/com/hotwj/platform/pay/domain/PayWalletTransaction.java new file mode 100644 index 0000000..c43d00a --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/PayWalletTransaction.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.pay.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 会员钱包流水对象 pay_wallet_transaction + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("pay_wallet_transaction") +public class PayWalletTransaction extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 编号 + */ + @TableId(value = "id") + private Long id; + + /** + * 钱包 id + */ + private Long walletId; + + /** + * 关联类型 + */ + private Long bizType; + + /** + * 关联业务编号 + */ + private String bizId; + + /** + * 流水号 + */ + private String no; + + /** + * 流水标题 + */ + private String title; + + /** + * 交易金额 + */ + private BigDecimal price; + + /** + * 余额 + */ + private BigDecimal balance; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 备注 + */ + private String remark; +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/bo/PayAppBo.java b/src/main/java/com/hotwj/platform/pay/domain/bo/PayAppBo.java new file mode 100644 index 0000000..38b65f6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/bo/PayAppBo.java @@ -0,0 +1,64 @@ +package com.hotwj.platform.pay.domain.bo; + +import com.hotwj.platform.pay.domain.PayApp; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 支付应用信息业务对象 pay_app + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = PayApp.class, reverseConvertGenerate = false) +public class PayAppBo extends BaseEntity { + + /** + * 应用编号 + */ + @NotNull(message = "应用编号不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 应用标识 + */ + @NotBlank(message = "应用标识不能为空", groups = {AddGroup.class, EditGroup.class}) + private String appKey; + + /** + * 应用名 + */ + @NotBlank(message = "应用名不能为空", groups = {AddGroup.class, EditGroup.class}) + private String name; + + /** + * 开启状态 + */ + @NotNull(message = "开启状态不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 支付结果的回调地址 + */ + @NotBlank(message = "支付结果的回调地址不能为空", groups = {AddGroup.class, EditGroup.class}) + private String orderNotifyUrl; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/bo/PayChannelBo.java b/src/main/java/com/hotwj/platform/pay/domain/bo/PayChannelBo.java new file mode 100644 index 0000000..4970186 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/bo/PayChannelBo.java @@ -0,0 +1,65 @@ +package com.hotwj.platform.pay.domain.bo; + +import com.hotwj.platform.pay.domain.PayChannel; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 支付渠道 + * 业务对象 pay_channel + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = PayChannel.class, reverseConvertGenerate = false) +public class PayChannelBo extends BaseEntity { + + /** + * 商户编号 + */ + @NotNull(message = "商户编号不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 渠道编码 + */ + @NotBlank(message = "渠道编码不能为空", groups = {AddGroup.class, EditGroup.class}) + private String code; + + /** + * 开启状态 + */ + @NotNull(message = "开启状态不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 应用编号 + */ + @NotNull(message = "应用编号不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long appId; + + /** + * 支付渠道配置 + */ + @NotBlank(message = "支付渠道配置不能为空", groups = {AddGroup.class, EditGroup.class}) + private String config; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/bo/PayOrderBo.java b/src/main/java/com/hotwj/platform/pay/domain/bo/PayOrderBo.java new file mode 100644 index 0000000..b31baad --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/bo/PayOrderBo.java @@ -0,0 +1,142 @@ +package com.hotwj.platform.pay.domain.bo; + +import com.hotwj.platform.pay.domain.PayOrder; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.math.BigDecimal; +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 支付订单 + * 业务对象 pay_order + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = PayOrder.class, reverseConvertGenerate = false) +public class PayOrderBo extends BaseEntity { + + /** + * 支付订单编号 + */ + @NotNull(message = "支付订单编号不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 应用编号 + */ + @NotNull(message = "应用编号不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long appId; + + /** + * 业务订单号 + */ + private String merchantOrderNo; + + /** + * 业务类型 + */ + private String bizOrderType; + + /** + * 业务主键 + */ + private Long bizOrderId; + + /** + * 订单标题 + */ + private String subject; + + /** + * 订单描述 + */ + private String body; + + /** + * 渠道编号 + */ + private Long channelId; + + /** + * 渠道编码 + */ + private String channelCode; + + /** + * 异步通知地址 + */ + @NotBlank(message = "异步通知地址不能为空", groups = {AddGroup.class, EditGroup.class}) + private String notifyUrl; + + /** + * 同步跳转地址 + */ + private String returnUrl; + + /** + * 支付金额 + */ + @NotNull(message = "支付金额不能为空", groups = {AddGroup.class, EditGroup.class}) + private BigDecimal price; + + /** + * 支付状态 + */ + @NotNull(message = "支付状态不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long status; + + /** + * 用户 IP + */ + @NotBlank(message = "用户 IP不能为空", groups = {AddGroup.class, EditGroup.class}) + private String userIp; + + /** + * 订单失效时间 + */ + @NotNull(message = "订单失效时间不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date expireTime; + + /** + * 订单支付成功时间 + */ + private Date successTime; + + /** + * 支付成功的订单拓展单编号 + */ + private Long extensionId; + + /** + * 支付订单号 + */ + private String no; + + /** + * 渠道用户编号 + */ + private String channelUserId; + + /** + * 渠道订单号 + */ + private String channelOrderNo; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/bo/PayOrderExtensionBo.java b/src/main/java/com/hotwj/platform/pay/domain/bo/PayOrderExtensionBo.java new file mode 100644 index 0000000..fddc5fc --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/bo/PayOrderExtensionBo.java @@ -0,0 +1,92 @@ +package com.hotwj.platform.pay.domain.bo; + +import com.hotwj.platform.pay.domain.PayOrderExtension; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 支付订单 + * 业务对象 pay_order_extension + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = PayOrderExtension.class, reverseConvertGenerate = false) +public class PayOrderExtensionBo extends BaseEntity { + + /** + * 支付订单编号 + */ + @NotNull(message = "支付订单编号不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 支付订单号 + */ + @NotBlank(message = "支付订单号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String no; + + /** + * 支付订单编号 + */ + @NotNull(message = "支付订单编号不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long orderId; + + /** + * 渠道编号 + */ + @NotNull(message = "渠道编号不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long channelId; + + /** + * 渠道编码 + */ + @NotBlank(message = "渠道编码不能为空", groups = {AddGroup.class, EditGroup.class}) + private String channelCode; + + /** + * 用户 IP + */ + @NotBlank(message = "用户 IP不能为空", groups = {AddGroup.class, EditGroup.class}) + private String userIp; + + /** + * 支付状态 + */ + @NotNull(message = "支付状态不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long status; + + /** + * 支付渠道的额外参数 + */ + private String channelExtras; + + /** + * 渠道调用报错时,错误码 + */ + private String channelErrorCode; + + /** + * 渠道调用报错时,错误信息 + */ + private String channelErrorMsg; + + /** + * 支付渠道异步通知的内容 + */ + private String channelNotifyData; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/bo/PayWalletBatchRechargeBo.java b/src/main/java/com/hotwj/platform/pay/domain/bo/PayWalletBatchRechargeBo.java new file mode 100644 index 0000000..d88713c --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/bo/PayWalletBatchRechargeBo.java @@ -0,0 +1,37 @@ +package com.hotwj.platform.pay.domain.bo; + +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 企业钱包批量充值请求 + * + * @author shihongwei + * @date 2026-05-09 + */ +@Data +public class PayWalletBatchRechargeBo { + + /** + * 企业ID集合,逗号分隔 + */ + @NotBlank(message = "企业ids不能为空") + private String companyIds; + + /** + * 充值金额 + */ + @NotNull(message = "充值金额不能为空") + @DecimalMin(value = "0.01", message = "充值金额必须大于0") + private BigDecimal amount; + + /** + * 充值备注 + */ + @NotBlank(message = "充值备注不能为空") + private String remark; +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/bo/PayWalletBo.java b/src/main/java/com/hotwj/platform/pay/domain/bo/PayWalletBo.java new file mode 100644 index 0000000..9bb65b5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/bo/PayWalletBo.java @@ -0,0 +1,77 @@ +package com.hotwj.platform.pay.domain.bo; + +import com.hotwj.platform.pay.domain.PayWallet; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.math.BigDecimal; + +/** + * 会员钱包业务对象 pay_wallet + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = PayWallet.class, reverseConvertGenerate = false) +public class PayWalletBo extends BaseEntity { + + /** + * 编号 + */ + @NotNull(message = "编号不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 用户编号(企业则为企业id) + */ + @NotNull(message = "用户编号(企业则为企业id)不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long userId; + + /** + * 余额 + */ + @NotNull(message = "余额不能为空", groups = {AddGroup.class, EditGroup.class}) + private BigDecimal balance; + + /** + * 累计支出 + */ + @NotNull(message = "累计支出不能为空", groups = {AddGroup.class, EditGroup.class}) + private BigDecimal totalExpense; + + /** + * 累计充值 + */ + @NotNull(message = "累计充值不能为空", groups = {AddGroup.class, EditGroup.class}) + private BigDecimal totalRecharge; + + /** + * 冻结金额 + */ + @NotNull(message = "冻结金额不能为空", groups = {AddGroup.class, EditGroup.class}) + private BigDecimal freezePrice; + + /** + * 1.个人账户 2.企业账户 + */ + private Long walletType; + + /** + * 冻结状态:0=正常 1=冻结 + */ + private Long freezeStatus; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/bo/PayWalletFreezeBo.java b/src/main/java/com/hotwj/platform/pay/domain/bo/PayWalletFreezeBo.java new file mode 100644 index 0000000..57a44f6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/bo/PayWalletFreezeBo.java @@ -0,0 +1,26 @@ +package com.hotwj.platform.pay.domain.bo; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * 钱包冻结/解冻请求 + * + * @author shihongwei + * @date 2026-05-09 + */ +@Data +public class PayWalletFreezeBo { + + /** + * 钱包ID + */ + @NotNull(message = "钱包ID不能为空") + private Long walletId; + + /** + * 冻结状态:0=正常 1=冻结 + */ + @NotNull(message = "冻结状态不能为空") + private Long freezeStatus; +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/bo/PayWalletTransactionBo.java b/src/main/java/com/hotwj/platform/pay/domain/bo/PayWalletTransactionBo.java new file mode 100644 index 0000000..85e94d4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/bo/PayWalletTransactionBo.java @@ -0,0 +1,83 @@ +package com.hotwj.platform.pay.domain.bo; + +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.pay.domain.PayWalletTransaction; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +import java.math.BigDecimal; + +/** + * 会员钱包流水业务对象 pay_wallet_transaction + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = PayWalletTransaction.class, reverseConvertGenerate = false) +public class PayWalletTransactionBo extends BaseEntity { + + /** + * 编号 + */ + @NotNull(message = "编号不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 钱包 id + */ + @NotNull(message = "钱包 id不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long walletId; + + /** + * 关联类型 + */ + @NotNull(message = "关联类型不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long bizType; + + /** + * 关联业务编号 + */ + @NotBlank(message = "关联业务编号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String bizId; + + /** + * 流水号 + */ + @NotBlank(message = "流水号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String no; + + /** + * 流水标题 + */ + @NotBlank(message = "流水标题不能为空", groups = {AddGroup.class, EditGroup.class}) + private String title; + + /** + * 交易金额 + */ + @NotNull(message = "交易金额不能为空", groups = {AddGroup.class, EditGroup.class}) + private BigDecimal price; + + /** + * 余额 + */ + @NotNull(message = "余额不能为空", groups = {AddGroup.class, EditGroup.class}) + private BigDecimal balance; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + /** + * 备注 + */ + private String remark; +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/vo/PayAppVo.java b/src/main/java/com/hotwj/platform/pay/domain/vo/PayAppVo.java new file mode 100644 index 0000000..19475f3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/vo/PayAppVo.java @@ -0,0 +1,85 @@ +package com.hotwj.platform.pay.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.pay.domain.PayApp; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 支付应用信息视图对象 pay_app + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = PayApp.class) +public class PayAppVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 应用编号 + */ + @ExcelProperty(value = "应用编号") + private Long id; + + /** + * 应用标识 + */ + @ExcelProperty(value = "应用标识") + private String appKey; + + /** + * 应用名 + */ + @ExcelProperty(value = "应用名") + private String name; + + /** + * 开启状态 + */ + @ExcelProperty(value = "开启状态") + private Long status; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 支付结果的回调地址 + */ + @ExcelProperty(value = "支付结果的回调地址") + private String orderNotifyUrl; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/vo/PayChannelVo.java b/src/main/java/com/hotwj/platform/pay/domain/vo/PayChannelVo.java new file mode 100644 index 0000000..af037d7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/vo/PayChannelVo.java @@ -0,0 +1,86 @@ +package com.hotwj.platform.pay.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.pay.domain.PayChannel; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 支付渠道 + * 视图对象 pay_channel + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = PayChannel.class) +public class PayChannelVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 商户编号 + */ + @ExcelProperty(value = "商户编号") + private Long id; + + /** + * 渠道编码 + */ + @ExcelProperty(value = "渠道编码") + private String code; + + /** + * 开启状态 + */ + @ExcelProperty(value = "开启状态") + private Long status; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 应用编号 + */ + @ExcelProperty(value = "应用编号") + private Long appId; + + /** + * 支付渠道配置 + */ + @ExcelProperty(value = "支付渠道配置") + private String config; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/vo/PayCompanyWalletBalanceVo.java b/src/main/java/com/hotwj/platform/pay/domain/vo/PayCompanyWalletBalanceVo.java new file mode 100644 index 0000000..ebfd833 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/vo/PayCompanyWalletBalanceVo.java @@ -0,0 +1,70 @@ +package com.hotwj.platform.pay.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 企业钱包余额分页视图 + * + * @author shihongwei + * @date 2026-05-09 + */ +@Data +public class PayCompanyWalletBalanceVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 钱包ID + */ + private Long payWalletId; + + /** + * 企业ID + */ + private Long companyId; + + /** + * 企业名称 + */ + private String companyName; + + /** + * 企业负责人 + */ + private String responsibleName; + + /** + * 手机号 + */ + private String responsibleMobile; + + /** + * 企业类型 + */ + private String companyType; + + /** + * 行业类型 + */ + private String industry; + + /** + * 企业状态 + */ + private Long status; + + /** + * 账户余额 + */ + private BigDecimal balance; + + /** + * 冻结状态:0=正常 1=冻结 + */ + private Long freezeStatus; +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/vo/PayOrderExtensionVo.java b/src/main/java/com/hotwj/platform/pay/domain/vo/PayOrderExtensionVo.java new file mode 100644 index 0000000..0bf55c8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/vo/PayOrderExtensionVo.java @@ -0,0 +1,116 @@ +package com.hotwj.platform.pay.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.pay.domain.PayOrderExtension; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 支付订单 + * 视图对象 pay_order_extension + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = PayOrderExtension.class) +public class PayOrderExtensionVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 支付订单编号 + */ + @ExcelProperty(value = "支付订单编号") + private Long id; + + /** + * 支付订单号 + */ + @ExcelProperty(value = "支付订单号") + private String no; + + /** + * 支付订单编号 + */ + @ExcelProperty(value = "支付订单编号") + private Long orderId; + + /** + * 渠道编号 + */ + @ExcelProperty(value = "渠道编号") + private Long channelId; + + /** + * 渠道编码 + */ + @ExcelProperty(value = "渠道编码") + private String channelCode; + + /** + * 用户 IP + */ + @ExcelProperty(value = "用户 IP") + private String userIp; + + /** + * 支付状态 + */ + @ExcelProperty(value = "支付状态") + private Long status; + + /** + * 支付渠道的额外参数 + */ + @ExcelProperty(value = "支付渠道的额外参数") + private String channelExtras; + + /** + * 渠道调用报错时,错误码 + */ + @ExcelProperty(value = "渠道调用报错时,错误码") + private String channelErrorCode; + + /** + * 渠道调用报错时,错误信息 + */ + @ExcelProperty(value = "渠道调用报错时,错误信息") + private String channelErrorMsg; + + /** + * 支付渠道异步通知的内容 + */ + @ExcelProperty(value = "支付渠道异步通知的内容") + private String channelNotifyData; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/vo/PayOrderVo.java b/src/main/java/com/hotwj/platform/pay/domain/vo/PayOrderVo.java new file mode 100644 index 0000000..298583b --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/vo/PayOrderVo.java @@ -0,0 +1,174 @@ +package com.hotwj.platform.pay.domain.vo; + +import java.math.BigDecimal; +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonFormat; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.pay.domain.PayOrder; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 支付订单 + * 视图对象 pay_order + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = PayOrder.class) +public class PayOrderVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 支付订单编号 + */ + @ExcelProperty(value = "支付订单编号") + private Long id; + + /** + * 应用编号 + */ + @ExcelProperty(value = "应用编号") + private Long appId; + + /** + * 业务订单号 + */ + @ExcelProperty(value = "业务订单号") + private String merchantOrderNo; + + /** + * 业务类型 + */ + @ExcelProperty(value = "业务类型") + private String bizOrderType; + + /** + * 业务主键 + */ + @ExcelProperty(value = "业务主键") + private Long bizOrderId; + + /** + * 订单标题 + */ + @ExcelProperty(value = "订单标题") + private String subject; + + /** + * 订单描述 + */ + @ExcelProperty(value = "订单描述") + private String body; + + /** + * 渠道编号 + */ + @ExcelProperty(value = "渠道编号") + private Long channelId; + + /** + * 渠道编码 + */ + @ExcelProperty(value = "渠道编码") + private String channelCode; + + /** + * 异步通知地址 + */ + @ExcelProperty(value = "异步通知地址") + private String notifyUrl; + + /** + * 同步跳转地址 + */ + @ExcelProperty(value = "同步跳转地址") + private String returnUrl; + + /** + * 支付金额 + */ + @ExcelProperty(value = "支付金额") + private BigDecimal price; + + /** + * 支付状态 + */ + @ExcelProperty(value = "支付状态") + private Long status; + + /** + * 用户 IP + */ + @ExcelProperty(value = "用户 IP") + private String userIp; + + /** + * 订单失效时间 + */ + @ExcelProperty(value = "订单失效时间") + private Date expireTime; + + /** + * 订单支付成功时间 + */ + @ExcelProperty(value = "订单支付成功时间") + private Date successTime; + + /** + * 支付成功的订单拓展单编号 + */ + @ExcelProperty(value = "支付成功的订单拓展单编号") + private Long extensionId; + + /** + * 支付订单号 + */ + @ExcelProperty(value = "支付订单号") + private String no; + + /** + * 渠道用户编号 + */ + @ExcelProperty(value = "渠道用户编号") + private String channelUserId; + + /** + * 渠道订单号 + */ + @ExcelProperty(value = "渠道订单号") + private String channelOrderNo; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/vo/PayRechargeOrderVo.java b/src/main/java/com/hotwj/platform/pay/domain/vo/PayRechargeOrderVo.java new file mode 100644 index 0000000..3a6e708 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/vo/PayRechargeOrderVo.java @@ -0,0 +1,47 @@ +package com.hotwj.platform.pay.domain.vo; + +import com.hotwj.platform.pay.domain.PayRechargeOrder; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 充值订单视图对象 + */ +@Data +@AutoMapper(target = PayRechargeOrder.class) +public class PayRechargeOrderVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + + private Long companyId; + + private Long accountId; + + private Long userId; + + private String rechargeOrderNo; + + private BigDecimal rechargeAmount; + + private Long payMethod; + + private Long payScene; + + private Long payStatus; + + private String thirdTradeNo; + + private Date paidTime; + + private String failReason; + + private String remark; +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/vo/PayWalletTransactionVo.java b/src/main/java/com/hotwj/platform/pay/domain/vo/PayWalletTransactionVo.java new file mode 100644 index 0000000..9860d41 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/vo/PayWalletTransactionVo.java @@ -0,0 +1,102 @@ +package com.hotwj.platform.pay.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.pay.domain.PayWalletTransaction; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 会员钱包流水视图对象 pay_wallet_transaction + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = PayWalletTransaction.class) +public class PayWalletTransactionVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 编号 + */ + @ExcelProperty(value = "编号") + private Long id; + + /** + * 钱包 id + */ + @ExcelProperty(value = "钱包 id") + private Long walletId; + + /** + * 关联类型 + */ + @ExcelProperty(value = "关联类型") + private Long bizType; + + /** + * 关联业务编号 + */ + @ExcelProperty(value = "关联业务编号") + private String bizId; + + /** + * 流水号 + */ + @ExcelProperty(value = "流水号") + private String no; + + /** + * 流水标题 + */ + @ExcelProperty(value = "流水标题") + private String title; + + /** + * 交易金额 + */ + @ExcelProperty(value = "交易金额") + private BigDecimal price; + + /** + * 余额 + */ + @ExcelProperty(value = "余额") + private BigDecimal balance; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; +} diff --git a/src/main/java/com/hotwj/platform/pay/domain/vo/PayWalletVo.java b/src/main/java/com/hotwj/platform/pay/domain/vo/PayWalletVo.java new file mode 100644 index 0000000..9472189 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/domain/vo/PayWalletVo.java @@ -0,0 +1,99 @@ +package com.hotwj.platform.pay.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.pay.domain.PayWallet; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 会员钱包视图对象 pay_wallet + * + * @author shihongwei + * @date 2026-05-08 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = PayWallet.class) +public class PayWalletVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 编号 + */ + @ExcelProperty(value = "编号") + private Long id; + + /** + * 用户编号(企业则为企业id) + */ + @ExcelProperty(value = "用户编号", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "企=业则为企业id") + private Long userId; + + /** + * 余额 + */ + @ExcelProperty(value = "余额") + private BigDecimal balance; + + /** + * 累计支出 + */ + @ExcelProperty(value = "累计支出") + private BigDecimal totalExpense; + + /** + * 累计充值 + */ + @ExcelProperty(value = "累计充值") + private BigDecimal totalRecharge; + + /** + * 冻结金额 + */ + @ExcelProperty(value = "冻结金额") + private BigDecimal freezePrice; + + /** + * 1.个人账户 2.企业账户 + */ + @ExcelProperty(value = "1.个人账户 2.企业账户") + private Long walletType; + + /** + * 冻结状态:0=正常 1=冻结 + */ + @ExcelProperty(value = "冻结状态:0正常 1冻结") + private Long freezeStatus; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/pay/enums/PayChannelCodeEnum.java b/src/main/java/com/hotwj/platform/pay/enums/PayChannelCodeEnum.java new file mode 100644 index 0000000..8e506ce --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/enums/PayChannelCodeEnum.java @@ -0,0 +1,37 @@ +package com.hotwj.platform.pay.enums; + +import lombok.Getter; + +import java.util.Arrays; + +@Getter +public enum PayChannelCodeEnum { + + WECHAT_QR("WECHAT_QR", "WECHAT", "QR", 1L, 4L), + WECHAT_H5("WECHAT_H5", "WECHAT", "H5", 1L, 1L), + WECHAT_APP("WECHAT_APP", "WECHAT", "APP", 1L, 3L), + ALIPAY_QR("ALIPAY_QR", "ALIPAY", "QR", 2L, 4L), + ALIPAY_H5("ALIPAY_H5", "ALIPAY", "H5", 2L, 1L), + ALIPAY_APP("ALIPAY_APP", "ALIPAY", "APP", 2L, 3L); + + private final String code; + private final String provider; + private final String scene; + private final Long payMethod; + private final Long payScene; + + PayChannelCodeEnum(String code, String provider, String scene, Long payMethod, Long payScene) { + this.code = code; + this.provider = provider; + this.scene = scene; + this.payMethod = payMethod; + this.payScene = payScene; + } + + public static PayChannelCodeEnum of(String provider, String scene) { + return Arrays.stream(values()) + .filter(item -> item.provider.equalsIgnoreCase(provider) && item.scene.equalsIgnoreCase(scene)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("不支持的支付方式组合: " + provider + "-" + scene)); + } +} diff --git a/src/main/java/com/hotwj/platform/pay/enums/PayOrderBizTypeEnum.java b/src/main/java/com/hotwj/platform/pay/enums/PayOrderBizTypeEnum.java new file mode 100644 index 0000000..d8f3963 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/enums/PayOrderBizTypeEnum.java @@ -0,0 +1,18 @@ +package com.hotwj.platform.pay.enums; + +import lombok.Getter; + +@Getter +public enum PayOrderBizTypeEnum { + + FUND_RECHARGE("FUND_RECHARGE", "钱包充值"), + HOUR_PACKAGE("HOUR_PACKAGE", "课时套餐购买"); + + private final String code; + private final String description; + + PayOrderBizTypeEnum(String code, String description) { + this.code = code; + this.description = description; + } +} diff --git a/src/main/java/com/hotwj/platform/pay/enums/PayOrderStatusEnum.java b/src/main/java/com/hotwj/platform/pay/enums/PayOrderStatusEnum.java new file mode 100644 index 0000000..3b01ea1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/enums/PayOrderStatusEnum.java @@ -0,0 +1,20 @@ +package com.hotwj.platform.pay.enums; + +public enum PayOrderStatusEnum { + + WAITING(1L), + PAYING(2L), + SUCCESS(3L), + FAILED(4L), + CLOSED(5L); + + private final Long status; + + PayOrderStatusEnum(Long status) { + this.status = status; + } + + public Long getStatus() { + return status; + } +} diff --git a/src/main/java/com/hotwj/platform/pay/mapper/PayAppMapper.java b/src/main/java/com/hotwj/platform/pay/mapper/PayAppMapper.java new file mode 100644 index 0000000..6e4da12 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/mapper/PayAppMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.pay.mapper; + +import com.hotwj.platform.pay.domain.PayApp; +import com.hotwj.platform.pay.domain.vo.PayAppVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 支付应用信息Mapper接口 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Mapper +public interface PayAppMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/pay/mapper/PayChannelMapper.java b/src/main/java/com/hotwj/platform/pay/mapper/PayChannelMapper.java new file mode 100644 index 0000000..bc64204 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/mapper/PayChannelMapper.java @@ -0,0 +1,18 @@ +package com.hotwj.platform.pay.mapper; + +import com.hotwj.platform.pay.domain.PayChannel; +import com.hotwj.platform.pay.domain.vo.PayChannelVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 支付渠道 + * Mapper接口 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Mapper +public interface PayChannelMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/pay/mapper/PayOrderExtensionMapper.java b/src/main/java/com/hotwj/platform/pay/mapper/PayOrderExtensionMapper.java new file mode 100644 index 0000000..6bb290b --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/mapper/PayOrderExtensionMapper.java @@ -0,0 +1,18 @@ +package com.hotwj.platform.pay.mapper; + +import com.hotwj.platform.pay.domain.PayOrderExtension; +import com.hotwj.platform.pay.domain.vo.PayOrderExtensionVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 支付订单 + * Mapper接口 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Mapper +public interface PayOrderExtensionMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/pay/mapper/PayOrderMapper.java b/src/main/java/com/hotwj/platform/pay/mapper/PayOrderMapper.java new file mode 100644 index 0000000..9c4bd7d --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/mapper/PayOrderMapper.java @@ -0,0 +1,18 @@ +package com.hotwj.platform.pay.mapper; + +import com.hotwj.platform.pay.domain.PayOrder; +import com.hotwj.platform.pay.domain.vo.PayOrderVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 支付订单 + * Mapper接口 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Mapper +public interface PayOrderMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/pay/mapper/PayRechargeOrderMapper.java b/src/main/java/com/hotwj/platform/pay/mapper/PayRechargeOrderMapper.java new file mode 100644 index 0000000..0f91f39 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/mapper/PayRechargeOrderMapper.java @@ -0,0 +1,13 @@ +package com.hotwj.platform.pay.mapper; + +import com.hotwj.platform.pay.domain.PayRechargeOrder; +import com.hotwj.platform.pay.domain.vo.PayRechargeOrderVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 充值订单Mapper接口 + */ +@Mapper +public interface PayRechargeOrderMapper extends BaseMapperPlus { +} diff --git a/src/main/java/com/hotwj/platform/pay/mapper/PayWalletMapper.java b/src/main/java/com/hotwj/platform/pay/mapper/PayWalletMapper.java new file mode 100644 index 0000000..9848151 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/mapper/PayWalletMapper.java @@ -0,0 +1,28 @@ +package com.hotwj.platform.pay.mapper; + +import com.hotwj.platform.pay.domain.PayWallet; +import com.hotwj.platform.pay.domain.vo.PayCompanyWalletBalanceVo; +import com.hotwj.platform.pay.domain.vo.PayWalletVo; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 会员钱包Mapper接口 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Mapper +public interface PayWalletMapper extends BaseMapperPlus { + + /** + * 分页查询企业钱包余额列表 + * + * @param page 分页参数 + * @param keyword 模糊关键词(企业名/负责人/手机号) + * @return 分页结果 + */ + Page selectCompanyWalletPage(Page page, @Param("keyword") String keyword); +} diff --git a/src/main/java/com/hotwj/platform/pay/mapper/PayWalletTransactionMapper.java b/src/main/java/com/hotwj/platform/pay/mapper/PayWalletTransactionMapper.java new file mode 100644 index 0000000..ac92a43 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/mapper/PayWalletTransactionMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.pay.mapper; + +import com.hotwj.platform.pay.domain.PayWalletTransaction; +import com.hotwj.platform.pay.domain.vo.PayWalletTransactionVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 会员钱包流水Mapper接口 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Mapper +public interface PayWalletTransactionMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/pay/pay.md b/src/main/java/com/hotwj/platform/pay/pay.md new file mode 100644 index 0000000..335e331 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/pay.md @@ -0,0 +1,422 @@ +# Pay 模块前端对接文档 + +## 1. 说明 + +- 模块路径:`/pay/open`(前台充值支付) +- 本文重点:前端发起钱包充值、拉起支付、轮询支付状态 +- 统一响应结构:`R` + +```json +{ + "code": 200, + "msg": "操作成功", + "data": {} +} +``` + +- `code = 200` 表示成功,非 `200` 视为失败 + +## 2. 鉴权要求 + +- `POST /pay/open/recharge/unified-order` +- `GET /pay/open/recharge/status/{rechargeOrderNo}` + +以上两个接口都需要登录态(与系统统一鉴权方式一致,前端按当前项目 token 方案携带请求头即可)。 + +第三方支付回调接口由支付平台调用,前端不用调用: + +- `POST /pay/open/notify/alipay/{channelId}` +- `POST /pay/open/notify/wechat/{channelId}` + +## 3. 枚举约定 + +### _3.1 下单参数枚举_ + +- `provider`:`WECHAT` / `ALIPAY` +- `scene`:`QR` / `H5` / `APP` +- `walletType`:`1` 个人钱包,`2` 企业钱包(默认 `2`) + +支持的有效组合: + +- `WECHAT + QR` -> `WECHAT_QR` +- `WECHAT + H5` -> `WECHAT_H5` +- `WECHAT + APP` -> `WECHAT_APP` +- `ALIPAY + QR` -> `ALIPAY_QR` +- `ALIPAY + H5` -> `ALIPAY_H5` +- `ALIPAY + APP` -> `ALIPAY_APP` + +### 3.2 支付状态 + +- `1` 待支付(WAITING) +- `2` 支付中(PAYING) +- `3` 支付成功(SUCCESS) +- `4` 支付失败(FAILED) +- `5` 已关闭(CLOSED) + +### 3.3 展示模式(下单响应) + +- `QR_CODE`:展示二维码(用 `codeUrl`) +- `REDIRECT`:跳转支付链接(用 `payUrl`) +- `APP`:APP 拉起支付(`orderString` 或 `appPayParams`) + +## 4. 接口一:统一下单 + +- 地址:`POST /pay/open/recharge/unified-order` +- Content-Type:`application/json` + +### 4.1 请求参数 + +```json +{ + "amount": 100.00, + "provider": "WECHAT", + "scene": "H5", + "walletType": 2, + "returnUrl": "https://your-frontend.com/pay-return" +} +``` + +字段说明: + +- `amount`:充值金额,必须大于 `0.01` +- `provider`:支付渠道类型(`WECHAT` / `ALIPAY`) +- `scene`:支付场景(`QR` / `H5` / `APP`) +- `walletType`:钱包类型(可不传,默认企业钱包 `2`) +- `returnUrl`:支付宝 H5 跳转地址建议传;微信场景可按需传 + +防重说明: + +- 同一用户同一钱包在存在“待支付/支付中且未过期”的充值订单时,后端会拒绝重复下单。 +- 返回错误示例:`存在待支付充值订单,请先完成支付(订单号:RC...)` + +### 4.2 响应示例 + +```json +{ + "code": 200, + "msg": "操作成功", + "data": { + "rechargeOrderNo": "RC20260509143000XXXXXXXXXX", + "payOrderNo": "PO20260509143000XXXXXXXXXX", + "channelCode": "WECHAT_H5", + "displayMode": "REDIRECT", + "codeUrl": null, + "payUrl": "https://wx.tenpay.com/...", + "orderString": null, + "appPayParams": null, + "expireTime": "2026-05-09T14:33:00.000+08:00" + } +} +``` + +`APP` 场景返回示例(微信): + +```json +{ + "displayMode": "APP", + "appPayParams": { + "appid": "wx...", + "partnerid": "19...", + "prepayid": "wx2014...", + "package": "Sign=WXPay", + "noncestr": "xxx", + "timestamp": "1715239200", + "sign": "xxx" + } +} +``` + +`APP` 场景返回示例(支付宝): + +```json +{ + "displayMode": "APP", + "orderString": "app_id=...&method=alipay.trade.app.pay&..." +} +``` + +## 5. 接口二:查询充值状态 + +- 地址:`GET /pay/open/recharge/status/{rechargeOrderNo}` + +### 5.1 响应示例 + +```json +{ + "code": 200, + "msg": "操作成功", + "data": { + "rechargeOrderNo": "RC20260509143000XXXXXXXXXX", + "payOrderNo": "PO20260509143000XXXXXXXXXX", + "payStatus": 2, + "amount": 100.00, + "thirdTradeNo": null, + "paidTime": null + } +} +``` + +## 6. 接口三:查询可用支付方式 + +- 地址:`GET /pay/open/recharge/channels` + +### 6.1 响应示例 + +```json +{ + "code": 200, + "msg": "操作成功", + "data": [ + { + "provider": "ALIPAY", + "scene": "H5", + "channelCode": "ALIPAY_H5", + "payMethod": 2, + "payScene": 1, + "displayName": "支付宝H5" + }, + { + "provider": "ALIPAY", + "scene": "APP", + "channelCode": "ALIPAY_APP", + "payMethod": 2, + "payScene": 3, + "displayName": "支付宝APP" + } + ] +} +``` + +## 7. 钱包充值流程图(前端调用顺序) + +```mermaid +sequenceDiagram + autonumber + participant FE as 前端 + participant BE as 后端(/pay/open) + participant ALI as 支付宝/微信 + + FE->>BE: POST /recharge/unified-order + BE-->>FE: R\n(displayMode + payUrl/codeUrl/orderString/appPayParams + rechargeOrderNo) + + alt displayMode = QR_CODE + FE->>FE: 渲染二维码(codeUrl)\n用户扫码支付 + else displayMode = REDIRECT + FE->>ALI: 浏览器跳转 payUrl + else displayMode = APP + FE->>ALI: APP 拉起支付\n(支付宝 orderString / 微信 appPayParams) + end + + ALI-->>BE: 异步回调 notify\n(前端不调用) + loop 每2秒轮询一次(最多90秒) + FE->>BE: GET /recharge/status/{rechargeOrderNo} + BE-->>FE: R(payStatus) + end + + alt payStatus = 3 + FE->>FE: 展示支付成功 + else payStatus = 4 or 5 + FE->>FE: 展示支付失败/已关闭 + end +``` + +## 8. 前端依次调用哪些接口 + +1. 可先查余额:`GET /pay/open/wallet/balance?walletType=2`(不传默认企业钱包)。 +2. 再调方式接口:`GET /pay/open/recharge/channels`,渲染可选支付方式。 +3. 用户选中支付方式后,再调下单:`POST /pay/open/recharge/unified-order`(传 `provider+scene`)。 +4. 根据返回字段拉起支付: + - `displayMode=QR_CODE` -> 使用 `codeUrl` + - `displayMode=REDIRECT` -> 跳转 `payUrl` + - `displayMode=APP` -> 支付宝用 `orderString`,微信用 `appPayParams` +5. 保存 `rechargeOrderNo`,开始轮询:`GET /pay/open/recharge/status/{rechargeOrderNo}` +6. 轮询终止条件: + - `payStatus=3`:成功,停止轮询 + - `payStatus=4/5`:失败或关闭,停止轮询 + - 超时(建议 90 秒):停止轮询并提示用户手动刷新 + +说明:`/pay/open/notify/alipay/{channelId}` 与 `/pay/open/notify/wechat/{channelId}` 是三方回调接口,由支付宝/微信调用,前端无需调用。 + +## 9. 前端最小调用示例(Axios) + +```ts +import axios from "axios"; + +export async function createRechargeOrder(payload: { + amount: number; + provider: "WECHAT" | "ALIPAY"; + scene: "QR" | "H5" | "APP"; + walletType?: 1 | 2; + returnUrl?: string; +}) { + const res = await axios.post("/pay/open/recharge/unified-order", payload); + if (res.data?.code !== 200) throw new Error(res.data?.msg || "下单失败"); + return res.data.data; +} + +export async function listRechargeChannels() { + const res = await axios.get("/pay/open/recharge/channels"); + if (res.data?.code !== 200) throw new Error(res.data?.msg || "获取支付方式失败"); + return res.data.data; +} + +export async function queryWalletBalance(walletType = 2) { + const res = await axios.get("/pay/open/wallet/balance", {params: {walletType}}); + if (res.data?.code !== 200) throw new Error(res.data?.msg || "查询余额失败"); + return res.data.data; +} + +export async function queryRechargeStatus(rechargeOrderNo: string) { + const res = await axios.get(`/pay/open/recharge/status/${rechargeOrderNo}`); + if (res.data?.code !== 200) throw new Error(res.data?.msg || "查询失败"); + return res.data.data; +} +``` + +## 10. 总部批量充值企业钱包 + +- 地址:`POST /platform/wallet/batchRechargeCompany` +- 权限:`platform:wallet:recharge` +- 说明:按企业ID列表批量给企业钱包加余额;若企业钱包不存在则自动创建;每个企业写一条钱包流水(含备注) + +请求示例: + +```json +{ + "companyIds": "1,2,3", + "amount": 100.00, + "remark": "总部活动补贴" +} +``` + +返回示例(`data` 为成功充值企业数量): + +```json +{ + "code": 200, + "msg": "操作成功", + "data": 3 +} +``` + +## 11. 钱包冻结/解冻 + +- 地址:`POST /platform/wallet/freeze` +- 权限:`platform:wallet:edit` +- 说明:总部端可冻结或解冻指定钱包;冻结后,钱包不能充值和使用 + +请求示例: + +```json +{ + "walletId": 1, + "freezeStatus": 1 +} +``` + +字段说明: + +- `walletId`:钱包ID +- `freezeStatus`:冻结状态,`0=正常`,`1=冻结` + +返回示例: + +```json +{ + "code": 200, + "msg": "操作成功", + "data": null +} +``` + +## 12. 后台管理接口(可选) + +以下是 `pay` 模块后台管理接口前缀(用于运营后台,不是 C 端支付主流程): + +- `/platform/app`:支付应用管理 +- `/platform/channel`:支付渠道管理 +- `/platform/order`:支付订单管理 +- `/platform/orderExtension`:支付订单拓展单管理 +- `/platform/wallet`:钱包管理 +- `/platform/walletTransaction`:钱包流水管理 + +钱包管理补充接口: + +- `GET /platform/wallet/companyBalanceList`:分页返回企业名称、企业负责人、手机号、账户余额(支持 `keyword` 搜索企业名/负责人/手机号) +- `GET /platform/wallet/headquarters/balance?walletId=xxx`:总部端按钱包ID查询钱包余额 +- `POST /platform/wallet/batchRechargeCompany`:总部批量充值企业钱包 +- `POST /platform/wallet/freeze`:总部端冻结/解冻钱包;冻结后钱包不能充值和使用 +- `GET /platform/walletTransaction/headquarters/list?walletId=xxx`:总部端按钱包ID分页查询钱包流水 + +权限点均为 `platform:*` 形式(如 `platform:wallet:list`)。 + +## 13. Pay 表查询接口对照 + +说明: + +- 下表包含 `pay` 模块涉及的主要表及其“查询接口”。 +- `开放接口` 主要给前端 C 端使用;`后台接口` 主要给运营后台使用(需 `platform:*` 权限)。 + +| 表名 | 用途 | 查询接口 | +|--------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `pay_app` | 支付应用配置 | `GET /platform/app/list`(分页),`GET /platform/app/{id}`(详情) | +| `pay_channel` | 支付渠道配置 | `GET /platform/channel/list`(分页),`GET /platform/channel/{id}`(详情),`GET /pay/open/recharge/channels`(开放:可用支付方式) | +| `pay_order` | 支付主订单 | `GET /platform/order/list`(分页),`GET /platform/order/{id}`(详情) | +| `pay_order_extension` | 支付拓展单(三方侧单号等) | `GET /platform/orderExtension/list`(分页),`GET /platform/orderExtension/{id}`(详情) | +| `pay_recharge_order` | 充值业务单 | 当前无独立后台 Controller;开放侧可用 `GET /pay/open/recharge/status/{rechargeOrderNo}` 按充值单号查询状态 | +| `pay_wallet` | 钱包主表(个人/企业) | `GET /platform/wallet/list`(分页),`GET /platform/wallet/{id}`(详情),`GET /platform/wallet/companyBalanceList`(企业余额分页),`GET /platform/wallet/headquarters/balance?walletId=xxx`(总部端:按钱包ID查余额),`GET /pay/open/wallet/balance?walletType=1/2`(开放:余额),`POST /platform/wallet/batchRechargeCompany`(总部批量充值企业钱包),`POST /platform/wallet/freeze`(冻结/解冻钱包) | +| `pay_wallet_transaction` | 钱包流水 | `GET /platform/walletTransaction/list`(分页),`GET /platform/walletTransaction/{id}`(详情),`GET /platform/walletTransaction/company/current/list`(企业端:当前企业流水分页),`GET /platform/walletTransaction/company/list?companyId=xxx`(总部端:指定企业流水分页),`GET /platform/walletTransaction/headquarters/list?walletId=xxx`(总部端:按钱包ID分页查流水) | + +## 14. Pay 表字段总览 + +说明: + +- 以下字段按当前 `pay` 模块实体与现有建表脚本整理。 +- 通用审计字段统一为:`create_dept`、`create_by`、`create_by_name`、`create_time`、`update_by`、`update_by_name`、 + `update_time`、`is_deleted`。 + +### 14.1 pay_app + +- 业务字段:`id`、`app_key`、`name`、`status`、`remark`、`order_notify_url` +- 通用字段:`create_dept`、`create_by`、`create_by_name`、`create_time`、`update_by`、`update_by_name`、`update_time`、 + `is_deleted` + +### 14.2 pay_channel + +- 业务字段:`id`、`code`、`status`、`remark`、`app_id`、`config` +- 通用字段:`create_dept`、`create_by`、`create_by_name`、`create_time`、`update_by`、`update_by_name`、`update_time`、 + `is_deleted` + +### 14.3 pay_order + +- 业务字段:`id`、`app_id`、`merchant_order_no`、`biz_order_type`、`biz_order_id`、`subject`、`body`、`channel_id`、 + `channel_code`、`notify_url`、`return_url`、`price`、`status`、`user_ip`、`expire_time`、`success_time`、`extension_id`、`no`、 + `channel_user_id`、`channel_order_no` +- 通用字段:`create_dept`、`create_by`、`create_by_name`、`create_time`、`update_by`、`update_by_name`、`update_time`、 + `is_deleted` + +### 14.4 pay_order_extension + +- 业务字段:`id`、`no`、`order_id`、`channel_id`、`channel_code`、`user_ip`、`status`、`channel_extras`、`channel_error_code`、 + `channel_error_msg`、`channel_notify_data` +- 通用字段:`create_dept`、`create_by`、`create_by_name`、`create_time`、`update_by`、`update_by_name`、`update_time`、 + `is_deleted` + +### 14.5 pay_recharge_order + +- 业务字段:`id`、`company_id`、`account_id`、`user_id`、`recharge_order_no`、`recharge_amount`、`pay_method`、`pay_scene`、 + `pay_status`、`third_trade_no`、`paid_time`、`fail_reason`、`remark` +- 通用字段:`create_dept`、`create_by`、`create_by_name`、`create_time`、`update_by`、`update_by_name`、`update_time`、 + `is_deleted` + +### 14.6 pay_wallet + +- 业务字段:`id`、`user_id`、`balance`、`total_expense`、`total_recharge`、`freeze_price`、`wallet_type`、`freeze_status` +- 通用字段:`create_dept`、`create_by`、`create_by_name`、`create_time`、`update_by`、`update_by_name`、`update_time`、 + `is_deleted` + +### 14.7 pay_wallet_transaction + +- 业务字段:`id`、`wallet_id`、`biz_type`、`biz_id`、`no`、`title`、`price`、`balance` +- 通用字段:`create_dept`、`create_by`、`create_by_name`、`create_time`、`update_by`、`update_by_name`、`update_time`、 + `is_deleted` diff --git a/src/main/java/com/hotwj/platform/pay/service/IPayAppService.java b/src/main/java/com/hotwj/platform/pay/service/IPayAppService.java new file mode 100644 index 0000000..e9a3aea --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/service/IPayAppService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.pay.service; + +import com.hotwj.platform.pay.domain.bo.PayAppBo; +import com.hotwj.platform.pay.domain.vo.PayAppVo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 支付应用信息Service接口 + * + * @author shihongwei + * @date 2026-05-08 + */ +public interface IPayAppService { + + /** + * 查询支付应用信息 + * + * @param id 主键 + * @return 支付应用信息 + */ + PayAppVo queryById(Long id); + + /** + * 分页查询支付应用信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 支付应用信息分页列表 + */ + TableDataInfo queryPageList(PayAppBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的支付应用信息列表 + * + * @param bo 查询条件 + * @return 支付应用信息列表 + */ + List queryList(PayAppBo bo); + + /** + * 新增支付应用信息 + * + * @param bo 支付应用信息 + * @return 是否新增成功 + */ + Boolean insertByBo(PayAppBo bo); + + /** + * 修改支付应用信息 + * + * @param bo 支付应用信息 + * @return 是否修改成功 + */ + Boolean updateByBo(PayAppBo bo); + + /** + * 校验并批量删除支付应用信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/pay/service/IPayChannelService.java b/src/main/java/com/hotwj/platform/pay/service/IPayChannelService.java new file mode 100644 index 0000000..2f0778a --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/service/IPayChannelService.java @@ -0,0 +1,74 @@ +package com.hotwj.platform.pay.service; + +import com.hotwj.platform.pay.domain.bo.PayChannelBo; +import com.hotwj.platform.pay.domain.vo.PayChannelVo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 支付渠道 + * Service接口 + * + * @author shihongwei + * @date 2026-05-08 + */ +public interface IPayChannelService { + + /** + * 查询支付渠道 + * + * @param id 主键 + * @return 支付渠道 + */ + PayChannelVo queryById(Long id); + + /** + * 分页查询支付渠道 + * 列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 支付渠道 + * 分页列表 + */ + TableDataInfo queryPageList(PayChannelBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的支付渠道 + * 列表 + * + * @param bo 查询条件 + * @return 支付渠道 + * 列表 + */ + List queryList(PayChannelBo bo); + + /** + * 新增支付渠道 + * + * @param bo 支付渠道 + * @return 是否新增成功 + */ + Boolean insertByBo(PayChannelBo bo); + + /** + * 修改支付渠道 + * + * @param bo 支付渠道 + * @return 是否修改成功 + */ + Boolean updateByBo(PayChannelBo bo); + + /** + * 校验并批量删除支付渠道 + * 信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/pay/service/IPayOrderExtensionService.java b/src/main/java/com/hotwj/platform/pay/service/IPayOrderExtensionService.java new file mode 100644 index 0000000..504a359 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/service/IPayOrderExtensionService.java @@ -0,0 +1,74 @@ +package com.hotwj.platform.pay.service; + +import com.hotwj.platform.pay.domain.bo.PayOrderExtensionBo; +import com.hotwj.platform.pay.domain.vo.PayOrderExtensionVo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 支付订单 + * Service接口 + * + * @author shihongwei + * @date 2026-05-08 + */ +public interface IPayOrderExtensionService { + + /** + * 查询支付订单 + * + * @param id 主键 + * @return 支付订单 + */ + PayOrderExtensionVo queryById(Long id); + + /** + * 分页查询支付订单 + * 列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 支付订单 + * 分页列表 + */ + TableDataInfo queryPageList(PayOrderExtensionBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的支付订单 + * 列表 + * + * @param bo 查询条件 + * @return 支付订单 + * 列表 + */ + List queryList(PayOrderExtensionBo bo); + + /** + * 新增支付订单 + * + * @param bo 支付订单 + * @return 是否新增成功 + */ + Boolean insertByBo(PayOrderExtensionBo bo); + + /** + * 修改支付订单 + * + * @param bo 支付订单 + * @return 是否修改成功 + */ + Boolean updateByBo(PayOrderExtensionBo bo); + + /** + * 校验并批量删除支付订单 + * 信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/pay/service/IPayOrderService.java b/src/main/java/com/hotwj/platform/pay/service/IPayOrderService.java new file mode 100644 index 0000000..c3c0b0b --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/service/IPayOrderService.java @@ -0,0 +1,74 @@ +package com.hotwj.platform.pay.service; + +import com.hotwj.platform.pay.domain.bo.PayOrderBo; +import com.hotwj.platform.pay.domain.vo.PayOrderVo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 支付订单 + * Service接口 + * + * @author shihongwei + * @date 2026-05-08 + */ +public interface IPayOrderService { + + /** + * 查询支付订单 + * + * @param id 主键 + * @return 支付订单 + */ + PayOrderVo queryById(Long id); + + /** + * 分页查询支付订单 + * 列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 支付订单 + * 分页列表 + */ + TableDataInfo queryPageList(PayOrderBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的支付订单 + * 列表 + * + * @param bo 查询条件 + * @return 支付订单 + * 列表 + */ + List queryList(PayOrderBo bo); + + /** + * 新增支付订单 + * + * @param bo 支付订单 + * @return 是否新增成功 + */ + Boolean insertByBo(PayOrderBo bo); + + /** + * 修改支付订单 + * + * @param bo 支付订单 + * @return 是否修改成功 + */ + Boolean updateByBo(PayOrderBo bo); + + /** + * 校验并批量删除支付订单 + * 信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/pay/service/IPayWalletService.java b/src/main/java/com/hotwj/platform/pay/service/IPayWalletService.java new file mode 100644 index 0000000..803b8ae --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/service/IPayWalletService.java @@ -0,0 +1,96 @@ +package com.hotwj.platform.pay.service; + +import com.hotwj.platform.pay.domain.bo.PayWalletBo; +import com.hotwj.platform.pay.domain.bo.PayWalletBatchRechargeBo; +import com.hotwj.platform.pay.domain.bo.PayWalletFreezeBo; +import com.hotwj.platform.pay.domain.vo.PayCompanyWalletBalanceVo; +import com.hotwj.platform.pay.domain.vo.PayWalletVo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 会员钱包Service接口 + * + * @author shihongwei + * @date 2026-05-08 + */ +public interface IPayWalletService { + + /** + * 查询会员钱包 + * + * @param id 主键 + * @return 会员钱包 + */ + PayWalletVo queryById(Long id); + + /** + * 分页查询会员钱包列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 会员钱包分页列表 + */ + TableDataInfo queryPageList(PayWalletBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的会员钱包列表 + * + * @param bo 查询条件 + * @return 会员钱包列表 + */ + List queryList(PayWalletBo bo); + + /** + * 新增会员钱包 + * + * @param bo 会员钱包 + * @return 是否新增成功 + */ + Boolean insertByBo(PayWalletBo bo); + + /** + * 修改会员钱包 + * + * @param bo 会员钱包 + * @return 是否修改成功 + */ + Boolean updateByBo(PayWalletBo bo); + + /** + * 校验并批量删除会员钱包信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 总部批量充值企业钱包 + * + * @param bo 批量充值参数 + * @return 成功充值企业数量 + */ + Integer batchRechargeCompanyWallet(PayWalletBatchRechargeBo bo); + + /** + * 分页查询企业钱包余额列表 + * + * @param keyword 企业名/负责人/手机号关键词 + * @param pageQuery 分页参数 + * @return 企业钱包余额分页数据 + */ + TableDataInfo queryCompanyWalletPage(String keyword, PageQuery pageQuery); + + /** + * 钱包冻结/解冻 + * + * @param bo 冻结参数 + * @return 是否成功 + */ + Boolean changeFreezeStatus(PayWalletFreezeBo bo); +} diff --git a/src/main/java/com/hotwj/platform/pay/service/IPayWalletTransactionService.java b/src/main/java/com/hotwj/platform/pay/service/IPayWalletTransactionService.java new file mode 100644 index 0000000..004ccdd --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/service/IPayWalletTransactionService.java @@ -0,0 +1,78 @@ +package com.hotwj.platform.pay.service; + +import com.hotwj.platform.pay.domain.bo.PayWalletTransactionBo; +import com.hotwj.platform.pay.domain.vo.PayWalletTransactionVo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 会员钱包流水Service接口 + * + * @author shihongwei + * @date 2026-05-08 + */ +public interface IPayWalletTransactionService { + + /** + * 查询会员钱包流水 + * + * @param id 主键 + * @return 会员钱包流水 + */ + PayWalletTransactionVo queryById(Long id); + + /** + * 分页查询会员钱包流水列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 会员钱包流水分页列表 + */ + TableDataInfo queryPageList(PayWalletTransactionBo bo, PageQuery pageQuery); + + /** + * 分页查询企业钱包流水列表 + * + * @param companyId 企业ID + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 企业钱包流水分页列表 + */ + TableDataInfo queryCompanyWalletPage(Long companyId, PayWalletTransactionBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的会员钱包流水列表 + * + * @param bo 查询条件 + * @return 会员钱包流水列表 + */ + List queryList(PayWalletTransactionBo bo); + + /** + * 新增会员钱包流水 + * + * @param bo 会员钱包流水 + * @return 是否新增成功 + */ + Boolean insertByBo(PayWalletTransactionBo bo); + + /** + * 修改会员钱包流水 + * + * @param bo 会员钱包流水 + * @return 是否修改成功 + */ + Boolean updateByBo(PayWalletTransactionBo bo); + + /** + * 校验并批量删除会员钱包流水信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/pay/service/PayRechargeOpenService.java b/src/main/java/com/hotwj/platform/pay/service/PayRechargeOpenService.java new file mode 100644 index 0000000..7a1b199 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/service/PayRechargeOpenService.java @@ -0,0 +1,28 @@ +package com.hotwj.platform.pay.service; + +import com.hotwj.platform.pay.controller.vo.PayRechargeStatusRespVO; +import com.hotwj.platform.pay.controller.vo.PayRechargeChannelRespVO; +import com.hotwj.platform.pay.controller.vo.PayRechargeUnifiedOrderReqVO; +import com.hotwj.platform.pay.controller.vo.PayRechargeUnifiedOrderRespVO; +import com.hotwj.platform.pay.controller.vo.PayWalletBalanceRespVO; +import jakarta.servlet.http.HttpServletRequest; + +import java.util.List; +import java.util.Map; + +public interface PayRechargeOpenService { + + PayRechargeUnifiedOrderRespVO unifiedOrder(PayRechargeUnifiedOrderReqVO reqVO, HttpServletRequest request); + + List listRechargeChannels(); + + PayWalletBalanceRespVO queryWalletBalance(Long walletType); + + PayRechargeStatusRespVO queryRechargeStatus(String rechargeOrderNo); + + String handleAlipayNotify(Long channelId, Map params); + + String handleWechatNotify(Long channelId, String body, HttpServletRequest request); + + void compensatePayOrders(); +} diff --git a/src/main/java/com/hotwj/platform/pay/service/impl/PayAppServiceImpl.java b/src/main/java/com/hotwj/platform/pay/service/impl/PayAppServiceImpl.java new file mode 100644 index 0000000..2ec594f --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/service/impl/PayAppServiceImpl.java @@ -0,0 +1,138 @@ +package com.hotwj.platform.pay.service.impl; + +import com.hotwj.platform.pay.domain.PayApp; +import com.hotwj.platform.pay.domain.bo.PayAppBo; +import com.hotwj.platform.pay.domain.vo.PayAppVo; +import com.hotwj.platform.pay.mapper.PayAppMapper; +import com.hotwj.platform.pay.service.IPayAppService; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 支付应用信息Service业务层处理 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class PayAppServiceImpl implements IPayAppService { + + private final PayAppMapper baseMapper; + + /** + * 查询支付应用信息 + * + * @param id 主键 + * @return 支付应用信息 + */ + @Override + public PayAppVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询支付应用信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 支付应用信息分页列表 + */ + @Override + public TableDataInfo queryPageList(PayAppBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的支付应用信息列表 + * + * @param bo 查询条件 + * @return 支付应用信息列表 + */ + @Override + public List queryList(PayAppBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(PayAppBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(PayApp::getId); + lqw.eq(StringUtils.isNotBlank(bo.getAppKey()), PayApp::getAppKey, bo.getAppKey()); + lqw.like(StringUtils.isNotBlank(bo.getName()), PayApp::getName, bo.getName()); + lqw.eq(bo.getStatus() != null, PayApp::getStatus, bo.getStatus()); + lqw.eq(StringUtils.isNotBlank(bo.getOrderNotifyUrl()), PayApp::getOrderNotifyUrl, bo.getOrderNotifyUrl()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), PayApp::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), PayApp::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, PayApp::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增支付应用信息 + * + * @param bo 支付应用信息 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(PayAppBo bo) { + PayApp add = MapstructUtils.convert(bo, PayApp.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改支付应用信息 + * + * @param bo 支付应用信息 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(PayAppBo bo) { + PayApp update = MapstructUtils.convert(bo, PayApp.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(PayApp entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除支付应用信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/pay/service/impl/PayChannelServiceImpl.java b/src/main/java/com/hotwj/platform/pay/service/impl/PayChannelServiceImpl.java new file mode 100644 index 0000000..aa3f808 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/service/impl/PayChannelServiceImpl.java @@ -0,0 +1,144 @@ +package com.hotwj.platform.pay.service.impl; + +import com.hotwj.platform.pay.domain.PayChannel; +import com.hotwj.platform.pay.domain.bo.PayChannelBo; +import com.hotwj.platform.pay.domain.vo.PayChannelVo; +import com.hotwj.platform.pay.mapper.PayChannelMapper; +import com.hotwj.platform.pay.service.IPayChannelService; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 支付渠道 + * Service业务层处理 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class PayChannelServiceImpl implements IPayChannelService { + + private final PayChannelMapper baseMapper; + + /** + * 查询支付渠道 + * + * @param id 主键 + * @return 支付渠道 + */ + @Override + public PayChannelVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询支付渠道 + * 列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 支付渠道 + * 分页列表 + */ + @Override + public TableDataInfo queryPageList(PayChannelBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的支付渠道 + * 列表 + * + * @param bo 查询条件 + * @return 支付渠道 + * 列表 + */ + @Override + public List queryList(PayChannelBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(PayChannelBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(PayChannel::getId); + lqw.eq(StringUtils.isNotBlank(bo.getCode()), PayChannel::getCode, bo.getCode()); + lqw.eq(bo.getStatus() != null, PayChannel::getStatus, bo.getStatus()); + lqw.eq(bo.getAppId() != null, PayChannel::getAppId, bo.getAppId()); + lqw.eq(StringUtils.isNotBlank(bo.getConfig()), PayChannel::getConfig, bo.getConfig()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), PayChannel::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), PayChannel::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, PayChannel::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增支付渠道 + * + * @param bo 支付渠道 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(PayChannelBo bo) { + PayChannel add = MapstructUtils.convert(bo, PayChannel.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改支付渠道 + * + * @param bo 支付渠道 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(PayChannelBo bo) { + PayChannel update = MapstructUtils.convert(bo, PayChannel.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(PayChannel entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除支付渠道 + * 信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/pay/service/impl/PayOrderExtensionServiceImpl.java b/src/main/java/com/hotwj/platform/pay/service/impl/PayOrderExtensionServiceImpl.java new file mode 100644 index 0000000..f1b0afe --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/service/impl/PayOrderExtensionServiceImpl.java @@ -0,0 +1,150 @@ +package com.hotwj.platform.pay.service.impl; + +import com.hotwj.platform.pay.domain.PayOrderExtension; +import com.hotwj.platform.pay.domain.bo.PayOrderExtensionBo; +import com.hotwj.platform.pay.domain.vo.PayOrderExtensionVo; +import com.hotwj.platform.pay.mapper.PayOrderExtensionMapper; +import com.hotwj.platform.pay.service.IPayOrderExtensionService; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 支付订单 + * Service业务层处理 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class PayOrderExtensionServiceImpl implements IPayOrderExtensionService { + + private final PayOrderExtensionMapper baseMapper; + + /** + * 查询支付订单 + * + * @param id 主键 + * @return 支付订单 + */ + @Override + public PayOrderExtensionVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询支付订单 + * 列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 支付订单 + * 分页列表 + */ + @Override + public TableDataInfo queryPageList(PayOrderExtensionBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的支付订单 + * 列表 + * + * @param bo 查询条件 + * @return 支付订单 + * 列表 + */ + @Override + public List queryList(PayOrderExtensionBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(PayOrderExtensionBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(PayOrderExtension::getId); + lqw.eq(StringUtils.isNotBlank(bo.getNo()), PayOrderExtension::getNo, bo.getNo()); + lqw.eq(bo.getOrderId() != null, PayOrderExtension::getOrderId, bo.getOrderId()); + lqw.eq(bo.getChannelId() != null, PayOrderExtension::getChannelId, bo.getChannelId()); + lqw.eq(StringUtils.isNotBlank(bo.getChannelCode()), PayOrderExtension::getChannelCode, bo.getChannelCode()); + lqw.eq(StringUtils.isNotBlank(bo.getUserIp()), PayOrderExtension::getUserIp, bo.getUserIp()); + lqw.eq(bo.getStatus() != null, PayOrderExtension::getStatus, bo.getStatus()); + lqw.eq(StringUtils.isNotBlank(bo.getChannelExtras()), PayOrderExtension::getChannelExtras, bo.getChannelExtras()); + lqw.eq(StringUtils.isNotBlank(bo.getChannelErrorCode()), PayOrderExtension::getChannelErrorCode, bo.getChannelErrorCode()); + lqw.eq(StringUtils.isNotBlank(bo.getChannelErrorMsg()), PayOrderExtension::getChannelErrorMsg, bo.getChannelErrorMsg()); + lqw.eq(StringUtils.isNotBlank(bo.getChannelNotifyData()), PayOrderExtension::getChannelNotifyData, bo.getChannelNotifyData()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), PayOrderExtension::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), PayOrderExtension::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, PayOrderExtension::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增支付订单 + * + * @param bo 支付订单 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(PayOrderExtensionBo bo) { + PayOrderExtension add = MapstructUtils.convert(bo, PayOrderExtension.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改支付订单 + * + * @param bo 支付订单 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(PayOrderExtensionBo bo) { + PayOrderExtension update = MapstructUtils.convert(bo, PayOrderExtension.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(PayOrderExtension entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除支付订单 + * 信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/pay/service/impl/PayOrderServiceImpl.java b/src/main/java/com/hotwj/platform/pay/service/impl/PayOrderServiceImpl.java new file mode 100644 index 0000000..6c51e78 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/service/impl/PayOrderServiceImpl.java @@ -0,0 +1,153 @@ +package com.hotwj.platform.pay.service.impl; + +import com.hotwj.platform.pay.domain.PayOrder; +import com.hotwj.platform.pay.domain.bo.PayOrderBo; +import com.hotwj.platform.pay.domain.vo.PayOrderVo; +import com.hotwj.platform.pay.mapper.PayOrderMapper; +import com.hotwj.platform.pay.service.IPayOrderService; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 支付订单 + * Service业务层处理 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class PayOrderServiceImpl implements IPayOrderService { + + private final PayOrderMapper baseMapper; + + /** + * 查询支付订单 + * + * @param id 主键 + * @return 支付订单 + */ + @Override + public PayOrderVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询支付订单 + * 列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 支付订单 + * 分页列表 + */ + @Override + public TableDataInfo queryPageList(PayOrderBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的支付订单 + * 列表 + * + * @param bo 查询条件 + * @return 支付订单 + * 列表 + */ + @Override + public List queryList(PayOrderBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(PayOrderBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(PayOrder::getId); + lqw.eq(bo.getAppId() != null, PayOrder::getAppId, bo.getAppId()); + lqw.eq(bo.getChannelId() != null, PayOrder::getChannelId, bo.getChannelId()); + lqw.eq(StringUtils.isNotBlank(bo.getChannelCode()), PayOrder::getChannelCode, bo.getChannelCode()); + lqw.eq(StringUtils.isNotBlank(bo.getNotifyUrl()), PayOrder::getNotifyUrl, bo.getNotifyUrl()); + lqw.eq(bo.getPrice() != null, PayOrder::getPrice, bo.getPrice()); + lqw.eq(bo.getStatus() != null, PayOrder::getStatus, bo.getStatus()); + lqw.eq(StringUtils.isNotBlank(bo.getUserIp()), PayOrder::getUserIp, bo.getUserIp()); + lqw.eq(bo.getExpireTime() != null, PayOrder::getExpireTime, bo.getExpireTime()); + lqw.eq(bo.getSuccessTime() != null, PayOrder::getSuccessTime, bo.getSuccessTime()); + lqw.eq(bo.getExtensionId() != null, PayOrder::getExtensionId, bo.getExtensionId()); + lqw.eq(StringUtils.isNotBlank(bo.getNo()), PayOrder::getNo, bo.getNo()); + lqw.eq(StringUtils.isNotBlank(bo.getChannelUserId()), PayOrder::getChannelUserId, bo.getChannelUserId()); + lqw.eq(StringUtils.isNotBlank(bo.getChannelOrderNo()), PayOrder::getChannelOrderNo, bo.getChannelOrderNo()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), PayOrder::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), PayOrder::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, PayOrder::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增支付订单 + * + * @param bo 支付订单 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(PayOrderBo bo) { + PayOrder add = MapstructUtils.convert(bo, PayOrder.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改支付订单 + * + * @param bo 支付订单 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(PayOrderBo bo) { + PayOrder update = MapstructUtils.convert(bo, PayOrder.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(PayOrder entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除支付订单 + * 信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/pay/service/impl/PayRechargeOpenServiceImpl.java b/src/main/java/com/hotwj/platform/pay/service/impl/PayRechargeOpenServiceImpl.java new file mode 100644 index 0000000..c3c01b5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/service/impl/PayRechargeOpenServiceImpl.java @@ -0,0 +1,991 @@ +package com.hotwj.platform.pay.service.impl; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.Header; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.fasterxml.jackson.databind.JsonNode; +import com.hotwj.platform.pay.controller.vo.PayRechargeChannelRespVO; +import com.hotwj.platform.pay.controller.vo.PayRechargeStatusRespVO; +import com.hotwj.platform.pay.controller.vo.PayRechargeUnifiedOrderReqVO; +import com.hotwj.platform.pay.controller.vo.PayRechargeUnifiedOrderRespVO; +import com.hotwj.platform.pay.controller.vo.PayWalletBalanceRespVO; +import com.hotwj.platform.pay.domain.PayApp; +import com.hotwj.platform.pay.domain.PayChannel; +import com.hotwj.platform.pay.domain.PayOrder; +import com.hotwj.platform.pay.domain.PayOrderExtension; +import com.hotwj.platform.pay.domain.PayRechargeOrder; +import com.hotwj.platform.pay.domain.PayWallet; +import com.hotwj.platform.pay.domain.PayWalletTransaction; +import com.hotwj.platform.pay.enums.PayChannelCodeEnum; +import com.hotwj.platform.pay.enums.PayOrderBizTypeEnum; +import com.hotwj.platform.pay.enums.PayOrderStatusEnum; +import com.hotwj.platform.pay.mapper.PayAppMapper; +import com.hotwj.platform.pay.mapper.PayChannelMapper; +import com.hotwj.platform.pay.mapper.PayOrderExtensionMapper; +import com.hotwj.platform.pay.mapper.PayOrderMapper; +import com.hotwj.platform.pay.mapper.PayRechargeOrderMapper; +import com.hotwj.platform.pay.mapper.PayWalletMapper; +import com.hotwj.platform.pay.mapper.PayWalletTransactionMapper; +import com.hotwj.platform.pay.service.PayRechargeOpenService; +import com.hotwj.platform.pay.utils.PayCryptoUtils; +import jakarta.servlet.http.HttpServletRequest; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.ServletUtils; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +public class PayRechargeOpenServiceImpl implements PayRechargeOpenService { + + private static final String DEFAULT_PAY_APP_KEY = "wallet-recharge"; + private static final String ALIPAY_GATEWAY = "https://openapi.alipay.com/gateway.do"; + private static final String WECHAT_GATEWAY = "https://api.mch.weixin.qq.com"; + private static final DateTimeFormatter ALIPAY_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private static final int ORDER_EXPIRE_MINUTES = 3; + private static final int COMPENSATE_BATCH_SIZE = 50; + private static final long WALLET_BIZ_TYPE_RECHARGE = 1L; + private static final long WALLET_FREEZE_STATUS_NORMAL = 0L; + private static final long WALLET_FREEZE_STATUS_FROZEN = 1L; + + private final PayAppMapper payAppMapper; + private final PayChannelMapper payChannelMapper; + private final PayOrderMapper payOrderMapper; + private final PayOrderExtensionMapper payOrderExtensionMapper; + private final PayRechargeOrderMapper payRechargeOrderMapper; + private final PayWalletMapper payWalletMapper; + private final PayWalletTransactionMapper payWalletTransactionMapper; + + @Override + public List listRechargeChannels() { + PayApp payApp = getActivePayApp(); + List channels = payChannelMapper.selectList(Wrappers.lambdaQuery() + .eq(PayChannel::getAppId, payApp.getId()) + .eq(PayChannel::getStatus, 1L) + .orderByAsc(PayChannel::getId)); + if (channels == null || channels.isEmpty()) { + return List.of(); + } + List result = new ArrayList<>(); + for (PayChannel channel : channels) { + PayChannelCodeEnum codeEnum = Arrays.stream(PayChannelCodeEnum.values()) + .filter(item -> StrUtil.equalsIgnoreCase(item.getCode(), channel.getCode())) + .findFirst() + .orElse(null); + if (codeEnum == null) { + continue; + } + PayRechargeChannelRespVO respVO = new PayRechargeChannelRespVO(); + respVO.setProvider(codeEnum.getProvider()); + respVO.setScene(codeEnum.getScene()); + respVO.setChannelCode(codeEnum.getCode()); + respVO.setPayMethod(codeEnum.getPayMethod()); + respVO.setPayScene(codeEnum.getPayScene()); + respVO.setDisplayName(buildChannelDisplayName(codeEnum)); + result.add(respVO); + } + return result; + } + + @Override + public PayWalletBalanceRespVO queryWalletBalance(Long walletType) { + Long userId = LoginHelper.getUserId(); + Long companyId = LoginHelper.getCompanyId(); + if (userId == null || companyId == null) { + throw new ServiceException("登录状态异常,请重新登录后查询钱包余额"); + } + Long actualWalletType = walletType == null ? 2L : walletType; + Long walletUserId = resolveWalletUserId(companyId, userId, actualWalletType); + PayWallet wallet = payWalletMapper.selectOne(Wrappers.lambdaQuery() + .eq(PayWallet::getUserId, walletUserId) + .eq(PayWallet::getWalletType, actualWalletType) + .last("limit 1")); + PayWalletBalanceRespVO respVO = new PayWalletBalanceRespVO(); + respVO.setWalletType(actualWalletType); + respVO.setBalance(wallet == null || wallet.getBalance() == null ? BigDecimal.ZERO : wallet.getBalance()); + respVO.setFreezeStatus(wallet == null || wallet.getFreezeStatus() == null ? WALLET_FREEZE_STATUS_NORMAL : wallet.getFreezeStatus()); + return respVO; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public PayRechargeUnifiedOrderRespVO unifiedOrder(PayRechargeUnifiedOrderReqVO reqVO, HttpServletRequest request) { + Long userId = LoginHelper.getUserId(); + Long companyId = LoginHelper.getCompanyId(); + if (userId == null || companyId == null) { + throw new ServiceException("登录状态异常,请重新登录后发起支付"); + } + + PayChannelCodeEnum channelCodeEnum = PayChannelCodeEnum.of(reqVO.getProvider(), reqVO.getScene()); + PayApp payApp = getActivePayApp(); + PayChannel payChannel = getActiveChannel(payApp.getId(), channelCodeEnum.getCode()); + PayWallet wallet = getOrCreateWallet(companyId, userId, reqVO.getWalletType()); + ensureWalletAvailable(wallet, "钱包已冻结,无法充值"); + checkDuplicatePendingRechargeOrder(userId, wallet.getId()); + + BigDecimal amount = normalizeAmount(reqVO.getAmount()); + Date expireTime = DateUtil.offsetMinute(new Date(), ORDER_EXPIRE_MINUTES); + String rechargeOrderNo = buildNo("RC"); + String payOrderNo = buildNo("PO"); + String extensionNo = buildNo("PX"); + String notifyUrl = buildNotifyUrl(request, reqVO.getProvider(), payChannel.getId()); + + PayRechargeOrder rechargeOrder = new PayRechargeOrder(); + rechargeOrder.setCompanyId(companyId); + rechargeOrder.setAccountId(wallet.getId()); + rechargeOrder.setUserId(userId); + rechargeOrder.setRechargeOrderNo(rechargeOrderNo); + rechargeOrder.setRechargeAmount(amount); + rechargeOrder.setPayMethod(channelCodeEnum.getPayMethod()); + rechargeOrder.setPayScene(channelCodeEnum.getPayScene()); + rechargeOrder.setPayStatus(PayOrderStatusEnum.WAITING.getStatus()); + rechargeOrder.setRemark("钱包充值"); + payRechargeOrderMapper.insert(rechargeOrder); + + PayOrder payOrder = new PayOrder(); + payOrder.setAppId(payApp.getId()); + payOrder.setMerchantOrderNo(rechargeOrderNo); + payOrder.setBizOrderType(PayOrderBizTypeEnum.FUND_RECHARGE.getCode()); + payOrder.setBizOrderId(rechargeOrder.getId()); + payOrder.setSubject("钱包充值"); + payOrder.setBody("用户/企业钱包余额充值"); + payOrder.setNotifyUrl(notifyUrl); + payOrder.setReturnUrl(reqVO.getReturnUrl()); + payOrder.setPrice(amount); + payOrder.setStatus(PayOrderStatusEnum.WAITING.getStatus()); + payOrder.setUserIp(ServletUtils.getClientIP(request)); + payOrder.setExpireTime(expireTime); + payOrder.setNo(payOrderNo); + payOrderMapper.insert(payOrder); + + PayOrderExtension extension = new PayOrderExtension(); + extension.setNo(extensionNo); + extension.setOrderId(payOrder.getId()); + extension.setChannelId(payChannel.getId()); + extension.setChannelCode(payChannel.getCode()); + extension.setUserIp(payOrder.getUserIp()); + extension.setStatus(PayOrderStatusEnum.WAITING.getStatus()); + payOrderExtensionMapper.insert(extension); + + payOrder.setExtensionId(extension.getId()); + payOrderMapper.updateById(payOrder); + + String rawResponse; + PayRechargeUnifiedOrderRespVO respVO = new PayRechargeUnifiedOrderRespVO(); + respVO.setRechargeOrderNo(rechargeOrderNo); + respVO.setPayOrderNo(payOrderNo); + respVO.setChannelCode(payChannel.getCode()); + respVO.setExpireTime(expireTime); + + if ("ALIPAY".equalsIgnoreCase(reqVO.getProvider())) { + AlipayChannelConfig config = parseAlipayConfig(payChannel.getConfig()); + rawResponse = buildAlipayPayPayload(config, channelCodeEnum, extensionNo, amount, payOrder.getSubject(), notifyUrl, reqVO.getReturnUrl(), respVO); + } else { + WechatChannelConfig config = parseWechatConfig(payChannel.getConfig()); + rawResponse = buildWechatPayPayload(config, channelCodeEnum, extensionNo, amount, payOrder.getSubject(), notifyUrl, payOrder.getUserIp(), respVO); + } + + extension.setChannelExtras(rawResponse); + payOrderExtensionMapper.updateById(extension); + PayRechargeOrder updateRechargeOrder = new PayRechargeOrder(); + updateRechargeOrder.setId(rechargeOrder.getId()); + updateRechargeOrder.setPayStatus(PayOrderStatusEnum.PAYING.getStatus()); + payRechargeOrderMapper.updateById(updateRechargeOrder); + return respVO; + } + + @Override + public PayRechargeStatusRespVO queryRechargeStatus(String rechargeOrderNo) { + PayRechargeOrder rechargeOrder = payRechargeOrderMapper.selectOne(Wrappers.lambdaQuery() + .eq(PayRechargeOrder::getRechargeOrderNo, rechargeOrderNo) + .last("limit 1")); + if (rechargeOrder == null) { + throw new ServiceException("充值订单不存在"); + } + PayOrder payOrder = payOrderMapper.selectOne(Wrappers.lambdaQuery() + .eq(PayOrder::getBizOrderType, PayOrderBizTypeEnum.FUND_RECHARGE.getCode()) + .eq(PayOrder::getBizOrderId, rechargeOrder.getId()) + .last("limit 1")); + PayRechargeStatusRespVO respVO = new PayRechargeStatusRespVO(); + respVO.setRechargeOrderNo(rechargeOrder.getRechargeOrderNo()); + respVO.setPayOrderNo(payOrder == null ? null : payOrder.getNo()); + respVO.setPayStatus(rechargeOrder.getPayStatus()); + respVO.setAmount(rechargeOrder.getRechargeAmount()); + respVO.setThirdTradeNo(rechargeOrder.getThirdTradeNo()); + respVO.setPaidTime(rechargeOrder.getPaidTime()); + return respVO; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public String handleAlipayNotify(Long channelId, Map params) { + PayChannel payChannel = getChannelById(channelId); + AlipayChannelConfig config = parseAlipayConfig(payChannel.getConfig()); + + String sign = params.get("sign"); + if (StrUtil.isBlank(sign)) { + throw new ServiceException("支付宝回调签名为空"); + } + Map signParams = new TreeMap<>(); + params.forEach((key, value) -> { + if (!"sign".equals(key) && !"sign_type".equals(key) && StrUtil.isNotBlank(value)) { + signParams.put(key, value); + } + }); + String content = buildPlainQuery(signParams, false); + if (!PayCryptoUtils.rsaVerify(content, sign, config.getAlipayPublicKey())) { + throw new ServiceException("支付宝回调验签失败"); + } + + String tradeStatus = params.get("trade_status"); + if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) { + LocalDateTime paidTime = LocalDateTime.parse(params.get("gmt_payment"), ALIPAY_TIME_FORMATTER); + markOrderPaid(params.get("out_trade_no"), params.get("trade_no"), params.get("buyer_id"), + JsonUtils.toJsonString(params), Date.from(paidTime.atZone(ZoneId.systemDefault()).toInstant())); + } else if ("TRADE_CLOSED".equals(tradeStatus)) { + markOrderClosed(params.get("out_trade_no"), params.get("trade_no"), JsonUtils.toJsonString(params)); + } + return "success"; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public String handleWechatNotify(Long channelId, String body, HttpServletRequest request) { + try { + PayChannel payChannel = getChannelById(channelId); + WechatChannelConfig config = parseWechatConfig(payChannel.getConfig()); + String timestamp = ServletUtils.getHeader(request, "Wechatpay-Timestamp"); + String nonce = ServletUtils.getHeader(request, "Wechatpay-Nonce"); + String signature = ServletUtils.getHeader(request, "Wechatpay-Signature"); + String verifyMessage = timestamp + "\n" + nonce + "\n" + body + "\n"; + if (!PayCryptoUtils.rsaVerify(verifyMessage, signature, config.getPlatformPublicKey())) { + throw new ServiceException("微信回调验签失败"); + } + + JsonNode root = JsonUtils.getObjectMapper().readTree(body); + JsonNode resource = root.path("resource"); + String plainText = PayCryptoUtils.aesGcmDecrypt(config.getApiV3Key(), + resource.path("associated_data").asText(), + resource.path("nonce").asText(), + resource.path("ciphertext").asText()); + JsonNode notify = JsonUtils.getObjectMapper().readTree(plainText); + String tradeState = notify.path("trade_state").asText(); + String outTradeNo = notify.path("out_trade_no").asText(); + if ("SUCCESS".equals(tradeState)) { + Date paidTime = Date.from(OffsetDateTime.parse(notify.path("success_time").asText()).toInstant()); + markOrderPaid(outTradeNo, notify.path("transaction_id").asText(), notify.path("payer").path("openid").asText(""), + plainText, paidTime); + } else if ("CLOSED".equals(tradeState) || "PAYERROR".equals(tradeState)) { + markOrderClosed(outTradeNo, notify.path("transaction_id").asText(), plainText); + } + return "{\"code\":\"SUCCESS\",\"message\":\"成功\"}"; + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("处理微信支付回调失败: " + e.getMessage()); + } + } + + @Override + public void compensatePayOrders() { + Date now = new Date(); + List recentOrders = payOrderMapper.selectList(Wrappers.lambdaQuery() + .in(PayOrder::getStatus, PayOrderStatusEnum.WAITING.getStatus(), PayOrderStatusEnum.PAYING.getStatus()) + .gt(PayOrder::getExpireTime, now) + .ge(PayOrder::getCreateTime, DateUtil.offsetMinute(now, -ORDER_EXPIRE_MINUTES)) + .orderByAsc(PayOrder::getCreateTime) + .last("limit " + COMPENSATE_BATCH_SIZE)); + List expiredOrders = payOrderMapper.selectList(Wrappers.lambdaQuery() + .in(PayOrder::getStatus, PayOrderStatusEnum.WAITING.getStatus(), PayOrderStatusEnum.PAYING.getStatus()) + .isNotNull(PayOrder::getExpireTime) + .le(PayOrder::getExpireTime, now) + .orderByAsc(PayOrder::getExpireTime) + .last("limit " + COMPENSATE_BATCH_SIZE)); + + int queried = 0; + int closed = 0; + for (PayOrder payOrder : recentOrders) { + if (compensateSingleOrder(payOrder, false)) { + queried++; + } + } + for (PayOrder payOrder : expiredOrders) { + if (compensateSingleOrder(payOrder, true)) { + closed++; + } + } + if (queried > 0 || closed > 0) { + log.info("支付补偿任务完成:主动查单 {} 笔,超时关单 {} 笔", queried, closed); + } + } + + private String buildAlipayPayPayload(AlipayChannelConfig config, PayChannelCodeEnum channelCodeEnum, + String extensionNo, BigDecimal amount, String subject, String notifyUrl, + String returnUrl, PayRechargeUnifiedOrderRespVO respVO) { + Map bizContent = new LinkedHashMap<>(); + bizContent.put("out_trade_no", extensionNo); + bizContent.put("total_amount", amount.setScale(2, RoundingMode.HALF_UP).toPlainString()); + bizContent.put("subject", subject); + bizContent.put("timeout_express", ORDER_EXPIRE_MINUTES + "m"); + + if (PayChannelCodeEnum.ALIPAY_QR == channelCodeEnum) { + String response = invokeAlipayOpenApi(config, "alipay.trade.precreate", bizContent, notifyUrl, null); + try { + JsonNode root = JsonUtils.getObjectMapper().readTree(response); + JsonNode node = root.path("alipay_trade_precreate_response"); + if (!"10000".equals(node.path("code").asText())) { + throw new ServiceException("支付宝下单失败: " + node.path("sub_msg").asText(node.path("msg").asText())); + } + respVO.setDisplayMode("QR_CODE"); + respVO.setCodeUrl(node.path("qr_code").asText()); + return response; + } catch (Exception e) { + throw new ServiceException("解析支付宝预下单响应失败: " + e.getMessage()); + } + } + + if (PayChannelCodeEnum.ALIPAY_H5 == channelCodeEnum) { + bizContent.put("product_code", "QUICK_WAP_WAY"); + String payUrl = buildAlipayRedirectUrl(config, "alipay.trade.wap.pay", bizContent, notifyUrl, returnUrl); + respVO.setDisplayMode("REDIRECT"); + respVO.setPayUrl(payUrl); + return payUrl; + } + + bizContent.put("product_code", "QUICK_MSECURITY_PAY"); + String orderString = buildAlipayAppOrderString(config, "alipay.trade.app.pay", bizContent, notifyUrl); + respVO.setDisplayMode("APP"); + respVO.setOrderString(orderString); + return orderString; + } + + private String buildWechatPayPayload(WechatChannelConfig config, PayChannelCodeEnum channelCodeEnum, + String extensionNo, BigDecimal amount, String subject, String notifyUrl, + String userIp, PayRechargeUnifiedOrderRespVO respVO) { + try { + Map bodyMap = new LinkedHashMap<>(); + bodyMap.put("appid", config.getAppId()); + bodyMap.put("mchid", config.getMerchantId()); + bodyMap.put("description", subject); + bodyMap.put("out_trade_no", extensionNo); + bodyMap.put("notify_url", notifyUrl); + bodyMap.put("amount", Map.of("total", toFen(amount), "currency", "CNY")); + bodyMap.put("time_expire", toOffsetDateTime(DateUtil.offsetMinute(new Date(), ORDER_EXPIRE_MINUTES))); + + String path; + if (PayChannelCodeEnum.WECHAT_QR == channelCodeEnum) { + path = "/v3/pay/transactions/native"; + bodyMap.put("scene_info", Map.of("payer_client_ip", userIp)); + } else if (PayChannelCodeEnum.WECHAT_H5 == channelCodeEnum) { + path = "/v3/pay/transactions/h5"; + bodyMap.put("scene_info", Map.of( + "payer_client_ip", userIp, + "h5_info", Map.of("type", "Wap") + )); + } else { + path = "/v3/pay/transactions/app"; + } + + String requestBody = JsonUtils.toJsonString(bodyMap); + String response = invokeWechatApi(config, "POST", path, requestBody); + JsonNode node = JsonUtils.getObjectMapper().readTree(response); + if (PayChannelCodeEnum.WECHAT_QR == channelCodeEnum) { + respVO.setDisplayMode("QR_CODE"); + respVO.setCodeUrl(node.path("code_url").asText()); + return response; + } + if (PayChannelCodeEnum.WECHAT_H5 == channelCodeEnum) { + respVO.setDisplayMode("REDIRECT"); + respVO.setPayUrl(node.path("h5_url").asText()); + return response; + } + String prepayId = node.path("prepay_id").asText(); + String nonceStr = IdUtil.fastSimpleUUID(); + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + String packageValue = "Sign=WXPay"; + String message = config.getAppId() + "\n" + timestamp + "\n" + nonceStr + "\n" + prepayId + "\n"; + String sign = PayCryptoUtils.rsaSign(message, config.getPrivateKey()); + Map appParams = new LinkedHashMap<>(); + appParams.put("appid", config.getAppId()); + appParams.put("partnerid", config.getMerchantId()); + appParams.put("prepayid", prepayId); + appParams.put("package", packageValue); + appParams.put("noncestr", nonceStr); + appParams.put("timestamp", timestamp); + appParams.put("sign", sign); + respVO.setDisplayMode("APP"); + respVO.setAppPayParams(appParams); + return JsonUtils.toJsonString(appParams); + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("微信支付下单失败: " + e.getMessage()); + } + } + + private String invokeAlipayOpenApi(AlipayChannelConfig config, String method, Map bizContent, + String notifyUrl, String returnUrl) { + Map params = buildAlipayCommonParams(config, method, notifyUrl, returnUrl); + params.put("biz_content", JsonUtils.toJsonString(bizContent)); + params.put("sign", signAlipayParams(params, config.getPrivateKey())); + String response = HttpRequest.post(defaultIfBlank(config.getGateway(), ALIPAY_GATEWAY)) + .header(Header.CONTENT_TYPE, "application/x-www-form-urlencoded;charset=UTF-8") + .body(buildPlainQuery(params, true)) + .execute() + .body(); + if (StrUtil.isBlank(response)) { + throw new ServiceException("支付宝网关返回为空"); + } + return response; + } + + private String buildAlipayRedirectUrl(AlipayChannelConfig config, String method, Map bizContent, + String notifyUrl, String returnUrl) { + Map params = buildAlipayCommonParams(config, method, notifyUrl, returnUrl); + params.put("biz_content", JsonUtils.toJsonString(bizContent)); + params.put("sign", signAlipayParams(params, config.getPrivateKey())); + return defaultIfBlank(config.getGateway(), ALIPAY_GATEWAY) + "?" + buildPlainQuery(params, true); + } + + private String buildAlipayAppOrderString(AlipayChannelConfig config, String method, Map bizContent, String notifyUrl) { + Map params = buildAlipayCommonParams(config, method, notifyUrl, null); + params.put("biz_content", JsonUtils.toJsonString(bizContent)); + params.put("sign", signAlipayParams(params, config.getPrivateKey())); + return buildPlainQuery(params, true); + } + + private Map buildAlipayCommonParams(AlipayChannelConfig config, String method, String notifyUrl, String returnUrl) { + Map params = new TreeMap<>(); + params.put("app_id", config.getAppId()); + params.put("method", method); + params.put("charset", "UTF-8"); + params.put("sign_type", "RSA2"); + params.put("timestamp", DateUtil.format(new Date(), DatePattern.NORM_DATETIME_PATTERN)); + params.put("version", "1.0"); + params.put("notify_url", notifyUrl); + if (StrUtil.isNotBlank(returnUrl)) { + params.put("return_url", returnUrl); + } + return params; + } + + private String signAlipayParams(Map params, String privateKey) { + return PayCryptoUtils.rsaSign(buildPlainQuery(params, false), privateKey); + } + + private String invokeWechatApi(WechatChannelConfig config, String method, String path, String body) { + String nonceStr = IdUtil.fastSimpleUUID(); + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + String message = method + "\n" + path + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n"; + String signature = PayCryptoUtils.rsaSign(message, config.getPrivateKey()); + String authorization = String.format( + "WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%s\",serial_no=\"%s\",signature=\"%s\"", + config.getMerchantId(), nonceStr, timestamp, config.getMerchantSerialNo(), signature + ); + HttpResponse httpResponse = HttpRequest.of(defaultIfBlank(config.getGateway(), WECHAT_GATEWAY) + path) + .method(cn.hutool.http.Method.valueOf(method)) + .header("Authorization", authorization) + .header("Accept", "application/json") + .header("Content-Type", "application/json") + .body(body) + .execute(); + String response = httpResponse.body(); + if (StrUtil.isBlank(response) && httpResponse.getStatus() != 204) { + throw new ServiceException("微信网关返回为空"); + } + return StrUtil.blankToDefault(response, ""); + } + + private boolean compensateSingleOrder(PayOrder payOrder, boolean expired) { + try { + PaymentOrderContext context = buildOrderContext(payOrder); + PaymentChannelResult result = queryRemoteOrder(context); + if (result == null) { + return false; + } + if (result.isSuccess()) { + markOrderPaid(context.extension.getNo(), result.thirdTradeNo, result.channelUserId, result.rawData, result.paidTime); + return false; + } + if (result.isClosed()) { + markOrderClosed(context.extension.getNo(), result.thirdTradeNo, result.rawData); + return expired; + } + if (expired) { + closeRemoteOrder(context); + markOrderClosed(context.extension.getNo(), result.thirdTradeNo, result.rawData); + return true; + } + return true; + } catch (Exception e) { + log.warn("支付补偿处理失败 payOrderNo={} expired={}", payOrder.getNo(), expired, e); + return false; + } + } + + private PaymentOrderContext buildOrderContext(PayOrder payOrder) { + PayOrderExtension extension = payOrderExtensionMapper.selectOne(Wrappers.lambdaQuery() + .eq(PayOrderExtension::getOrderId, payOrder.getId()) + .orderByDesc(PayOrderExtension::getId) + .last("limit 1")); + if (extension == null) { + throw new ServiceException("支付拓展单不存在: " + payOrder.getNo()); + } + PayChannel channel = getChannelById(extension.getChannelId()); + return new PaymentOrderContext(payOrder, extension, channel); + } + + private PaymentChannelResult queryRemoteOrder(PaymentOrderContext context) throws Exception { + if (isAlipayChannel(context.channel.getCode())) { + return queryAlipayOrder(context); + } + if (isWechatChannel(context.channel.getCode())) { + return queryWechatOrder(context); + } + throw new ServiceException("暂不支持的支付渠道: " + context.channel.getCode()); + } + + private void closeRemoteOrder(PaymentOrderContext context) throws Exception { + if (isAlipayChannel(context.channel.getCode())) { + closeAlipayOrder(context); + return; + } + if (isWechatChannel(context.channel.getCode())) { + closeWechatOrder(context); + return; + } + throw new ServiceException("暂不支持的支付渠道: " + context.channel.getCode()); + } + + private PaymentChannelResult queryAlipayOrder(PaymentOrderContext context) throws Exception { + AlipayChannelConfig config = parseAlipayConfig(context.channel.getConfig()); + Map bizContent = Map.of("out_trade_no", context.extension.getNo()); + String response = invokeAlipayOpenApi(config, "alipay.trade.query", bizContent, context.payOrder.getNotifyUrl(), context.payOrder.getReturnUrl()); + JsonNode root = JsonUtils.getObjectMapper().readTree(response); + JsonNode node = root.path("alipay_trade_query_response"); + String code = node.path("code").asText(); + if (!"10000".equals(code) && !"40004".equals(code)) { + throw new ServiceException("支付宝查单失败: " + node.path("sub_msg").asText(node.path("msg").asText())); + } + String tradeStatus = node.path("trade_status").asText(); + PaymentChannelResult result = new PaymentChannelResult(); + result.rawData = response; + result.thirdTradeNo = node.path("trade_no").asText(null); + result.channelUserId = node.path("buyer_user_id").asText(null); + // 支付宝返回 40004(交易不存在) 在刚下单阶段可能是暂态,不应直接本地关单; + // 对于超时单会在过期补偿分支里继续执行关单(且关单接口对不存在交易已做幂等处理)。 + if ("40004".equals(code)) { + return result; + } + result.success = "TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus); + result.closed = "TRADE_CLOSED".equals(tradeStatus); + String paidTime = node.path("send_pay_date").asText(node.path("gmt_payment").asText()); + if (StrUtil.isNotBlank(paidTime)) { + LocalDateTime localDateTime = LocalDateTime.parse(paidTime, ALIPAY_TIME_FORMATTER); + result.paidTime = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); + } + return result; + } + + private void closeAlipayOrder(PaymentOrderContext context) { + AlipayChannelConfig config = parseAlipayConfig(context.channel.getConfig()); + Map bizContent = Map.of("out_trade_no", context.extension.getNo()); + String response = invokeAlipayOpenApi(config, "alipay.trade.close", bizContent, context.payOrder.getNotifyUrl(), context.payOrder.getReturnUrl()); + try { + JsonNode root = JsonUtils.getObjectMapper().readTree(response); + JsonNode node = root.path("alipay_trade_close_response"); + String code = node.path("code").asText(); + String subCode = node.path("sub_code").asText(); + // 交易不存在时无需再关单,视为幂等成功,避免补偿任务持续报错 + if ("40004".equals(code) && "ACQ.TRADE_NOT_EXIST".equalsIgnoreCase(subCode)) { + return; + } + if (!"10000".equals(code)) { + throw new ServiceException("支付宝关单失败: " + node.path("sub_msg").asText(node.path("msg").asText())); + } + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("解析支付宝关单响应失败: " + e.getMessage()); + } + } + + private PaymentChannelResult queryWechatOrder(PaymentOrderContext context) throws Exception { + WechatChannelConfig config = parseWechatConfig(context.channel.getConfig()); + String path = "/v3/pay/transactions/out-trade-no/" + context.extension.getNo() + "?mchid=" + config.getMerchantId(); + String response = invokeWechatApi(config, "GET", path, ""); + JsonNode node = JsonUtils.getObjectMapper().readTree(response); + PaymentChannelResult result = new PaymentChannelResult(); + result.rawData = response; + result.thirdTradeNo = node.path("transaction_id").asText(null); + result.channelUserId = node.path("payer").path("openid").asText(null); + String tradeState = node.path("trade_state").asText(); + result.success = "SUCCESS".equals(tradeState); + result.closed = "CLOSED".equals(tradeState) || "PAYERROR".equals(tradeState) || "REVOKED".equals(tradeState); + String successTime = node.path("success_time").asText(); + if (StrUtil.isNotBlank(successTime)) { + result.paidTime = Date.from(OffsetDateTime.parse(successTime).toInstant()); + } + return result; + } + + private void closeWechatOrder(PaymentOrderContext context) { + WechatChannelConfig config = parseWechatConfig(context.channel.getConfig()); + String path = "/v3/pay/transactions/out-trade-no/" + context.extension.getNo() + "/close"; + String body = JsonUtils.toJsonString(Map.of("mchid", config.getMerchantId())); + invokeWechatApi(config, "POST", path, body); + } + + @Transactional(rollbackFor = Exception.class) + protected void markOrderPaid(String extensionNo, String thirdTradeNo, String payerId, String notifyData, Date paidTime) { + PayOrderExtension extension = payOrderExtensionMapper.selectOne(Wrappers.lambdaQuery() + .eq(PayOrderExtension::getNo, extensionNo) + .last("limit 1")); + if (extension == null) { + throw new ServiceException("支付拓展单不存在: " + extensionNo); + } + if (Objects.equals(extension.getStatus(), PayOrderStatusEnum.SUCCESS.getStatus())) { + return; + } + PayOrder payOrder = payOrderMapper.selectById(extension.getOrderId()); + if (payOrder == null) { + throw new ServiceException("支付订单不存在"); + } + PayRechargeOrder rechargeOrder = payRechargeOrderMapper.selectById(payOrder.getBizOrderId()); + if (rechargeOrder == null) { + throw new ServiceException("充值订单不存在"); + } + + extension.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); + extension.setChannelNotifyData(truncate(notifyData)); + payOrderExtensionMapper.updateById(extension); + + payOrder.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); + payOrder.setSuccessTime(paidTime); + payOrder.setExtensionId(extension.getId()); + payOrder.setChannelId(extension.getChannelId()); + payOrder.setChannelCode(extension.getChannelCode()); + payOrder.setChannelUserId(StrUtil.blankToDefault(payerId, payOrder.getChannelUserId())); + payOrder.setChannelOrderNo(thirdTradeNo); + payOrderMapper.updateById(payOrder); + + if (!Objects.equals(rechargeOrder.getPayStatus(), PayOrderStatusEnum.SUCCESS.getStatus())) { + rechargeOrder.setPayStatus(PayOrderStatusEnum.SUCCESS.getStatus()); + rechargeOrder.setThirdTradeNo(thirdTradeNo); + rechargeOrder.setPaidTime(paidTime); + payRechargeOrderMapper.updateById(rechargeOrder); + increaseWalletBalance(rechargeOrder.getAccountId(), rechargeOrder.getRechargeAmount(), rechargeOrder.getRechargeOrderNo()); + } + } + + @Transactional(rollbackFor = Exception.class) + protected void markOrderClosed(String extensionNo, String thirdTradeNo, String notifyData) { + if (StrUtil.isBlank(extensionNo)) { + return; + } + PayOrderExtension extension = payOrderExtensionMapper.selectOne(Wrappers.lambdaQuery() + .eq(PayOrderExtension::getNo, extensionNo) + .last("limit 1")); + if (extension == null || Objects.equals(extension.getStatus(), PayOrderStatusEnum.SUCCESS.getStatus())) { + return; + } + extension.setStatus(PayOrderStatusEnum.CLOSED.getStatus()); + extension.setChannelNotifyData(truncate(notifyData)); + payOrderExtensionMapper.updateById(extension); + + PayOrder payOrder = payOrderMapper.selectById(extension.getOrderId()); + if (payOrder != null && !Objects.equals(payOrder.getStatus(), PayOrderStatusEnum.SUCCESS.getStatus())) { + payOrder.setStatus(PayOrderStatusEnum.CLOSED.getStatus()); + payOrder.setChannelOrderNo(thirdTradeNo); + payOrderMapper.updateById(payOrder); + } + if (payOrder != null && Objects.equals(payOrder.getBizOrderType(), PayOrderBizTypeEnum.FUND_RECHARGE.getCode())) { + PayRechargeOrder rechargeOrder = payRechargeOrderMapper.selectById(payOrder.getBizOrderId()); + if (rechargeOrder != null && !Objects.equals(rechargeOrder.getPayStatus(), PayOrderStatusEnum.SUCCESS.getStatus())) { + rechargeOrder.setPayStatus(PayOrderStatusEnum.CLOSED.getStatus()); + rechargeOrder.setThirdTradeNo(thirdTradeNo); + payRechargeOrderMapper.updateById(rechargeOrder); + } + } + } + + private PayWallet getOrCreateWallet(Long companyId, Long userId, Long walletType) { + Long actualWalletType = walletType == null ? 2L : walletType; + Long walletUserId = resolveWalletUserId(companyId, userId, actualWalletType); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() + .eq(PayWallet::getUserId, walletUserId) + .eq(PayWallet::getWalletType, actualWalletType) + .last("limit 1"); + PayWallet wallet = payWalletMapper.selectOne(wrapper); + if (wallet != null) { + return wallet; + } + wallet = new PayWallet(); + wallet.setUserId(walletUserId); + wallet.setWalletType(actualWalletType); + wallet.setBalance(BigDecimal.ZERO); + wallet.setTotalExpense(BigDecimal.ZERO); + wallet.setTotalRecharge(BigDecimal.ZERO); + wallet.setFreezePrice(BigDecimal.ZERO); + wallet.setFreezeStatus(WALLET_FREEZE_STATUS_NORMAL); + payWalletMapper.insert(wallet); + return wallet; + } + + private Long resolveWalletUserId(Long companyId, Long userId, Long walletType) { + return Objects.equals(walletType, 2L) ? companyId : userId; + } + + private void checkDuplicatePendingRechargeOrder(Long userId, Long walletId) { + PayRechargeOrder latestRechargeOrder = payRechargeOrderMapper.selectOne(Wrappers.lambdaQuery() + .eq(PayRechargeOrder::getUserId, userId) + .eq(PayRechargeOrder::getAccountId, walletId) + .in(PayRechargeOrder::getPayStatus, PayOrderStatusEnum.WAITING.getStatus(), PayOrderStatusEnum.PAYING.getStatus()) + .orderByDesc(PayRechargeOrder::getId) + .last("limit 1")); + if (latestRechargeOrder == null) { + return; + } + Date now = new Date(); + PayOrder pendingPayOrder = payOrderMapper.selectOne(Wrappers.lambdaQuery() + .eq(PayOrder::getBizOrderType, PayOrderBizTypeEnum.FUND_RECHARGE.getCode()) + .eq(PayOrder::getBizOrderId, latestRechargeOrder.getId()) + .in(PayOrder::getStatus, PayOrderStatusEnum.WAITING.getStatus(), PayOrderStatusEnum.PAYING.getStatus()) + .and(wrapper -> wrapper.isNull(PayOrder::getExpireTime).or().gt(PayOrder::getExpireTime, now)) + .orderByDesc(PayOrder::getId) + .last("limit 1")); + if (pendingPayOrder != null) { + throw new ServiceException("存在待支付充值订单,请先完成支付(订单号:" + latestRechargeOrder.getRechargeOrderNo() + ")"); + } + } + + private void increaseWalletBalance(Long walletId, BigDecimal amount, String bizId) { + PayWallet wallet = payWalletMapper.selectById(walletId); + if (wallet == null) { + throw new ServiceException("钱包不存在"); + } + ensureWalletAvailable(wallet, "钱包已冻结,无法充值"); + BigDecimal balance = Objects.requireNonNullElse(wallet.getBalance(), BigDecimal.ZERO); + BigDecimal totalRecharge = Objects.requireNonNullElse(wallet.getTotalRecharge(), BigDecimal.ZERO); + wallet.setBalance(balance.add(amount)); + wallet.setTotalRecharge(totalRecharge.add(amount)); + if (payWalletMapper.updateById(wallet) <= 0) { + throw new ServiceException("更新钱包余额失败,请重试"); + } + + PayWalletTransaction transaction = new PayWalletTransaction(); + transaction.setWalletId(walletId); + transaction.setBizType(WALLET_BIZ_TYPE_RECHARGE); + transaction.setBizId(bizId); + transaction.setNo(buildNo("WT")); + transaction.setTitle("钱包充值"); + transaction.setPrice(amount); + transaction.setBalance(wallet.getBalance()); + if (payWalletTransactionMapper.insert(transaction) <= 0) { + throw new ServiceException("写入钱包流水失败,请重试"); + } + } + + private void ensureWalletAvailable(PayWallet wallet, String message) { + if (wallet != null && Objects.equals(wallet.getFreezeStatus(), WALLET_FREEZE_STATUS_FROZEN)) { + throw new ServiceException(message); + } + } + + private PayApp getActivePayApp() { + PayApp app = payAppMapper.selectOne(Wrappers.lambdaQuery() + .eq(PayApp::getAppKey, DEFAULT_PAY_APP_KEY) + .eq(PayApp::getStatus, 1L) + .last("limit 1")); + if (app != null) { + return app; + } + app = payAppMapper.selectOne(Wrappers.lambdaQuery() + .eq(PayApp::getStatus, 1L) + .orderByAsc(PayApp::getId) + .last("limit 1")); + if (app == null) { + throw new ServiceException("未配置可用的支付应用,请先维护 pay_app"); + } + return app; + } + + private PayChannel getActiveChannel(Long appId, String channelCode) { + PayChannel channel = payChannelMapper.selectOne(Wrappers.lambdaQuery() + .eq(PayChannel::getAppId, appId) + .eq(PayChannel::getCode, channelCode) + .eq(PayChannel::getStatus, 1L) + .last("limit 1")); + if (channel == null) { + throw new ServiceException("支付渠道未配置或未启用: " + channelCode); + } + return channel; + } + + private PayChannel getChannelById(Long channelId) { + PayChannel channel = payChannelMapper.selectById(channelId); + if (channel == null) { + throw new ServiceException("支付渠道不存在"); + } + return channel; + } + + private AlipayChannelConfig parseAlipayConfig(String configJson) { + AlipayChannelConfig config = JsonUtils.parseObject(configJson, AlipayChannelConfig.class); + if (config == null || StrUtil.hasBlank(config.getAppId(), config.getPrivateKey(), config.getAlipayPublicKey())) { + throw new ServiceException("支付宝渠道配置不完整"); + } + return config; + } + + private WechatChannelConfig parseWechatConfig(String configJson) { + WechatChannelConfig config = JsonUtils.parseObject(configJson, WechatChannelConfig.class); + if (config == null || StrUtil.hasBlank(config.getAppId(), config.getMerchantId(), config.getMerchantSerialNo(), + config.getPrivateKey(), config.getApiV3Key(), config.getPlatformPublicKey())) { + throw new ServiceException("微信渠道配置不完整"); + } + return config; + } + + private String buildNotifyUrl(HttpServletRequest request, String provider, Long channelId) { + StringBuilder builder = new StringBuilder(); + builder.append(request.getScheme()).append("://").append(request.getServerName()); + if (request.getServerPort() != 80 && request.getServerPort() != 443) { + builder.append(":").append(request.getServerPort()); + } + builder.append("/pay/open/notify/") + .append(provider.toLowerCase()) + .append("/") + .append(channelId); + return builder.toString(); + } + + private String buildPlainQuery(Map params, boolean urlEncode) { + return params.entrySet().stream() + .filter(entry -> StrUtil.isNotBlank(entry.getValue())) + .sorted(Comparator.comparing(Map.Entry::getKey)) + .map(entry -> entry.getKey() + "=" + (urlEncode ? urlEncode(entry.getValue()) : entry.getValue())) + .collect(Collectors.joining("&")); + } + + private String urlEncode(String value) { + return URLEncoder.encode(value, StandardCharsets.UTF_8); + } + + private BigDecimal normalizeAmount(BigDecimal amount) { + return amount.setScale(2, RoundingMode.HALF_UP); + } + + private int toFen(BigDecimal amount) { + return normalizeAmount(amount).multiply(BigDecimal.valueOf(100)).intValueExact(); + } + + private String buildNo(String prefix) { + return prefix + DateUtil.format(new Date(), "yyyyMMddHHmmss") + StrUtil.sub(IdUtil.fastSimpleUUID(), 0, 10).toUpperCase(); + } + + private String truncate(String text) { + if (text == null) { + return null; + } + return text.length() > 2000 ? text.substring(0, 2000) : text; + } + + private String defaultIfBlank(String value, String defaultValue) { + return StrUtil.isBlank(value) ? defaultValue : value; + } + + private String buildChannelDisplayName(PayChannelCodeEnum channelCodeEnum) { + String providerName = "ALIPAY".equalsIgnoreCase(channelCodeEnum.getProvider()) ? "支付宝" : "微信"; + String sceneName = switch (channelCodeEnum.getScene().toUpperCase()) { + case "QR" -> "扫码"; + case "H5" -> "H5"; + case "APP" -> "APP"; + default -> channelCodeEnum.getScene(); + }; + return providerName + sceneName; + } + + private boolean isAlipayChannel(String channelCode) { + return StrUtil.startWithIgnoreCase(channelCode, "ALIPAY"); + } + + private boolean isWechatChannel(String channelCode) { + return StrUtil.startWithIgnoreCase(channelCode, "WECHAT"); + } + + private String toOffsetDateTime(Date date) { + return date.toInstant().atOffset(ZoneOffset.ofHours(8)).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } + + private record PaymentOrderContext(PayOrder payOrder, PayOrderExtension extension, PayChannel channel) { + } + + private static class PaymentChannelResult { + private boolean success; + private boolean closed; + private String thirdTradeNo; + private String channelUserId; + private String rawData; + private Date paidTime; + + private boolean isSuccess() { + return success; + } + + private boolean isClosed() { + return closed; + } + } + + @Data + private static class AlipayChannelConfig { + private String appId; + private String privateKey; + private String alipayPublicKey; + private String gateway; + } + + @Data + private static class WechatChannelConfig { + private String appId; + private String merchantId; + private String merchantSerialNo; + private String privateKey; + private String apiV3Key; + private String platformPublicKey; + private String gateway; + } +} diff --git a/src/main/java/com/hotwj/platform/pay/service/impl/PayWalletServiceImpl.java b/src/main/java/com/hotwj/platform/pay/service/impl/PayWalletServiceImpl.java new file mode 100644 index 0000000..95ca044 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/service/impl/PayWalletServiceImpl.java @@ -0,0 +1,278 @@ +package com.hotwj.platform.pay.service.impl; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.pay.domain.PayWallet; +import com.hotwj.platform.pay.domain.PayWalletTransaction; +import com.hotwj.platform.pay.domain.bo.PayWalletBatchRechargeBo; +import com.hotwj.platform.pay.domain.bo.PayWalletBo; +import com.hotwj.platform.pay.domain.bo.PayWalletFreezeBo; +import com.hotwj.platform.pay.domain.vo.PayCompanyWalletBalanceVo; +import com.hotwj.platform.pay.domain.vo.PayWalletVo; +import com.hotwj.platform.pay.mapper.PayWalletMapper; +import com.hotwj.platform.pay.mapper.PayWalletTransactionMapper; +import com.hotwj.platform.pay.service.IPayWalletService; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Collection; +import java.util.Set; + +/** + * 会员钱包Service业务层处理 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class PayWalletServiceImpl implements IPayWalletService { + + private static final Long COMPANY_WALLET_TYPE = 2L; + private static final Long WALLET_BIZ_TYPE_ADMIN_RECHARGE = 2L; + private static final Long WALLET_FREEZE_STATUS_NORMAL = 0L; + private static final Long WALLET_FREEZE_STATUS_FROZEN = 1L; + + private final PayWalletMapper baseMapper; + private final PayWalletTransactionMapper payWalletTransactionMapper; + + /** + * 查询会员钱包 + * + * @param id 主键 + * @return 会员钱包 + */ + @Override + public PayWalletVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询会员钱包列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 会员钱包分页列表 + */ + @Override + public TableDataInfo queryPageList(PayWalletBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + @Override + public TableDataInfo queryCompanyWalletPage(String keyword, PageQuery pageQuery) { + Page page = baseMapper.selectCompanyWalletPage(pageQuery.build(), keyword); + return TableDataInfo.build(page); + } + + /** + * 查询符合条件的会员钱包列表 + * + * @param bo 查询条件 + * @return 会员钱包列表 + */ + @Override + public List queryList(PayWalletBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(PayWalletBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(PayWallet::getId); + lqw.eq(bo.getUserId() != null, PayWallet::getUserId, bo.getUserId()); + lqw.eq(bo.getBalance() != null, PayWallet::getBalance, bo.getBalance()); + lqw.eq(bo.getTotalExpense() != null, PayWallet::getTotalExpense, bo.getTotalExpense()); + lqw.eq(bo.getTotalRecharge() != null, PayWallet::getTotalRecharge, bo.getTotalRecharge()); + lqw.eq(bo.getFreezePrice() != null, PayWallet::getFreezePrice, bo.getFreezePrice()); + lqw.eq(bo.getWalletType() != null, PayWallet::getWalletType, bo.getWalletType()); + lqw.eq(bo.getFreezeStatus() != null, PayWallet::getFreezeStatus, bo.getFreezeStatus()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), PayWallet::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), PayWallet::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, PayWallet::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增会员钱包 + * + * @param bo 会员钱包 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(PayWalletBo bo) { + PayWallet add = MapstructUtils.convert(bo, PayWallet.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改会员钱包 + * + * @param bo 会员钱包 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(PayWalletBo bo) { + PayWallet update = MapstructUtils.convert(bo, PayWallet.class); + PayWallet current = baseMapper.selectById(update.getId()); + if (current == null) { + throw new ServiceException("钱包不存在"); + } + if (update.getFreezeStatus() == null) { + update.setFreezeStatus(current.getFreezeStatus()); + } + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + @Override + public Boolean changeFreezeStatus(PayWalletFreezeBo bo) { + if (!WALLET_FREEZE_STATUS_NORMAL.equals(bo.getFreezeStatus()) && !WALLET_FREEZE_STATUS_FROZEN.equals(bo.getFreezeStatus())) { + throw new ServiceException("冻结状态取值不正确"); + } + PayWallet wallet = baseMapper.selectById(bo.getWalletId()); + if (wallet == null) { + throw new ServiceException("钱包不存在"); + } + wallet.setFreezeStatus(bo.getFreezeStatus()); + return baseMapper.updateById(wallet) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(PayWallet entity) { + if (entity.getFreezeStatus() == null) { + entity.setFreezeStatus(WALLET_FREEZE_STATUS_NORMAL); + } + if (!WALLET_FREEZE_STATUS_NORMAL.equals(entity.getFreezeStatus()) && !WALLET_FREEZE_STATUS_FROZEN.equals(entity.getFreezeStatus())) { + throw new ServiceException("冻结状态取值不正确"); + } + } + + /** + * 校验并批量删除会员钱包信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Integer batchRechargeCompanyWallet(PayWalletBatchRechargeBo bo) { + Set companyIds = parseCompanyIds(bo.getCompanyIds()); + if (companyIds.isEmpty()) { + throw new ServiceException("企业ids不能为空"); + } + BigDecimal amount = bo.getAmount().setScale(2, RoundingMode.HALF_UP); + if (amount.compareTo(BigDecimal.ZERO) <= 0) { + throw new ServiceException("充值金额必须大于0"); + } + String batchBizId = "BRC" + IdUtil.getSnowflakeNextIdStr(); + for (Long companyId : companyIds) { + PayWallet wallet = getOrCreateCompanyWallet(companyId); + ensureWalletNotFrozen(wallet, "钱包已冻结,无法充值"); + BigDecimal balance = wallet.getBalance() == null ? BigDecimal.ZERO : wallet.getBalance(); + BigDecimal totalRecharge = wallet.getTotalRecharge() == null ? BigDecimal.ZERO : wallet.getTotalRecharge(); + wallet.setBalance(balance.add(amount)); + wallet.setTotalRecharge(totalRecharge.add(amount)); + if (baseMapper.updateById(wallet) <= 0) { + throw new ServiceException("更新企业钱包余额失败,企业ID: " + companyId); + } + + PayWalletTransaction transaction = new PayWalletTransaction(); + transaction.setWalletId(wallet.getId()); + transaction.setBizType(WALLET_BIZ_TYPE_ADMIN_RECHARGE); + transaction.setBizId(batchBizId); + transaction.setNo(buildWalletTransactionNo()); + transaction.setTitle("总部批量充值"); + transaction.setPrice(amount); + transaction.setBalance(wallet.getBalance()); + transaction.setRemark(bo.getRemark()); + if (payWalletTransactionMapper.insert(transaction) <= 0) { + throw new ServiceException("写入钱包流水失败,企业ID: " + companyId); + } + } + return companyIds.size(); + } + + private PayWallet getOrCreateCompanyWallet(Long companyId) { + PayWallet wallet = baseMapper.selectOne(Wrappers.lambdaQuery() + .eq(PayWallet::getUserId, companyId) + .eq(PayWallet::getWalletType, COMPANY_WALLET_TYPE) + .last("limit 1")); + if (wallet != null) { + return wallet; + } + wallet = new PayWallet(); + wallet.setUserId(companyId); + wallet.setWalletType(COMPANY_WALLET_TYPE); + wallet.setBalance(BigDecimal.ZERO); + wallet.setTotalExpense(BigDecimal.ZERO); + wallet.setTotalRecharge(BigDecimal.ZERO); + wallet.setFreezePrice(BigDecimal.ZERO); + wallet.setFreezeStatus(WALLET_FREEZE_STATUS_NORMAL); + if (baseMapper.insert(wallet) <= 0) { + throw new ServiceException("创建企业钱包失败,企业ID: " + companyId); + } + return wallet; + } + + private void ensureWalletNotFrozen(PayWallet wallet, String message) { + if (wallet != null && WALLET_FREEZE_STATUS_FROZEN.equals(wallet.getFreezeStatus())) { + throw new ServiceException(message); + } + } + + private Set parseCompanyIds(String companyIds) { + List idList = StrUtil.splitTrim(StrUtil.blankToDefault(companyIds, ""), ','); + Set result = new LinkedHashSet<>(); + for (String id : idList) { + if (StrUtil.isBlank(id)) { + continue; + } + try { + result.add(Long.valueOf(id)); + } catch (NumberFormatException e) { + throw new ServiceException("企业ID格式不正确: " + id); + } + } + return result; + } + + private String buildWalletTransactionNo() { + return "WT" + IdUtil.getSnowflakeNextIdStr(); + } +} diff --git a/src/main/java/com/hotwj/platform/pay/service/impl/PayWalletTransactionServiceImpl.java b/src/main/java/com/hotwj/platform/pay/service/impl/PayWalletTransactionServiceImpl.java new file mode 100644 index 0000000..ec57621 --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/service/impl/PayWalletTransactionServiceImpl.java @@ -0,0 +1,168 @@ +package com.hotwj.platform.pay.service.impl; + +import com.hotwj.platform.pay.domain.PayWalletTransaction; +import com.hotwj.platform.pay.domain.PayWallet; +import com.hotwj.platform.pay.domain.bo.PayWalletTransactionBo; +import com.hotwj.platform.pay.domain.vo.PayWalletTransactionVo; +import com.hotwj.platform.pay.mapper.PayWalletMapper; +import com.hotwj.platform.pay.mapper.PayWalletTransactionMapper; +import com.hotwj.platform.pay.service.IPayWalletTransactionService; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 会员钱包流水Service业务层处理 + * + * @author shihongwei + * @date 2026-05-08 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class PayWalletTransactionServiceImpl implements IPayWalletTransactionService { + + private static final Long COMPANY_WALLET_TYPE = 2L; + + private final PayWalletTransactionMapper baseMapper; + private final PayWalletMapper payWalletMapper; + + /** + * 查询会员钱包流水 + * + * @param id 主键 + * @return 会员钱包流水 + */ + @Override + public PayWalletTransactionVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询会员钱包流水列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 会员钱包流水分页列表 + */ + @Override + public TableDataInfo queryPageList(PayWalletTransactionBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + @Override + public TableDataInfo queryCompanyWalletPage(Long companyId, PayWalletTransactionBo bo, PageQuery pageQuery) { + Long walletId = resolveCompanyWalletId(companyId); + PayWalletTransactionBo queryBo = bo == null ? new PayWalletTransactionBo() : bo; + // 企业钱包流水查询必须锁定到目标企业钱包,避免越权传参。 + queryBo.setWalletId(walletId == null ? -1L : walletId); + LambdaQueryWrapper lqw = buildQueryWrapper(queryBo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的会员钱包流水列表 + * + * @param bo 查询条件 + * @return 会员钱包流水列表 + */ + @Override + public List queryList(PayWalletTransactionBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(PayWalletTransactionBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(PayWalletTransaction::getId); + lqw.eq(bo.getWalletId() != null, PayWalletTransaction::getWalletId, bo.getWalletId()); + lqw.eq(bo.getBizType() != null, PayWalletTransaction::getBizType, bo.getBizType()); + lqw.eq(StringUtils.isNotBlank(bo.getBizId()), PayWalletTransaction::getBizId, bo.getBizId()); + lqw.eq(StringUtils.isNotBlank(bo.getNo()), PayWalletTransaction::getNo, bo.getNo()); + lqw.eq(StringUtils.isNotBlank(bo.getTitle()), PayWalletTransaction::getTitle, bo.getTitle()); + lqw.eq(bo.getPrice() != null, PayWalletTransaction::getPrice, bo.getPrice()); + lqw.eq(bo.getBalance() != null, PayWalletTransaction::getBalance, bo.getBalance()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), PayWalletTransaction::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), PayWalletTransaction::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, PayWalletTransaction::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + private Long resolveCompanyWalletId(Long companyId) { + if (companyId == null) { + return null; + } + PayWallet wallet = payWalletMapper.selectOne(Wrappers.lambdaQuery() + .eq(PayWallet::getUserId, companyId) + .eq(PayWallet::getWalletType, COMPANY_WALLET_TYPE) + .last("limit 1")); + return wallet == null ? null : wallet.getId(); + } + + /** + * 新增会员钱包流水 + * + * @param bo 会员钱包流水 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(PayWalletTransactionBo bo) { + PayWalletTransaction add = MapstructUtils.convert(bo, PayWalletTransaction.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改会员钱包流水 + * + * @param bo 会员钱包流水 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(PayWalletTransactionBo bo) { + PayWalletTransaction update = MapstructUtils.convert(bo, PayWalletTransaction.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(PayWalletTransaction entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除会员钱包流水信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/pay/task/PayOrderCompensateTask.java b/src/main/java/com/hotwj/platform/pay/task/PayOrderCompensateTask.java new file mode 100644 index 0000000..e95b91a --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/task/PayOrderCompensateTask.java @@ -0,0 +1,23 @@ +package com.hotwj.platform.pay.task; + +import com.baomidou.lock.annotation.Lock4j; +import com.hotwj.platform.pay.service.PayRechargeOpenService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class PayOrderCompensateTask { + + private final PayRechargeOpenService payRechargeOpenService; + + @Lock4j(name = "pay:compensate:scan", expire = 55000, acquireTimeout = 3000) + @Scheduled(cron = "0 * * * * ?") + public void compensateOrders() { + log.debug("开始执行支付补偿任务"); + payRechargeOpenService.compensatePayOrders(); + } +} diff --git a/src/main/java/com/hotwj/platform/pay/utils/PayCryptoUtils.java b/src/main/java/com/hotwj/platform/pay/utils/PayCryptoUtils.java new file mode 100644 index 0000000..4f67a7b --- /dev/null +++ b/src/main/java/com/hotwj/platform/pay/utils/PayCryptoUtils.java @@ -0,0 +1,93 @@ +package com.hotwj.platform.pay.utils; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.util.StrUtil; +import org.dromara.common.core.exception.ServiceException; + +import javax.crypto.Cipher; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +public final class PayCryptoUtils { + + private PayCryptoUtils() { + } + + public static PrivateKey loadPrivateKey(String pemText) { + try { + String normalized = normalizePem(pemText) + .replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", "") + .replaceAll("\\s+", ""); + byte[] decoded = Base64.decode(normalized); + return KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded)); + } catch (Exception e) { + throw new ServiceException("解析商户私钥失败: " + e.getMessage()); + } + } + + public static PublicKey loadPublicKey(String pemText) { + try { + String normalized = normalizePem(pemText) + .replace("-----BEGIN PUBLIC KEY-----", "") + .replace("-----END PUBLIC KEY-----", "") + .replaceAll("\\s+", ""); + byte[] decoded = Base64.decode(normalized); + return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded)); + } catch (Exception e) { + throw new ServiceException("解析平台公钥失败: " + e.getMessage()); + } + } + + public static String rsaSign(String content, String privateKeyPem) { + try { + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initSign(loadPrivateKey(privateKeyPem)); + signature.update(content.getBytes(StandardCharsets.UTF_8)); + return Base64.encode(signature.sign()); + } catch (Exception e) { + throw new ServiceException("RSA 签名失败: " + e.getMessage()); + } + } + + public static boolean rsaVerify(String content, String sign, String publicKeyPem) { + try { + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initVerify(loadPublicKey(publicKeyPem)); + signature.update(content.getBytes(StandardCharsets.UTF_8)); + return signature.verify(Base64.decode(sign)); + } catch (Exception e) { + throw new ServiceException("RSA 验签失败: " + e.getMessage()); + } + } + + public static String aesGcmDecrypt(String apiV3Key, String associatedData, String nonce, String cipherText) { + try { + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec(apiV3Key.getBytes(StandardCharsets.UTF_8), "AES"); + GCMParameterSpec gcm = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8)); + cipher.init(Cipher.DECRYPT_MODE, keySpec, gcm); + if (StrUtil.isNotBlank(associatedData)) { + cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8)); + } + byte[] plain = cipher.doFinal(Base64.decode(cipherText)); + return new String(plain, StandardCharsets.UTF_8); + } catch (Exception e) { + throw new ServiceException("微信回调解密失败: " + e.getMessage()); + } + } + + private static String normalizePem(String text) { + if (StrUtil.isBlank(text)) { + throw new ServiceException("支付密钥配置不能为空"); + } + return text.replace("\\n", "\n").trim(); + } +} diff --git a/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/controller/HotPolicyRevisionController.java b/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/controller/HotPolicyRevisionController.java new file mode 100644 index 0000000..aecf3ec --- /dev/null +++ b/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/controller/HotPolicyRevisionController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.regulationManagement.policyRevision.controller; + +import com.hotwj.platform.regulationManagement.policyRevision.domain.bo.HotPolicyRevisionBo; +import com.hotwj.platform.regulationManagement.policyRevision.domain.vo.HotPolicyRevisionVo; +import com.hotwj.platform.regulationManagement.policyRevision.service.IHotPolicyRevisionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 制度修正记录 + * + * @author shihongwei + * @date 2025-12-11 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/regulationManagement/policyRevision") +@Tag(name = "制度修正记录", description = "制度修正记录增删改查与导出") +public class HotPolicyRevisionController extends BaseController { + + private final IHotPolicyRevisionService hotPolicyRevisionService; + + /** + * 查询制度修正记录列表 + */ + //@SaCheckPermission("regulationManagement:policyRevision:list") + @GetMapping("/list") + @Operation(summary = "分页查询制度修正记录列表") + public TableDataInfo list(HotPolicyRevisionBo bo, PageQuery pageQuery) { + return hotPolicyRevisionService.queryPageList(bo, pageQuery); + } + + /** + * 导出制度修正记录列表 + */ + //@SaCheckPermission("regulationManagement:policyRevision:export") + @Log(title = "制度修正记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出制度修正记录列表") + public void export(HotPolicyRevisionBo bo, HttpServletResponse response) { + List list = hotPolicyRevisionService.queryList(bo); + ExcelUtil.exportExcel(list, "制度修正记录", HotPolicyRevisionVo.class, response); + } + + /** + * 获取制度修正记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("regulationManagement:policyRevision:query") + @GetMapping("/{id}") + @Operation(summary = "查询制度修正记录详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotPolicyRevisionService.queryById(id)); + } + + /** + * 新增制度修正记录 + */ + //@SaCheckPermission("regulationManagement:policyRevision:add") + @Log(title = "制度修正记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增制度修正记录") + public R add(@Validated(AddGroup.class) @RequestBody HotPolicyRevisionBo bo) { + return toAjax(hotPolicyRevisionService.insertByBo(bo)); + } + + /** + * 修改制度修正记录 + */ + //@SaCheckPermission("regulationManagement:policyRevision:edit") + @Log(title = "制度修正记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改制度修正记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotPolicyRevisionBo bo) { + return toAjax(hotPolicyRevisionService.updateByBo(bo)); + } + + /** + * 删除制度修正记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("regulationManagement:policyRevision:remove") + @Log(title = "制度修正记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除制度修正记录") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotPolicyRevisionService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/domain/HotPolicyRevision.java b/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/domain/HotPolicyRevision.java new file mode 100644 index 0000000..221e966 --- /dev/null +++ b/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/domain/HotPolicyRevision.java @@ -0,0 +1,90 @@ +package com.hotwj.platform.regulationManagement.policyRevision.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 制度修正记录对象 hot_policy_revision + * + * @author shihongwei + * @date 2025-12-11 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_policy_revision") +public class HotPolicyRevision extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 制度ID + */ + private Long policyId; + + /** + * 修订版本号(从1递增) + */ + private Long versionNo; + + /** + * 修订说明 + */ + private String changeNote; + + /** + * 内容快照(修正后 富文本) + */ + private String content; + + /** + * 修正前(富文本) + */ + private String correction; + + /** + * 附件快照URL(或OSS地址) + */ + private String attachmentUrl; + + /** + * 来源模板ID + */ + private Long templateId; + + /** + * 修订人 + */ + private Long createdBy; + + /** + * 修订时间 + */ + private Date createdTime; + + /** + * 0=正常 1=删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/domain/bo/HotPolicyRevisionBo.java b/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/domain/bo/HotPolicyRevisionBo.java new file mode 100644 index 0000000..a86e3d4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/domain/bo/HotPolicyRevisionBo.java @@ -0,0 +1,94 @@ +package com.hotwj.platform.regulationManagement.policyRevision.domain.bo; + +import com.hotwj.platform.regulationManagement.policyRevision.domain.HotPolicyRevision; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 制度修正记录业务对象 hot_policy_revision + * + * @author shihongwei + * @date 2025-12-11 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotPolicyRevision.class, reverseConvertGenerate = false) +public class HotPolicyRevisionBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 制度ID + */ + private Long policyId; + + /** + * 修订版本号(从1递增) + */ + private Long versionNo; + + /** + * 修订说明 + */ + private String changeNote; + + /** + * 内容快照(修正后 富文本) + */ + private String content; + + /** + * 修正前(富文本) + */ + private String correction; + + /** + * 附件快照URL(或OSS地址) + */ + private String attachmentUrl; + + /** + * 来源模板ID + */ + private Long templateId; + + /** + * 修订人 + */ + private Long createdBy; + + /** + * 修订时间 + */ + private Date createdTime; + + /** + * 0=正常 1=删除 + */ + private Long isDeleted; + + /** + * 制度名称 + */ + private String policyName; + + /** + * 制度类型 + */ + private String policyType; +} diff --git a/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/domain/vo/HotPolicyRevisionVo.java b/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/domain/vo/HotPolicyRevisionVo.java new file mode 100644 index 0000000..31cb0c1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/domain/vo/HotPolicyRevisionVo.java @@ -0,0 +1,112 @@ +package com.hotwj.platform.regulationManagement.policyRevision.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.regulationManagement.policyRevision.domain.HotPolicyRevision; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 制度修正记录视图对象 hot_policy_revision + * + * @author shihongwei + * @date 2025-12-11 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotPolicyRevision.class) +public class HotPolicyRevisionVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 制度ID + */ + @ExcelProperty(value = "制度ID") + private Long policyId; + + /** + * 修订版本号(从1递增) + */ + @ExcelProperty(value = "修订版本号(从1递增)") + private Long versionNo; + + /** + * 修订说明 + */ + @ExcelProperty(value = "修订说明") + private String changeNote; + + /** + * 内容快照(修正后 富文本) + */ + @ExcelProperty(value = "内容快照", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "修=正后,富=文本") + private String content; + + /** + * 修正前(富文本) + */ + @ExcelProperty(value = "修正前", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "富=文本") + private String correction; + + /** + * 附件快照URL(或OSS地址) + */ + @ExcelProperty(value = "附件快照URL", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "或=OSS地址") + private String attachmentUrl; + + /** + * 来源模板ID + */ + @ExcelProperty(value = "来源模板ID") + private Long templateId; + + /** + * 修订人 + */ + @ExcelProperty(value = "修订人") + private Long createdBy; + + private String createByName; + + private String policyTitle; + + private String policyType; + + /** + * 修订时间 + */ + @ExcelProperty(value = "修订时间") + private Date createdTime; + + /** + * 0=正常 1=删除 + */ + @ExcelProperty(value = "0=正常 1=删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/mapper/HotPolicyRevisionMapper.java b/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/mapper/HotPolicyRevisionMapper.java new file mode 100644 index 0000000..c551eeb --- /dev/null +++ b/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/mapper/HotPolicyRevisionMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.regulationManagement.policyRevision.mapper; + +import com.hotwj.platform.regulationManagement.policyRevision.domain.HotPolicyRevision; +import com.hotwj.platform.regulationManagement.policyRevision.domain.vo.HotPolicyRevisionVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 制度修正记录Mapper接口 + * + * @author shihongwei + * @date 2025-12-11 + */ +@Mapper +public interface HotPolicyRevisionMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/service/IHotPolicyRevisionService.java b/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/service/IHotPolicyRevisionService.java new file mode 100644 index 0000000..7396cee --- /dev/null +++ b/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/service/IHotPolicyRevisionService.java @@ -0,0 +1,84 @@ +package com.hotwj.platform.regulationManagement.policyRevision.service; + +import com.hotwj.platform.regulationManagement.policyRevision.domain.bo.HotPolicyRevisionBo; +import com.hotwj.platform.regulationManagement.policyRevision.domain.vo.HotPolicyRevisionVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 制度修正记录Service接口 + * + * @author shihongwei + * @date 2025-12-11 + */ +public interface IHotPolicyRevisionService { + + /** + * 查询制度修正记录 + * + * @param id 主键 + * @return 制度修正记录 + */ + HotPolicyRevisionVo queryById(Long id); + + /** + * 分页查询制度修正记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 制度修正记录分页列表 + */ + TableDataInfo queryPageList(HotPolicyRevisionBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的制度修正记录列表 + * + * @param bo 查询条件 + * @return 制度修正记录列表 + */ + List queryList(HotPolicyRevisionBo bo); + + /** + * 新增制度修正记录 + * + * @param bo 制度修正记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotPolicyRevisionBo bo); + + /** + * 修改制度修正记录 + * + * @param bo 制度修正记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotPolicyRevisionBo bo); + + /** + * 校验并批量删除制度修正记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 计算下一个版本号 + * + * @param policyId 制度ID + * @return 下一个版本号 + */ + Long nextVersionNo(Long policyId); + + /** + * 创建修正记录 + * + * @param bo 修正记录 + * @return 是否成功 + */ + Boolean createRevision(HotPolicyRevisionBo bo); +} diff --git a/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/service/impl/HotPolicyRevisionServiceImpl.java b/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/service/impl/HotPolicyRevisionServiceImpl.java new file mode 100644 index 0000000..2aa79e4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/regulationManagement/policyRevision/service/impl/HotPolicyRevisionServiceImpl.java @@ -0,0 +1,199 @@ +package com.hotwj.platform.regulationManagement.policyRevision.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.regulationManagement.policyRevision.domain.HotPolicyRevision; +import com.hotwj.platform.regulationManagement.policyRevision.domain.bo.HotPolicyRevisionBo; +import com.hotwj.platform.regulationManagement.policyRevision.domain.vo.HotPolicyRevisionVo; +import com.hotwj.platform.regulationManagement.policyRevision.mapper.HotPolicyRevisionMapper; +import com.hotwj.platform.regulationManagement.policyRevision.service.IHotPolicyRevisionService; +import com.hotwj.platform.resourceManagement.companyPolicy.domain.HotCompanyPolicy; +import com.hotwj.platform.resourceManagement.companyPolicy.mapper.HotCompanyPolicyMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.system.service.ISysUserService; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 制度修正记录Service业务层处理 + * + * @author shihongwei + * @date 2025-12-11 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotPolicyRevisionServiceImpl implements IHotPolicyRevisionService { + + private final HotPolicyRevisionMapper baseMapper; + private final HotCompanyPolicyMapper hotCompanyPolicyMapper; + private final ISysUserService userService; + + /** + * 查询制度修正记录 + * + * @param id 主键 + * @return 制度修正记录 + */ + @Override + public HotPolicyRevisionVo queryById(Long id) { + HotPolicyRevisionVo vo = baseMapper.selectVoById(id); + if (vo == null) { + return null; + } + if (vo.getPolicyId() != null) { + HotCompanyPolicy policy = hotCompanyPolicyMapper.selectById(vo.getPolicyId()); + if (policy != null) { + vo.setPolicyTitle(policy.getPolicyTitle()); + vo.setPolicyType(policy.getPolicyType()); + } + } + + return vo; + } + + /** + * 分页查询制度修正记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 制度修正记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotPolicyRevisionBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + List records = result.getRecords(); + if (records != null && !records.isEmpty()) { + List policyIds = records.stream().map(HotPolicyRevisionVo::getPolicyId).filter(Objects::nonNull).distinct().collect(Collectors.toList()); + Map policyMap = hotCompanyPolicyMapper.selectList( + Wrappers.lambdaQuery() + .in(HotCompanyPolicy::getId, policyIds) + .select(HotCompanyPolicy::getId, HotCompanyPolicy::getPolicyTitle, HotCompanyPolicy::getPolicyType) + ).stream().collect(Collectors.toMap(HotCompanyPolicy::getId, Function.identity())); + for (HotPolicyRevisionVo vo : records) { + HotCompanyPolicy p = vo.getPolicyId() != null ? policyMap.get(vo.getPolicyId()) : null; + if (p != null) { + vo.setPolicyTitle(p.getPolicyTitle()); + vo.setPolicyType(p.getPolicyType()); + } + } + } + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的制度修正记录列表 + * + * @param bo 查询条件 + * @return 制度修正记录列表 + */ + @Override + public List queryList(HotPolicyRevisionBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotPolicyRevisionBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotPolicyRevision::getCreatedTime); + lqw.eq(bo.getCompanyId() != null, HotPolicyRevision::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getPolicyId() != null, HotPolicyRevision::getPolicyId, bo.getPolicyId()); + lqw.eq(bo.getVersionNo() != null, HotPolicyRevision::getVersionNo, bo.getVersionNo()); + lqw.eq(StringUtils.isNotBlank(bo.getChangeNote()), HotPolicyRevision::getChangeNote, bo.getChangeNote()); + lqw.eq(StringUtils.isNotBlank(bo.getContent()), HotPolicyRevision::getContent, bo.getContent()); + lqw.eq(StringUtils.isNotBlank(bo.getAttachmentUrl()), HotPolicyRevision::getAttachmentUrl, bo.getAttachmentUrl()); + lqw.eq(bo.getTemplateId() != null, HotPolicyRevision::getTemplateId, bo.getTemplateId()); + lqw.eq(bo.getCreatedBy() != null, HotPolicyRevision::getCreatedBy, bo.getCreatedBy()); + lqw.apply(StringUtils.isNotBlank(bo.getPolicyName()), "policy_id in (select id from hot_company_policy where policy_title like {0})", "%" + bo.getPolicyName() + "%"); + lqw.apply(StringUtils.isNotBlank(bo.getPolicyType()), "policy_id in (select id from hot_company_policy where policy_type = {0})", bo.getPolicyType()); + if (params != null && params.get("beginTime") != null && params.get("endTime") != null) { + lqw.between(HotPolicyRevision::getCreatedTime, params.get("beginTime"), params.get("endTime")); + } else { + lqw.eq(bo.getCreatedTime() != null, HotPolicyRevision::getCreatedTime, bo.getCreatedTime()); + } + lqw.eq(bo.getIsDeleted() != null, HotPolicyRevision::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增制度修正记录 + * + * @param bo 制度修正记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotPolicyRevisionBo bo) { + HotPolicyRevision add = MapstructUtils.convert(bo, HotPolicyRevision.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改制度修正记录 + * + * @param bo 制度修正记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotPolicyRevisionBo bo) { + HotPolicyRevision update = MapstructUtils.convert(bo, HotPolicyRevision.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotPolicyRevision entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除制度修正记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + public Long nextVersionNo(Long policyId) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(HotPolicyRevision::getPolicyId, policyId); + lqw.orderByDesc(HotPolicyRevision::getVersionNo); + lqw.last("limit 1"); + HotPolicyRevision last = baseMapper.selectOne(lqw); + Long lastNo = last != null ? last.getVersionNo() : 0L; + return lastNo + 1; + } + + @Override + public Boolean createRevision(HotPolicyRevisionBo bo) { + return insertByBo(bo); + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/controller/DriverFileController.java b/src/main/java/com/hotwj/platform/reportStatistics/controller/DriverFileController.java new file mode 100644 index 0000000..be28c66 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/controller/DriverFileController.java @@ -0,0 +1,217 @@ +package com.hotwj.platform.reportStatistics.controller; + +import cn.dev33.satoken.spring.SpringMVCUtil; +import cn.hutool.core.io.IoUtil; +import com.hotwj.platform.reportStatistics.service.IDriverFilePrintService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +/** + * 驾驶员档案打印模块 + * + * @author shihongwei + * @date 2026-01-04 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/reportStatistics/driverFile") +@Tag(name = "驾驶员档案打印", description = "驾驶员档案打印") +public class DriverFileController extends BaseController { + + private final IDriverFilePrintService driverFilePrintService; + + /** + * 驾驶员应聘表 + */ + //@SaCheckPermission("reportStatistics:driverFile:applicationForm") + @PostMapping("/applicationForm") + @Operation(summary = "驾驶员应聘表") + public void applicationForm(@RequestParam("ids") List driverIds) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (driverIds == null || driverIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少驾驶员ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdf = driverFilePrintService.generateApplicationForm(driverIds); + writeAttachment(response, "驾驶员应聘表.pdf", pdf, "application/pdf"); + } + + + /** + * 身份证扫描件 + */ + //@SaCheckPermission("reportStatistics:driverFile:idCardScan") + @PostMapping("/idCardScan") + @Operation(summary = "驾驶员身份证扫描件") + public void idCardScan(@RequestParam("ids") List driverIds) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (driverIds == null || driverIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少驾驶员ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdf = driverFilePrintService.generateIdCardScan(driverIds); + writeAttachment(response, "驾驶员身份证扫描件.pdf", pdf, "application/pdf"); + } + /** + * 驾驶员机动车驾驶证复印件 + */ + //@SaCheckPermission("reportStatistics:driverFile:driverLicenseCopy") + @PostMapping("/driverLicenseCopy") + @Operation(summary = "驾驶员机动车驾驶证复印件") + public void driverLicenseCopy(@RequestParam("ids") List driverIds) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (driverIds == null || driverIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少驾驶员ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdf = driverFilePrintService.generateDriverLicenseCopy(driverIds); + writeAttachment(response, "驾驶员机动车驾驶证复印件.pdf", pdf, "application/pdf"); + } + + /** + * 驾驶员从业资格证复印件 + */ + //@SaCheckPermission("reportStatistics:driverFile:qualificationCertificateCopy") + @PostMapping("/qualificationCertificateCopy") + @Operation(summary = "驾驶员从业资格证复印件") + public void qualificationCertificateCopy(@RequestParam("ids") List driverIds) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (driverIds == null || driverIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少驾驶员ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdf = driverFilePrintService.generateQualificationCertificateCopy(driverIds); + writeAttachment(response, "驾驶员从业资格证复印件.pdf", pdf, "application/pdf"); + } + + /** + * 驾驶员资格审查及技能考核登记表 + */ + //@SaCheckPermission("reportStatistics:driverFile:qualificationReview") + @PostMapping("/qualificationReview") + @Operation(summary = "驾驶员资格审查及技能考核登记表") + public void qualificationReview(@RequestParam("ids") List driverIds) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (driverIds == null || driverIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少驾驶员ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdf = driverFilePrintService.generateQualificationReview(driverIds); + writeAttachment(response, "驾驶员资格审查及技能考核登记表.pdf", pdf, "application/pdf"); + } + + /** + * 驾驶员安全行车事故记录 + */ + //@SaCheckPermission("reportStatistics:driverFile:accidentRecord") + @PostMapping("/accidentRecord") + @Operation(summary = "驾驶员安全行车事故记录") + public void accidentRecord(@RequestParam("ids") List driverIds) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (driverIds == null || driverIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少驾驶员ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdf = driverFilePrintService.generateAccidentRecord(driverIds); + writeAttachment(response, "驾驶员安全行车事故记录.pdf", pdf, "application/pdf"); + } + + /** + * 驾驶员违章记录 + */ + //@SaCheckPermission("reportStatistics:driverFile:violationRecord") + @PostMapping("/violationRecord") + @Operation(summary = "驾驶员违章记录") + public void violationRecord(@RequestParam("ids") List driverIds) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (driverIds == null || driverIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少驾驶员ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdf = driverFilePrintService.generateViolationRecord(driverIds); + writeAttachment(response, "驾驶员违章记录.pdf", pdf, "application/pdf"); + } + + //@SaCheckPermission("reportStatistics:driverFile:annualAssessmentRecord") + @PostMapping("/annualAssessmentRecord") + @Operation(summary = "驾驶员年度考核记录") + public void annualAssessmentRecord(@RequestParam("ids") List driverIds) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (driverIds == null || driverIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少驾驶员ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdf = driverFilePrintService.generateAnnualAssessmentRecord(driverIds); + writeAttachment(response, "驾驶员年度考核记录.pdf", pdf, "application/pdf"); + } + + //@SaCheckPermission("reportStatistics:driverFile:medicalExaminationReport") + @PostMapping("/medicalExaminationReport") + @Operation(summary = "驾驶员体检报告") + public void medicalExaminationReport(@RequestParam("ids") List driverIds) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (driverIds == null || driverIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少驾驶员ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdf = driverFilePrintService.generateMedicalExaminationReport(driverIds); + writeAttachment(response, "驾驶员体检报告.pdf", pdf, "application/pdf"); + } + + //@SaCheckPermission("reportStatistics:driverFile:rewardPunishmentRecord") + @PostMapping("/rewardPunishmentRecord") + @Operation(summary = "员工奖惩记录表") + public void rewardPunishmentRecord(@RequestParam("ids") List driverIds) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (driverIds == null || driverIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少驾驶员ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdf = driverFilePrintService.generateRewardPunishmentRecord(driverIds); + writeAttachment(response, "驾驶员奖惩记录表.pdf", pdf, "application/pdf"); + } + + /** + * 一键打印 + */ + //@SaCheckPermission("reportStatistics:driverFile:oneClickPrint") + @PostMapping("/oneClickPrint") + @Operation(summary = "驾驶员档案一键打印") + public void oneClickPrint(@RequestParam("ids") List driverIds, @RequestParam("types") List types) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (driverIds == null || driverIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少驾驶员ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + if (types == null || types.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少打印类型参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdfBytes = driverFilePrintService.generateOneClickPrintPdf(driverIds, types); + writeAttachment(response, "驾驶员档案一键打印.pdf", pdfBytes, "application/pdf"); + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/controller/LearnPrintReportController.java b/src/main/java/com/hotwj/platform/reportStatistics/controller/LearnPrintReportController.java new file mode 100644 index 0000000..525eb3a --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/controller/LearnPrintReportController.java @@ -0,0 +1,336 @@ +package com.hotwj.platform.reportStatistics.controller; + +import com.hotwj.platform.reportStatistics.domain.vo.OfflineTrainingListVo; +import com.hotwj.platform.reportStatistics.domain.vo.TrainingPlanDetailVo; +import com.hotwj.platform.reportStatistics.service.ISecurityManageFilePrintService; +import com.hotwj.platform.securityManagement.securityMeeting.domain.bo.HotSecurityMeetingBo; +import com.hotwj.platform.securityManagement.securityMeeting.domain.vo.HotSecurityMeetingVo; +import com.hotwj.platform.securityManagement.securityMeeting.service.IHotSecurityMeetingService; +import com.hotwj.platform.securityManagement.training.domain.bo.HotTrainingBo; +import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo; +import com.hotwj.platform.securityManagement.training.service.IHotTrainingService; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.bo.HotTrainingCourseRecordBo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.vo.HotTrainingCourseRecordVo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.service.IHotTrainingCourseRecordService; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.bo.HotTrainingParticipantBo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.HotTrainingParticipantVo; +import com.hotwj.platform.securityManagement.trainingParticipant.service.IHotTrainingParticipantService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.file.FileUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; +import java.util.*; + +/** + * 学习报表打印模块 + * + * @author shihongwei + * @date 2026-02-18 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/reportStatistics/learnPrintReport") +@Tag(name = "学习报表打印", description = "学习报表打印") +public class LearnPrintReportController extends BaseController { + + private final IHotTrainingService hotTrainingService; + private final IHotTrainingParticipantService trainingParticipantService; + private final IHotTrainingCourseRecordService trainingCourseRecordService; + private final ISecurityManageFilePrintService securityManageFilePrintService; + private final IHotSecurityMeetingService hotSecurityMeetingService; + + //@SaCheckPermission("reportStatistics:preJobTrainingPlan:list") + @GetMapping("/trainingPlan/list") + @Operation(summary = "培训计划-分页查询") + public TableDataInfo preJobTrainingPlanList( + @RequestParam Long companyId, + @RequestParam(required = false) String planName, + @RequestParam(required = false) String personType, + @RequestParam(required = false) String trainingType, + @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startBeginDate, + @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startEndDate, + @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endBeginDate, + @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endEndDate, + @RequestParam(required = false) Long isEnabled, + @RequestParam(required = false) String planOwner, + PageQuery pageQuery + ) { + HotTrainingBo bo = new HotTrainingBo(); + bo.setCompanyId(companyId); + bo.setPlanName(planName); + bo.setPersonType(personType); + bo.setIsEnabled(isEnabled); + bo.setPlanOwner(planOwner); + String finalTrainingType = StringUtils.isBlank(trainingType) ? "pre-job" : trainingType; + bo.setTrainingType(finalTrainingType); + bo.setIsDeleted(0L); + if (startBeginDate != null) { + bo.getParams().put("startBeginDate", startBeginDate); + } + if (startEndDate != null) { + bo.getParams().put("startEndDate", startEndDate); + } + if (endBeginDate != null) { + bo.getParams().put("endBeginDate", endBeginDate); + } + if (endEndDate != null) { + bo.getParams().put("endEndDate", endEndDate); + } + return hotTrainingService.queryPageList(bo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:trainingPlanDetail:list") + @GetMapping("/trainingPlan/detail") + @Operation(summary = "培训计划明细-分页查询") + public TableDataInfo trainingPlanDetail( + @RequestParam Long companyId, + @RequestParam Long trainingId, + @RequestParam(required = false) String userName, + @RequestParam(required = false) String phone, + @RequestParam(required = false) Long personType, + @RequestParam(required = false) Long isCompleted, + PageQuery pageQuery + ) { + HotTrainingParticipantBo bo = new HotTrainingParticipantBo(); + bo.setCompanyId(companyId); + bo.setTrainingId(trainingId); + bo.setUserName(userName); + bo.setPhone(phone); + bo.setPersonType(personType); + bo.setIsCompleted(isCompleted); + bo.setIsDeleted(0L); + TableDataInfo participantPage = trainingParticipantService.queryPageList(bo, pageQuery); + List participantRows = participantPage.getRows(); + if (participantRows == null || participantRows.isEmpty()) { + return new TableDataInfo<>(new ArrayList<>(), 0); + } + HotTrainingVo trainingVo = hotTrainingService.queryById(trainingId); + Long isMakeUp = trainingVo != null ? trainingVo.getIsMakeUp() : null; + HotTrainingCourseRecordBo recordBo = new HotTrainingCourseRecordBo(); + recordBo.setCompanyId(companyId); + recordBo.setTrainingId(trainingId); + recordBo.setIsDeleted(0L); + List records = trainingCourseRecordService.queryList(recordBo); + Map signatureMap = new HashMap<>(); + for (HotTrainingCourseRecordVo record : records) { + if (record.getSignatureOssId() != null && StringUtils.isNotBlank(record.getUserId())) { + signatureMap.putIfAbsent(record.getUserId(), record.getSignatureOssId()); + } + } + List list = new ArrayList<>(); + for (HotTrainingParticipantVo p : participantRows) { + TrainingPlanDetailVo vo = new TrainingPlanDetailVo(); + vo.setUserId(p.getUserId()); + vo.setUserName(p.getUserName()); + vo.setPhone(p.getPhone()); + vo.setPersonType(p.getPersonType()); + vo.setCompleteTime(p.getCompleteTime()); + vo.setIsCompleted(p.getIsCompleted()); + vo.setIsMakeUp(isMakeUp); + vo.setSignatureOssId(signatureMap.get(p.getUserId())); + list.add(vo); + } + return new TableDataInfo<>(list, participantPage.getTotal()); + } + + //@SaCheckPermission("reportStatistics:trainingPlanDetail:list") + @GetMapping("/trainingPlan/monthlyStat/exportPdf") + @Operation(summary = "培训计划月度统计-PDF导出") + public void trainingPlanMonthlyStatExportPdf( + @RequestParam Long companyId, + @RequestParam Long trainingId, + @RequestParam(value = "userIds", required = false) List userIds, + HttpServletResponse response + ) { + byte[] bytes = securityManageFilePrintService.generateTrainingPlanMonthlyStatPdf(companyId, trainingId, userIds); + try { + FileUtils.setAttachmentResponseHeader(response, "培训计划月度统计表.pdf"); + response.setContentType("application/pdf"); + response.getOutputStream().write(bytes); + } catch (IOException e) { + throw new RuntimeException("导出PDF异常", e); + } + } + + //@SaCheckPermission("reportStatistics:trainingPlanDetail:list") + @GetMapping("/trainingPlan/studyRecord/exportPdf") + @Operation(summary = "学习记录打印-PDF导出") + public void studyRecordExportPdf( + @RequestParam Long companyId, + @RequestParam Long trainingId, + @RequestParam("userIds") List userIds, + HttpServletResponse response + ) { + byte[] bytes = securityManageFilePrintService.generateStudyRecordPdf(companyId, trainingId, userIds); + try { + FileUtils.setAttachmentResponseHeader(response, "学习记录表.pdf"); + response.setContentType("application/pdf"); + response.getOutputStream().write(bytes); + } catch (IOException e) { + throw new RuntimeException("导出PDF异常", e); + } + } + + //@SaCheckPermission("reportStatistics:trainingPlanDetail:list") + @GetMapping("/trainingPlan/studyDetail/exportPdf") + @Operation(summary = "学习明细打印-PDF导出") + public void studyDetailExportPdf( + @RequestParam Long companyId, + @RequestParam Long trainingId, + @RequestParam("userIds") java.util.List userIds, + HttpServletResponse response + ) { + byte[] bytes = securityManageFilePrintService.generateStudyDetailPdf(companyId, trainingId, userIds); + try { + FileUtils.setAttachmentResponseHeader(response, "学习明细表.pdf"); + response.setContentType("application/pdf"); + response.getOutputStream().write(bytes); + } catch (IOException e) { + throw new RuntimeException("导出PDF异常", e); + } + } + + //@SaCheckPermission("reportStatistics:preJobTrainingPlan:list") + @GetMapping("/offlineTraining/list") + @Operation(summary = "线下培训统计-分页查询") + public TableDataInfo offlineTrainingList( + @RequestParam Long companyId, + @RequestParam(required = false) String type, + @RequestParam(required = false) String category, + @RequestParam(required = false) String name, + @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date beginTime, + @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endTime, + PageQuery pageQuery + ) { + HotTrainingBo bo = new HotTrainingBo(); + bo.setCompanyId(companyId); + bo.setMeetingType(type); + bo.setTrainingType("offline"); + bo.setMeetingCategory(category); + bo.setPlanName(name); + bo.setIsDeleted(0L); + if (beginTime != null) { + bo.getParams().put("startBeginDate", beginTime); + } + if (endTime != null) { + bo.getParams().put("startEndDate", endTime); + } + TableDataInfo pageInfo = hotTrainingService.queryPageList(bo, pageQuery); + + // 转换为 OfflineTrainingListVo + List list = new ArrayList<>(); + if (pageInfo.getRows() != null) { + for (HotTrainingVo vo : pageInfo.getRows()) { + OfflineTrainingListVo item = new OfflineTrainingListVo(); + item.setId(vo.getId()); + item.setMeetingTopic(vo.getPlanName()); // 名称 + item.setMeetingCategory(vo.getMeetingCategory()); // 种类 + item.setOfflineMeetingType(vo.getMeetingType()); // 类型 + item.setEndTime(vo.getEndTime()); // 结束时间 + item.setMeetingStatus(vo.getIsIssued() != null && vo.getIsIssued() == 1 ? "已下发" : "未下发"); // 状态 (根据业务逻辑映射) + item.setStartTime(vo.getStartTime()); + item.setLocation(vo.getTrainingLocation()); + item.setHostName(vo.getTeacherName()); + item.setRecorderName(vo.getRecorderName()); + list.add(item); + } + } + + TableDataInfo result = new TableDataInfo<>(); + result.setRows(list); + result.setTotal(pageInfo.getTotal()); + result.setCode(pageInfo.getCode()); + result.setMsg(pageInfo.getMsg()); + return result; + } + + //@SaCheckPermission("reportStatistics:preJobTrainingPlan:list") + @GetMapping("/offlineMeeting/list") + @Operation(summary = "线下会议统计-分页查询") + public TableDataInfo offlineMeetingList( + @RequestParam Long companyId, + @RequestParam(required = false) String type, + @RequestParam(required = false) String category, + @RequestParam(required = false) String name, + @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date beginTime, + @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endTime, + PageQuery pageQuery + ) { + HotSecurityMeetingBo bo = new HotSecurityMeetingBo(); + bo.setCompanyId(companyId); + bo.setMeetingType("offline"); // 线下会议 + bo.setOfflineMeetingType(type); // 具体的线下会议类型 + bo.setMeetingCategory(category); + bo.setMeetingTopic(name); + bo.setIsDeleted(0L); + Map params = new HashMap<>(); + if (beginTime != null) { + params.put("beginTime", beginTime); + } + if (endTime != null) { + params.put("endTime", endTime); + } + bo.setParams(params); + TableDataInfo pageInfo = hotSecurityMeetingService.queryPageList(bo, pageQuery); + + // 转换为 OfflineTrainingListVo + List list = new ArrayList<>(); + if (pageInfo.getRows() != null) { + for (HotSecurityMeetingVo vo : pageInfo.getRows()) { + OfflineTrainingListVo item = new OfflineTrainingListVo(); + item.setId(vo.getId()); + item.setMeetingTopic(vo.getMeetingTopic()); // 名称 + item.setMeetingCategory(vo.getMeetingCategory()); // 种类 + item.setOfflineMeetingType(vo.getOfflineMeetingType()); // 类型 + item.setEndTime(vo.getEndTime()); // 结束时间 + item.setMeetingStatus(vo.getMeetingStatus()); // 状态 + item.setStartTime(vo.getStartTime()); + item.setLocation(vo.getLocation()); + item.setHostName(vo.getHostName()); + item.setRecorderName(vo.getRecorderName()); + list.add(item); + } + } + + TableDataInfo result = new TableDataInfo<>(); + result.setRows(list); + result.setTotal(pageInfo.getTotal()); + result.setCode(pageInfo.getCode()); + result.setMsg(pageInfo.getMsg()); + return result; + } + + //@SaCheckPermission("reportStatistics:preJobTrainingPlan:list") + @GetMapping("/offlineTraining/exportPdf") + @Operation(summary = "线下培训会议照片打印-PDF导出") + public void offlineTrainingPhotoExportPdf( + @RequestParam Long companyId, + @RequestParam("trainingIds") java.util.List trainingIds, + @RequestParam(value = "type", required = false, defaultValue = "1") Integer type, + HttpServletResponse response + ) { + byte[] bytes = securityManageFilePrintService.generateOfflineTrainingPhotoPdf(companyId, trainingIds, type); + try { + String fileName = type == 2 ? "线下会议记录.pdf" : "线下培训记录.pdf"; + FileUtils.setAttachmentResponseHeader(response, fileName); + response.setContentType("application/pdf"); + response.getOutputStream().write(bytes); + } catch (IOException e) { + throw new RuntimeException("导出PDF异常", e); + } + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/controller/LedgerController.java b/src/main/java/com/hotwj/platform/reportStatistics/controller/LedgerController.java new file mode 100644 index 0000000..8e8b9ed --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/controller/LedgerController.java @@ -0,0 +1,153 @@ +package com.hotwj.platform.reportStatistics.controller; + +import cn.dev33.satoken.spring.SpringMVCUtil; +import com.hotwj.platform.reportStatistics.domain.bo.ReportLedgerQueryBo; +import com.hotwj.platform.reportStatistics.domain.vo.*; +import com.hotwj.platform.reportStatistics.service.ILedgerReportService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 台账信息 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/reportStatistics/ledger") +@Tag(name = "台账信息", description = "台账信息") +public class LedgerController extends BaseController { + + private final ILedgerReportService ledgerReportService; + + //@SaCheckPermission("reportStatistics:driverInfoLedger:list") + @GetMapping("/driverInfo/list") + @Operation(summary = "从业人员信息管理台账-分页") + public TableDataInfo driverInfoList(@Valid ReportLedgerQueryBo bo, PageQuery pageQuery) { + return ledgerReportService.queryDriverInfo(null, bo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:driverInfoLedger:export") + @PostMapping("/driverInfo/export") + @Operation(summary = "从业人员信息管理台账-导出") + public void driverInfoExport(@Valid ReportLedgerQueryBo bo) { + HttpServletResponse response = SpringMVCUtil.getResponse(); + ExcelUtil.exportExcel(ledgerReportService.listDriverInfo(null, bo), "从业人员信息管理台账", DriverInfoLedgerVo.class, response); + } + + //@SaCheckPermission("reportStatistics:driverHealthLedger:list") + @GetMapping("/driverHealth/list") + @Operation(summary = "驾驶员身体健康台账-分页") + public TableDataInfo driverHealthList(@Valid ReportLedgerQueryBo bo, PageQuery pageQuery) { + return ledgerReportService.queryDriverHealth(null, bo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:driverHealthLedger:export") + @PostMapping("/driverHealth/export") + @Operation(summary = "驾驶员身体健康台账-导出") + public void driverHealthExport(@Valid ReportLedgerQueryBo bo) { + HttpServletResponse response = SpringMVCUtil.getResponse(); + ExcelUtil.exportExcel(ledgerReportService.listDriverHealth(null, bo), "驾驶员身体健康台账", DriverHealthLedgerVo.class, response); + } + + //@SaCheckPermission("reportStatistics:profCertScoreLedger:list") + @GetMapping("/profCertScore/list") + @Operation(summary = "从业资格证记分登记台账-分页") + public TableDataInfo profCertScoreList(@Valid ReportLedgerQueryBo bo, PageQuery pageQuery) { + return ledgerReportService.queryProfCertScore(null, bo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:profCertScoreLedger:export") + @PostMapping("/profCertScore/export") + @Operation(summary = "从业资格证记分登记台账-导出") + public void profCertScoreExport(@Valid ReportLedgerQueryBo bo) { + HttpServletResponse response = SpringMVCUtil.getResponse(); + ExcelUtil.exportExcel(ledgerReportService.listProfCertScore(null, bo), "从业资格证打分登记台账", ProfCertScoreLedgerVo.class, response); + } + + //@SaCheckPermission("reportStatistics:driverIntegrityLedger:list") + @GetMapping("/driverIntegrity/list") + @Operation(summary = "驾驶员诚信考核等级台账-分页") + public TableDataInfo driverIntegrityList(@Valid ReportLedgerQueryBo bo, PageQuery pageQuery) { + return ledgerReportService.queryDriverIntegrity(null, bo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:driverIntegrityLedger:export") + @PostMapping("/driverIntegrity/export") + @Operation(summary = "驾驶员诚信考核等级台账-导出") + public void driverIntegrityExport(@Valid ReportLedgerQueryBo bo) { + HttpServletResponse response = SpringMVCUtil.getResponse(); + ExcelUtil.exportExcel(ledgerReportService.listDriverIntegrity(null, bo), "驾驶员诚信考核等级台账", DriverIntegrityLedgerVo.class, response); + } + + //@SaCheckPermission("reportStatistics:vehicleInfoLedger:list") + @GetMapping("/vehicleInfo/list") + @Operation(summary = "车辆信息登记台账-分页") + public TableDataInfo vehicleInfoList(@Valid ReportLedgerQueryBo bo, PageQuery pageQuery) { + return ledgerReportService.queryVehicleInfo(bo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:vehicleInfoLedger:export") + @PostMapping("/vehicleInfo/export") + @Operation(summary = "车辆信息登记台账-导出") + public void vehicleInfoExport(@Valid ReportLedgerQueryBo bo) { + HttpServletResponse response = SpringMVCUtil.getResponse(); + ExcelUtil.exportExcel(ledgerReportService.listVehicleInfo(bo), "车辆信息登记台账", VehicleInfoLedgerVo.class, response); + } + + //@SaCheckPermission("reportStatistics:accidentLedger:list") + @GetMapping("/accident/list") + @Operation(summary = "安全事故登记台账-分页") + public TableDataInfo accidentList(@Valid ReportLedgerQueryBo bo, PageQuery pageQuery) { + return ledgerReportService.queryAccident(bo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:accidentLedger:export") + @PostMapping("/accident/export") + @Operation(summary = "安全事故登记台账-导出") + public void accidentExport(@Valid ReportLedgerQueryBo bo) { + HttpServletResponse response = SpringMVCUtil.getResponse(); + java.util.List list = ledgerReportService.listAccident(bo); + java.util.List exportList = new java.util.ArrayList<>(); + for (AccidentLedgerVo vo : list) { + AccidentLedgerExportVo exportVo = new AccidentLedgerExportVo(); + exportVo.setAccidentTime(vo.getAccidentTime()); + exportVo.setPlateNumber(vo.getPlateNumber()); + exportVo.setDriverName(vo.getDriverName()); + // 解析事故等级 + if (org.dromara.common.core.utils.StringUtils.isNotEmpty(vo.getAccidentLevel())) { + try { + String levelName = cn.hutool.json.JSONUtil.parseObj(vo.getAccidentLevel()).getStr("levelName"); + exportVo.setAccidentLevel(levelName); + } catch (Exception e) { + exportVo.setAccidentLevel(vo.getAccidentLevel()); + } + } + exportVo.setMainCause(vo.getMainCause()); + exportVo.setOccurLocation(vo.getOccurLocation()); + exportVo.setDeathToll(vo.getDeathToll()); + exportVo.setNumberSeriouslyInjured(vo.getNumberSeriouslyInjured()); + exportVo.setNumberMinorInjuries(vo.getNumberMinorInjuries()); + exportVo.setEconomicLoss(vo.getEconomicLoss()); + exportVo.setDivisionResponsibilities(vo.getDivisionResponsibilities()); + exportVo.setCaseClosingTime(vo.getCaseClosingTime()); + exportVo.setRemark(vo.getRemark()); + exportList.add(exportVo); + } + ExcelUtil.exportExcel(exportList, "安全事故登记台账", AccidentLedgerExportVo.class, response); + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/controller/PersonManagementController.java b/src/main/java/com/hotwj/platform/reportStatistics/controller/PersonManagementController.java new file mode 100644 index 0000000..5e010e9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/controller/PersonManagementController.java @@ -0,0 +1,210 @@ +package com.hotwj.platform.reportStatistics.controller; + +import com.hotwj.platform.reportStatistics.domain.bo.ReportLedgerQueryBo; +import com.hotwj.platform.reportStatistics.domain.vo.*; +import com.hotwj.platform.reportStatistics.service.ILedgerReportService; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.vo.HotGovEnterpriseUnitVo; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.service.IHotGovEnterpriseUnitService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 从业人员信息管理台账(政府端) + * + * @author shihongwei + * @date 2026-01-06 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/reportStatistics/personManagement") +@Tag(name = "从业人员信息管理台账(政府端)", description = "从业人员信息管理台账(政府端)") +public class PersonManagementController extends BaseController { + + private final ILedgerReportService ledgerReportService; + private final IHotGovEnterpriseUnitService hotGovEnterpriseUnitService; + + //@SaCheckPermission("personManagement:driverInfo:list") + @GetMapping("/driverInfo/list") + @Operation(summary = "从业人员信息管理台账-分页(政府端)") + public TableDataInfo driverInfoList(ReportLedgerQueryBo bo, PageQuery pageQuery) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return TableDataInfo.build(); + } + HotGovEnterpriseUnitVo unitVo = hotGovEnterpriseUnitService.queryById(loginUser.getCompanyId()); + if (unitVo == null || unitVo.getDistrictCode() == null) { + return TableDataInfo.build(); + } + return ledgerReportService.queryDriverInfo(unitVo.getDistrictCode(), bo, pageQuery); + } + + //@SaCheckPermission("personManagement:driverHealth:list") + @GetMapping("/driverHealth/list") + @Operation(summary = "驾驶员身体健康台账-分页(政府端)") + public TableDataInfo driverHealthList(ReportLedgerQueryBo bo, PageQuery pageQuery) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return TableDataInfo.build(); + } + HotGovEnterpriseUnitVo unitVo = hotGovEnterpriseUnitService.queryById(loginUser.getCompanyId()); + if (unitVo == null || unitVo.getDistrictCode() == null) { + return TableDataInfo.build(); + } + return ledgerReportService.queryDriverHealth(unitVo.getDistrictCode(), bo, pageQuery); + } + + //@SaCheckPermission("personManagement:profCertScore:list") + @GetMapping("/profCertScore/list") + @Operation(summary = "从业资格证记分登记台账-分页(政府端)") + public TableDataInfo profCertScoreList(ReportLedgerQueryBo bo, PageQuery pageQuery) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return TableDataInfo.build(); + } + HotGovEnterpriseUnitVo unitVo = hotGovEnterpriseUnitService.queryById(loginUser.getCompanyId()); + if (unitVo == null || unitVo.getDistrictCode() == null) { + return TableDataInfo.build(); + } + //离职人员不显示 + bo.setStatus(1L); + return ledgerReportService.queryProfCertScore(unitVo.getDistrictCode(), bo, pageQuery); + } + + //@SaCheckPermission("personManagement:driverIntegrity:list") + @GetMapping("/driverIntegrity/list") + @Operation(summary = "驾驶员诚信考核等级台账-分页(政府端)") + public TableDataInfo driverIntegrityList(ReportLedgerQueryBo bo, PageQuery pageQuery) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return TableDataInfo.build(); + } + HotGovEnterpriseUnitVo unitVo = hotGovEnterpriseUnitService.queryById(loginUser.getCompanyId()); + if (unitVo == null || unitVo.getDistrictCode() == null) { + return TableDataInfo.build(); + } + //离职人员不显示 + bo.setStatus(1L); + return ledgerReportService.queryDriverIntegrity(unitVo.getDistrictCode(), bo, pageQuery); + } + + //@SaCheckPermission("personManagement:preJobTraining:list") + @GetMapping("/preJobTraining/list") + @Operation(summary = "从业人员岗前培训考核记录-分页(政府端)") + public TableDataInfo preJobTrainingList(ReportLedgerQueryBo bo, PageQuery pageQuery) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return TableDataInfo.build(); + } + HotGovEnterpriseUnitVo unitVo = hotGovEnterpriseUnitService.queryById(loginUser.getCompanyId()); + if (unitVo == null || unitVo.getDistrictCode() == null) { + return TableDataInfo.build(); + } + // 仅统计审核通过的人员,保持与明细列表一致 + bo.setAuditStatus(1); + return ledgerReportService.queryGovPreJobTraining(unitVo.getDistrictCode(), bo, pageQuery); + } + + //@SaCheckPermission("personManagement:preJobTraining:detail") + @GetMapping("/preJobTraining/detail") + @Operation(summary = "从业人员岗前培训考核记录-明细列表(政府端)") + public TableDataInfo preJobTrainingDetail(ReportLedgerQueryBo bo, PageQuery pageQuery) { + if (bo.getCompanyId() == null) { + return TableDataInfo.build(); + } + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return TableDataInfo.build(); + } + HotGovEnterpriseUnitVo unitVo = hotGovEnterpriseUnitService.queryById(loginUser.getCompanyId()); + if (unitVo == null || unitVo.getDistrictCode() == null) { + return TableDataInfo.build(); + } + //离职人员不显示 + bo.setStatus(1L); + bo.setAuditStatus(1); + return ledgerReportService.queryGovPreJobTrainingDetail(unitVo.getDistrictCode(), bo, pageQuery); + } + + //@SaCheckPermission("personManagement:safetyEducationTraining:list") + @GetMapping("/safetyEducationTraining/list") + @Operation(summary = "安全生产教育培训记录-分页(政府端)") + public TableDataInfo safetyEducationTrainingList(ReportLedgerQueryBo bo, PageQuery pageQuery) { + if (bo.getCompanyId() == null) { + return TableDataInfo.build(); + } + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return TableDataInfo.build(); + } + return ledgerReportService.queryGovSafetyEducationTraining(bo, pageQuery); + } + + //@SaCheckPermission("personManagement:safetyEducationTraining:query") + @GetMapping("/safetyEducationTraining/detail/{id}") + @Operation(summary = "安全生产教育培训记录-详情(政府端)") + public R safetyEducationTrainingDetail(@NotNull(message = "主键不能为空") @PathVariable Long id) { + return R.ok(ledgerReportService.queryGovSafetyEducationTrainingDetail(id)); + } + + //@SaCheckPermission("personManagement:violationInfo:list") + @GetMapping("/violationInfo/list") + @Operation(summary = "违法违章信息排查记录-分页(政府端)") + public TableDataInfo violationInfoList(@Validated ReportLedgerQueryBo bo, PageQuery pageQuery) { + if (bo.getCompanyId() == null) { + throw new IllegalArgumentException("CompanyId不能为空"); + } + //离职人员不显示 + bo.setStatus(1L); + bo.setAuditStatus(1); + return ledgerReportService.queryGovViolationInfoLedger(bo, pageQuery); + } + + //@SaCheckPermission("personManagement:violationInfo:list") + @GetMapping("/violationInfo/detail/list") + @Operation(summary = "违法违章信息排查记录-详情分页(政府端)") + public TableDataInfo violationInfoDetailList(@Validated ReportLedgerQueryBo bo, PageQuery pageQuery) { + if (bo.getCompanyId() == null) { + throw new IllegalArgumentException("CompanyId不能为空"); + } + if (bo.getStartDate() == null || bo.getEndDate() == null) { + throw new IllegalArgumentException("开始时间和结束时间不能为空"); + } + //离职人员不显示 + bo.setStatus(1L); + bo.setAuditStatus(1); + return ledgerReportService.queryGovViolationInfoDetail(bo, pageQuery); + } + + //@SaCheckPermission("personManagement:violationHandling:list") + @GetMapping("/violationHandling/list") + @Operation(summary = "违章处理记录表-分页(政府端)") + public TableDataInfo violationHandlingList(ReportLedgerQueryBo bo, PageQuery pageQuery) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return TableDataInfo.build(); + } + HotGovEnterpriseUnitVo unitVo = hotGovEnterpriseUnitService.queryById(loginUser.getCompanyId()); + if (unitVo == null || unitVo.getDistrictCode() == null) { + return TableDataInfo.build(); + } + //离职人员不显示 + bo.setStatus(1L); + bo.setAuditStatus(1); + return ledgerReportService.queryGovViolationHandlingLedger(unitVo.getDistrictCode(), bo, pageQuery); + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/controller/SecurityManageFileController.java b/src/main/java/com/hotwj/platform/reportStatistics/controller/SecurityManageFileController.java new file mode 100644 index 0000000..8921b08 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/controller/SecurityManageFileController.java @@ -0,0 +1,311 @@ +package com.hotwj.platform.reportStatistics.controller; + +import cn.dev33.satoken.spring.SpringMVCUtil; +import com.hotwj.platform.reportStatistics.domain.bo.ReportLedgerQueryBo; +import com.hotwj.platform.reportStatistics.domain.vo.*; +import com.hotwj.platform.reportStatistics.service.ILedgerReportService; +import com.hotwj.platform.reportStatistics.service.ISecurityManageFilePrintService; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.bo.HotHiddenDangerPlanBo; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.vo.HotHiddenDangerPlanVo; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.service.IHotHiddenDangerPlanService; +import com.hotwj.platform.securityManagement.safetyInvestment.domain.bo.HotSafetyInvestmentBo; +import com.hotwj.platform.securityManagement.safetyInvestment.domain.vo.HotSafetyInvestmentVo; +import com.hotwj.platform.securityManagement.safetyInvestment.service.IHotSafetyInvestmentService; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.bo.HotServiceQualityComplaintBo; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.vo.HotServiceQualityComplaintVo; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.service.IHotServiceQualityComplaintService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.file.FileUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; + +/** + * 安全生产管理档案 + * + * @author shihongwei + * @date 2026-01-08 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/reportStatistics/securityManageFile") +@Tag(name = "安全生产管理档案", description = "安全生产管理档案") +public class SecurityManageFileController extends BaseController { + + private final ILedgerReportService ledgerReportService; + private final ISecurityManageFilePrintService securityManageFilePrintService; + private final IHotHiddenDangerPlanService hotHiddenDangerPlanService; + private final IHotSafetyInvestmentService hotSafetyInvestmentService; + private final IHotServiceQualityComplaintService hotServiceQualityComplaintService; + + //@SaCheckPermission("reportStatistics:preJobTraining:list") + @GetMapping("/preJobTraining/list") + @Operation(summary = "从业人员岗前培训考核记录-分页") + public TableDataInfo preJobTrainingList(@Valid ReportLedgerQueryBo bo, PageQuery pageQuery) { + return ledgerReportService.queryPreJobTraining(bo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:preJobTraining:export") + @PostMapping("/preJobTraining/exportPdf") + @Operation(summary = "从业人员岗前培训考核记录-PDF导出") + public void preJobTrainingExportPdf(@Valid ReportLedgerQueryBo bo) { + byte[] bytes = securityManageFilePrintService.generatePreJobTrainingPdf(bo); + try { + HttpServletResponse response = SpringMVCUtil.getResponse(); + FileUtils.setAttachmentResponseHeader(response, "从业人员岗前培训考核记录.pdf"); + response.setContentType("application/pdf"); + response.getOutputStream().write(bytes); + } catch (IOException e) { + throw new RuntimeException("导出PDF异常", e); + } + } + + //@SaCheckPermission("reportStatistics:safetyEducation:list") + @GetMapping("/safetyEducation/list") + @Operation(summary = "安全生产教育培训记录-分页") + public TableDataInfo safetyEducationList(@Valid ReportLedgerQueryBo bo, PageQuery pageQuery) { + return ledgerReportService.queryGovSafetyEducationTraining(bo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:safetyEducation:export") + @PostMapping("/safetyEducation/exportPdf") + @Operation(summary = "安全生产教育培训记录-PDF导出") + public void safetyEducationExportPdf(@Valid ReportLedgerQueryBo bo) { + byte[] bytes = securityManageFilePrintService.generateSafetyEducationTrainingPdf(bo); + try { + HttpServletResponse resp = SpringMVCUtil.getResponse(); + FileUtils.setAttachmentResponseHeader(resp, "安全生产教育培训记录.pdf"); + resp.setContentType("application/pdf"); + resp.getOutputStream().write(bytes); + } catch (IOException e) { + throw new RuntimeException("导出PDF异常", e); + } + } + + //@SaCheckPermission("reportStatistics:violationInvestigation:list") + @GetMapping("/violationInvestigation/list") + @Operation(summary = "违法违章隐患排查记录-分页") + public TableDataInfo violationInvestigationList(@Valid ReportLedgerQueryBo bo, PageQuery pageQuery) { + return ledgerReportService.queryGovViolationInfoLedger(bo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:violationInvestigation:export") + @PostMapping("/violationInvestigation/exportPdf") + @Operation(summary = "违法违章隐患排查记录-PDF导出") + public void violationInvestigationExportPdf(@Valid ReportLedgerQueryBo bo) { + byte[] bytes = securityManageFilePrintService.generateViolationInvestigationPdf(bo); + try { + HttpServletResponse resp = SpringMVCUtil.getResponse(); + FileUtils.setAttachmentResponseHeader(resp, "违法违章信息排查记录.pdf"); + resp.setContentType("application/pdf"); + resp.getOutputStream().write(bytes); + } catch (IOException e) { + throw new RuntimeException("导出PDF异常", e); + } + } + + //@SaCheckPermission("reportStatistics:violationHandling:list") + @GetMapping("/violationHandling/list") + @Operation(summary = "违法违章处理记录-分页") + public TableDataInfo violationHandlingList(@Valid ReportLedgerQueryBo bo, PageQuery pageQuery) { + if (bo.getCompanyId() == null) { + throw new IllegalArgumentException("CompanyId不能为空"); + } + return ledgerReportService.queryGovViolationHandlingLedger(null, bo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:violationHandling:export") + @PostMapping("/violationHandling/exportPdf") + @Operation(summary = "违法违章处理记录-PDF导出") + public void violationHandlingExportPdf(@Valid ReportLedgerQueryBo bo) { + byte[] bytes = securityManageFilePrintService.generateViolationHandlingPdf(bo); + try { + HttpServletResponse resp = SpringMVCUtil.getResponse(); + FileUtils.setAttachmentResponseHeader(resp, "违法违章处理记录.pdf"); + resp.setContentType("application/pdf"); + resp.getOutputStream().write(bytes); + } catch (IOException e) { + throw new RuntimeException("导出PDF异常", e); + } + } + + //@SaCheckPermission("reportStatistics:dynamicMonitoringViolation:list") + @GetMapping("/dynamicMonitoringViolation/list") + @Operation(summary = "车辆动态监控违法行为处理记录-分页") + public TableDataInfo dynamicMonitoringViolationList(@Valid ReportLedgerQueryBo bo, PageQuery pageQuery) { + if (bo.getCompanyId() == null) { + throw new IllegalArgumentException("CompanyId不能为空"); + } + return ledgerReportService.queryGovGpsViolation(null, bo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:dynamicMonitoringViolation:export") + @PostMapping("/dynamicMonitoringViolation/exportPdf") + @Operation(summary = "车辆动态监控违法行为处理记录-PDF导出") + public void dynamicMonitoringViolationExportPdf(@Valid ReportLedgerQueryBo bo, HttpServletResponse response) { + byte[] bytes = securityManageFilePrintService.generateGpsViolationPdf(bo); + try { + FileUtils.setAttachmentResponseHeader(response, "车辆动态监控违法行为处理记录.pdf"); + response.setContentType("application/pdf"); + response.getOutputStream().write(bytes); + } catch (IOException e) { + throw new RuntimeException("导出PDF异常", e); + } + } + + //@SaCheckPermission("reportStatistics:safetyMeeting:list") + @GetMapping("/safetyMeeting/list") + @Operation(summary = "安全生产会议记录-分页") + public TableDataInfo safetyMeetingList(@Valid ReportLedgerQueryBo bo, PageQuery pageQuery) { + if (bo.getCompanyId() == null) { + throw new IllegalArgumentException("CompanyId不能为空"); + } + return ledgerReportService.querySafetyProductionMeeting(bo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:safetyMeeting:export") + @PostMapping("/safetyMeeting/exportPdf") + @Operation(summary = "安全生产会议记录-PDF导出") + public void safetyMeetingExportPdf(@Valid ReportLedgerQueryBo bo, HttpServletResponse response) { + byte[] bytes = securityManageFilePrintService.generateSafetyMeetingPdf(bo); + try { + FileUtils.setAttachmentResponseHeader(response, "安全生产会议记录.pdf"); + response.setContentType("application/pdf"); + response.getOutputStream().write(bytes); + } catch (IOException e) { + throw new RuntimeException("导出PDF异常", e); + } + } + + //@SaCheckPermission("reportStatistics:safetyFireHazardRectification:list") + @GetMapping("/safetyFireHazardRectification/list") + @Operation(summary = "安全生产(消防)隐患排查整改记录-分页") + public TableDataInfo safetyFireHazardRectificationList(@Valid ReportLedgerQueryBo bo, PageQuery pageQuery) { + if (bo.getCompanyId() == null) { + throw new IllegalArgumentException("CompanyId不能为空"); + } + HotHiddenDangerPlanBo planBo = new HotHiddenDangerPlanBo(); + planBo.setCompanyId(bo.getCompanyId()); + planBo.setPlanName(bo.getPlanName()); + planBo.setStartDate(bo.getStartDate()); + planBo.setEndDate(bo.getEndDate()); + planBo.setIsDeleted(0L); + return hotHiddenDangerPlanService.queryPageList(planBo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:safetyFireHazardRectification:export") + @PostMapping("/safetyFireHazardRectification/exportPdf") + @Operation(summary = "安全生产(消防)隐患排查项-PDF导出") + public void safetyFireHazardRectificationExportPdf(@Valid ReportLedgerQueryBo bo, HttpServletResponse response) { + byte[] bytes = securityManageFilePrintService.generateSafetyFireHazardInspectionPdf(bo); + try { + FileUtils.setAttachmentResponseHeader(response, "安全生产(消防)隐患排查整改记录.pdf"); + response.setContentType("application/pdf"); + response.getOutputStream().write(bytes); + } catch (IOException e) { + throw new RuntimeException("导出PDF异常", e); + } + } + + //@SaCheckPermission("reportStatistics:safetyProductionInput:list") + @GetMapping("/safetyProductionInput/list") + @Operation(summary = "安全生产投入登记本-分页") + public TableDataInfo safetyProductionInputList(@Valid ReportLedgerQueryBo bo, PageQuery pageQuery) { + HotSafetyInvestmentBo investmentBo = new HotSafetyInvestmentBo(); + investmentBo.setCompanyId(bo.getCompanyId()); + investmentBo.setIsDeleted(0L); + String yearStr = bo.getKeyword(); + if (org.dromara.common.core.utils.StringUtils.isNotBlank(yearStr)) { + try { + int year = Integer.parseInt(yearStr); + java.util.Calendar calendar = java.util.Calendar.getInstance(); + calendar.clear(); + calendar.set(java.util.Calendar.YEAR, year); + java.util.Date begin = calendar.getTime(); + calendar.set(java.util.Calendar.MONTH, java.util.Calendar.DECEMBER); + calendar.set(java.util.Calendar.DAY_OF_MONTH, 31); + calendar.set(java.util.Calendar.HOUR_OF_DAY, 23); + calendar.set(java.util.Calendar.MINUTE, 59); + calendar.set(java.util.Calendar.SECOND, 59); + java.util.Date end = calendar.getTime(); + investmentBo.getParams().put("beginInvestmentTime", begin); + investmentBo.getParams().put("endInvestmentTime", end); + } catch (NumberFormatException ignored) { + // 非法年度字符串则不按年度过滤 + } + } + return hotSafetyInvestmentService.queryPageList(investmentBo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:safetyProductionInput:export") + @PostMapping("/safetyProductionInput/exportPdf") + @Operation(summary = "安全生产投入登记本-PDF导出") + public void safetyProductionInputExportPdf(@Valid ReportLedgerQueryBo bo, HttpServletResponse response) { + byte[] bytes = securityManageFilePrintService.generateSafetyProductionInputPdf(bo); + try { + FileUtils.setAttachmentResponseHeader(response, "安全生产投入登记本.pdf"); + response.setContentType("application/pdf"); + response.getOutputStream().write(bytes); + } catch (IOException e) { + throw new RuntimeException("导出PDF异常", e); + } + } + + //@SaCheckPermission("reportStatistics:serviceQuality:list") + @GetMapping("/serviceQuality/list") + @Operation(summary = "服务质量记录-分页") + public TableDataInfo serviceQualityList(@Valid ReportLedgerQueryBo bo, PageQuery pageQuery) { + if (bo.getCompanyId() == null) { + throw new IllegalArgumentException("CompanyId不能为空"); + } + HotServiceQualityComplaintBo complaintBo = new HotServiceQualityComplaintBo(); + complaintBo.setCompanyId(bo.getCompanyId()); + complaintBo.setIsDeleted(0L); + if (bo.getKeyword() != null && !bo.getKeyword().isEmpty()) { + complaintBo.setComplaintTarget(bo.getKeyword()); + } + if (bo.getStartDate() != null) { + java.util.Calendar calendar = java.util.Calendar.getInstance(); + calendar.setTime(bo.getStartDate()); + calendar.set(java.util.Calendar.HOUR_OF_DAY, 0); + calendar.set(java.util.Calendar.MINUTE, 0); + calendar.set(java.util.Calendar.SECOND, 0); + calendar.set(java.util.Calendar.MILLISECOND, 0); + java.util.Date begin = calendar.getTime(); + calendar.set(java.util.Calendar.HOUR_OF_DAY, 23); + calendar.set(java.util.Calendar.MINUTE, 59); + calendar.set(java.util.Calendar.SECOND, 59); + calendar.set(java.util.Calendar.MILLISECOND, 999); + java.util.Date end = calendar.getTime(); + complaintBo.getParams().put("beginCaseTime", begin); + complaintBo.getParams().put("endCaseTime", end); + } + return hotServiceQualityComplaintService.queryPageList(complaintBo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:serviceQuality:export") + @PostMapping("/serviceQuality/exportPdf") + @Operation(summary = "服务质量记录-PDF导出") + public void serviceQualityExportPdf(@Valid ReportLedgerQueryBo bo, HttpServletResponse response) { + byte[] bytes = securityManageFilePrintService.generateServiceQualityPdf(bo); + try { + FileUtils.setAttachmentResponseHeader(response, "安全生产服务质量记录.pdf"); + response.setContentType("application/pdf"); + response.getOutputStream().write(bytes); + } catch (IOException e) { + throw new RuntimeException("导出PDF异常", e); + } + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/controller/SecurityManagementController.java b/src/main/java/com/hotwj/platform/reportStatistics/controller/SecurityManagementController.java new file mode 100644 index 0000000..92c04cd --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/controller/SecurityManagementController.java @@ -0,0 +1,65 @@ +package com.hotwj.platform.reportStatistics.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.hotwj.platform.reportStatistics.domain.bo.ReportLedgerQueryBo; +import com.hotwj.platform.reportStatistics.domain.vo.GovAccidentLedgerVo; +import com.hotwj.platform.reportStatistics.domain.vo.SafetyProductionMeetingLedgerVo; +import com.hotwj.platform.reportStatistics.service.ILedgerReportService; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.vo.HotGovEnterpriseUnitVo; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.service.IHotGovEnterpriseUnitService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 安全生产管理 + * + * @author shihongwei + * @date 2026-02-03 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/reportStatistics/securityManagement") +@Tag(name = "安全生产管理", description = "安全生产管理") +public class SecurityManagementController extends BaseController { + + private final ILedgerReportService ledgerReportService; + private final IHotGovEnterpriseUnitService hotGovEnterpriseUnitService; + + //@SaCheckPermission("reportStatistics:securityMeeting:list") + @GetMapping("/meeting/list") + @Operation(summary = "安全生产会议记录") + public TableDataInfo meetingList(ReportLedgerQueryBo bo, PageQuery pageQuery) { + if (bo.getCompanyId() == null) { + throw new ServiceException("企业ID不能为空"); + } + return ledgerReportService.querySafetyProductionMeeting(bo, pageQuery); + } + + //@SaCheckPermission("reportStatistics:securityAccident:list") + @GetMapping("/accident/list") + @Operation(summary = "安全事故台账") + public TableDataInfo accidentList(ReportLedgerQueryBo bo, PageQuery pageQuery) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return TableDataInfo.build(); + } + HotGovEnterpriseUnitVo unitVo = hotGovEnterpriseUnitService.queryById(loginUser.getCompanyId()); + if (unitVo == null || unitVo.getDistrictCode() == null) { + return TableDataInfo.build(); + } + return ledgerReportService.queryGovAccident(unitVo.getDistrictCode(), bo, pageQuery); + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/controller/SysReportTemplateController.java b/src/main/java/com/hotwj/platform/reportStatistics/controller/SysReportTemplateController.java new file mode 100644 index 0000000..b4c1a19 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/controller/SysReportTemplateController.java @@ -0,0 +1,109 @@ +package com.hotwj.platform.reportStatistics.controller; + +import com.hotwj.platform.reportStatistics.domain.bo.SysReportTemplateBo; +import com.hotwj.platform.reportStatistics.domain.vo.SysReportTemplateVo; +import com.hotwj.platform.reportStatistics.service.ISysReportTemplateService; +import com.hotwj.platform.reportStatistics.service.ReportRenderService; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 报表模板 + * + * @author shihongwei + * @date 2024-03-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/report/template") +public class SysReportTemplateController extends BaseController { + + private final ISysReportTemplateService sysReportTemplateService; + private final ReportRenderService reportRenderService; + + /** + * 查询报表模板列表 + */ + //@SaCheckPermission("report:template:list") + @GetMapping("/list") + public TableDataInfo list(SysReportTemplateBo bo, PageQuery pageQuery) { + return sysReportTemplateService.queryPageList(bo, pageQuery); + } + + /** + * 导出报表模板列表 + */ + //@SaCheckPermission("report:template:export") + @Log(title = "报表模板", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(SysReportTemplateBo bo, HttpServletResponse response) { + List list = sysReportTemplateService.queryList(bo); + ExcelUtil.exportExcel(list, "报表模板", SysReportTemplateVo.class, response); + } + + /** + * 获取报表模板详细信息 + */ + //@SaCheckPermission("report:template:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { + return R.ok(sysReportTemplateService.queryById(id)); + } + + /** + * 新增报表模板 + */ + //@SaCheckPermission("report:template:add") + @Log(title = "报表模板", businessType = BusinessType.INSERT) + @PostMapping + public R add(@Validated(AddGroup.class) @RequestBody SysReportTemplateBo bo) { + return toAjax(sysReportTemplateService.insertByBo(bo)); + } + + /** + * 修改报表模板 + */ + //@SaCheckPermission("report:template:edit") + @Log(title = "报表模板", businessType = BusinessType.UPDATE) + @PutMapping + public R edit(@Validated(EditGroup.class) @RequestBody SysReportTemplateBo bo) { + return toAjax(sysReportTemplateService.updateByBo(bo)); + } + + /** + * 同步模版 + */ + //@SaCheckPermission("report:template:sync") + @Log(title = "同步模版", businessType = BusinessType.UPDATE) + @PostMapping("/sync") + public R sync() { + sysReportTemplateService.syncTemplatesFromClasspath(); + return R.ok(); + } + + /** + * 删除报表模板 + */ + //@SaCheckPermission("report:template:remove") + @Log(title = "报表模板", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { + return toAjax(sysReportTemplateService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/controller/VehicleFileController.java b/src/main/java/com/hotwj/platform/reportStatistics/controller/VehicleFileController.java new file mode 100644 index 0000000..e85a299 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/controller/VehicleFileController.java @@ -0,0 +1,216 @@ +package com.hotwj.platform.reportStatistics.controller; + +import cn.dev33.satoken.spring.SpringMVCUtil; +import cn.hutool.core.io.IoUtil; +import com.hotwj.platform.reportStatistics.service.IVehicleFilePrintService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +/** + * 车辆档案打印模块 + * + * @author shihongwei + * @date 2025-12-31 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/reportStatistics/vehicleFile") +@Tag(name = "车辆档案打印", description = "车辆档案打印") +public class VehicleFileController extends BaseController { + + private final IVehicleFilePrintService vehicleFilePrintService; + + /** + * 公司车辆基本信息表 + */ + //@SaCheckPermission("reportStatistics:vehicleFile:baseInfo") + @PostMapping("/baseInfo") + @Operation(summary = "公司车辆基本信息表") + public void baseInfo(@RequestParam("ids") List vehicleIds) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (vehicleIds == null || vehicleIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少车辆ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdf = vehicleFilePrintService.generateBaseInfoPdf(vehicleIds); + writeAttachment(response, "公司车辆基本信息.pdf", pdf, "application/pdf"); + } + + /** + * 车辆行驶证驾驶证 + */ + //@SaCheckPermission("reportStatistics:vehicleFile:license") + @PostMapping("/license") + @Operation(summary = "车辆行驶证驾驶证") + public void license(@RequestParam("ids") List vehicleIds) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (vehicleIds == null || vehicleIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少车辆ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdf = vehicleFilePrintService.generateLicensePdf(vehicleIds); + writeAttachment(response, "车辆行驶证驾驶证.pdf", pdf, "application/pdf"); + } + + /** + * 安全技术检测报告 + */ + //@SaCheckPermission("reportStatistics:vehicleFile:inspectionReport") + @Operation(summary = "安全技术检测报告") + @PostMapping("/inspectionReport") + public void inspectionReport(@RequestParam("ids") List vehicleIds, + @RequestParam(value = "inspectionStartDate", required = false) String inspectionStartDate, + @RequestParam(value = "inspectionEndDate", required = false) String inspectionEndDate, + @RequestParam(value = "printEmptyData", required = false) Integer printEmptyData) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (vehicleIds == null || vehicleIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少车辆ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + + byte[] pdfBytes = vehicleFilePrintService.generateInspectionReportPdf(vehicleIds, inspectionStartDate, inspectionEndDate, printEmptyData); + writeAttachment(response, "安全技术检测报告.pdf", pdfBytes, "application/pdf"); + } + + + /** + * 二级维护记录 + */ + //@SaCheckPermission("reportStatistics:vehicleFile:maintenanceRecord") + @PostMapping("/maintenanceRecord") + @Operation(summary = "二级维护记录") + public void maintenanceRecord(@RequestParam("ids") List vehicleIds, + @RequestParam(value = "maintenanceStartDate", required = false) String maintenanceStartDate, + @RequestParam(value = "maintenanceEndDate", required = false) String maintenanceEndDate, + @RequestParam(value = "printEmptyData", required = false) Integer printEmptyData) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (vehicleIds == null || vehicleIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少车辆ID参数".getBytes(StandardCharsets.UTF_8)); + } + byte[] pdfBytes = vehicleFilePrintService.generateMaintenanceRecordPdf(vehicleIds, maintenanceStartDate, maintenanceEndDate, printEmptyData); + writeAttachment(response, "二级维护记录.pdf", pdfBytes, "application/pdf"); + } + + /** + * 车辆出险记录 + */ + //@SaCheckPermission("reportStatistics:vehicleFile:accidentRecords") + @PostMapping("/accidentRecords") + @Operation(summary = "车辆出险记录") + public void accidentRecords(@RequestParam("ids") List vehicleIds, + @RequestParam(value = "beginTime", required = false) String beginTime, + @RequestParam(value = "endTime", required = false) String endTime, + @RequestParam(value = "printEmptyData", required = false) Integer printEmptyData) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (vehicleIds == null || vehicleIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少车辆ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdfBytes = vehicleFilePrintService.generateClaimRecordPdf(vehicleIds, beginTime, endTime, printEmptyData); + writeAttachment(response, "车辆出险记录.pdf", pdfBytes, "application/pdf"); + } + + + /** + * 车辆行驶里程记录 + */ + //@SaCheckPermission("reportStatistics:vehicleFile:mileageRecord") + @PostMapping("/mileageRecord") + @Operation(summary = "车辆行驶里程记录") + public void mileageRecord(@RequestParam("ids") List vehicleIds, + @RequestParam(value = "beginTime", required = false) String beginTime, + @RequestParam(value = "endTime", required = false) String endTime, + @RequestParam(value = "printEmptyData", required = false) Integer printEmptyData) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (vehicleIds == null || vehicleIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少车辆ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdfBytes = vehicleFilePrintService.generateMileageRecordPdf(vehicleIds, beginTime, endTime, printEmptyData); + writeAttachment(response, "车辆行驶里程记录.pdf", pdfBytes, "application/pdf"); + } + + /** + * 车辆维护和修理记录打印 + */ + //@SaCheckPermission("reportStatistics:vehicleFile:maintenanceRepairRecords") + @PostMapping("/maintenanceRepairRecords") + @Operation(summary = "车辆维护和修理记录打印") + public void maintenanceRepairRecords(@RequestParam("ids") List vehicleIds, + @RequestParam(value = "beginTime", required = false) String beginTime, + @RequestParam(value = "endTime", required = false) String endTime, + @RequestParam(value = "printEmptyData", required = false) Integer printEmptyData) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (vehicleIds == null || vehicleIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少车辆ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdfBytes = vehicleFilePrintService.generateRepairRecordPdf(vehicleIds, beginTime, endTime, printEmptyData); + writeAttachment(response, "车辆维护和修理记录.pdf", pdfBytes, "application/pdf"); + } + + /** + * 交通事故记录 + */ + //@SaCheckPermission("reportStatistics:vehicleFile:trafficAccidentRecord") + @PostMapping("/trafficAccidentRecord") + @Operation(summary = "交通事故记录打印") + public void trafficAccidentRecord(@RequestParam("ids") List vehicleIds, + @RequestParam(value = "beginTime", required = false) String beginTime, + @RequestParam(value = "endTime", required = false) String endTime, + @RequestParam(value = "printEmptyData", required = false) Integer printEmptyData) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (vehicleIds == null || vehicleIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少车辆ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdfBytes = vehicleFilePrintService.generateTrafficAccidentRecordPdf(vehicleIds, beginTime, endTime, printEmptyData); + writeAttachment(response, "交通事故记录.pdf", pdfBytes, "application/pdf"); + } + + /** + * 一键打印 + */ + //@SaCheckPermission("reportStatistics:vehicleFile:oneClickPrint") + @PostMapping("/oneClickPrint") + @Operation(summary = "车辆档案一键打印") + public void oneClickPrint(@RequestParam("ids") List vehicleIds, + @RequestParam("types") List types, + @RequestParam(value = "printEmptyData", required = false) Integer printEmptyData) throws Exception { + HttpServletResponse response = SpringMVCUtil.getResponse(); + if (vehicleIds == null || vehicleIds.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少车辆ID参数".getBytes(StandardCharsets.UTF_8)); + return; + } + if (types == null || types.isEmpty()) { + response.setStatus(400); + IoUtil.write(response.getOutputStream(), false, "缺少打印类型参数".getBytes(StandardCharsets.UTF_8)); + return; + } + byte[] pdfBytes = vehicleFilePrintService.generateOneClickPrintPdf(vehicleIds, types, printEmptyData); + writeAttachment(response, "车辆档案一键打印.pdf", pdfBytes, "application/pdf"); + } + + +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/controller/VehicleManagementController.java b/src/main/java/com/hotwj/platform/reportStatistics/controller/VehicleManagementController.java new file mode 100644 index 0000000..ab7c102 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/controller/VehicleManagementController.java @@ -0,0 +1,72 @@ +package com.hotwj.platform.reportStatistics.controller; + +import com.hotwj.platform.reportStatistics.domain.bo.ReportLedgerQueryBo; +import com.hotwj.platform.reportStatistics.domain.vo.GovGpsViolationLedgerVo; +import com.hotwj.platform.reportStatistics.domain.vo.GovVehicleInfoLedgerVo; +import com.hotwj.platform.reportStatistics.service.ILedgerReportService; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.vo.HotGovEnterpriseUnitVo; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.service.IHotGovEnterpriseUnitService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 车辆信息管理台账(政府端) + * + * @author shihongwei + * @date 2026-02-03 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/reportStatistics/vehicleManagement") +@Tag(name = "车辆信息管理台账(政府端)", description = "车辆信息管理台账(政府端)") +public class VehicleManagementController extends BaseController { + + private final ILedgerReportService ledgerReportService; + private final IHotGovEnterpriseUnitService hotGovEnterpriseUnitService; + + //@SaCheckPermission("vehicleManagement:vehicleInfo:list") + @GetMapping("/vehicleInfo/list") + @Operation(summary = "车辆信息管理台账-分页(政府端)") + public TableDataInfo vehicleInfoList(ReportLedgerQueryBo bo, PageQuery pageQuery) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return TableDataInfo.build(); + } + HotGovEnterpriseUnitVo unitVo = hotGovEnterpriseUnitService.queryById(loginUser.getCompanyId()); + if (unitVo == null || unitVo.getDistrictCode() == null) { + return TableDataInfo.build(); + } + bo.setVehicleStatus(1); + bo.setOperationStatus(1); + return ledgerReportService.queryGovVehicleInfo(unitVo.getDistrictCode(), bo, pageQuery); + } + + //@SaCheckPermission("vehicleManagement:gpsViolation:list") + @GetMapping("/gpsViolation/list") + @Operation(summary = "车辆GPS违章-分页(政府端)") + public TableDataInfo gpsViolationList(ReportLedgerQueryBo bo, PageQuery pageQuery) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return TableDataInfo.build(); + } + HotGovEnterpriseUnitVo unitVo = hotGovEnterpriseUnitService.queryById(loginUser.getCompanyId()); + if (unitVo == null || unitVo.getDistrictCode() == null) { + return TableDataInfo.build(); + } + bo.setNeedInterview(1); + bo.setProcessStatus(2); + return ledgerReportService.queryGovGpsViolation(unitVo.getDistrictCode(), bo, pageQuery); + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/SysReportTemplate.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/SysReportTemplate.java new file mode 100644 index 0000000..2a7b20e --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/SysReportTemplate.java @@ -0,0 +1,70 @@ +package com.hotwj.platform.reportStatistics.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 报表模板对象 sys_report_template + * + * @author shihongwei + * @date 2024-03-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName(value = "sys_report_template", autoResultMap = true) +public class SysReportTemplate extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId + private Long id; + + /** + * 模板编码 + */ + private String templateCode; + + /** + * 模板名称 + */ + private String templateName; + + /** + * 模板内容 + */ + private String content; + + /** + * 变量元数据 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Object variablesSchema; + + /** + * 预览测试数据 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Object mockData; + + /** + * 版本号 + */ + private Integer version; + + /** + * 备注 + */ + private String remark; + +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/bo/ReportLedgerQueryBo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/bo/ReportLedgerQueryBo.java new file mode 100644 index 0000000..a870342 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/bo/ReportLedgerQueryBo.java @@ -0,0 +1,96 @@ +package com.hotwj.platform.reportStatistics.domain.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * 驾驶员台账查询参数 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Data +public class ReportLedgerQueryBo { + + @Schema(description = "公司ID") + private Long companyId; + + @Schema(description = "驾驶员姓名") + private String driverName; + + @Schema(description = "身份证号") + private String idCardNo; + + @Schema(description = "驾驶证号") + private String driverLicenseNo; + + @Schema(description = "车牌号") + private String plateNumber; + + @Schema(description = "事故等级") + private String accidentLevel; + + @DateTimeFormat(pattern = "yyyy-MM-dd") + @Schema(description = "事故日期") + private Date accidentDate; + + @DateTimeFormat(pattern = "yyyy-MM-dd") + @Schema(description = "结案日期") + private Date caseClosingDate; + + @Schema(description = "状态:1=正常 0=禁用") + private Long status; + + @DateTimeFormat(pattern = "yyyy-MM-dd") + @Schema(description = "开始日期") + private Date startDate; + + @DateTimeFormat(pattern = "yyyy-MM-dd") + @Schema(description = "结束日期") + private Date endDate; + + @Schema(description = "关键字") + private String keyword; + + @Schema(description = "是否完成培训:1=是 0=否") + private Integer isCompletedTraining; + + @Schema(description = "计划名称") + private String planName; + + @Schema(description = "ID列表,逗号分隔") + private String ids; + + @Schema(description = "负责人姓名") + private String managerName; + + @Schema(description = "负责人电话") + private String managerPhone; + + @Schema(description = "是否整改") + private Long isRectified; + + @Schema(description = "会议主题") + private String meetingSubject; + + @Schema(description = "会议种类") + private String meetingCategory; + + @Schema(description = "审核状态:0=待审核 1=审核通过 2=审核驳回") + private Integer auditStatus; + + @Schema(description = "车辆状态 1=正常, 0=停用") + private Integer vehicleStatus; + + @Schema(description = "运营状态:1=正常,2=暂停,3=租赁,4=报废,5=过户") + private Integer operationStatus; + + @Schema(description = "是否需要约谈 0=否 1=是") + private Integer needInterview; + + @Schema(description = "处理状态 0=待处理 1=处理中 2=已处理") + private Integer processStatus; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/bo/SysReportTemplateBo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/bo/SysReportTemplateBo.java new file mode 100644 index 0000000..3a55e04 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/bo/SysReportTemplateBo.java @@ -0,0 +1,72 @@ +package com.hotwj.platform.reportStatistics.domain.bo; + +import com.hotwj.platform.reportStatistics.domain.SysReportTemplate; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 报表模板业务对象 sys_report_template + * + * @author shihongwei + * @date 2024-03-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysReportTemplate.class, reverseConvertGenerate = false) +public class SysReportTemplateBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 模板编码 + */ + @NotBlank(message = "模板编码不能为空", groups = {AddGroup.class, EditGroup.class}) + private String templateCode; + + /** + * 模板名称 + */ + @NotBlank(message = "模板名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String templateName; + + /** + * 模板内容 + */ + private String content; + + /** + * 变量元数据 + */ + private Object variablesSchema; + + /** + * 预览测试数据 + */ + private Object mockData; + + /** + * 版本号 + */ + private Integer version; + + /** + * 备注 + */ + private String remark; + + /** + * 预览时使用的真实业务参数 (Map) + */ + private Object businessParams; + +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/AccidentLedgerExportVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/AccidentLedgerExportVo.java new file mode 100644 index 0000000..3f1843b --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/AccidentLedgerExportVo.java @@ -0,0 +1,54 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * 事故台账导出VO类 + * + * @author shihongwei + * @date 2026-02-27 + */ +@Data +public class AccidentLedgerExportVo { + @ExcelProperty(value = "事故日期", index = 0) + private Date accidentTime; + + @ExcelProperty(value = "车牌号", index = 1) + private String plateNumber; + + @ExcelProperty(value = "驾驶员", index = 2) + private String driverName; + + @ExcelProperty(value = "事故等级", index = 3) + private String accidentLevel; + + @ExcelProperty(value = "事故原因", index = 4) + private String mainCause; + + @ExcelProperty(value = "事故地点", index = 5) + private String occurLocation; + + @ExcelProperty(value = {"事故伤害(人)", "死亡"}, index = 6) + private Long deathToll; + + @ExcelProperty(value = {"事故伤害(人)", "重伤"}, index = 7) + private Long numberSeriouslyInjured; + + @ExcelProperty(value = {"事故伤害(人)", "轻伤"}, index = 8) + private Long numberMinorInjuries; + + @ExcelProperty(value = "直接经济损失(万元)", index = 9) + private Long economicLoss; + + @ExcelProperty(value = "\"四不放过\"落实情况", index = 10) + private String divisionResponsibilities; + + @ExcelProperty(value = "结案日期", index = 11) + private Date caseClosingTime; + + @ExcelProperty(value = "备注", index = 12) + private String remark; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/AccidentLedgerVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/AccidentLedgerVo.java new file mode 100644 index 0000000..322eb07 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/AccidentLedgerVo.java @@ -0,0 +1,47 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * 事故台账VO类 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Data +public class AccidentLedgerVo { + @ExcelProperty("发生时间") + private Date accidentTime; + @ExcelProperty("驾驶员") + private String driverName; + @ExcelProperty("车牌号") + private String plateNumber; + @ExcelProperty("地点") + private String occurLocation; + @ExcelProperty("死亡人数") + private Long deathToll; + @ExcelProperty("重伤人数") + private Long numberSeriouslyInjured; + @ExcelProperty("轻伤人数") + private Long numberMinorInjuries; + @ExcelProperty("经济损失") + private Long economicLoss; + @ExcelProperty("事故原因") + private String mainCause; + @ExcelProperty("处理结果") + private String divisionResponsibilities; + @ExcelProperty("处理情况") + private String treatmentResult; + @ExcelProperty("事故等级") + private String accidentLevel; + + // 可能有其他字段 + @ExcelProperty("结案日期") + private Date caseClosingTime; + + @ExcelProperty("备注") + private String remark; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/DriverHealthLedgerVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/DriverHealthLedgerVo.java new file mode 100644 index 0000000..b1549b2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/DriverHealthLedgerVo.java @@ -0,0 +1,49 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.util.Date; + +/** + * 驾驶员健康台账VO类 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Data +public class DriverHealthLedgerVo { + @ExcelProperty("所属企业") + private String companyName; + + @ExcelProperty("驾驶员姓名") + private String driverName; + + @ExcelProperty("身份证号") + private String idCardNo; + + @ExcelProperty(value = "性别", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_user_sex") + private String gender; + + @ExcelProperty("出生年月") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date birthDate; + + @ExcelProperty("体检时间") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date reportDate; + + @ExcelProperty("有无毒驾、酒驾记录") + private String hasDrugOrDrinkDrive; + + @ExcelProperty(value = "健康状况是否良好", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_health") + private String healthStatus; + + @ExcelProperty("备注") + private String remark; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/DriverInfoLedgerVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/DriverInfoLedgerVo.java new file mode 100644 index 0000000..17823f4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/DriverInfoLedgerVo.java @@ -0,0 +1,85 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnore; +import cn.idev.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.util.Date; + +/** + * 驾驶员台账VO类 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Data +public class DriverInfoLedgerVo { + + @ExcelIgnore + private String companyName; + + @ExcelProperty("驾驶员姓名") + private String driverName; + + @ExcelProperty({"驾驶证", "驾驶证号"}) + private String driverLicenseNo; + + @ExcelProperty({"驾驶证", "准驾车型"}) + private String driverLicenseVehicleType; + + @ExcelProperty({"驾驶证", "初次领证日期"}) + @JsonFormat(pattern = "yyyy-MM-dd") + private Date driverLicenseFirstIssueDate; + + @ExcelProperty({"驾驶证", "有效期"}) + private String driverLicenseExpireDate; + + @ExcelProperty({"从业资格证", "资格证号"}) + private String qualificationNo; + + @ExcelDictFormat(dictType = "hot_category_qualification_certificate") + @ExcelProperty(value = {"从业资格证", "从业类别"}, converter = ExcelDictConvert.class) + private String qualificationType; + + @ExcelProperty({"从业资格证", "初次领证日期"}) + @JsonFormat(pattern = "yyyy-MM-dd") + private Date qualificationFirstIssueDate; + + @ExcelProperty({"从业资格证", "有效期"}) + @JsonFormat(pattern = "yyyy-MM-dd") + private Date qualificationValidEndDate; + + @ExcelProperty("驾驶车辆车牌号") + private String plateNumber; + + @ExcelProperty({"入职培训", "培训时间"}) + @JsonFormat(pattern = "yyyy-MM-dd") + private Date entryTrainingDate; + + @ExcelProperty({"入职培训", "考核结果(分数)"}) + private String entryTrainingScore; + + @ExcelProperty({"入职培训", "入职体检时间"}) + @JsonFormat(pattern = "yyyy-MM-dd") + private Date entryPhysicalExamDate; + + @ExcelProperty({"进出企业时间", "进入"}) + @JsonFormat(pattern = "yyyy-MM-dd") + private Date entryDate; + + @ExcelProperty({"进出企业时间", "退出"}) + @JsonFormat(pattern = "yyyy-MM-dd") + private Date leaveDate; + + @ExcelProperty("有无发生人员伤亡事故") + private String hasAccident; + + @ExcelProperty("有无毒驾酒驾记录") + private String hasDrugOrDrinkDrive; + + @ExcelProperty("是否在职") + private String isEmployed; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/DriverIntegrityLedgerVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/DriverIntegrityLedgerVo.java new file mode 100644 index 0000000..7907d9a --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/DriverIntegrityLedgerVo.java @@ -0,0 +1,40 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +/** + * 驾驶员诚信台账VO类 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Data +public class DriverIntegrityLedgerVo { + @ExcelProperty("所属企业") + private String companyName; + + @ExcelProperty("驾驶员姓名") + private String driverName; + + @ExcelProperty("身份证号") + private String idCardNo; + + @ExcelProperty("从业资格证号") + private String certNo; + + @ExcelProperty("计分周期") + private String scoringCycle; + + @ExcelProperty("累计记分") + private Integer accumulatedScore; + + @ExcelProperty(value = "诚信考核等级", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_integrity_level") + private String integrityLevel; + + @ExcelProperty("备注") + private String remark; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovAccidentLedgerVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovAccidentLedgerVo.java new file mode 100644 index 0000000..8b17ffb --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovAccidentLedgerVo.java @@ -0,0 +1,52 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 政府端事故台账VO类 + * + * @author shihongwei + * @date 2026-02-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(description = "政府端事故台账VO") +public class GovAccidentLedgerVo extends AccidentLedgerVo { + + @Schema(description = "主键ID") + @ExcelProperty("主键ID") + private Long id; + + @Schema(description = "企业名称") + @ExcelProperty(value = "企业名称", index = 0) + private String companyName; + + @Schema(description = "企业负责人") + @ExcelProperty(value = "企业负责人") + private String responsibleName; + + @Schema(description = "负责人手机号") + @ExcelProperty(value = "负责人手机号") + private String responsibleMobile; + + @Schema(description = "处理结果") + @ExcelProperty(value = "处理结果", index = 10) + private String divisionResponsibilities; + + @Schema(description = "结案日期") + @ExcelProperty(value = "结案日期", index = 11) + private Date caseClosingTime; + + @Schema(description = "备注") + @ExcelProperty(value = "备注", index = 12) + private String remark; + + @Schema(description = "\"四不放过\"落实情况") + @ExcelProperty(value = "\"四不放过\"落实情况", index = 13) + private String treatmentResult; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovGpsViolationLedgerVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovGpsViolationLedgerVo.java new file mode 100644 index 0000000..3444cc9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovGpsViolationLedgerVo.java @@ -0,0 +1,62 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 车辆GPS违章记录VO(政府端) + * + * @author shihongwei + * @date 2026-02-03 + */ +@Data +public class GovGpsViolationLedgerVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @ExcelProperty(value = "ID") + private Long id; + + /** + * 企业名称 + */ + @ExcelProperty(value = "企业名称") + private String companyName; + + /** + * 驾驶员姓名 + */ + @ExcelProperty(value = "驾驶员姓名") + private String driverName; + + /** + * 身份证号 + */ + @ExcelProperty(value = "身份证号") + private String idCardNo; + + /** + * 车牌号 + */ + @ExcelProperty(value = "车牌号") + private String plateNumber; + + /** + * 登记时间 + */ + @ExcelProperty(value = "登记时间") + private Date createTime; + + /** + * 发生时间 + */ + private Date occurTime; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovPreJobTrainingDetailVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovPreJobTrainingDetailVo.java new file mode 100644 index 0000000..ef716f5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovPreJobTrainingDetailVo.java @@ -0,0 +1,79 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 政府端岗前培训考核记录明细VO + * + * @author shihongwei + * @date 2026-02-02 + */ +@Data +public class GovPreJobTrainingDetailVo { + + @ExcelProperty("驾驶员姓名") + private String driverName; + + @ExcelProperty("身份证号") + private String idCardNo; + + @ExcelProperty("是否完成培训") + private String isCompletedTraining; + + @ExcelProperty(value = "性别", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_user_sex") + @Translation(type = TransConstant.DICT_TYPE_TO_LABEL, other = "sys_user_sex") + private String gender; + + @ExcelProperty("出生年月") + private String birthDate; + + @ExcelProperty(value = "文化程度", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_academic") + @Translation(type = TransConstant.DICT_TYPE_TO_LABEL, other = "hot_academic") + private String education; + + @ExcelProperty("参与工作时间") + private String startWorkDate; + + @ExcelProperty("入职时间") + private String entryDate; + + @ExcelProperty("岗位") + private String post; + + @ExcelProperty("家庭住址") + private String currentAddress; + + @ExcelProperty("培训时间") + private Date trainingTime; + + @ExcelProperty("培训地点") + private String trainingLocation; + + @ExcelProperty("培训内容") + private String trainingContent; + + @ExcelProperty("培训学时") + private BigDecimal trainingHours; + + @ExcelProperty("考核成绩") + private BigDecimal examScore; + + @ExcelProperty("被考人签名") + private String examineeSignature; + + @ExcelProperty("考核人签名") + private String examinerSignature; + + @ExcelProperty("备注") + private String remark; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovPreJobTrainingLedgerVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovPreJobTrainingLedgerVo.java new file mode 100644 index 0000000..ea95993 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovPreJobTrainingLedgerVo.java @@ -0,0 +1,34 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; + +/** + * 政府端岗前培训考核记录VO + * + * @author shihongwei + * @date 2026-02-02 + */ +@Data +public class GovPreJobTrainingLedgerVo { + + private Long companyId; + + @ExcelProperty("企业名称") + private String companyName; + + @ExcelProperty("负责人名称") + private String managerName; + + @ExcelProperty("负责人电话") + private String managerPhone; + + @ExcelProperty("总培训人数") + private Integer totalTrainingCount; + + @ExcelProperty("完成培训人数") + private Integer completedTrainingCount; + + @ExcelProperty("未完成培训人数") + private Integer uncompletedTrainingCount; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovSafetyEducationTrainingDetailVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovSafetyEducationTrainingDetailVo.java new file mode 100644 index 0000000..8b0fb6f --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovSafetyEducationTrainingDetailVo.java @@ -0,0 +1,46 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 政府端安全生产教育培训-详情VO + * + * @author shihongwei + * @date 2026-02-02 + */ +@Data +public class GovSafetyEducationTrainingDetailVo { + + @ExcelProperty("培训计划名称") + private String planName; + + @ExcelProperty("培训时间段") + private String trainingPeriod; + + @ExcelProperty("地点") + private String location; + + @ExcelProperty("主持人") + private String host; + + @ExcelProperty("记录人") + private String recorder; + + @ExcelProperty("应到人数") + private Long expectedCount; + + @ExcelProperty("实到人数") + private Long actualCount; + + @ExcelProperty("缺席人数") + private Long absentCount; + + @ExcelProperty("培训内容") + private String trainingContent; + + @ExcelProperty("培训人信息") + private List participantList; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovSafetyEducationTrainingParticipantVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovSafetyEducationTrainingParticipantVo.java new file mode 100644 index 0000000..3f4b878 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovSafetyEducationTrainingParticipantVo.java @@ -0,0 +1,63 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnore; +import cn.idev.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import lombok.Data; +import org.dromara.common.json.handler.BigDecimalTwoDecimalSerializer; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 政府端安全生产教育培训-参训人员信息VO + * + * @author shihongwei + * @date 2026-02-02 + */ +@Data +public class GovSafetyEducationTrainingParticipantVo { + + @ExcelProperty("姓名") + private String userName; + + @ExcelProperty("车牌号") + private String plateNumber; + + @ExcelProperty("性别") + private String gender; + + @ExcelProperty("身份证号") + private String idCardNo; + + @ExcelProperty("手机号") + private String phone; + + @ExcelProperty("是否完成") + private Long isCompleted; + + @ExcelProperty("学习进度") + @JsonSerialize(using = BigDecimalTwoDecimalSerializer.class) + private BigDecimal learnProgress; + + @ExcelProperty("是否通过") + private Long isPass; + + @ExcelProperty("完成时间") + private Date completeTime; + + @ExcelIgnore + private String completeTimeLabel; + + @ExcelIgnore + private Long signatureOssId; + + @ExcelIgnore + private String studyPhotoOssId; + + @ExcelIgnore + private String signatureUrl; + + @ExcelIgnore + private String avatarUrl; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovSafetyEducationTrainingVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovSafetyEducationTrainingVo.java new file mode 100644 index 0000000..6bec27c --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovSafetyEducationTrainingVo.java @@ -0,0 +1,42 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * 政府端安全生产教育培训记录VO + * + * @author shihongwei + * @date 2026-02-02 + */ +@Data +public class GovSafetyEducationTrainingVo { + + @ExcelProperty("计划名称") + private String planName; + + @ExcelProperty("计划ID") + private Long id; + + @ExcelProperty("计划开始时间") + private Date planStartDate; + + @ExcelProperty("计划结束时间") + private Date planEndDate; + + /** + * 是否截止 + */ + private String isClosed; + + @ExcelProperty("应到人数") + private Long expectedCount; + + @ExcelProperty("实到人数") + private Long actualCount; + + @ExcelProperty("缺席人数") + private Long absentCount; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovVehicleInfoLedgerVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovVehicleInfoLedgerVo.java new file mode 100644 index 0000000..a225b1c --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovVehicleInfoLedgerVo.java @@ -0,0 +1,24 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 车辆信息台账VO类(政府端) + * + * @author shihongwei + * @date 2026-02-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class GovVehicleInfoLedgerVo extends VehicleInfoLedgerVo { + + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty(value = "企业名称", order = 0) + private String companyName; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovViolationHandlingLedgerVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovViolationHandlingLedgerVo.java new file mode 100644 index 0000000..dd94971 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovViolationHandlingLedgerVo.java @@ -0,0 +1,62 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 违章处理记录VO(政府端) + * + * @author shihongwei + * @date 2026-02-02 + */ +@Data +public class GovViolationHandlingLedgerVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @ExcelProperty(value = "ID") + private Long id; + + /** + * 企业名称 + */ + @ExcelProperty(value = "企业名称") + private String companyName; + + /** + * 驾驶员姓名 + */ + @ExcelProperty(value = "驾驶员姓名") + private String driverName; + + /** + * 身份证号 + */ + @ExcelProperty(value = "身份证号") + private String idCardNo; + + /** + * 车牌号 + */ + @ExcelProperty(value = "车牌号") + private String plateNumber; + + /** + * 登记时间 + */ + @ExcelProperty(value = "登记时间") + private Date createTime; + + /** + * 发生时间 + */ + private Date occurTime; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovViolationInfoDetailVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovViolationInfoDetailVo.java new file mode 100644 index 0000000..dea9442 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovViolationInfoDetailVo.java @@ -0,0 +1,56 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 违法违章信息排查记录详情VO + * + * @author shihongwei + * @date 2026-02-02 + */ +@Data +public class GovViolationInfoDetailVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @ExcelProperty(value = "ID") + private Long id; + + /** + * 违法违章车辆 + */ + @ExcelProperty(value = "违法违章车辆") + private String plateNumber; + + /** + * 驾驶员 + */ + @ExcelProperty(value = "驾驶员") + private String driverName; + + /** + * 违法违章信息 + */ + @ExcelProperty(value = "违法违章信息") + private String violationContent; + + /** + * 记录人姓名 + */ + private String recorderName; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovViolationInfoLedgerVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovViolationInfoLedgerVo.java new file mode 100644 index 0000000..32efbfe --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/GovViolationInfoLedgerVo.java @@ -0,0 +1,20 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; + +/** + * 违法违章排查记录VO + * + * @author shihongwei + * @date 2026-02-02 + */ +@Data +public class GovViolationInfoLedgerVo { + + @ExcelProperty("计划名称") + private String planName; + + @ExcelProperty("违章数量") + private Long violationCount; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/OfflineTrainingListVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/OfflineTrainingListVo.java new file mode 100644 index 0000000..95b5f23 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/OfflineTrainingListVo.java @@ -0,0 +1,50 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import lombok.Data; + +import java.util.Date; + +/** + * 线下培训统计VO (统一接口返回结构) + */ +@Data +public class OfflineTrainingListVo { + + /** + * 主键 + */ + private Long id; + + /** + * (课程/会议) 名称 - 对应 planName + */ + private String meetingTopic; + + /** + * (课程/会议) 种类 - 对应 meetingCategory + */ + private String meetingCategory; + + /** + * (课程/会议) 类型 - 对应 meetingType (实际存的是 offlineMeetingType) + */ + private String offlineMeetingType; + + /** + * 结束时间 + */ + private Date endTime; + + /** + * (课程/会议) 状态 - 对应 isIssued/isEnabled 等状态逻辑,这里根据业务定义 + * 培训目前没有直接的 meetingStatus,暂用 isIssued (是否下发) 或 自定义逻辑 + * 这里为了统一,定义为 String + */ + private String meetingStatus; + + // 补充其他可能需要的字段 + private Date startTime; + private String location; + private String hostName; + private String recorderName; +} \ No newline at end of file diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/OnlineTrainingMonthlyRowVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/OnlineTrainingMonthlyRowVo.java new file mode 100644 index 0000000..ee54579 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/OnlineTrainingMonthlyRowVo.java @@ -0,0 +1,19 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import lombok.Data; + +import java.util.Date; + +@Data +public class OnlineTrainingMonthlyRowVo { + private Integer index; + private String userId; + private String userName; + private String plateNumber; + private String progressLabel; + private String phone; + private String photoUrl; + private String signatureUrl; + private String examStatus; + private Date completeTime; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/PreJobTrainingLedgerVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/PreJobTrainingLedgerVo.java new file mode 100644 index 0000000..f842ea2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/PreJobTrainingLedgerVo.java @@ -0,0 +1,58 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * 从业人员岗前培训考核记录VO + * + * @author shihongwei + * @date 2026-01-08 + */ +@Data +public class PreJobTrainingLedgerVo { + /** + * 公司ID + */ + private Long companyId; + /** + * 驾驶员ID + */ + private String driverId; + @ExcelProperty("驾驶员姓名") + private String driverName; + /** + * 性别 + */ + private String gender; + @ExcelProperty("身份证号") + private String idCardNo; + /** + * 出生日期 + */ + private Date birthDate; + /** + * 学历 + */ + private String education; + /** + * 参加工作时间 + */ + private Date startWorkDate; + /** + * 现居地 + */ + private String currentAddress; + @ExcelProperty("入职时间") + private Date entryDate; + @ExcelProperty("是否完成培训") + private String isCompletedTraining; + /** + * 培训ID + */ + private Long trainingId; + @ExcelProperty("是否在职") + private String isEmployed; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/ProfCertScoreLedgerVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/ProfCertScoreLedgerVo.java new file mode 100644 index 0000000..ff08bae --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/ProfCertScoreLedgerVo.java @@ -0,0 +1,43 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +/** + * 专业资格证得分台账VO类 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Data +public class ProfCertScoreLedgerVo { + @ExcelProperty("所属企业") + private String companyName; + @ExcelProperty("驾驶员姓名") + private String driverName; + @ExcelProperty("身份证号") + private String idCardNo; + @ExcelProperty("从业资格证号") + private String certNo; + @ExcelProperty("有效起始日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date validFrom; + @ExcelProperty("有效期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date validTo; + @ExcelProperty("记分时间") + private String scoreTime; + @ExcelProperty("记分事项") + private String scoreItem; + @ExcelProperty("记分分值") + private Integer score; + @ExcelProperty("累计记分") + private Integer totalScore; + @ExcelProperty("是否督促学习") + private String isUrgeStudy; + @ExcelProperty("备注") + private String remark; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/SafetyProductionMeetingLedgerVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/SafetyProductionMeetingLedgerVo.java new file mode 100644 index 0000000..309d1a5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/SafetyProductionMeetingLedgerVo.java @@ -0,0 +1,66 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 安全生产会议记录VO + * + * @author shihongwei + * @date 2026-02-03 + */ +@Data +public class SafetyProductionMeetingLedgerVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 会议id + */ + @ExcelProperty(value = "会议id") + private Long meetingId; + + /** + * 会议主题 + */ + @ExcelProperty(value = "会议主题") + private String meetingSubject; + + /** + * 会议种类 + */ + @ExcelProperty(value = "会议种类", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "字=典") + private String meetingCategory; + + /** + * 会议开始时间 + */ + @ExcelProperty(value = "会议开始时间") + private Date meetingStartTime; + + /** + * 应到人数 + */ + @ExcelProperty(value = "应到人数") + private Long expectedAttendees; + + /** + * 实到人数 + */ + @ExcelProperty(value = "实到人数") + private Long actualAttendees; + + /** + * 缺席人数 + */ + @ExcelProperty(value = "缺席人数") + private Long absentAttendees; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/SysReportTemplateVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/SysReportTemplateVo.java new file mode 100644 index 0000000..4ac6482 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/SysReportTemplateVo.java @@ -0,0 +1,87 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.reportStatistics.domain.SysReportTemplate; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 报表模板视图对象 sys_report_template + * + * @author shihongwei + * @date 2024-03-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysReportTemplate.class) +public class SysReportTemplateVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 模板编码 + */ + @ExcelProperty(value = "模板编码") + private String templateCode; + + /** + * 模板名称 + */ + @ExcelProperty(value = "模板名称") + private String templateName; + + /** + * 模板内容 + */ + @ExcelProperty(value = "模板内容") + private String content; + + /** + * 变量元数据 + */ + @ExcelProperty(value = "变量元数据") + private Object variablesSchema; + + /** + * 预览测试数据 + */ + @ExcelProperty(value = "预览测试数据") + private Object mockData; + + /** + * 版本号 + */ + @ExcelProperty(value = "版本号") + private Integer version; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + /** + * 更新时间 + */ + @ExcelProperty(value = "更新时间") + private Date updateTime; + +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/TrainingPlanDetailVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/TrainingPlanDetailVo.java new file mode 100644 index 0000000..c7d6436 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/TrainingPlanDetailVo.java @@ -0,0 +1,25 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import lombok.Data; + +import java.util.Date; + +@Data +public class TrainingPlanDetailVo { + + private String userId; + + private String userName; + + private String phone; + + private Long personType; + + private Date completeTime; + + private Long signatureOssId; + + private Long isMakeUp; + + private Long isCompleted; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/VehicleInfoLedgerVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/VehicleInfoLedgerVo.java new file mode 100644 index 0000000..070e1d7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/VehicleInfoLedgerVo.java @@ -0,0 +1,62 @@ +package com.hotwj.platform.reportStatistics.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +/** + * 车辆信息台账VO类 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Data +public class VehicleInfoLedgerVo { + + @ExcelProperty(value = "序号", index = 0) + private Integer index; + + @ExcelProperty("车牌号") + private String plateNumber; + + @ExcelProperty("车辆注册登记日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date firstRegisterDate; + + @ExcelProperty(value = {"行驶证", "注册日期"}) + @JsonFormat(pattern = "yyyy-MM-dd") + private Date vehicleLicenseRegisterDate; + + @ExcelProperty(value = {"行驶证", "有效期"}) + private String vehicleLicenseValidEndDate; + + @ExcelProperty(value = {"道路运输证", "发证日期"}) + @JsonFormat(pattern = "yyyy-MM-dd") + private Date roadTransportCertIssueDate; + + @ExcelProperty(value = {"道路运输证", "有效期"}) + private String roadTransportCertValidEndDate; + + @ExcelProperty(value = {"车辆检测", "车辆技术等级"}) + private String technicalLevel; + + @ExcelProperty(value = {"车辆检测", "检测时间"}) + @JsonFormat(pattern = "yyyy-MM-dd") + private Date inspectionDate; + + @ExcelProperty(value = {"车辆检测", "检测有效期"}) + @JsonFormat(pattern = "yyyy-MM-dd") + private Date inspectionValidDate; + + @ExcelProperty(value = {"二级维护", "维护周期"}) + private String maintenanceCycle; + + @ExcelProperty(value = {"二级维护", "维护时间"}) + @JsonFormat(pattern = "yyyy-MM-dd") + private Date maintenanceDate; + + @ExcelProperty("备注") + private String remark; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/security/OnlineTrainingMonthlyRow.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/security/OnlineTrainingMonthlyRow.java new file mode 100644 index 0000000..769cea8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/security/OnlineTrainingMonthlyRow.java @@ -0,0 +1,15 @@ +package com.hotwj.platform.reportStatistics.domain.vo.security; + +import lombok.Data; + +@Data +public class OnlineTrainingMonthlyRow { + private int index; + private String userName; + private String plateNumber; + private String progressLabel; + private String phone; + private String photoUrl; + private String signatureUrl; + private String examStatus; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/security/PreJobTrainingDetail.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/security/PreJobTrainingDetail.java new file mode 100644 index 0000000..18386b1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/security/PreJobTrainingDetail.java @@ -0,0 +1,32 @@ +package com.hotwj.platform.reportStatistics.domain.vo.security; + +import lombok.Data; + +import java.util.List; + +@Data +public class PreJobTrainingDetail { + private String driverId; + private String driverName; + private String gender; + private String idCardNo; + private String birthDate; + private String education; + private String startWorkDate; + private String currentAddress; + private String entryDateLabel; + private String driverSignImgUrl; + private String auditorSignImgUrl; + private String totalScore; + private List rows; + + @Data + public static class PreJobTrainingRow { + private String planName; + private String trainingTime; + private String location; + private String courseName; + private String studyDuration; + private String examScore; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/security/StudyDetailData.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/security/StudyDetailData.java new file mode 100644 index 0000000..ecd8e9b --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/security/StudyDetailData.java @@ -0,0 +1,32 @@ +package com.hotwj.platform.reportStatistics.domain.vo.security; + +import com.hotwj.platform.securityManagement.trainingAnswer.domain.vo.HotTrainingAnswerRecordVo; +import lombok.Data; + +import java.util.List; + +@Data +public class StudyDetailData { + private String userName; + private Long examScore; + private Long passScore; + private String passStatus; // 合格/不合格 + + // 课程列表 + private List courseList; + + // 答题明细(分组) + private List singleChoiceList; // 单选列表 + private List multiChoiceList; // 多选列表 + private List judgeList; // 判断列表 + + // 试卷信息 + private String trainingName; // 培训计划名称(从第一条记录获取) + private Integer singleCount; // 单选数量 + private Integer multiCount; // 多选数量 + private Integer judgeCount; // 判断数量 + private Long totalScore; // 总分 + private Long singleTotalScore; // 单选总分 + private Long multiTotalScore; // 多选总分 + private Long judgeTotalScore; // 判断总分 +} \ No newline at end of file diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/security/StudyRecordCourseVo.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/security/StudyRecordCourseVo.java new file mode 100644 index 0000000..e6b5b36 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/security/StudyRecordCourseVo.java @@ -0,0 +1,33 @@ +package com.hotwj.platform.reportStatistics.domain.vo.security; + +import lombok.Data; + +import java.util.Date; + +@Data +public class StudyRecordCourseVo { + /** + * 课程名称 + */ + private String courseName; + + /** + * 学习时间(最早的一条学习记录时间) + */ + private Date studyTime; + + /** + * 完成学习时间 + */ + private Date completeTime; + + /** + * 学习时长(秒) + */ + private Long duration; + + /** + * 是否补学(是/否) + */ + private String isMakeUp; +} \ No newline at end of file diff --git a/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/security/StudyRecordData.java b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/security/StudyRecordData.java new file mode 100644 index 0000000..40f8b21 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/domain/vo/security/StudyRecordData.java @@ -0,0 +1,26 @@ +package com.hotwj.platform.reportStatistics.domain.vo.security; + +import lombok.Data; + +import java.util.Date; +import java.util.List; + +@Data +public class StudyRecordData { + private String userId; + private String userName; + private String idCardNo; + private String plateNumber; + private Date firstStudyDate; + private String courseName; + private Integer studyMinutes; + private Integer learnMinutes; + private Integer planMinutes; + private String makeUpLabel; + private String photoUrl1; + private String photoUrl2; + private List photoUrls; + private String signatureUrl; + + private List courseList; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/integration/GotenbergClient.java b/src/main/java/com/hotwj/platform/reportStatistics/integration/GotenbergClient.java new file mode 100644 index 0000000..34d5717 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/integration/GotenbergClient.java @@ -0,0 +1,137 @@ +package com.hotwj.platform.reportStatistics.integration; + +import cn.hutool.core.io.resource.BytesResource; +import cn.hutool.core.io.resource.FileResource; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.nio.charset.StandardCharsets; + +@Slf4j +@Component +public class GotenbergClient { + @Value("${gotenberg.base-url:https://127.0.0.1:3000}") + private String gotenbergBaseUrl; + @Value("${gotenberg.connect-timeout-ms:10000}") + private int connectTimeoutMs; + @Value("${gotenberg.read-timeout-ms:300000}") + private int readTimeoutMs; + @Value("${gotenberg.retry-count:2}") + private int retryCount; + @Value("${gotenberg.retry-interval-ms:500}") + private long retryIntervalMs; + + private String buildConvertUrl() { + String base = gotenbergBaseUrl; + if (base.endsWith("/")) { + base = base.substring(0, base.length() - 1); + } + return base + "/forms/chromium/convert/html"; + } + + public byte[] convertHtmlToPdf(File htmlFile, String footerHtml) { + return convertHtmlToPdf(htmlFile, footerHtml, false); + } + + public byte[] convertHtmlToPdf(File htmlFile, String footerHtml, boolean landscape) { + return executeWithRetry(() -> { + HttpRequest req = HttpRequest.post(buildConvertUrl()) + .setConnectionTimeout(connectTimeoutMs) + .setReadTimeout(readTimeoutMs) + .form("index.html", new FileResource(htmlFile) { + @Override + public String getName() { + return "index.html"; + } + }) + .form("printBackground", "true") + .form("landscape", String.valueOf(landscape)); + + if (footerHtml != null) { + req.form("footer.html", new BytesResource(footerHtml.getBytes(StandardCharsets.UTF_8), "footer.html")); + } + return executeAndValidate(req); + }); + } + + public byte[] convertHtmlToPdf(String htmlContent) { + return convertHtmlToPdf(htmlContent, null, false); + } + + public byte[] convertHtmlToPdf(String htmlContent, String footerHtml, boolean landscape) { + return executeWithRetry(() -> { + HttpRequest req = HttpRequest.post(buildConvertUrl()) + .setConnectionTimeout(connectTimeoutMs) + .setReadTimeout(readTimeoutMs) + .form("index.html", new BytesResource(htmlContent.getBytes(StandardCharsets.UTF_8), "index.html")) + .form("printBackground", "true") + .form("landscape", String.valueOf(landscape)); + + if (footerHtml != null) { + req.form("footer.html", new BytesResource(footerHtml.getBytes(StandardCharsets.UTF_8), "footer.html")); + } + return executeAndValidate(req); + }); + } + + private byte[] executeAndValidate(HttpRequest req) { + try (HttpResponse response = req.execute()) { + if (!response.isOk()) { + String body = response.body(); + throw new ServiceException("Gotenberg 转换失败,HTTP状态码: " + response.getStatus() + + ", 响应: " + truncate(body, 300)); + } + byte[] bytes = response.bodyBytes(); + if (bytes == null || bytes.length == 0) { + throw new ServiceException("Gotenberg 转换失败:返回空PDF内容"); + } + return bytes; + } + } + + private byte[] executeWithRetry(PdfRequestExecutor executor) { + int maxAttempts = Math.max(1, retryCount + 1); + Exception lastException = null; + for (int attempt = 1; attempt <= maxAttempts; attempt++) { + try { + return executor.execute(); + } catch (Exception e) { + lastException = e; + if (attempt >= maxAttempts) { + break; + } + log.warn("Gotenberg 转换失败,准备重试。attempt={}/{}", attempt, maxAttempts, e); + if (retryIntervalMs > 0) { + try { + Thread.sleep(retryIntervalMs); + } catch (InterruptedException interruptedException) { + Thread.currentThread().interrupt(); + throw new ServiceException("Gotenberg 转换被中断"); + } + } + } + } + + if (lastException instanceof ServiceException serviceException) { + throw serviceException; + } + throw new ServiceException("Gotenberg 转换失败: " + (lastException == null ? "未知异常" : lastException.getMessage())); + } + + private String truncate(String text, int maxLength) { + if (text == null || text.length() <= maxLength) { + return text; + } + return text.substring(0, maxLength) + "..."; + } + + @FunctionalInterface + private interface PdfRequestExecutor { + byte[] execute(); + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/mapper/LedgerReportMapper.java b/src/main/java/com/hotwj/platform/reportStatistics/mapper/LedgerReportMapper.java new file mode 100644 index 0000000..f8bacae --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/mapper/LedgerReportMapper.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.reportStatistics.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.reportStatistics.domain.bo.ReportLedgerQueryBo; +import com.hotwj.platform.reportStatistics.domain.vo.*; +import com.hotwj.platform.reportStatistics.domain.vo.security.PreJobTrainingDetail; +import com.hotwj.platform.reportStatistics.domain.vo.security.StudyRecordCourseVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; + +/** + * 驾驶员台账Mapper接口 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Mapper +public interface LedgerReportMapper { + /** + * 查询学员的课程学习记录 + */ + List selectStudyRecordCourses(@Param("companyId") Long companyId, @Param("trainingId") Long trainingId, @Param("userId") String userId); + + List selectDriverInfoPage(Page page, @Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectDriverInfoList(@Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectDriverHealthPage(Page page, @Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectDriverHealthList(@Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectProfCertScorePage(Page page, @Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectProfCertScoreList(@Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectDriverIntegrityPage(Page page, @Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectDriverIntegrityList(@Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectVehicleInfoPage(Page page, @Param("bo") ReportLedgerQueryBo bo); + + List selectVehicleInfoList(@Param("bo") ReportLedgerQueryBo bo); + + List selectGovVehicleInfoPage(Page page, @Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectGovVehicleInfoList(@Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectGovGpsViolationPage(Page page, @Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectAccidentPage(Page page, @Param("bo") ReportLedgerQueryBo bo); + + List selectAccidentList(@Param("bo") ReportLedgerQueryBo bo); + + List selectGovAccidentPage(Page page, @Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectGovAccidentList(@Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectPreJobTrainingPage(Page page, @Param("bo") ReportLedgerQueryBo bo); + + List selectPreJobTrainingList(@Param("bo") ReportLedgerQueryBo bo); + + List selectGovPreJobTrainingPage(Page page, @Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectGovPreJobTrainingList(@Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectGovPreJobTrainingDetailPage(Page page, @Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectGovSafetyEducationTrainingPage(Page page, @Param("bo") ReportLedgerQueryBo bo); + + /** + * 查询安全生产教育培训详情(主信息) + */ + GovSafetyEducationTrainingDetailVo selectGovSafetyEducationTrainingDetail(@Param("id") Long id); + + /** + * 查询安全生产教育培训参训人员列表 + */ + List selectGovSafetyEducationTrainingParticipants(@Param("trainingId") Long trainingId); + + /** + * 查询安全生产教育培训内容列表(课程名称) + */ + List selectGovSafetyEducationTrainingCourseNames(@Param("trainingId") Long trainingId); + + /** + * 统计指定时间段内的违章数量(按月分组) + * + * @param bo 查询条件 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 统计结果 + */ + List selectViolationCountByDateRange(@Param("bo") ReportLedgerQueryBo bo, @Param("startTime") Date startTime, @Param("endTime") Date endTime); + + List selectGovViolationInfoDetailPage(Page page, @Param("bo") ReportLedgerQueryBo bo); + + List selectGovViolationHandlingPage(Page page, @Param("districtCode") String districtCode, @Param("bo") ReportLedgerQueryBo bo); + + List selectSafetyProductionMeetingPage(Page page, @Param("bo") ReportLedgerQueryBo bo); + + List selectOnlineTrainingMonthlyRows(@Param("companyId") Long companyId, @Param("trainingId") Long trainingId, @Param("userIds") List userIds); + + /** + * 从业人员岗前培训明细行(仅在线培训,按创建时间) + */ + List selectDriverPreJobTrainingRows(@Param("companyId") Long companyId, + @Param("userId") String userId, + @Param("trainingId") Long trainingId); + + /** + * 查询驾驶员的所有岗前培训ID列表 + */ + List selectDriverPreJobTrainingIds(@Param("companyId") Long companyId, @Param("userId") String userId); +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/mapper/SysReportTemplateMapper.java b/src/main/java/com/hotwj/platform/reportStatistics/mapper/SysReportTemplateMapper.java new file mode 100644 index 0000000..9e0c734 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/mapper/SysReportTemplateMapper.java @@ -0,0 +1,15 @@ +package com.hotwj.platform.reportStatistics.mapper; + +import com.hotwj.platform.reportStatistics.domain.SysReportTemplate; +import com.hotwj.platform.reportStatistics.domain.vo.SysReportTemplateVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 报表模板Mapper接口 + * + * @author shihongwei + * @date 2024-03-15 + */ +public interface SysReportTemplateMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/pdf/OfficialInformationSealPostProcessor.java b/src/main/java/com/hotwj/platform/reportStatistics/pdf/OfficialInformationSealPostProcessor.java new file mode 100644 index 0000000..3317bcd --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/pdf/OfficialInformationSealPostProcessor.java @@ -0,0 +1,53 @@ +package com.hotwj.platform.reportStatistics.pdf; + +import com.hotwj.platform.common.utils.PdfWatermarkUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Component; + +import java.io.InputStream; + +/** + * 信息化印章 PDF 后处理器 + * 从 classpath 加载印章图片,并在 PDF 每页右上角追加绘制。 + */ +@Slf4j +@Component +public class OfficialInformationSealPostProcessor implements PdfPostProcessor { + + private static final String SEAL_RESOURCE = "images/officialInformationSeal.png"; + private static volatile byte[] cachedSealBytes; + + @Override + public byte[] process(byte[] pdfBytes) { + byte[] sealBytes = getSealBytes(); + if (sealBytes == null || sealBytes.length == 0) { + return pdfBytes; + } + return PdfWatermarkUtil.addImageTopRight(pdfBytes, sealBytes); + } + + private byte[] getSealBytes() { + if (cachedSealBytes != null) { + return cachedSealBytes; + } + synchronized (OfficialInformationSealPostProcessor.class) { + if (cachedSealBytes != null) { + return cachedSealBytes; + } + try { + ClassPathResource resource = new ClassPathResource(SEAL_RESOURCE); + if (!resource.exists()) { + return null; + } + try (InputStream is = resource.getInputStream()) { + cachedSealBytes = is.readAllBytes(); + } + return cachedSealBytes; + } catch (Exception e) { + log.error("Failed to load official information seal image", e); + return null; + } + } + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/pdf/PdfPostProcessor.java b/src/main/java/com/hotwj/platform/reportStatistics/pdf/PdfPostProcessor.java new file mode 100644 index 0000000..18d5ace --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/pdf/PdfPostProcessor.java @@ -0,0 +1,10 @@ +package com.hotwj.platform.reportStatistics.pdf; + +/** + * PDF 后处理器 + * 用于在 PDF 生成完成后进行二次加工(如加水印、加印章、加页码等)。 + */ +@FunctionalInterface +public interface PdfPostProcessor { + byte[] process(byte[] pdfBytes); +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/cover/DailyTrainingCoverProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/cover/DailyTrainingCoverProvider.java new file mode 100644 index 0000000..039734e --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/cover/DailyTrainingCoverProvider.java @@ -0,0 +1,65 @@ +package com.hotwj.platform.reportStatistics.provider.cover; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import com.hotwj.platform.common.mapper.PublicMapper; +import com.hotwj.platform.common.vo.AddressVo; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 日常安全培训封面 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class DailyTrainingCoverProvider implements IReportDataProvider { + + private final ISysCompanyService sysCompanyService; + private final PublicMapper publicMapper; + + @Override + public String getTemplateCode() { + return "templates/common/dailyTrainingCover.html.vm"; + } + + @Override + public String getReportType() { + return "DAILY_TRAINING_COVER"; + } + + @Override + public Map prepareData(Map params) { + Long companyId = MapUtil.getLong(params, "companyId"); + Map data = new HashMap<>(); + + SysCompanyVo company = sysCompanyService.queryById(companyId); + String companyName = ""; + String regionName = ""; + if (company != null) { + companyName = StringUtils.blankToDefault(company.getCompanyName(), ""); + if (StringUtils.isNotBlank(company.getRegionCode())) { + AddressVo addressVo = publicMapper.selectAddressByCode(company.getRegionCode()); + if (addressVo != null) { + regionName = addressVo.getLabel(); + } + } + } + data.put("companyName", companyName); + data.put("regionName", regionName); + // DateUtil.format 使用 SimpleDateFormat,它会尝试解析所有字母。'n', 'b', 's', 'p' 等被误认为是格式化指令。 + // 需要将非日期格式化字符用单引号括起来 + data.put("printYear", DateUtil.format(new Date(), "yyyy")); + + return data; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/cover/PrejobTrainingCoverProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/cover/PrejobTrainingCoverProvider.java new file mode 100644 index 0000000..7ab7f76 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/cover/PrejobTrainingCoverProvider.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.reportStatistics.provider.cover; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import com.hotwj.platform.common.mapper.PublicMapper; +import com.hotwj.platform.common.vo.AddressVo; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 岗前培训封面 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class PrejobTrainingCoverProvider implements IReportDataProvider { + + private final ISysCompanyService sysCompanyService; + private final PublicMapper publicMapper; + + @Override + public String getTemplateCode() { + return "templates/common/traingCover.html.vm"; + } + + @Override + public String getReportType() { + return "PREJOB_TRAINING_COVER"; + } + + @Override + public Map prepareData(Map params) { + Long companyId = MapUtil.getLong(params, "companyId"); + String coverName = MapUtil.getStr(params, "coverName"); + Map data = new HashMap<>(); + + SysCompanyVo company = sysCompanyService.queryById(companyId); + String companyName = ""; + String regionName = ""; + if (company != null) { + companyName = StringUtils.blankToDefault(company.getCompanyName(), ""); + if (StringUtils.isNotBlank(company.getRegionCode())) { + AddressVo addressVo = publicMapper.selectAddressByCode(company.getRegionCode()); + if (addressVo != null) { + regionName = addressVo.getLabel(); + } + } + } + data.put("companyName", companyName); + data.put("coverName", coverName); + data.put("regionName", regionName + "道路运输行业"); + // 使用   实体来增加空格的显示宽度,确保在 HTML 中正确渲染 + // DateUtil.format 使用 SimpleDateFormat,它会尝试解析所有字母。'n', 'b', 's', 'p' 等被误认为是格式化指令。 + // 需要将非日期格式化字符用单引号括起来 + data.put("printDate", DateUtil.format(new Date(), "yyyy' 年     月'")); + + return data; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/AnnualAssessmentRecordProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/AnnualAssessmentRecordProvider.java new file mode 100644 index 0000000..e952697 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/AnnualAssessmentRecordProvider.java @@ -0,0 +1,190 @@ +package com.hotwj.platform.reportStatistics.provider.driver; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson2.JSON; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.driverManagement.driverAnnualAssessment.domain.bo.HotDriverAnnualAssessmentBo; +import com.hotwj.platform.driverManagement.driverAnnualAssessment.domain.vo.HotDriverAnnualAssessmentVo; +import com.hotwj.platform.driverManagement.driverAnnualAssessment.service.IHotDriverAnnualAssessmentService; +import com.hotwj.platform.reportStatistics.service.IDriverFilePrintService; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员年度考核记录数据提供者 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class AnnualAssessmentRecordProvider implements IReportDataProvider { + + private final IHotDriverService driverService; + private final IHotDriverAnnualAssessmentService annualAssessmentService; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/driver/annualAssessmentRecord.html.vm"; + } + + @Override + public String getReportType() { + return IDriverFilePrintService.TYPE_ANNUAL_ASSESSMENT_RECORD; + } + + @Override + public String getReportTitle() { + return "驾驶员年度考核记录"; + } + + @Override + public Map prepareData(Map params) { + String driverId = MapUtil.getStr(params, "driverId"); + if (StrUtil.isBlank(driverId)) { + return new HashMap<>(); + } + + HotDriverVo driver = driverService.queryById(driverId); + if (driver == null) { + return new HashMap<>(); + } + + Map ctx = new HashMap<>(); + ctx.put("name", driver.getName()); + ctx.put("idCardNo", driver.getIdCardNo()); + + HotDriverAnnualAssessmentBo bo = new HotDriverAnnualAssessmentBo(); + bo.setDriverId(driverId); + bo.setIsDeleted(0L); + List list = annualAssessmentService.queryList(bo); + + List> categories = new ArrayList<>(); + String result = ""; + String assessTime = ""; + String evaluatorSignatureUrl = ""; + String assessedSignatureUrl = ""; + int totalValue = 0; + int totalScoreActual = 0; + + if (CollUtil.isNotEmpty(list)) { + HotDriverAnnualAssessmentVo vo = list.get(0); + result = vo.getAssessmentResult(); + assessTime = vo.getAssessmentTime() != null ? DateUtil.format(vo.getAssessmentTime(), "yyyy-MM-dd") : ""; + + if (vo.getEvaluatorSignatureId() != null) { + evaluatorSignatureUrl = ossService.selectUrlByIds(String.valueOf(vo.getEvaluatorSignatureId())); + } + if (vo.getAssessedSignatureId() != null) { + assessedSignatureUrl = ossService.selectUrlByIds(String.valueOf(vo.getAssessedSignatureId())); + } + + + // 解析考核内容 + Map root = null; + try { + if (StrUtil.isNotBlank(vo.getAssessmentContent())) { + root = JSON.parseObject(vo.getAssessmentContent(), Map.class); + } + } catch (Exception ignored) { + } + + Map names = new HashMap<>(); + names.put("discipline", "工作纪律"); + names.put("safety", "安全驾驶"); + names.put("service", "服务质量"); + names.put("learn", "培训学习"); + + Map>> grouped = new HashMap<>(); + grouped.put("discipline", new ArrayList<>()); + grouped.put("safety", new ArrayList<>()); + grouped.put("service", new ArrayList<>()); + grouped.put("learn", new ArrayList<>()); + + if (root != null) { + Object idCard = root.get("driverIdCard"); + if (idCard != null) { + ctx.put("idCardNo", String.valueOf(idCard)); + } + + // 获取扣分详情 + Object detailsObj = root.get("details"); + + if (detailsObj instanceof List dlist) { + for (Object o : dlist) { + // fastjson2 可能会解析成 JSONObject (Map) + if (o instanceof Map) { + Map m = (Map) o; + String cat = String.valueOf(m.get("category")); + String content = String.valueOf(m.get("content")); + Number score = m.get("score") instanceof Number ? (Number) m.get("score") : 0; + Number deduction = m.get("deduction") instanceof Number ? (Number) m.get("deduction") : 0; + + Map item = new HashMap<>(); + item.put("content", content); + item.put("score", score.intValue()); // 单项总分 + item.put("deduction", deduction.intValue()); // 扣分 + + if (grouped.containsKey(cat)) { + grouped.get(cat).add(item); + } + } + } + } + } + + String[] order = new String[]{"discipline", "safety", "service", "learn"}; + for (String key : order) { + List> dets = grouped.get(key); + + // 计算当前分类的总分值 (累加该分类下所有题目的分数) + int categoryTotalScore = 0; + int categoryTotalDeduction = 0; + + if (dets != null) { + for (Map it : dets) { + int itemScore = (int) it.get("score"); + int itemDeduction = (int) it.get("deduction"); + categoryTotalScore += itemScore; + categoryTotalDeduction += itemDeduction; + } + } + + // 计算当前分类的实际得分 (总分 - 扣分) + int actual = Math.max(categoryTotalScore - categoryTotalDeduction, 0); + + // 累加总分 + totalValue += categoryTotalScore; + totalScoreActual += actual; + + Map cat = new HashMap<>(); + cat.put("name", names.get(key)); + cat.put("value", categoryTotalScore); + cat.put("score", actual); + cat.put("details", dets == null ? new ArrayList<>() : dets); + categories.add(cat); + } + } + + ctx.put("result", result); + ctx.put("assessTime", assessTime); + ctx.put("evaluatorSignatureUrl", evaluatorSignatureUrl); + ctx.put("assessedSignatureUrl", assessedSignatureUrl); + ctx.put("categories", categories); + ctx.put("totalValue", totalValue); + ctx.put("totalScore", totalScoreActual); + + return ctx; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/DriverAccidentRecordProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/DriverAccidentRecordProvider.java new file mode 100644 index 0000000..aa9fd68 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/DriverAccidentRecordProvider.java @@ -0,0 +1,105 @@ +package com.hotwj.platform.reportStatistics.provider.driver; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.reportStatistics.service.IDriverFilePrintService; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.securityManagement.accidentArchive.domain.bo.HotAccidentArchiveBo; +import com.hotwj.platform.securityManagement.accidentArchive.domain.vo.HotAccidentArchiveVo; +import com.hotwj.platform.securityManagement.accidentArchive.service.IHotAccidentArchiveService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员事故记录数据提供者 + * + * @author shihongwei + * @date 2026-03-01 + */ +@Component("driverAccidentRecordProvider") +@RequiredArgsConstructor +public class DriverAccidentRecordProvider implements IReportDataProvider { + + private final IHotDriverService driverService; + private final IHotAccidentArchiveService accidentArchiveService; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/driver/accidentRecord.html.vm"; + } + + @Override + public boolean isLandscape() { + return true; + } + + @Override + public String getReportType() { + return IDriverFilePrintService.TYPE_ACCIDENT_RECORD; + } + + @Override + public String getReportTitle() { + return "驾驶员安全行车事故记录"; + } + + @Override + public Map prepareData(Map params) { + + String driverId = MapUtil.getStr(params, "driverId"); + + HotDriverVo driver = driverService.queryById(driverId); + if (driver == null) { + return MapUtil.empty(); + } + Map data = new HashMap<>(); + + data.put("driverName", driver.getName()); + + // Fetch accidents + HotAccidentArchiveBo bo = new HotAccidentArchiveBo(); + bo.setDriverId(driverId); + bo.setIsDeleted(0L); + // Ensure not deleted? Service usually handles logic deletion. + List list = accidentArchiveService.queryList(bo); + + // Process list for view (e.g. format dates, process signature if available in accident record) + List> accidents = new ArrayList<>(); + if (CollUtil.isNotEmpty(list)) { + for (HotAccidentArchiveVo vo : list) { + Map map = new HashMap<>(); + map.put("accidentTime", vo.getAccidentTime() != null ? DateUtil.format(vo.getAccidentTime(), "yyyy-MM-dd") : ""); + map.put("plateNumber", vo.getPlateNumber()); + map.put("occurLocation", vo.getOccurLocation()); + map.put("mainCause", vo.getMainCause()); + map.put("deathToll", vo.getDeathToll() != null ? vo.getDeathToll() : 0); + map.put("numberSeriouslyInjured", vo.getNumberSeriouslyInjured() != null ? vo.getNumberSeriouslyInjured() : 0); + map.put("numberMinorInjuries", vo.getNumberMinorInjuries() != null ? vo.getNumberMinorInjuries() : 0); + map.put("economicLoss", vo.getEconomicLoss() != null ? vo.getEconomicLoss() : 0); + map.put("caseClosingTime", vo.getCaseClosingTime() != null ? DateUtil.format(vo.getCaseClosingTime(), "yyyy-MM-dd") : ""); + map.put("divisionResponsibilities", vo.getDivisionResponsibilities()); // Processing result + map.put("remark", vo.getRemark()); + + if (StringUtils.isNotBlank(vo.getResponsiblePersonSignature())) { + map.put("signatureUrl", ossService.selectUrlByIds(vo.getResponsiblePersonSignature())); + } + + accidents.add(map); + } + } + data.put("accidents", accidents); + + return data; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/DriverApplicationFormProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/DriverApplicationFormProvider.java new file mode 100644 index 0000000..5169cf1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/DriverApplicationFormProvider.java @@ -0,0 +1,130 @@ +package com.hotwj.platform.reportStatistics.provider.driver; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.driverManagement.driverFamilyMember.domain.bo.HotDriverFamilyMemberBo; +import com.hotwj.platform.driverManagement.driverFamilyMember.domain.vo.HotDriverFamilyMemberVo; +import com.hotwj.platform.driverManagement.driverFamilyMember.service.IHotDriverFamilyMemberService; +import com.hotwj.platform.driverManagement.driverWorkExperience.domain.bo.HotDriverWorkExperienceBo; +import com.hotwj.platform.driverManagement.driverWorkExperience.domain.vo.HotDriverWorkExperienceVo; +import com.hotwj.platform.driverManagement.driverWorkExperience.service.IHotDriverWorkExperienceService; +import com.hotwj.platform.reportStatistics.service.IDriverFilePrintService; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员应聘表数据提供者 + * + * @author shihongwei + */ +@Component("driverApplicationFormProvider") +@RequiredArgsConstructor +public class DriverApplicationFormProvider implements IReportDataProvider { + + private final IHotDriverService driverService; + private final IHotDriverFamilyMemberService driverFamilyMemberService; + private final IHotDriverWorkExperienceService workExperienceService; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/driver/driverApplicationForm.html.vm"; + } + + @Override + public String getReportType() { + return IDriverFilePrintService.TYPE_APPLICATION_FORM; + } + + @Override + public String getReportTitle() { + return "驾驶员应聘表"; + } + + @Override + public Map prepareData(Map params) { + String driverId = MapUtil.getStr(params, "driverId"); + if (StrUtil.isBlank(driverId)) { + return MapUtil.empty(); + } + + HotDriverVo driver = driverService.queryById(driverId); + if (driver == null) { + return MapUtil.empty(); + } + if (StrUtil.isNotBlank(driver.getSignatureUrl()) + && !StrUtil.startWithAnyIgnoreCase(driver.getSignatureUrl(), "http://", "https://")) { + String url = ossService.selectUrlByIds(driver.getSignatureUrl()); + if (StrUtil.isNotBlank(url)) { + driver.setSignatureUrl(url); + } + } + + Map data = new HashMap<>(); + + // 字典翻译交由 tool 处理,此处仅需放入原始值或对象 + data.put("driver", driver); + + // 家庭成员 + HotDriverFamilyMemberBo familyBo = new HotDriverFamilyMemberBo(); + familyBo.setDriverId(driver.getId()); + List familyMembers = driverFamilyMemberService.queryList(familyBo); + + // 补全家庭成员到2行,保持表格美观 + int familyRows = 2; + List> familyList = new ArrayList<>(); + if (CollUtil.isNotEmpty(familyMembers)) { + for (HotDriverFamilyMemberVo member : familyMembers) { + Map m = new HashMap<>(); + m.put("name", member.getName()); + m.put("relationship", member.getRelationship()); + m.put("birthMonth", member.getBirthMonth()); // 放入 Date 对象 + m.put("phone", member.getPhone()); + familyList.add(m); + } + } + // 填充空行 + while (familyList.size() < familyRows) { + familyList.add(new HashMap<>()); + } + data.put("familyMembers", familyList); + + // 工作经历 + HotDriverWorkExperienceBo workBo = new HotDriverWorkExperienceBo(); + workBo.setDriverId(driverId); + List workExperiences = workExperienceService.queryList(workBo); + + // 补全工作经历到3行 + int workRows = 3; + List> workList = new ArrayList<>(); + if (CollUtil.isNotEmpty(workExperiences)) { + for (HotDriverWorkExperienceVo work : workExperiences) { + Map m = new HashMap<>(); + m.put("startDate", work.getStartDate()); + m.put("endDate", work.getEndDate()); + m.put("employer", work.getEmployer()); + m.put("jobTitle", work.getJobTitle()); + m.put("description", work.getDescription()); // 增加 description 字段 + workList.add(m); + } + } + + // 填充空行 + while (workList.size() < workRows) { + workList.add(new HashMap<>()); + } + data.put("workExperiences", workList); + + return data; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/DriverLicenseCopyProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/DriverLicenseCopyProvider.java new file mode 100644 index 0000000..6a0f412 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/DriverLicenseCopyProvider.java @@ -0,0 +1,70 @@ +package com.hotwj.platform.reportStatistics.provider.driver; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.reportStatistics.service.IDriverFilePrintService; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * 驾驶员机动车驾驶证复印件数据提供者 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class DriverLicenseCopyProvider implements IReportDataProvider { + + private final IHotDriverService driverService; + + @Override + public String getTemplateCode() { + return "templates/driver/driverLicenseCopy.html.vm"; + } + + @Override + public String getReportType() { + return IDriverFilePrintService.TYPE_DRIVER_LICENSE_COPY; + } + + @Override + public String getReportTitle() { + return "驾驶员机动车驾驶证复印件"; + } + + @Override + public Map prepareData(Map params) { + String driverId = MapUtil.getStr(params, "driverId"); + if (StrUtil.isBlank(driverId)) { + return MapUtil.empty(); + } + + HotDriverVo driver = driverService.queryById(driverId); + if (driver == null) { + return MapUtil.empty(); + } + + Map data = new HashMap<>(); + data.put("driver", driver); + + // Date formatting + data.put("driverLicenseFirstIssueDate", driver.getDriverLicenseFirstIssueDate() != null ? DateUtil.format(driver.getDriverLicenseFirstIssueDate(), "yyyy-MM-dd") : ""); + // Valid period handling + String validPeriod = "长期"; + if (driver.getDriverLicenseLongTerm() == null || driver.getDriverLicenseLongTerm() == 0) { + validPeriod = driver.getDriverLicenseExpireDate() != null ? DateUtil.format(driver.getDriverLicenseExpireDate(), "yyyy-MM-dd") : ""; + } + data.put("validPeriod", validPeriod); + + // Image URLs will be handled by template using $tool.getOssUrls() + + return data; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/IdCardScanProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/IdCardScanProvider.java new file mode 100644 index 0000000..da4d404 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/IdCardScanProvider.java @@ -0,0 +1,64 @@ +package com.hotwj.platform.reportStatistics.provider.driver; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.reportStatistics.service.IDriverFilePrintService; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * 驾驶员身份证扫描件数据提供者 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class IdCardScanProvider implements IReportDataProvider { + + private final IHotDriverService driverService; + + @Override + public String getTemplateCode() { + return "templates/driver/idCardScan.html.vm"; + } + + @Override + public String getReportType() { + return IDriverFilePrintService.TYPE_ID_CARD_SCAN; + } + + @Override + public String getReportTitle() { + return "驾驶员身份证扫描件"; + } + + @Override + public Map prepareData(Map params) { + String driverId = MapUtil.getStr(params, "driverId"); + if (StrUtil.isBlank(driverId)) { + return MapUtil.empty(); + } + + HotDriverVo driver = driverService.queryById(driverId); + if (driver == null) { + return MapUtil.empty(); + } + + Map data = new HashMap<>(); + data.put("driver", driver); + // 图片处理:模版需要 frontImages 和 backImages 列表 + // 虽然可以用 $tool,但为了兼容性,我们最好准备好这些变量 + // 我们可以注入 OssService 或者使用静态工具类 + // 推荐:在 Provider 中不进行复杂逻辑,交给模版 Tool + // 但这里我们简单注入 Tool 到 data 中?不行,Tool 是 context 的。 + // 让我们修改模版使用 $tool.getOssUrls($driver.idcardFrontUrl) + + return data; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/MedicalExaminationReportProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/MedicalExaminationReportProvider.java new file mode 100644 index 0000000..ead06af --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/MedicalExaminationReportProvider.java @@ -0,0 +1,132 @@ +package com.hotwj.platform.reportStatistics.provider.driver; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.driverManagement.driverHealthReport.domain.bo.HotDriverHealthReportBo; +import com.hotwj.platform.driverManagement.driverHealthReport.domain.vo.HotDriverHealthReportVo; +import com.hotwj.platform.driverManagement.driverHealthReport.service.IHotDriverHealthReportService; +import com.hotwj.platform.reportStatistics.service.IDriverFilePrintService; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.DictService; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员体检报告数据提供者 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class MedicalExaminationReportProvider implements IReportDataProvider { + + private final IHotDriverService driverService; + private final IHotDriverHealthReportService healthReportService; + private final DictService dictService; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/driver/medicalExaminationReport.html.vm"; + } + + @Override + public String getReportType() { + return IDriverFilePrintService.TYPE_MEDICAL_EXAMINATION_REPORT; + } + + @Override + public String getReportTitle() { + return "驾驶员体检报告"; + } + + @Override + public Map prepareData(Map params) { + Map ctx = new HashMap<>(); + ctx.put("name", ""); + ctx.put("idCardNo", ""); + ctx.put("reportDate", ""); + ctx.put("healthStatus", ""); + ctx.put("hospital", ""); + ctx.put("remark", ""); + ctx.put("imageUrls", new ArrayList<>()); + + String driverId = MapUtil.getStr(params, "driverId"); + if (StrUtil.isBlank(driverId)) { + return ctx; + } + + HotDriverVo driver = driverService.queryById(driverId); + if (driver == null) { + return ctx; + } + + ctx.put("name", driver.getName()); + ctx.put("idCardNo", driver.getIdCardNo()); + + HotDriverHealthReportBo bo = new HotDriverHealthReportBo(); + bo.setDriverId(driverId); + bo.setIsDeleted(0L); + List list = healthReportService.queryList(bo); + + String reportDate = ""; + String healthStatusLabel = ""; + List imageUrls = new ArrayList<>(); + String hospital = ""; + String remark = ""; + + if (CollUtil.isNotEmpty(list)) { + HotDriverHealthReportVo vo = list.get(0); + reportDate = vo.getReportDate() != null ? DateUtil.format(vo.getReportDate(), "yyyy-MM-dd") : ""; + hospital = vo.getHospital(); + remark = vo.getRemark(); + + if (StringUtils.isNotBlank(vo.getHealthStatus())) { + String label = dictService.getDictLabel("hot_health", vo.getHealthStatus()); + healthStatusLabel = StringUtils.isBlank(label) ? vo.getHealthStatus() : label; + } + + // Image handling - keeping original logic of splitUrls and ossService + if (StringUtils.isNotBlank(vo.getPreviewImageUrl())) { + imageUrls = splitUrls(ossService.selectUrlByIds(vo.getPreviewImageUrl())); + } else if (StringUtils.isNotBlank(vo.getAttachmentUrl())) { + imageUrls = splitUrls(ossService.selectUrlByIds(vo.getAttachmentUrl())); + } + } + + ctx.put("reportDate", reportDate); + ctx.put("healthStatus", healthStatusLabel); + ctx.put("hospital", hospital); + ctx.put("remark", remark); + ctx.put("imageUrls", imageUrls); + + return ctx; + } + + // Helper method replicated from original service + private List splitUrls(String urls) { + if (StrUtil.isBlank(urls)) { + return new ArrayList<>(); + } + List list = new ArrayList<>(); + String[] parts = urls.split(","); + for (String p : parts) { + String v = p.trim(); + if (StrUtil.isNotBlank(v)) { + list.add(v); + } + } + return list; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/QualificationCertificateCopyProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/QualificationCertificateCopyProvider.java new file mode 100644 index 0000000..1c63b8d --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/QualificationCertificateCopyProvider.java @@ -0,0 +1,59 @@ +package com.hotwj.platform.reportStatistics.provider.driver; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.reportStatistics.service.IDriverFilePrintService; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * 驾驶员从业资格证复印件数据提供者 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class QualificationCertificateCopyProvider implements IReportDataProvider { + + private final IHotDriverService driverService; + + @Override + public String getTemplateCode() { + return "templates/driver/qualificationCertificateCopy.html.vm"; + } + + @Override + public String getReportType() { + return IDriverFilePrintService.TYPE_QUALIFICATION_CERTIFICATE_COPY; + } + + @Override + public String getReportTitle() { + return "驾驶员从业资格证复印件"; + } + + @Override + public Map prepareData(Map params) { + String driverId = MapUtil.getStr(params, "driverId"); + if (StrUtil.isBlank(driverId)) { + return MapUtil.empty(); + } + + HotDriverVo driver = driverService.queryById(driverId); + if (driver == null) { + return MapUtil.empty(); + } + + Map data = new HashMap<>(); + data.put("driver", driver); + // Images will be handled by template using $tool.getOssUrls() + + return data; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/QualificationReviewProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/QualificationReviewProvider.java new file mode 100644 index 0000000..f9454a0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/QualificationReviewProvider.java @@ -0,0 +1,119 @@ +package com.hotwj.platform.reportStatistics.provider.driver; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.driverManagement.driverSkillAssessment.domain.bo.HotDriverSkillAssessmentBo; +import com.hotwj.platform.driverManagement.driverSkillAssessment.domain.vo.HotDriverSkillAssessmentVo; +import com.hotwj.platform.driverManagement.driverSkillAssessment.service.IHotDriverSkillAssessmentService; +import com.hotwj.platform.reportStatistics.service.IDriverFilePrintService; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.DictService; +import org.dromara.common.core.service.OssService; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员资格审查及技能考核登记表数据提供者 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class QualificationReviewProvider implements IReportDataProvider { + + private final IHotDriverService driverService; + private final IHotDriverSkillAssessmentService driverSkillAssessmentService; + private final DictService dictService; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/driver/qualificationReview.html.vm"; + } + + @Override + public String getReportType() { + return IDriverFilePrintService.TYPE_QUALIFICATION_REVIEW; + } + + @Override + public String getReportTitle() { + return "驾驶员资格审查及技能考核登记表"; + } + + @Override + public Map prepareData(Map params) { + String driverId = MapUtil.getStr(params, "driverId"); + if (StrUtil.isBlank(driverId)) { + return MapUtil.empty(); + } + + HotDriverVo driver = driverService.queryById(driverId); + if (driver == null) { + return MapUtil.empty(); + } + + Map ctx = new HashMap<>(); + + // 字典翻译 + String genderLabel = dictService.getDictLabel("sys_user_sex", driver.getGender()); + String nationLabel = dictService.getDictLabel("nation", driver.getNation()); + String educationLabel = dictService.getDictLabel("hot_academic", driver.getEducation()); + + if (genderLabel != null) driver.setGender(genderLabel); + if (nationLabel != null) driver.setNation(nationLabel); + if (educationLabel != null) driver.setEducation(educationLabel); + + // 格式化日期字段为 yyyy-MM-dd + // 注意:HotDriverVo 中的日期字段是 Date 类型,如果直接修改为 String 需要使用额外字段或者Map + // 这里我们可以创建一个 Map 来覆盖 driver 中的日期字段 + Map driverMap = MapUtil.newHashMap(); + // 复制 driver 的属性到 map (这里简单起见,我们直接put需要修改的字段) + // 或者更简单的,我们把格式化后的日期字符串放入 ctx,模板里优先使用 ctx 中的变量 + + if (driver.getDriverLicenseLongTerm() != null && driver.getDriverLicenseLongTerm() == 1L) { + ctx.put("licenseValidityStr", "长期"); + } else if (driver.getDriverLicenseExpireDate() != null) { + ctx.put("licenseValidityStr", DateUtil.format(driver.getDriverLicenseExpireDate(), "yyyy-MM-dd")); + } + if (driver.getQualificationValidEndDate() != null) { + ctx.put("qualificationValidityStr", DateUtil.format(driver.getQualificationValidEndDate(), "yyyy-MM-dd")); + } + + ctx.put("driver", driver); + + // 组装数据 + HotDriverSkillAssessmentBo assessmentBo = new HotDriverSkillAssessmentBo(); + assessmentBo.setDriverId(driverId); + assessmentBo.setIsDeleted(0L); + List assessments = driverSkillAssessmentService.queryList(assessmentBo); + + if (!assessments.isEmpty()) { + HotDriverSkillAssessmentVo assessment = assessments.get(0); + ctx.put("examVehicleModel", assessment.getExamVehicleModel()); + ctx.put("assessDate", assessment.getAssessDate()); // Let template format + ctx.put("examRoute", assessment.getExamRoute()); + ctx.put("drivingExperience", assessment.getDrivingExperience()); + ctx.put("drivingMileage", assessment.getDrivingMileage()); + + ctx.put("chiefOpinion", assessment.getChiefOpinion()); + ctx.put("invigilatorOpinion", assessment.getInvigilatorOpinion()); + ctx.put("companyLeaderOpinion", assessment.getCompanyLeaderOpinion()); + + // 主考人员和监考人员签名 (转换为 URL) + ctx.put("chiefExaminerSignature", ossService.selectUrlByIds(assessment.getChiefExaminerSignature())); + ctx.put("invigilatorSignature", ossService.selectUrlByIds(assessment.getInvigilatorSignature())); + + ctx.put("assessment", assessment); + } + + return ctx; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/RewardPunishmentRecordProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/RewardPunishmentRecordProvider.java new file mode 100644 index 0000000..4d75126 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/RewardPunishmentRecordProvider.java @@ -0,0 +1,91 @@ +package com.hotwj.platform.reportStatistics.provider.driver; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.driverManagement.driverRewardPunishment.domain.bo.HotDriverRewardPunishmentBo; +import com.hotwj.platform.driverManagement.driverRewardPunishment.domain.vo.HotDriverRewardPunishmentVo; +import com.hotwj.platform.driverManagement.driverRewardPunishment.service.IHotDriverRewardPunishmentService; +import com.hotwj.platform.reportStatistics.service.IDriverFilePrintService; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员奖惩记录表数据提供者 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class RewardPunishmentRecordProvider implements IReportDataProvider { + + private final IHotDriverService driverService; + private final IHotDriverRewardPunishmentService rewardPunishmentService; + + @Override + public String getTemplateCode() { + return "templates/driver/rewardPunishmentRecord.html.vm"; + } + + @Override + public String getReportType() { + return IDriverFilePrintService.TYPE_REWARD_PUNISHMENT_RECORD; + } + + @Override + public String getReportTitle() { + return "奖惩记录表"; + } + + @Override + public Map prepareData(Map params) { + String driverId = MapUtil.getStr(params, "driverId"); + if (StrUtil.isBlank(driverId)) { + return new HashMap<>(); + } + + HotDriverVo driver = driverService.queryById(driverId); + if (driver == null) { + return new HashMap<>(); + } + + Map ctx = new HashMap<>(); + ctx.put("name", driver.getName()); + ctx.put("idCardNo", driver.getIdCardNo()); + + HotDriverRewardPunishmentBo bo = new HotDriverRewardPunishmentBo(); + bo.setDriverId(driverId); + List list = rewardPunishmentService.queryList(bo); + + List> rows = new ArrayList<>(); + if (CollUtil.isNotEmpty(list)) { + for (HotDriverRewardPunishmentVo vo : list) { + Map m = new HashMap<>(); + m.put("eventDate", vo.getEventDate() != null ? DateUtil.format(vo.getEventDate(), "yyyy-MM-dd") : ""); + + String categoryLabel = StringUtils.isBlank(vo.getCategory()) ? "" : vo.getCategory(); + m.put("item", categoryLabel); + + String decisionText = vo.getDecision(); + if (StringUtils.isBlank(decisionText)) { + decisionText = vo.getReason(); + } + m.put("decision", decisionText); + rows.add(m); + } + } + ctx.put("rows", rows); + + return ctx; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/ViolationRecordProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/ViolationRecordProvider.java new file mode 100644 index 0000000..7183055 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/driver/ViolationRecordProvider.java @@ -0,0 +1,97 @@ +package com.hotwj.platform.reportStatistics.provider.driver; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.reportStatistics.service.IDriverFilePrintService; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.securityManagement.violationInfo.domain.bo.HotViolationInfoBo; +import com.hotwj.platform.securityManagement.violationInfo.domain.vo.HotViolationInfoVo; +import com.hotwj.platform.securityManagement.violationInfo.service.IHotViolationInfoService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.DictService; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 驾驶员违章记录数据提供者 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class ViolationRecordProvider implements IReportDataProvider { + + private final IHotDriverService driverService; + private final IHotViolationInfoService violationInfoService; + private final DictService dictService; + + @Override + public String getTemplateCode() { + return "templates/driver/violationRecord.html.vm"; + } + + @Override + public String getReportType() { + return IDriverFilePrintService.TYPE_VIOLATION_RECORD; + } + + @Override + public String getReportTitle() { + return "驾驶员违章记录"; + } + + @Override + public boolean isLandscape() { + return true; + } + + @Override + public Map prepareData(Map params) { + String driverId = MapUtil.getStr(params, "driverId"); + if (StrUtil.isBlank(driverId)) { + return new HashMap<>(); + } + + HotDriverVo driver = driverService.queryById(driverId); + if (driver == null) { + return new HashMap<>(); + } + + Map data = new HashMap<>(); + data.put("driverName", driver.getName()); + + // Fetch violations + HotViolationInfoBo bo = new HotViolationInfoBo(); + bo.setDriverId(driverId); + bo.setIsDeleted(0L); + List list = violationInfoService.queryList(bo); + + List> violations = new ArrayList<>(); + if (CollUtil.isNotEmpty(list)) { + for (HotViolationInfoVo vo : list) { + Map map = new HashMap<>(); + map.put("violationTime", vo.getOccurTime() != null ? DateUtil.format(vo.getOccurTime(), "yyyy-MM-dd") : ""); + map.put("vehiclePlate", vo.getPlateNumber()); + map.put("violationLocation", vo.getViolationPlace()); + map.put("violationContent", vo.getViolationContent()); + map.put("violationType", dictService.getDictLabel("hot_violation_type", String.valueOf(vo.getViolationType()))); + map.put("fineAmount", vo.getFineAmount() != null ? vo.getFineAmount() : 0); + map.put("deductedPoints", vo.getDeductPoints() != null ? vo.getDeductPoints() : 0); + map.put("handlingStatus", vo.getDisposalResult()); + map.put("remark", vo.getRemark()); + violations.add(map); + } + } + data.put("violations", violations); + + return data; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/security/GpsViolationProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/GpsViolationProvider.java new file mode 100644 index 0000000..195bc26 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/GpsViolationProvider.java @@ -0,0 +1,115 @@ +package com.hotwj.platform.reportStatistics.provider.security; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import com.hotwj.platform.common.mapper.PublicMapper; +import com.hotwj.platform.common.vo.AddressVo; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.securityManagement.violationInfo.domain.vo.HotViolationInfoVo; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.*; + +@Component +@RequiredArgsConstructor +public class GpsViolationProvider implements IReportDataProvider { + + private final ISysCompanyService sysCompanyService; + private final PublicMapper publicMapper; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/securityManage/gpsViolationRecord.html.vm"; + } + + @Override + public String getReportTitle() { + return "车辆动态监控违法行为处理记录"; + } + + @Override + public Map prepareData(Map params) { + String mode = MapUtil.getStr(params, "mode"); + Long companyId = MapUtil.getLong(params, "companyId"); + + Map ctx = new HashMap<>(); + ctx.put("mode", mode); + + SysCompanyVo company = sysCompanyService.queryById(companyId); + String companyName = ""; + String regionName = ""; + if (company != null) { + companyName = StringUtils.blankToDefault(company.getCompanyName(), ""); + if (StringUtils.isNotBlank(company.getRegionCode())) { + AddressVo addressVo = publicMapper.selectAddressByCode(company.getRegionCode()); + if (addressVo != null) { + regionName = addressVo.getLabel(); + } + } + } + ctx.put("companyName", companyName); + + if ("cover".equals(mode)) { + ctx.put("regionName", regionName); + ctx.put("printYear", DateUtil.format(new Date(), "yyyy")); + } else if ("detail".equals(mode)) { + HotViolationInfoVo info = (HotViolationInfoVo) params.get("info"); + if (info != null) { + ctx.put("info", info); + Date recordTime = info.getCreateTime() != null ? info.getCreateTime() : info.getOccurTime(); + ctx.put("recordTime", recordTime); + ctx.put("responsibleSignatureUrl", resolveMaybeOssUrl(info.getResponsibleSignatureImage())); + ctx.put("partySignatureUrl", resolveMaybeOssUrl(info.getPartySignatureImage())); + } + } else if ("attachment".equals(mode)) { + HotViolationInfoVo info = (HotViolationInfoVo) params.get("info"); + if (info != null) { + String promise = ossService.selectUrlByIds(info.getPromiseUrls()); + ctx.put("promiseImages", splitUrls(promise)); + } + } + + return ctx; + } + + private String resolveMaybeOssUrl(String value) { + if (StringUtils.isBlank(value)) { + return null; + } + String first = value.split(",")[0].trim(); + if (StringUtils.isBlank(first)) { + return null; + } + if (StringUtils.ishttp(first)) { + return first; + } + if (first.matches("^\\d+$")) { + String urls = ossService.selectUrlByIds(first); + if (StringUtils.isBlank(urls)) { + return null; + } + return urls.split(",")[0].trim(); + } + return first; + } + + private List splitUrls(String urls) { + List result = new ArrayList<>(); + if (StringUtils.isBlank(urls)) { + return result; + } + for (String s : urls.split(",")) { + String t = s == null ? "" : s.trim(); + if (!t.isEmpty()) { + result.add(t); + } + } + return result; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/security/OfflineMeetingProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/OfflineMeetingProvider.java new file mode 100644 index 0000000..f707244 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/OfflineMeetingProvider.java @@ -0,0 +1,129 @@ +package com.hotwj.platform.reportStatistics.provider.security; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.hotwj.platform.common.mapper.PublicMapper; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.securityManagement.securityMeeting.domain.vo.HotSecurityMeetingVo; +import com.hotwj.platform.securityManagement.securityMeeting.service.IHotSecurityMeetingService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.*; + +@Component +@RequiredArgsConstructor +public class OfflineMeetingProvider implements IReportDataProvider { + + private final IHotSecurityMeetingService securityMeetingService; + private final PublicMapper publicMapper; + private final OssService ossService; + private final HotDriverMapper driverMapper; + + @Override + public String getTemplateCode() { + return "templates/securityManage/offlineMeeting.html.vm"; + } + + @Override + public String getReportTitle() { + return "线下会议记录表"; + } + + @Override + public Map prepareData(Map params) { + Long meetingId = MapUtil.getLong(params, "meetingId"); + Map ctx = new HashMap<>(); + if (meetingId == null) { + return ctx; + } + HotSecurityMeetingVo meeting = securityMeetingService.queryById(meetingId); + if (meeting == null) { + return ctx; + } + // 处理数据 + Map mMap = new HashMap<>(); + mMap.put("meetingTopic", StringUtils.blankToDefault(meeting.getMeetingTopic(), "")); + mMap.put("startTime", meeting.getStartTime()); + mMap.put("endTime", meeting.getEndTime()); + mMap.put("location", StringUtils.blankToDefault(meeting.getLocation(), "")); + mMap.put("hostName", StringUtils.blankToDefault(meeting.getHostName(), "")); + mMap.put("recorderName", StringUtils.blankToDefault(meeting.getRecorderName(), "")); + + // 参会人员 + String participantNames = ""; + String pIds = meeting.getParticipantIds(); + if (StringUtils.isNotBlank(pIds)) { + List ids = Arrays.stream(pIds.split(",")) + .filter(StringUtils::isNotBlank) + .map(String::trim) + .filter(StringUtils::isNotBlank) + .toList(); + if (!ids.isEmpty()) { + try { + String names = publicMapper.selectAttendeeNamesByIds(ids); + if (StringUtils.isNotBlank(names)) { + participantNames = names; + } + } catch (Exception ignored) { + } + } + } + mMap.put("participantNames", StringUtils.blankToDefault(participantNames, "")); + + // 会议内容 + mMap.put("meetingContent", StringUtils.blankToDefault(meeting.getMeetingContent(), "")); + + // 下月计划 + mMap.put("nextMonthPlan", StringUtils.blankToDefault(meeting.getNextMonthPlan(), "")); + + ctx.put("meeting", mMap); + + // 图片 + List imgUrls = new ArrayList<>(); + if (StringUtils.isNotBlank(meeting.getPhotoAttachments())) { + String photoJson = meeting.getPhotoAttachments(); + try { + if (JSONUtil.isTypeJSON(photoJson)) { + JSONObject json = JSONUtil.parseObj(photoJson); + if (!json.isEmpty()) { + for (Object val : json.values()) { + String url = resolveMaybeOssUrl(String.valueOf(val)); + if (StringUtils.isNotBlank(url)) { + imgUrls.add(url); + } + } + } + } else { + for (String s : photoJson.split(",")) { + String url = resolveMaybeOssUrl(s); + if (StringUtils.isNotBlank(url)) { + imgUrls.add(url); + } + } + } + } catch (Exception e) { + // ignore + } + } + ctx.put("imgUrls", imgUrls); + + return ctx; + } + + private String resolveMaybeOssUrl(String ossIdOrUrl) { + if (StringUtils.isBlank(ossIdOrUrl)) { + return null; + } + try { + Long ossId = Long.parseLong(ossIdOrUrl); + return ossService.selectUrlByIds(String.valueOf(ossId)); + } catch (NumberFormatException e) { + return ossIdOrUrl; + } + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/security/OfflineTrainingPhotoProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/OfflineTrainingPhotoProvider.java new file mode 100644 index 0000000..1ef7340 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/OfflineTrainingPhotoProvider.java @@ -0,0 +1,30 @@ +package com.hotwj.platform.reportStatistics.provider.security; + +import cn.hutool.core.map.MapUtil; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class OfflineTrainingPhotoProvider implements IReportDataProvider { + + @Override + public String getTemplateCode() { + return "templates/securityManage/offlineTrainingPhoto.html.vm"; + } + + @Override + public String getReportTitle() { + return "线下培训会议照片"; + } + + @Override + public Map prepareData(Map params) { + String imgUrl = MapUtil.getStr(params, "imgUrl"); + Map ctx = new HashMap<>(); + ctx.put("imgUrl", imgUrl); + return ctx; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/security/OfflineTrainingProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/OfflineTrainingProvider.java new file mode 100644 index 0000000..e359ba8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/OfflineTrainingProvider.java @@ -0,0 +1,186 @@ +package com.hotwj.platform.reportStatistics.provider.security; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.hotwj.platform.common.mapper.PublicMapper; +import com.hotwj.platform.common.utils.FileTextExtractor; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo; +import com.hotwj.platform.securityManagement.training.service.IHotTrainingService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.*; + +@Component +@RequiredArgsConstructor +public class OfflineTrainingProvider implements IReportDataProvider { + + private final IHotTrainingService trainingService; + private final PublicMapper publicMapper; + private final OssService ossService; + private final HotDriverMapper driverMapper; + + @Override + public String getTemplateCode() { + return "templates/securityManage/offlineTraining.html.vm"; + } + + @Override + public String getReportTitle() { + return "线下培训记录表"; + } + + @Override + public Map prepareData(Map params) { + Long trainingId = MapUtil.getLong(params, "trainingId"); + Map ctx = new HashMap<>(); + if (trainingId == null) { + return ctx; + } + HotTrainingVo training = trainingService.queryById(trainingId); + if (training == null) { + return ctx; + } + // 处理数据 + Map tMap = new HashMap<>(); + tMap.put("trainingSubject", StringUtils.blankToDefault(training.getPlanName(), "")); + tMap.put("startTime", training.getStartTime()); + tMap.put("endTime", training.getEndTime()); + tMap.put("location", StringUtils.blankToDefault(training.getTrainingLocation(), "线下")); + tMap.put("hostName", StringUtils.blankToDefault(training.getTeacherName(), "")); + tMap.put("recorderName", StringUtils.blankToDefault(training.getRecorderName(), "")); + + // 培训人员 + String participantNames = ""; + String pIds = training.getParticipantIds(); + if (StringUtils.isNotBlank(pIds)) { + List ids = Arrays.stream(pIds.split(",")) + .filter(StringUtils::isNotBlank) + .map(String::trim) + .filter(StringUtils::isNotBlank) + .toList(); + if (!ids.isEmpty()) { + try { + String names = publicMapper.selectAttendeeNamesByIds(ids); + if (StringUtils.isNotBlank(names)) { + participantNames = names; + } + } catch (Exception ignored) { + } + } + } + tMap.put("participantNames", StringUtils.blankToDefault(participantNames, "")); + + // 培训内容 (从 files 获取附件内容,支持doc/docx/xls/xlsx/ppt/pptx/txt/pdf) + StringBuilder trainingContentBuilder = new StringBuilder(); + String filesJson = training.getFiles(); + if (StringUtils.isNotBlank(filesJson)) { + List fileUrls = new ArrayList<>(); + try { + if (JSONUtil.isTypeJSON(filesJson)) { + JSONObject json = JSONUtil.parseObj(filesJson); + if (!json.isEmpty()) { + for (Object val : json.values()) { + String url = resolveMaybeOssUrl(String.valueOf(val)); + if (StringUtils.isNotBlank(url)) { + fileUrls.add(url); + } + } + } + } else { + for (String s : filesJson.split(",")) { + String url = resolveMaybeOssUrl(s); + if (StringUtils.isNotBlank(url)) { + fileUrls.add(url); + } + } + } + } catch (Exception e) { + // ignore + } + + for (String url : fileUrls) { + try { + String ext = ""; + if (url.contains("?")) { + String path = url.substring(0, url.indexOf("?")); + ext = StringUtils.substringAfterLast(path, "."); + } else { + ext = StringUtils.substringAfterLast(url, "."); + } + + if (StringUtils.isNotBlank(ext)) { + byte[] fileBytes = HttpUtil.downloadBytes(url); + if (fileBytes != null && fileBytes.length > 0) { + try (InputStream is = new ByteArrayInputStream(fileBytes)) { + String text = FileTextExtractor.extractText(is, ext); + if (StringUtils.isNotBlank(text)) { + if (trainingContentBuilder.length() > 0) { + trainingContentBuilder.append("\n"); + } + trainingContentBuilder.append(text); + } + } + } + } + } catch (Exception e) { + // ignore individual file errors + } + } + } + tMap.put("trainingContent", StringUtils.blankToDefault(trainingContentBuilder.toString(), "")); + + ctx.put("training", tMap); + + // 图片 + List imgUrls = new ArrayList<>(); + if (StringUtils.isNotBlank(training.getPhotos())) { + String photoJson = training.getPhotos(); + try { + if (JSONUtil.isTypeJSON(photoJson)) { + JSONObject json = JSONUtil.parseObj(photoJson); + if (!json.isEmpty()) { + for (Object val : json.values()) { + String url = resolveMaybeOssUrl(String.valueOf(val)); + if (StringUtils.isNotBlank(url)) { + imgUrls.add(url); + } + } + } + } else { + for (String s : photoJson.split(",")) { + String url = resolveMaybeOssUrl(s); + if (StringUtils.isNotBlank(url)) { + imgUrls.add(url); + } + } + } + } catch (Exception e) { + // ignore + } + } + ctx.put("imgUrls", imgUrls); + + return ctx; + } + + private String resolveMaybeOssUrl(String ossIdOrUrl) { + if (StringUtils.isBlank(ossIdOrUrl)) { + return null; + } + try { + Long ossId = Long.parseLong(ossIdOrUrl); + return ossService.selectUrlByIds(String.valueOf(ossId)); + } catch (NumberFormatException e) { + return ossIdOrUrl; + } + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/security/PreJobTrainingProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/PreJobTrainingProvider.java new file mode 100644 index 0000000..c7ca20b --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/PreJobTrainingProvider.java @@ -0,0 +1,235 @@ +package com.hotwj.platform.reportStatistics.provider.security; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import com.hotwj.platform.common.mapper.PublicMapper; +import com.hotwj.platform.common.vo.AddressVo; +import com.hotwj.platform.config.course.service.IHotCourseService; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.reportStatistics.domain.vo.PreJobTrainingLedgerVo; +import com.hotwj.platform.reportStatistics.domain.vo.security.PreJobTrainingDetail; +import com.hotwj.platform.reportStatistics.mapper.LedgerReportMapper; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.securityManagement.training.service.IHotTrainingService; +import com.hotwj.platform.securityManagement.trainingAnswer.domain.bo.TrainingAnswerRecordBo; +import com.hotwj.platform.securityManagement.trainingAnswer.domain.vo.HotTrainingAnswerRecordVo; +import com.hotwj.platform.securityManagement.trainingAnswer.service.IHotTrainingAnswerRecordService; +import com.hotwj.platform.securityManagement.trainingAudit.domain.bo.HotTrainingAuditBo; +import com.hotwj.platform.securityManagement.trainingAudit.domain.vo.HotTrainingAuditVo; +import com.hotwj.platform.securityManagement.trainingAudit.service.IHotTrainingAuditService; +import com.hotwj.platform.securityManagement.trainingCourseConfig.service.IHotTrainingCourseConfigService; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.bo.HotTrainingCourseRecordBo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.vo.HotTrainingCourseRecordVo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.service.IHotTrainingCourseRecordService; +import com.hotwj.platform.securityManagement.trainingParticipant.service.IHotTrainingParticipantService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.DictService; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.*; + +/** + * 从业人员岗前培训考核卡 Provider + */ +@Component +@RequiredArgsConstructor +public class PreJobTrainingProvider implements IReportDataProvider { + + + private final DictService dictService; + private final IHotTrainingCourseRecordService trainingCourseRecordService; + private final IHotTrainingAuditService trainingAuditService; + private final OssService ossService; + private final IHotDriverService driverService; + private final LedgerReportMapper ledgerReportMapper; + + @Override + public String getTemplateCode() { + return "templates/securityManage/preJobTrainingRecord.html.vm"; + } + + @Override + public boolean isLandscape() { + return true; + } + + @Override + public String getReportTitle() { + return "从业人员岗前培训考核卡"; + } + + @Override + public String getCoverProviderType() { + return "PREJOB_TRAINING_COVER"; + } + + @Override + public Map prepareData(Map params) { + String driverId = MapUtil.getStr(params, "driverId"); + + Map ctx = new HashMap<>(); + + // 如果 driverId 缺失,检查是否传入了 "driver" 对象(为了兼容旧逻辑或内部调用) + HotDriverVo driver = null; + if (StringUtils.isNotBlank(driverId)) { + driver = driverService.queryById(driverId); + } + + if (driver != null) { + Long companyId = driver.getCompanyId(); + // 翻译字典值 + driver.setGender(dictService.getDictLabel("sys_user_sex", driver.getGender())); + driver.setEducation(dictService.getDictLabel("hot_academic", driver.getEducation())); + + List details = new ArrayList<>(); + + // 查询该驾驶员的所有岗前培训ID + List trainingIds = ledgerReportMapper.selectDriverPreJobTrainingIds(companyId, driver.getId()); + + if (CollUtil.isNotEmpty(trainingIds)) { + for (Long trainingId : trainingIds) { + PreJobTrainingDetail detail = buildDetail(companyId, driver, trainingId); + details.add(detail); + } + } else { + // 如果没有培训记录,创建一个空的详情对象以显示基本信息 + PreJobTrainingDetail detail = buildDetail(companyId, driver, null); + details.add(detail); + } + + ctx.put("details", details); + } + + return ctx; + } + + private PreJobTrainingDetail buildDetail(Long companyId, HotDriverVo driver, Long trainingId) { + PreJobTrainingDetail detail = new PreJobTrainingDetail(); + detail.setDriverId(driver.getId()); + detail.setDriverName(driver.getName()); + detail.setGender(driver.getGender()); // Already translated + detail.setIdCardNo(driver.getIdCardNo()); + detail.setEntryDateLabel(driver.getEntryDate() != null ? DateUtil.format(driver.getEntryDate(), "yyyy-MM-dd") : ""); + detail.setBirthDate(driver.getBirthDate() != null ? DateUtil.format(driver.getBirthDate(), "yyyy-MM-dd") : ""); + detail.setEducation(driver.getEducation()); // Already translated + detail.setStartWorkDate(driver.getStartWorkDate() != null ? DateUtil.format(driver.getStartWorkDate(), "yyyy-MM-dd") : ""); + detail.setCurrentAddress(StringUtils.blankToDefault(driver.getCurrentAddress(), "")); + + // 查询指定培训ID的明细行 + // 如果 trainingId 为空(无记录),返回空列表 + List rows = new ArrayList<>(); + if (trainingId != null) { + rows = ledgerReportMapper.selectDriverPreJobTrainingRows(companyId, driver.getId(), trainingId); + } + + // 计算总分 + long totalScore = 0L; + if (CollUtil.isNotEmpty(rows)) { + for (PreJobTrainingDetail.PreJobTrainingRow row : rows) { + if (StringUtils.isNotBlank(row.getExamScore())) { + try { + totalScore += Double.parseDouble(row.getExamScore()); + } catch (NumberFormatException ignored) {} + } + } + detail.setTotalScore(String.valueOf(totalScore)); + } else { + detail.setTotalScore(""); + } + + int minRows = 8; + while (rows.size() < minRows) { + rows.add(new PreJobTrainingDetail.PreJobTrainingRow()); + } + detail.setRows(rows); + + if (trainingId != null) { + detail.setDriverSignImgUrl(resolveDriverSignatureUrl(companyId, trainingId, driver.getId())); + detail.setAuditorSignImgUrl(resolveAuditorSignImgUrl(companyId, trainingId, driver.getId())); + } + + return detail; + } + + private String resolveDriverSignatureUrl(Long companyId, Long trainingId, String driverId) { + if (companyId == null || trainingId == null || driverId == null) { + return null; + } + HotTrainingCourseRecordBo bo = new HotTrainingCourseRecordBo(); + bo.setCompanyId(companyId); + bo.setTrainingId(trainingId); + bo.setUserId(driverId); + bo.setIsDeleted(0L); + List list = trainingCourseRecordService.queryList(bo); + if (list == null || list.isEmpty()) { + return null; + } + Long ossId = null; + // Try to find a record with signature + for (HotTrainingCourseRecordVo vo : list) { + if (vo != null && vo.getSignatureOssId() != null) { + ossId = vo.getSignatureOssId(); + break; // Use the first one found + } + } + if (ossId == null) { + return null; + } + return resolveMaybeOssUrl(String.valueOf(ossId)); + } + + private String resolveAuditorSignImgUrl(Long companyId, Long trainingId, String driverId) { + if (companyId == null || trainingId == null || driverId == null) { + return null; + } + HotTrainingAuditBo bo = new HotTrainingAuditBo(); + bo.setCompanyId(companyId); + bo.setTrainingId(trainingId); + bo.setUserId(driverId); + bo.setIsPass(1L); + bo.setIsDeleted(0L); + List list = trainingAuditService.queryList(bo); + if (list == null || list.isEmpty()) { + return null; + } + String url = null; + for (HotTrainingAuditVo vo : list) { + if (vo != null && vo.getSignImgUrl() != null) { + url = vo.getSignImgUrl(); + break; + } + } + if (StringUtils.isBlank(url)) { + return null; + } + return resolveMaybeOssUrl(url); + } + + private String resolveMaybeOssUrl(String value) { + if (StringUtils.isBlank(value)) { + return null; + } + String first = value.split(",")[0].trim(); + if (StringUtils.isBlank(first)) { + return null; + } + if (first.startsWith("http")) { + return first; + } + if (first.matches("^\\d+$")) { + String urls = ossService.selectUrlByIds(first); + if (StringUtils.isBlank(urls)) { + return null; + } + return urls.split(",")[0].trim(); + } + return first; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/security/SafetyEducationTrainingProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/SafetyEducationTrainingProvider.java new file mode 100644 index 0000000..09333c2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/SafetyEducationTrainingProvider.java @@ -0,0 +1,135 @@ +package com.hotwj.platform.reportStatistics.provider.security; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.hotwj.platform.common.mapper.PublicMapper; +import com.hotwj.platform.reportStatistics.domain.vo.GovSafetyEducationTrainingDetailVo; +import com.hotwj.platform.reportStatistics.domain.vo.GovSafetyEducationTrainingParticipantVo; +import com.hotwj.platform.reportStatistics.mapper.LedgerReportMapper; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +@Component +@RequiredArgsConstructor +public class SafetyEducationTrainingProvider implements IReportDataProvider { + + private final ISysCompanyService sysCompanyService; + private final PublicMapper publicMapper; + private final LedgerReportMapper ledgerReportMapper; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/securityManage/safetyEducationTrainingRecord.html.vm"; + } + + @Override + public String getReportTitle() { + return "安全生产教育培训记录"; + } + + @Override + public Map prepareData(Map params) { + String mode = MapUtil.getStr(params, "mode"); + + Map ctx = new HashMap<>(); + ctx.put("mode", mode); + + if ("detail".equals(mode) || "signIn".equals(mode)) { + Long trainingId = MapUtil.getLong(params, "trainingId"); + if (trainingId != null) { + GovSafetyEducationTrainingDetailVo detail = buildSafetyEducationDetail(trainingId); + ctx.put("detail", detail); + } + } + + return ctx; + } + + private GovSafetyEducationTrainingDetailVo buildSafetyEducationDetail(Long trainingId) { + GovSafetyEducationTrainingDetailVo detail = ledgerReportMapper.selectGovSafetyEducationTrainingDetail(trainingId); + if (detail == null) { + return null; + } + List participants = ledgerReportMapper.selectGovSafetyEducationTrainingParticipants(trainingId); + + // 处理签名和头像 URL + if (participants != null) { + for (GovSafetyEducationTrainingParticipantVo p : participants) { + // 格式化完成时间 + if (p.getCompleteTime() != null) { + p.setCompleteTimeLabel(DateUtil.format(p.getCompleteTime(), "yyyy-MM-dd HH:mm")); + } + + // 处理签名 + if (p.getSignatureOssId() != null) { + p.setSignatureUrl(resolveMaybeOssUrl(String.valueOf(p.getSignatureOssId()))); + } + + // 处理学习照片(头像)- 取 JSON 中的最后一个值 + if (StringUtils.isNotBlank(p.getStudyPhotoOssId())) { + try { + JSONObject json = JSONUtil.parseObj(p.getStudyPhotoOssId()); + if (!json.isEmpty()) { + // 获取最后一个 key 的值 + // LinkedHashMap 保持插入顺序,但 JSONUtil 解析可能不保证。 + // 假设 JSON 结构是课程 ID 对应 OSS ID,且我们想要任意一个或者最后一个。 + // 这里取 values 集合的最后一个元素作为头像 + Object[] values = json.values().toArray(); + if (values.length > 0) { + String lastOssId = String.valueOf(values[values.length - 1]); + p.setAvatarUrl(resolveMaybeOssUrl(lastOssId)); + } + } + } catch (Exception e) { + // ignore parse error + } + } + } + } + + detail.setParticipantList(participants); + + List courseNames = ledgerReportMapper.selectGovSafetyEducationTrainingCourseNames(trainingId); + if (courseNames != null && !courseNames.isEmpty()) { + String content = IntStream.range(0, courseNames.size()) + .mapToObj(i -> (i + 1) + ":" + courseNames.get(i)) + .collect(Collectors.joining("\n")); + detail.setTrainingContent(content); + } + return detail; + } + + private String resolveMaybeOssUrl(String value) { + if (StringUtils.isBlank(value)) { + return null; + } + String first = value.split(",")[0].trim(); + if (StringUtils.isBlank(first)) { + return null; + } + if (first.startsWith("http")) { + return first; + } + if (first.matches("^\\d+$")) { + String urls = ossService.selectUrlByIds(first); + if (StringUtils.isBlank(urls)) { + return null; + } + return urls.split(",")[0].trim(); + } + return first; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/security/SafetyFireHazardInspectionProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/SafetyFireHazardInspectionProvider.java new file mode 100644 index 0000000..80c641b --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/SafetyFireHazardInspectionProvider.java @@ -0,0 +1,213 @@ +package com.hotwj.platform.reportStatistics.provider.security; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.hotwj.platform.common.mapper.PublicMapper; +import com.hotwj.platform.common.vo.AddressVo; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.vo.HotHiddenDangerInspectionVo; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.service.IHotHiddenDangerInspectionService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.json.utils.JsonUtils; +import org.springframework.stereotype.Component; + +import java.util.*; + +@Component +@RequiredArgsConstructor +public class SafetyFireHazardInspectionProvider implements IReportDataProvider { + + private final ISysCompanyService sysCompanyService; + private final PublicMapper publicMapper; + private final IHotHiddenDangerInspectionService hiddenDangerInspectionService; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/securityManage/safetyFireHazardInspectionRecord.html.vm"; + } + + @Override + public String getReportTitle() { + return "安全生产(消防)隐患排查整改记录"; + } + + @Override + public Map prepareData(Map params) { + String mode = MapUtil.getStr(params, "mode"); + Long companyId = MapUtil.getLong(params, "companyId"); + + Map ctx = new HashMap<>(); + ctx.put("mode", mode); + + SysCompanyVo company = sysCompanyService.queryById(companyId); + String companyName = ""; + String regionName = ""; + if (company != null) { + companyName = StringUtils.blankToDefault(company.getCompanyName(), ""); + if (StringUtils.isNotBlank(company.getRegionCode())) { + AddressVo addressVo = publicMapper.selectAddressByCode(company.getRegionCode()); + if (addressVo != null) { + regionName = addressVo.getLabel(); + } + } + } + ctx.put("companyName", companyName); + + if ("cover".equals(mode)) { + ctx.put("regionName", regionName); + ctx.put("printYear", DateUtil.format(new Date(), "yyyy")); + } else if ("detail".equals(mode)) { + HotHiddenDangerInspectionVo inspection = (HotHiddenDangerInspectionVo) params.get("inspection"); + if (inspection == null) { + Long inspectionId = MapUtil.getLong(params, "inspectionId"); + if (inspectionId != null) { + inspection = hiddenDangerInspectionService.queryById(inspectionId); + } + } + if (inspection != null) { + ctx.put("inspection", inspection); + ctx.put("checkContentLabel", buildInspectionContentLabel(inspection.getCheckContentJson())); + ctx.put("checkerSignUrl", resolveMaybeOssUrl(inspection.getSignImgUrl())); + ctx.put("approverSignUrl", resolveMaybeOssUrl(inspection.getApproverSignImgUrl())); + } + } else if ("photo".equals(mode)) { + HotHiddenDangerInspectionVo inspection = (HotHiddenDangerInspectionVo) params.get("inspection"); + if (inspection == null) { + Long inspectionId = MapUtil.getLong(params, "inspectionId"); + if (inspectionId != null) { + inspection = hiddenDangerInspectionService.queryById(inspectionId); + } + } + if (inspection != null) { + ctx.put("inspection", inspection); + List photos = resolveInspectionImages(inspection.getAttachmentUrl()); + ctx.put("photos", photos); + } + } + + return ctx; + } + + private String buildInspectionContentLabel(String json) { + List> items = parseInspectionContent(json); + if (items.isEmpty()) { + return StringUtils.blankToDefault(json, ""); + } + StringBuilder sb = new StringBuilder(); + int i = 1; + for (Map item : items) { + if (item == null) { + continue; + } + String content = String.valueOf(item.getOrDefault("content", "")); + String valueText = String.valueOf(item.getOrDefault("valueText", "")); + if (StringUtils.isBlank(content) && StringUtils.isBlank(valueText)) { + continue; + } + if (sb.length() > 0) { + sb.append("
"); + } + sb.append(i).append("、").append(content); + if (StringUtils.isNotBlank(valueText)) { + sb.append("(").append(valueText).append(")"); + } + i++; + } + return sb.toString(); + } + + private List> parseInspectionContent(String json) { + List> result = new ArrayList<>(); + if (StringUtils.isBlank(json)) { + return result; + } + try { + List> list = JsonUtils.parseObject( + json, + new TypeReference>>() { + } + ); + if (list == null) { + return result; + } + for (Map item : list) { + if (item == null) { + continue; + } + Object content = item.get("content"); + String contentText = content == null ? "" : String.valueOf(content); + Object normal = item.get("normalText"); + Object abnormal = item.get("abnormalText"); + Object res = item.get("result"); + String rv = res == null ? "" : String.valueOf(res); + String valueText; + if ("1".equals(rv)) { + valueText = normal == null ? "" : String.valueOf(normal); + } else if ("0".equals(rv)) { + valueText = abnormal == null ? "" : String.valueOf(abnormal); + } else { + valueText = normal == null ? "" : String.valueOf(normal); + } + item.put("content", contentText); + item.put("valueText", valueText); + result.add(item); + } + } catch (Exception e) { + return result; + } + return result; + } + + private List resolveInspectionImages(String attachmentUrl) { + List result = new ArrayList<>(); + if (StringUtils.isBlank(attachmentUrl)) { + return result; + } + String urls = attachmentUrl; + if (!(attachmentUrl.contains("http://") || attachmentUrl.contains("https://"))) { + urls = ossService.selectUrlByIds(attachmentUrl); + } + return splitUrls(urls); + } + + private List splitUrls(String urls) { + List result = new ArrayList<>(); + if (StringUtils.isBlank(urls)) { + return result; + } + for (String s : urls.split(",")) { + String t = s == null ? "" : s.trim(); + if (!t.isEmpty()) { + result.add(t); + } + } + return result; + } + + private String resolveMaybeOssUrl(String value) { + if (StringUtils.isBlank(value)) { + return null; + } + String first = value.split(",")[0].trim(); + if (StringUtils.isBlank(first)) { + return null; + } + if (StringUtils.ishttp(first)) { + return first; + } + if (first.matches("^\\d+$")) { + String urls = ossService.selectUrlByIds(first); + if (StringUtils.isBlank(urls)) { + return null; + } + return urls.split(",")[0].trim(); + } + return first; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/security/SafetyMeetingProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/SafetyMeetingProvider.java new file mode 100644 index 0000000..f1b612a --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/SafetyMeetingProvider.java @@ -0,0 +1,121 @@ +package com.hotwj.platform.reportStatistics.provider.security; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import com.hotwj.platform.common.mapper.PublicMapper; +import com.hotwj.platform.common.vo.AddressVo; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.securityManagement.securityMeeting.domain.vo.HotSecurityMeetingVo; +import com.hotwj.platform.securityManagement.securityMeeting.service.IHotSecurityMeetingService; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.bo.HotSecurityMeetingAttendeeBo; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.vo.HotSecurityMeetingAttendeeVo; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.service.IHotSecurityMeetingAttendeeService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +@RequiredArgsConstructor +public class SafetyMeetingProvider implements IReportDataProvider { + + private final ISysCompanyService sysCompanyService; + private final PublicMapper publicMapper; + private final IHotSecurityMeetingService securityMeetingService; + private final IHotSecurityMeetingAttendeeService securityMeetingAttendeeService; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/securityManage/safetyMeetingRecord.html.vm"; + } + + @Override + public String getReportTitle() { + return "安全生产会议记录"; + } + + @Override + public Map prepareData(Map params) { + String mode = MapUtil.getStr(params, "mode"); + Long companyId = MapUtil.getLong(params, "companyId"); + + Map ctx = new HashMap<>(); + ctx.put("mode", mode); + + SysCompanyVo company = sysCompanyService.queryById(companyId); + String companyName = ""; + String regionName = ""; + if (company != null) { + companyName = StringUtils.blankToDefault(company.getCompanyName(), ""); + if (StringUtils.isNotBlank(company.getRegionCode())) { + AddressVo addressVo = publicMapper.selectAddressByCode(company.getRegionCode()); + if (addressVo != null) { + regionName = addressVo.getLabel(); + } + } + } + ctx.put("companyName", companyName); + + if ("cover".equals(mode)) { + ctx.put("regionName", regionName); + ctx.put("printYear", DateUtil.format(new Date(), "yyyy")); + } else if ("detail".equals(mode) || "signIn".equals(mode)) { + Long meetingId = MapUtil.getLong(params, "meetingId"); + if (meetingId != null) { + HotSecurityMeetingVo meeting = securityMeetingService.queryById(meetingId); + if (meeting != null) { + HotSecurityMeetingAttendeeBo attendeeBo = new HotSecurityMeetingAttendeeBo(); + attendeeBo.setCompanyId(companyId); + attendeeBo.setMeetingId(meetingId); + attendeeBo.setIsDeleted(0L); + List attendees = securityMeetingAttendeeService.queryList(attendeeBo); + + if (attendees != null) { + for (HotSecurityMeetingAttendeeVo attendee : attendees) { + if (attendee.getAttendeePhotoOssId() != null) { + attendee.setAttendeePhotoUrl(resolveMaybeOssUrl(String.valueOf(attendee.getAttendeePhotoOssId()))); + } + if (attendee.getAttendeeSignOssId() != null) { + attendee.setAttendeeSignUrl(resolveMaybeOssUrl(String.valueOf(attendee.getAttendeeSignOssId()))); + } + } + } + + ctx.put("meeting", meeting); + ctx.put("attendees", attendees); + } + } + } + + return ctx; + } + + private String resolveMaybeOssUrl(String value) { + if (StringUtils.isBlank(value)) { + return null; + } + String first = value.split(",")[0].trim(); + if (StringUtils.isBlank(first)) { + return null; + } + if (first.startsWith("http")) { + return first; + } + if (first.matches("^\\d+$")) { + String urls = ossService.selectUrlByIds(first); + if (StringUtils.isBlank(urls)) { + return null; + } + return urls.split(",")[0].trim(); + } + return first; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/security/SafetyProductionInputProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/SafetyProductionInputProvider.java new file mode 100644 index 0000000..01f13c5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/SafetyProductionInputProvider.java @@ -0,0 +1,112 @@ +package com.hotwj.platform.reportStatistics.provider.security; + +import cn.hutool.core.map.MapUtil; +import com.hotwj.platform.common.mapper.PublicMapper; +import com.hotwj.platform.common.vo.AddressVo; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.securityManagement.safetyInvestment.domain.vo.HotSafetyInvestmentVo; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +@RequiredArgsConstructor +public class SafetyProductionInputProvider implements IReportDataProvider { + + private final ISysCompanyService sysCompanyService; + private final PublicMapper publicMapper; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/securityManage/safetyProductionInputRecord.html.vm"; + } + + @Override + public boolean isLandscape() { + return true; + } + + @Override + public String getReportTitle() { + return "安全生产投入登记本"; + } + + @Override + public Map prepareData(Map params) { + String mode = MapUtil.getStr(params, "mode"); + Long companyId = MapUtil.getLong(params, "companyId"); + + Map ctx = new HashMap<>(); + ctx.put("mode", mode); + + SysCompanyVo company = sysCompanyService.queryById(companyId); + String companyName = ""; + String regionName = ""; + if (company != null) { + companyName = StringUtils.blankToDefault(company.getCompanyName(), ""); + if (StringUtils.isNotBlank(company.getRegionCode())) { + AddressVo addressVo = publicMapper.selectAddressByCode(company.getRegionCode()); + if (addressVo != null) { + regionName = addressVo.getLabel(); + } + } + } + ctx.put("companyName", companyName); + + if ("cover".equals(mode)) { + String year = MapUtil.getStr(params, "year"); + ctx.put("regionName", regionName); + ctx.put("printYear", year); + } else if ("detail".equals(mode)) { + String year = MapUtil.getStr(params, "year"); + Long totalAmount = MapUtil.getLong(params, "totalAmount"); + List records = (List) params.get("records"); + + if (records != null) { + for (HotSafetyInvestmentVo record : records) { + if (StringUtils.isNotBlank(record.getResponsibleSignatureId())) { + record.setResponsibleSignatureUrl(resolveMaybeOssUrl(record.getResponsibleSignatureId())); + } + } + } + + ctx.put("printYear", year); + ctx.put("totalAmount", totalAmount); + ctx.put("records", records); + } else if ("receipt".equals(mode)) { + List receiptImages = (List) params.get("receiptImages"); + ctx.put("receiptImages", receiptImages); + } + + return ctx; + } + + private String resolveMaybeOssUrl(String value) { + if (StringUtils.isBlank(value)) { + return null; + } + String first = value.split(",")[0].trim(); + if (StringUtils.isBlank(first)) { + return null; + } + if (first.startsWith("http")) { + return first; + } + if (first.matches("^\\d+$")) { + String urls = ossService.selectUrlByIds(first); + if (StringUtils.isBlank(urls)) { + return null; + } + return urls.split(",")[0].trim(); + } + return first; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/security/ServiceQualityProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/ServiceQualityProvider.java new file mode 100644 index 0000000..3deaace --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/ServiceQualityProvider.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.reportStatistics.provider.security; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import com.hotwj.platform.common.mapper.PublicMapper; +import com.hotwj.platform.common.vo.AddressVo; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.vo.HotServiceQualityComplaintVo; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.service.IHotServiceQualityComplaintService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Component +@RequiredArgsConstructor +public class ServiceQualityProvider implements IReportDataProvider { + + private final ISysCompanyService sysCompanyService; + private final PublicMapper publicMapper; + private final IHotServiceQualityComplaintService serviceQualityComplaintService; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/securityManage/serviceQualityRecord.html.vm"; + } + + @Override + public String getReportTitle() { + return "安全生产服务质量记录"; + } + + @Override + public Map prepareData(Map params) { + String mode = MapUtil.getStr(params, "mode"); + Long companyId = MapUtil.getLong(params, "companyId"); + + Map ctx = new HashMap<>(); + ctx.put("mode", mode); + + SysCompanyVo company = sysCompanyService.queryById(companyId); + String companyName = ""; + String regionName = ""; + if (company != null) { + companyName = StringUtils.blankToDefault(company.getCompanyName(), ""); + if (StringUtils.isNotBlank(company.getRegionCode())) { + AddressVo addressVo = publicMapper.selectAddressByCode(company.getRegionCode()); + if (addressVo != null) { + regionName = addressVo.getLabel(); + } + } + } + ctx.put("companyName", companyName); + + if ("cover".equals(mode)) { + ctx.put("regionName", regionName); + ctx.put("printYear", DateUtil.format(new Date(), "yyyy")); + } else if ("detail".equals(mode)) { + HotServiceQualityComplaintVo complaint = (HotServiceQualityComplaintVo) params.get("complaint"); + if (complaint != null) { + ctx.put("complaint", complaint); + ctx.put("complaintTargetLabel", mapComplaintTarget(complaint.getComplaintTarget())); + ctx.put("acceptorSignUrl", resolveMaybeOssUrl(complaint.getAcceptorSignImage())); + ctx.put("handlerSignUrl", resolveMaybeOssUrl(complaint.getHandlerSignImage())); + } + } + + return ctx; + } + + private String mapComplaintTarget(String code) { + if (StringUtils.isBlank(code)) { + return ""; + } + if ("1".equals(code)) { + return "人员"; + } + if ("2".equals(code)) { + return "企业"; + } + if ("3".equals(code)) { + return "车辆"; + } + return code; + } + + private String resolveMaybeOssUrl(String value) { + if (StringUtils.isBlank(value)) { + return null; + } + String first = value.split(",")[0].trim(); + if (StringUtils.isBlank(first)) { + return null; + } + if (StringUtils.ishttp(first)) { + return first; + } + if (first.matches("^\\d+$")) { + String urls = ossService.selectUrlByIds(first); + if (StringUtils.isBlank(urls)) { + return null; + } + return urls.split(",")[0].trim(); + } + return first; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/security/StudyDetailProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/StudyDetailProvider.java new file mode 100644 index 0000000..08d3ef9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/StudyDetailProvider.java @@ -0,0 +1,235 @@ +package com.hotwj.platform.reportStatistics.provider.security; + +import com.hotwj.platform.reportStatistics.domain.vo.security.StudyDetailData; +import com.hotwj.platform.reportStatistics.domain.vo.security.StudyRecordCourseVo; +import com.hotwj.platform.reportStatistics.mapper.LedgerReportMapper; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo; +import com.hotwj.platform.securityManagement.training.service.IHotTrainingService; +import com.hotwj.platform.securityManagement.trainingAnswer.domain.vo.HotTrainingAnswerRecordVo; +import com.hotwj.platform.securityManagement.trainingAnswer.mapper.HotTrainingAnswerRecordMapper; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.bo.HotTrainingParticipantBo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.HotTrainingParticipantVo; +import com.hotwj.platform.securityManagement.trainingParticipant.service.IHotTrainingParticipantService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +@RequiredArgsConstructor +public class StudyDetailProvider implements IReportDataProvider { + + private final IHotTrainingService trainingService; + private final ISysCompanyService sysCompanyService; + private final HotTrainingAnswerRecordMapper trainingAnswerRecordMapper; + private final LedgerReportMapper ledgerReportMapper; + private final IHotTrainingParticipantService trainingParticipantService; + + @Override + public String getTemplateCode() { + return "templates/securityManage/studyDetail.html.vm"; + } + + @Override + public String getReportTitle() { + return "在线安全学习考试明细"; + } + + @Override + public Map prepareData(Map params) { + // 1. 初始化上下文 + Map ctx = initContext(params); + + // 2. 参数校验 + Long companyId = (Long) params.get("companyId"); + Long trainingId = (Long) params.get("trainingId"); + String userId = (String) params.get("userId"); + if (companyId == null || trainingId == null || StringUtils.isBlank(userId)) { + return ctx; + } + + // 3. 基础信息查询(培训计划、公司) + HotTrainingVo training = trainingService.queryById(trainingId); + if (training == null) { + return ctx; + } + ctx.put("training", training); + ctx.put("companyName", getCompanyName(companyId)); + + // 4. 构建详情数据 + StudyDetailData data = buildStudyDetailData(companyId, trainingId, userId); + ctx.put("data", data); + + // 5. 设置其他辅助数据 + fillExtraContext(ctx, data); + + return ctx; + } + + /** + * 初始化上下文(二维码等) + */ + private Map initContext(Map params) { + Map ctx = new HashMap<>(); + ctx.put("verifyUrl", params.get("verifyUrl")); + ctx.put("verifyQrBase64", params.get("verifyQrBase64")); + return ctx; + } + + /** + * 获取公司名称 + */ + private String getCompanyName(Long companyId) { + SysCompanyVo company = sysCompanyService.queryById(companyId); + return company != null ? StringUtils.blankToDefault(company.getCompanyName(), "") : ""; + } + + /** + * 构建主数据对象 + */ + private StudyDetailData buildStudyDetailData(Long companyId, Long trainingId, String userId) { + StudyDetailData data = new StudyDetailData(); + + // 1. 设置学员信息 + fillParticipantInfo(data, companyId, trainingId, userId); + + // 2. 设置课程列表 + fillCourseList(data, companyId, trainingId, userId); + + // 3. 设置答题明细及统计 + fillAnswerDetails(data, companyId, trainingId, userId); + + return data; + } + + /** + * 填充学员基本信息 + */ + private void fillParticipantInfo(StudyDetailData data, Long companyId, Long trainingId, String userId) { + HotTrainingParticipantBo participantBo = new HotTrainingParticipantBo(); + participantBo.setCompanyId(companyId); + participantBo.setTrainingId(trainingId); + participantBo.setUserId(userId); + participantBo.setIsDeleted(0L); + List participants = trainingParticipantService.queryList(participantBo); + if (participants != null && !participants.isEmpty()) { + HotTrainingParticipantVo p = participants.get(0); + data.setUserName(StringUtils.blankToDefault(p.getUserName(), "")); + } + } + + /** + * 填充课程列表 + */ + private void fillCourseList(StudyDetailData data, Long companyId, Long trainingId, String userId) { + List courseList = ledgerReportMapper.selectStudyRecordCourses(companyId, trainingId, userId); + if (courseList != null) { + for (StudyRecordCourseVo vo : courseList) { + if (vo.getDuration() != null) { + // 秒 -> 分钟,向上取整 + vo.setDuration((long) Math.ceil(vo.getDuration() / 60.0)); + } + } + } + data.setCourseList(courseList); + } + + /** + * 填充答题明细及统计 + */ + private void fillAnswerDetails(StudyDetailData data, Long companyId, Long trainingId, String userId) { + List detailList = trainingAnswerRecordMapper.selectPlanStudyDetailByTrainingAndUser(companyId, trainingId, userId); + if (detailList == null || detailList.isEmpty()) { + return; + } + + data.setTrainingName(detailList.get(0).getTrainingName()); // 设置计划名称 + data.setPassScore(detailList.get(0).getPassScore()); + + List singleList = new ArrayList<>(); + List multiList = new ArrayList<>(); + List judgeList = new ArrayList<>(); + + int singleCount = 0; + int multiCount = 0; + int judgeCount = 0; + long singleTotalScore = 0; + long multiTotalScore = 0; + long judgeTotalScore = 0; + long totalScore = 0; + long studentGainedScore = 0; + + for (HotTrainingAnswerRecordVo ans : detailList) { + Long type = ans.getQuestionType(); + Long score = ans.getQuestionScore() != null ? ans.getQuestionScore() : 0L; + + if (type != null) { + if (type == 1) { // 单选 + singleCount++; + singleTotalScore += score; + ans.setQuestionOrder((long) (singleList.size() + 1)); + singleList.add(ans); + } else if (type == 2) { // 多选 + multiCount++; + multiTotalScore += score; + ans.setQuestionOrder((long) (multiList.size() + 1)); + multiList.add(ans); + } else if (type == 3) { // 判断 + judgeCount++; + judgeTotalScore += score; + ans.setQuestionOrder((long) (judgeList.size() + 1)); + judgeList.add(ans); + } + } + totalScore += score; + + if (ans.getGainedScore() != null) { + studentGainedScore += ans.getGainedScore(); + } + } + + data.setSingleChoiceList(singleList); + data.setMultiChoiceList(multiList); + data.setJudgeList(judgeList); + + data.setSingleCount(singleCount); + data.setMultiCount(multiCount); + data.setJudgeCount(judgeCount); + data.setSingleTotalScore(singleTotalScore); + data.setMultiTotalScore(multiTotalScore); + data.setJudgeTotalScore(judgeTotalScore); + data.setTotalScore(totalScore); + data.setExamScore(studentGainedScore); + } + + /** + * 填充额外的上下文(兼容旧模板等需求) + */ + private void fillExtraContext(Map ctx, StudyDetailData data) { + if (data.getTotalScore() != null) { + ctx.put("paperTotalScore", data.getTotalScore()); + } + + // 兼容旧模板变量 + // 为了兼容,我们需要重新获取 raw list 或者从 categorized lists 重建, + // 但这里为了简化,我们假设模板已经更新或者我们只传递必要的最少信息。 + // 如果旧模板严重依赖 records 和 r0,可能需要从 data 中反向构建一个临时 list 或者保留原始 detailList + // 考虑到代码清晰度,建议更新模板。但为了保持行为一致: + List allRecords = new ArrayList<>(); + if (data.getSingleChoiceList() != null) allRecords.addAll(data.getSingleChoiceList()); + if (data.getMultiChoiceList() != null) allRecords.addAll(data.getMultiChoiceList()); + if (data.getJudgeList() != null) allRecords.addAll(data.getJudgeList()); + + if (!allRecords.isEmpty()) { + ctx.put("records", allRecords); + ctx.put("r0", allRecords.get(0)); + } + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/security/StudyRecordProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/StudyRecordProvider.java new file mode 100644 index 0000000..d3ed82b --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/StudyRecordProvider.java @@ -0,0 +1,331 @@ +package com.hotwj.platform.reportStatistics.provider.security; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.hotwj.platform.config.courseResource.domain.bo.HotCourseResourceBo; +import com.hotwj.platform.config.courseResource.domain.vo.HotCourseResourceVo; +import com.hotwj.platform.config.courseResource.service.IHotCourseResourceService; +import com.hotwj.platform.config.mediaResource.domain.HotMediaResource; +import com.hotwj.platform.config.mediaResource.mapper.HotMediaResourceMapper; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.reportStatistics.domain.vo.security.StudyRecordCourseVo; +import com.hotwj.platform.reportStatistics.domain.vo.security.StudyRecordData; +import com.hotwj.platform.reportStatistics.mapper.LedgerReportMapper; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo; +import com.hotwj.platform.securityManagement.training.service.IHotTrainingService; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.bo.HotTrainingCourseConfigBo; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.vo.HotTrainingCourseConfigVo; +import com.hotwj.platform.securityManagement.trainingCourseConfig.service.IHotTrainingCourseConfigService; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.bo.HotTrainingCourseRecordBo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.vo.HotTrainingCourseRecordVo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.service.IHotTrainingCourseRecordService; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.bo.HotTrainingParticipantBo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.HotTrainingParticipantVo; +import com.hotwj.platform.securityManagement.trainingParticipant.service.IHotTrainingParticipantService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +@Component +@RequiredArgsConstructor +public class StudyRecordProvider implements IReportDataProvider { + + private final IHotTrainingService trainingService; + private final ISysCompanyService sysCompanyService; + private final IHotTrainingParticipantService trainingParticipantService; + private final LedgerReportMapper ledgerReportMapper; + private final OssService ossService; + private final IHotDriverService driverService; + private final IHotTrainingCourseConfigService trainingCourseConfigService; + private final IHotCourseResourceService courseResourceService; + private final HotMediaResourceMapper mediaResourceMapper; + private final IHotTrainingCourseRecordService trainingCourseRecordService; + + @Override + public String getTemplateCode() { + return "templates/securityManage/studyRecord.html.vm"; + } + + @Override + public String getReportTitle() { + return "学习记录表"; + } + + @Override + public String getReportTitle(Map params) { + Long trainingId = MapUtil.getLong(params, "trainingId"); + if (trainingId != null) { + HotTrainingVo training = trainingService.queryById(trainingId); + if (training != null) { + return training.getPlanName() + "——学习记录表"; + } + } + return getReportTitle(); + } + + @Override + public Map prepareData(Map params) { + Map ctx = initContext(params); + Long companyId = MapUtil.getLong(params, "companyId"); + Long trainingId = MapUtil.getLong(params, "trainingId"); + String userId = MapUtil.getStr(params, "userId"); + if (companyId == null || trainingId == null || StringUtils.isBlank(userId)) { + return ctx; + } + + HotTrainingVo training = trainingService.queryById(trainingId); + if (training == null) { + return ctx; + } + + ctx.put("training", training); + ctx.put("companyName", getCompanyName(companyId)); + + String trainingContent = buildTrainingContent(trainingId); + StudyRecordData record = buildStudyRecord(companyId, trainingId, userId, trainingContent); + ctx.put("record", record); + return ctx; + } + + private StudyRecordData buildStudyRecord(Long companyId, Long trainingId, String userId, String trainingContent) { + StudyRecordData record = new StudyRecordData(); + record.setUserId(userId); + record.setCourseName(trainingContent); + + HotTrainingParticipantVo participant = getTrainingParticipant(companyId, trainingId, userId); + if (participant != null) { + record.setUserName(StringUtils.blankToDefault(participant.getUserName(), "")); + record.setIdCardNo(StringUtils.blankToDefault(participant.getIdCardNo(), "")); + record.setPhotoUrls(parsePhotoUrls(participant.getStudyPhotoOssId())); + record.setSignatureUrl(resolveMaybeOssUrl(participant.getSignatureOssId() == null ? null : String.valueOf(participant.getSignatureOssId()))); + } else { + record.setUserName(""); + record.setIdCardNo(""); + record.setPhotoUrls(List.of()); + record.setSignatureUrl(""); + } + + record.setPlateNumber(getPlateNumber(companyId, userId)); + + List courseList = ledgerReportMapper.selectStudyRecordCourses(companyId, trainingId, userId); + record.setCourseList(courseList); + + // 获取课程配置时长映射 (ConfigID -> Seconds) + Map configDurationMap = getConfigDurationMap(trainingId); + + Integer learnMinutes = computeLearnMinutes(companyId, trainingId, userId, configDurationMap); + Integer planMinutes = computePlanMinutes(configDurationMap); + record.setLearnMinutes(learnMinutes); + record.setPlanMinutes(planMinutes); + record.setStudyMinutes(learnMinutes); + return record; + } + + private Map getConfigDurationMap(Long trainingId) { + HotTrainingCourseConfigBo bo = new HotTrainingCourseConfigBo(); + bo.setTrainingId(trainingId); + List configs = trainingCourseConfigService.queryList(bo); + if (configs == null || configs.isEmpty()) { + return Collections.emptyMap(); + } + Map map = new HashMap<>(); + for (HotTrainingCourseConfigVo c : configs) { + long seconds = calculateCourseVideoSeconds(c.getCourseId()); + map.put(c.getId(), seconds); + } + return map; + } + + private Map initContext(Map params) { + Map ctx = new HashMap<>(); + ctx.put("verifyUrl", MapUtil.getStr(params, "verifyUrl")); + ctx.put("verifyQrBase64", MapUtil.getStr(params, "verifyQrBase64")); + return ctx; + } + + private String getCompanyName(Long companyId) { + SysCompanyVo company = sysCompanyService.queryById(companyId); + if (company == null) { + return ""; + } + return StringUtils.blankToDefault(company.getCompanyName(), ""); + } + + private String buildTrainingContent(Long trainingId) { + List courseNames = ledgerReportMapper.selectGovSafetyEducationTrainingCourseNames(trainingId); + if (courseNames == null || courseNames.isEmpty()) { + return ""; + } + return courseNames.stream() + .filter(Objects::nonNull) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.joining("\n")); + } + + private HotTrainingParticipantVo getTrainingParticipant(Long companyId, Long trainingId, String userId) { + HotTrainingParticipantBo bo = new HotTrainingParticipantBo(); + bo.setCompanyId(companyId); + bo.setTrainingId(trainingId); + bo.setUserId(userId); + bo.setIsDeleted(0L); + List participants = trainingParticipantService.queryList(bo); + if (participants == null || participants.isEmpty()) { + return null; + } + return participants.get(0); + } + + private List parsePhotoUrls(String photoJson) { + if (StringUtils.isBlank(photoJson)) { + return List.of(); + } + List urls = new ArrayList<>(); + try { + JSONObject json = JSONUtil.parseObj(photoJson); + if (!json.isEmpty()) { + for (Object val : json.values()) { + String url = resolveMaybeOssUrl(String.valueOf(val)); + if (StringUtils.isNotBlank(url)) { + urls.add(url); + } + } + } + } catch (Exception e) { + for (String s : photoJson.split(",")) { + String url = resolveMaybeOssUrl(s); + if (StringUtils.isNotBlank(url)) { + urls.add(url); + } + } + } + return urls; + } + + private String getPlateNumber(Long companyId, String userId) { + HotDriverVo driver = driverService.queryById(userId); + if (driver != null && Objects.equals(companyId, driver.getCompanyId())) { + return StringUtils.blankToDefault(driver.getPlateNumber(), ""); + } + return ""; + } + + private Integer computeLearnMinutes(Long companyId, Long trainingId, String userId, Map configDurationMap) { + HotTrainingCourseRecordBo bo = new HotTrainingCourseRecordBo(); + bo.setCompanyId(companyId); + bo.setTrainingId(trainingId); + bo.setUserId(userId); + bo.setIsDeleted(0L); + List records = trainingCourseRecordService.queryList(bo); + if (records == null || records.isEmpty()) { + return 0; + } + + Map maxByCourse = new HashMap<>(); + for (HotTrainingCourseRecordVo r : records) { + if (r.getTrainingCourseId() == null || r.getLearnDurationMin() == null) { + continue; + } + maxByCourse.merge(r.getTrainingCourseId(), r.getLearnDurationMin(), Math::max); + } + long totalSeconds = 0L; + for (Map.Entry entry : maxByCourse.entrySet()) { + Long configId = entry.getKey(); + Long actualSeconds = entry.getValue(); + if (actualSeconds == null) continue; + + // 获取该课程配置的计划时长(秒) + Long planSeconds = configDurationMap.getOrDefault(configId, 0L); + + // 取较小值,确保学习时长不大于课程时长 + long cappedSeconds = Math.min(actualSeconds, planSeconds); + totalSeconds += cappedSeconds; + } + // 数据库中存储的learnDurationMin实际上是秒,需要转换为分钟 + long minutes = (long) Math.ceil(totalSeconds / 60.0); + if (minutes > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + return (int) minutes; + } + + private Integer computePlanMinutes(Map configDurationMap) { + if (configDurationMap == null || configDurationMap.isEmpty()) { + return 0; + } + long totalSeconds = 0L; + for (Long seconds : configDurationMap.values()) { + if (seconds != null) { + totalSeconds += seconds; + } + } + long minutes = (long) Math.ceil(totalSeconds / 60.0); + if (minutes > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + return (int) minutes; + } + + private long calculateCourseVideoSeconds(Long courseId) { + if (courseId == null) { + return 0L; + } + HotCourseResourceBo resourceBo = new HotCourseResourceBo(); + resourceBo.setCourseId(courseId); + resourceBo.setResourceType(1L); + List resources = courseResourceService.queryList(resourceBo); + if (resources == null || resources.isEmpty()) { + return 0L; + } + + List mediaIds = resources.stream() + .map(HotCourseResourceVo::getResourceId) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (mediaIds.isEmpty()) { + return 0L; + } + + return mediaResourceMapper.selectList( + Wrappers.lambdaQuery() + .select(HotMediaResource::getDurationSeconds) + .in(HotMediaResource::getId, mediaIds) + .eq(HotMediaResource::getIsDeleted, 0L) + ).stream() + .map(HotMediaResource::getDurationSeconds) + .filter(Objects::nonNull) + .mapToLong(Long::longValue) + .sum(); + } + + private String resolveMaybeOssUrl(String value) { + if (StringUtils.isBlank(value)) { + return null; + } + String first = value.split(",")[0].trim(); + if (StringUtils.isBlank(first)) { + return null; + } + if (first.startsWith("http")) { + return first; + } + if (first.matches("^\\d+$")) { + String urls = ossService.selectUrlByIds(first); + if (StringUtils.isBlank(urls)) { + return null; + } + return urls.split(",")[0].trim(); + } + return first; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/security/TrainingPlanMonthlyStatProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/TrainingPlanMonthlyStatProvider.java new file mode 100644 index 0000000..6fe6bcf --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/TrainingPlanMonthlyStatProvider.java @@ -0,0 +1,280 @@ +package com.hotwj.platform.reportStatistics.provider.security; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.hotwj.platform.config.courseResource.domain.bo.HotCourseResourceBo; +import com.hotwj.platform.config.courseResource.domain.vo.HotCourseResourceVo; +import com.hotwj.platform.config.courseResource.service.IHotCourseResourceService; +import com.hotwj.platform.config.mediaResource.domain.HotMediaResource; +import com.hotwj.platform.config.mediaResource.mapper.HotMediaResourceMapper; +import com.hotwj.platform.reportStatistics.domain.vo.OnlineTrainingMonthlyRowVo; +import com.hotwj.platform.reportStatistics.domain.vo.security.OnlineTrainingMonthlyRow; +import com.hotwj.platform.reportStatistics.mapper.LedgerReportMapper; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo; +import com.hotwj.platform.securityManagement.training.service.IHotTrainingService; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.bo.HotTrainingCourseConfigBo; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.vo.HotTrainingCourseConfigVo; +import com.hotwj.platform.securityManagement.trainingCourseConfig.service.IHotTrainingCourseConfigService; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.TrainingParticipantStatVo; +import com.hotwj.platform.securityManagement.trainingParticipant.service.IHotTrainingParticipantService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +@Component +@RequiredArgsConstructor +public class TrainingPlanMonthlyStatProvider implements IReportDataProvider { + + private final IHotTrainingService trainingService; + private final ISysCompanyService sysCompanyService; + private final LedgerReportMapper ledgerReportMapper; + private final OssService ossService; + private final IHotTrainingParticipantService hotTrainingParticipantService; + private final IHotTrainingCourseConfigService hotTrainingCourseConfigService; + private final IHotCourseResourceService hotCourseResourceService; + private final HotMediaResourceMapper hotMediaResourceMapper; + + @Override + public String getTemplateCode() { + return "templates/securityManage/onlineEducationMonthlyStatistics.html.vm"; + } + + @Override + public String getReportTitle() { + return "线上教育培训月度统计表"; + } + + @Override + public Map prepareData(Map params) { + Map ctx = initContext(params); + Long companyId = MapUtil.getLong(params, "companyId"); + Long trainingId = MapUtil.getLong(params, "trainingId"); + if (companyId == null || trainingId == null) { + return ctx; + } + + HotTrainingVo training = trainingService.queryById(trainingId); + if (training == null || !Objects.equals(training.getCompanyId(), companyId)) { + return ctx; + } + + // 完善培训统计数据 + enrichTrainingStats(trainingId, training); + ctx.put("training", training); + putCompanyInfo(ctx, companyId); + + List normalizedUserIds = normalizeUserIds((List) params.get("userIds")); + // 计算学员学习进度 + Map progressMap = hotTrainingParticipantService.calculateProgressBatch(trainingId, companyId, normalizedUserIds); + ctx.put("trainingCourses", buildTrainingCourses(trainingId)); + ctx.put("trainingContent", buildTrainingContent(trainingId)); + + List voRows = ledgerReportMapper.selectOnlineTrainingMonthlyRows(companyId, trainingId, normalizedUserIds); + if (voRows == null || voRows.isEmpty()) { + return ctx; + } + + ctx.put("completedCount", countCompleted(voRows)); + ctx.put("rows", buildRows(voRows, progressMap)); + return ctx; + } + + private Map initContext(Map params) { + Map ctx = new HashMap<>(); + ctx.put("verifyUrl", MapUtil.getStr(params, "verifyUrl")); + ctx.put("verifyQrBase64", MapUtil.getStr(params, "verifyQrBase64")); + return ctx; + } + + private void enrichTrainingStats(Long trainingId, HotTrainingVo training) { + List stats = hotTrainingParticipantService.queryTrainingParticipantStats(List.of(trainingId)); + if (stats != null && !stats.isEmpty()) { + TrainingParticipantStatVo stat = stats.get(0); + training.setTotalCount(stat.getTotalCount()); + training.setPassedCount(stat.getPassedCount()); + training.setIncompleteCount(stat.getIncompleteCount()); + training.setCompleteCount(stat.getCompleteCount()); + training.setPendingReviewCount(stat.getPendingReviewCount()); + return; + } + training.setTotalCount(0L); + training.setPassedCount(0L); + training.setCompleteCount(0L); + training.setIncompleteCount(0L); + training.setPendingReviewCount(0L); + } + + private void putCompanyInfo(Map ctx, Long companyId) { + SysCompanyVo company = sysCompanyService.queryById(companyId); + String companyName = ""; + String responsibleName = ""; + if (company != null) { + companyName = StringUtils.blankToDefault(company.getCompanyName(), ""); + responsibleName = StringUtils.blankToDefault(company.getResponsibleName(), ""); + } + ctx.put("companyName", companyName); + ctx.put("responsibleName", responsibleName); + } + + private List normalizeUserIds(List userIds) { + if (userIds == null || userIds.isEmpty()) { + return null; + } + LinkedHashSet set = new LinkedHashSet<>(); + for (String id : userIds) { + String t = id == null ? "" : id.trim(); + if (!t.isEmpty()) { + set.add(t); + } + } + if (set.isEmpty()) { + return null; + } + return new ArrayList<>(set); + } + + private List> buildTrainingCourses(Long trainingId) { + HotTrainingCourseConfigBo courseConfigBo = new HotTrainingCourseConfigBo(); + courseConfigBo.setTrainingId(trainingId); + List courseConfigs = hotTrainingCourseConfigService.queryList(courseConfigBo); + if (courseConfigs == null || courseConfigs.isEmpty()) { + return List.of(); + } + + List> items = new ArrayList<>(courseConfigs.size()); + int idx = 1; + for (HotTrainingCourseConfigVo c : courseConfigs) { + Map item = new HashMap<>(); + item.put("index", idx++); + item.put("courseName", StringUtils.blankToDefault(c.getCourseName(), "")); + item.put("durationMin", getCourseVideoMinutes(c.getCourseId())); + items.add(item); + } + return items; + } + + private long getCourseVideoMinutes(Long courseId) { + if (courseId == null) { + return 0L; + } + HotCourseResourceBo resourceBo = new HotCourseResourceBo(); + resourceBo.setCourseId(courseId); + resourceBo.setResourceType(1L); + List resources = hotCourseResourceService.queryList(resourceBo); + if (resources == null || resources.isEmpty()) { + return 0L; + } + + List mediaIds = resources.stream() + .map(HotCourseResourceVo::getResourceId) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (mediaIds.isEmpty()) { + return 0L; + } + + long seconds = hotMediaResourceMapper.selectList( + Wrappers.lambdaQuery() + .select(HotMediaResource::getDurationSeconds) + .in(HotMediaResource::getId, mediaIds) + .eq(HotMediaResource::getIsDeleted, 0L) + ).stream() + .map(HotMediaResource::getDurationSeconds) + .filter(Objects::nonNull) + .mapToLong(Long::longValue) + .sum(); + return (seconds + 59) / 60; + } + + private String buildTrainingContent(Long trainingId) { + List courseNames = ledgerReportMapper.selectGovSafetyEducationTrainingCourseNames(trainingId); + if (courseNames == null || courseNames.isEmpty()) { + return ""; + } + return courseNames.stream() + .filter(Objects::nonNull) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.joining("\n")); + } + + private long countCompleted(List rows) { + return rows.stream().filter(r -> r.getCompleteTime() != null).count(); + } + + private List buildRows(List voRows, Map progressMap) { + List rows = new ArrayList<>(voRows.size()); + for (OnlineTrainingMonthlyRowVo vo : voRows) { + OnlineTrainingMonthlyRow row = new OnlineTrainingMonthlyRow(); + row.setIndex(vo.getIndex() != null ? vo.getIndex() : 0); + row.setUserName(StringUtils.blankToDefault(vo.getUserName(), "")); + row.setPlateNumber(StringUtils.blankToDefault(vo.getPlateNumber(), "")); + row.setProgressLabel(StringUtils.blankToDefault(vo.getProgressLabel(), "")); + row.setPhone(StringUtils.blankToDefault(vo.getPhone(), "")); + row.setPhotoUrl(extractPhotoUrl(vo.getPhotoUrl())); + row.setSignatureUrl(extractSignatureUrl(vo.getSignatureUrl())); + row.setExamStatus(StringUtils.blankToDefault(vo.getExamStatus(), "")); + rows.add(row); + } + return rows; + } + + + private String extractPhotoUrl(String raw) { + if (StringUtils.isBlank(raw)) { + return ""; + } + try { + JSONObject json = JSONUtil.parseObj(raw); + if (!json.isEmpty()) { + Object[] values = json.values().toArray(); + if (values.length > 0) { + String lastOssId = String.valueOf(values[values.length - 1]); + String resolved = resolveMaybeOssUrl(lastOssId); + return resolved == null ? "" : resolved; + } + } + } catch (Exception ignored) { + } + String resolved = resolveMaybeOssUrl(raw); + return resolved == null ? "" : resolved; + } + + private String extractSignatureUrl(String raw) { + if (StringUtils.isBlank(raw)) { + return ""; + } + String resolved = resolveMaybeOssUrl(raw); + return resolved == null ? "" : resolved; + } + + private String resolveMaybeOssUrl(String value) { + if (StringUtils.isBlank(value)) { + return null; + } + String first = value.split(",")[0].trim(); + if (StringUtils.isBlank(first)) { + return null; + } + if (first.startsWith("http")) { + return first; + } + if (first.matches("^\\d+$")) { + String urls = ossService.selectUrlByIds(first); + if (StringUtils.isBlank(urls)) { + return null; + } + return urls.split(",")[0].trim(); + } + return first; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/security/ViolationHandlingProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/ViolationHandlingProvider.java new file mode 100644 index 0000000..307d21b --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/ViolationHandlingProvider.java @@ -0,0 +1,116 @@ +package com.hotwj.platform.reportStatistics.provider.security; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import com.hotwj.platform.common.mapper.PublicMapper; +import com.hotwj.platform.common.vo.AddressVo; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.securityManagement.violationInfo.domain.vo.HotViolationInfoVo; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.*; + +@Component +@RequiredArgsConstructor +public class ViolationHandlingProvider implements IReportDataProvider { + + private final ISysCompanyService sysCompanyService; + private final PublicMapper publicMapper; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/securityManage/violationHandlingRecord.html.vm"; + } + + @Override + public String getReportTitle() { + return "违法违章处理记录"; + } + + @Override + public Map prepareData(Map params) { + String mode = MapUtil.getStr(params, "mode"); + Long companyId = MapUtil.getLong(params, "companyId"); + + Map ctx = new HashMap<>(); + ctx.put("mode", mode); + + SysCompanyVo company = sysCompanyService.queryById(companyId); + String companyName = ""; + String regionName = ""; + if (company != null) { + companyName = StringUtils.blankToDefault(company.getCompanyName(), ""); + if (StringUtils.isNotBlank(company.getRegionCode())) { + AddressVo addressVo = publicMapper.selectAddressByCode(company.getRegionCode()); + if (addressVo != null) { + regionName = addressVo.getLabel(); + } + } + } + ctx.put("companyName", companyName); + + if ("cover".equals(mode)) { + ctx.put("regionName", regionName); + ctx.put("printYear", DateUtil.format(new Date(), "yyyy")); + } else if ("detail".equals(mode)) { + HotViolationInfoVo info = (HotViolationInfoVo) params.get("info"); + if (info != null) { + ctx.put("info", info); + ctx.put("recordTime", info.getOccurTime()); + ctx.put("responsibleSignatureUrl", resolveMaybeOssUrl(info.getResponsibleSignatureImage())); + ctx.put("partySignatureUrl", resolveMaybeOssUrl(info.getPartySignatureImage())); + } + } else if ("attachment".equals(mode)) { + HotViolationInfoVo info = (HotViolationInfoVo) params.get("info"); + if (info != null) { + String promise = ossService.selectUrlByIds(info.getPromiseUrls()); + String photos = ossService.selectUrlByIds(info.getInterviewPhotoUrls()); + ctx.put("promiseImages", splitUrls(promise)); + ctx.put("interviewImages", splitUrls(photos)); + } + } + + return ctx; + } + + private String resolveMaybeOssUrl(String value) { + if (StringUtils.isBlank(value)) { + return null; + } + String first = value.split(",")[0].trim(); + if (StringUtils.isBlank(first)) { + return null; + } + if (StringUtils.ishttp(first)) { + return first; + } + if (first.matches("^\\d+$")) { + String urls = ossService.selectUrlByIds(first); + if (StringUtils.isBlank(urls)) { + return null; + } + return urls.split(",")[0].trim(); + } + return first; + } + + private List splitUrls(String urls) { + List result = new ArrayList<>(); + if (StringUtils.isBlank(urls)) { + return result; + } + for (String s : urls.split(",")) { + String t = s == null ? "" : s.trim(); + if (!t.isEmpty()) { + result.add(t); + } + } + return result; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/security/ViolationInvestigationProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/ViolationInvestigationProvider.java new file mode 100644 index 0000000..13d8c5d --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/security/ViolationInvestigationProvider.java @@ -0,0 +1,79 @@ +package com.hotwj.platform.reportStatistics.provider.security; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import com.hotwj.platform.common.mapper.PublicMapper; +import com.hotwj.platform.common.vo.AddressVo; +import com.hotwj.platform.reportStatistics.domain.vo.GovViolationInfoDetailVo; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +@RequiredArgsConstructor +public class ViolationInvestigationProvider implements IReportDataProvider { + + private final ISysCompanyService sysCompanyService; + private final PublicMapper publicMapper; + + @Override + public String getTemplateCode() { + return "templates/securityManage/violationInvestigationRecord.html.vm"; + } + + @Override + public boolean isLandscape() { + return true; + } + + @Override + public String getReportTitle() { + return "违法违章信息排查记录"; + } + + @Override + public Map prepareData(Map params) { + String mode = MapUtil.getStr(params, "mode"); + Long companyId = MapUtil.getLong(params, "companyId"); + + Map ctx = new HashMap<>(); + ctx.put("mode", mode); + + SysCompanyVo company = sysCompanyService.queryById(companyId); + String companyName = ""; + String regionName = ""; + if (company != null) { + companyName = StringUtils.blankToDefault(company.getCompanyName(), ""); + if (StringUtils.isNotBlank(company.getRegionCode())) { + AddressVo addressVo = publicMapper.selectAddressByCode(company.getRegionCode()); + if (addressVo != null) { + regionName = addressVo.getLabel(); + } + } + } + ctx.put("companyName", companyName); + + if ("cover".equals(mode)) { + ctx.put("regionName", regionName); + ctx.put("printYear", DateUtil.format(new Date(), "yyyy")); + } else if ("detail".equals(mode)) { + String managerName = MapUtil.getStr(params, "managerName"); + String investigateDate = MapUtil.getStr(params, "investigateDate"); + List details = (List) params.get("details"); + + ctx.put("managerName", StringUtils.blankToDefault(managerName, "")); + ctx.put("investigateDate", investigateDate); + ctx.put("details", details); + } + + return ctx; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleBaseInfoProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleBaseInfoProvider.java new file mode 100644 index 0000000..d6a6e80 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleBaseInfoProvider.java @@ -0,0 +1,80 @@ +package com.hotwj.platform.reportStatistics.provider.vehicle; + +import cn.hutool.core.map.MapUtil; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.reportStatistics.service.IVehicleFilePrintService; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * 车辆基本信息记录数据提供者 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class VehicleBaseInfoProvider implements IReportDataProvider { + + private final IHotVehicleService hotVehicleService; + private final ISysCompanyService sysCompanyService; + + @Override + public String getTemplateCode() { + return "templates/vehicle/baseInfo.html.vm"; + } + + @Override + public String getReportType() { + return IVehicleFilePrintService.TYPE_BASE_INFO; + } + + @Override + public String getReportTitle() { + return "公司车辆基本信息记录"; + } + + @Override + public Map prepareData(Map params) { + Long vehicleId = MapUtil.getLong(params, "vehicleId"); + if (vehicleId == null) { + return new HashMap<>(); + } + + HotVehicleVo vehicle = hotVehicleService.queryById(vehicleId); + if (vehicle == null) { + return new HashMap<>(); + } + + Map ctx = new HashMap<>(); + ctx.put("vehicle", vehicle); + + SysCompanyVo company = vehicle.getCompanyId() != null ? sysCompanyService.queryById(vehicle.getCompanyId()) : null; + if (company != null) { + ctx.put("company", company); + } + + // 处理组合字段 + // 外廓尺寸(mm) + String outerDimensions = String.format("%s*%s*%s", + vehicle.getVehicleLengthMm() != null ? vehicle.getVehicleLengthMm() : "", + vehicle.getVehicleWidthMm() != null ? vehicle.getVehicleWidthMm() : "", + vehicle.getVehicleHeightMm() != null ? vehicle.getVehicleHeightMm() : ""); + ctx.put("outerDimensions", outerDimensions); + + // 货箱内部尺寸(mm) + String cargoInnerDimensions = String.format("%s*%s*%s", + vehicle.getCargoInnerLengthMm() != null ? vehicle.getCargoInnerLengthMm() : "", + vehicle.getCargoInnerWidthMm() != null ? vehicle.getCargoInnerWidthMm() : "", + vehicle.getCargoInnerHeightMm() != null ? vehicle.getCargoInnerHeightMm() : ""); + ctx.put("cargoInnerDimensions", cargoInnerDimensions); + + return ctx; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleClaimRecordProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleClaimRecordProvider.java new file mode 100644 index 0000000..ec3fea7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleClaimRecordProvider.java @@ -0,0 +1,146 @@ +package com.hotwj.platform.reportStatistics.provider.vehicle; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.reportStatistics.service.IVehicleFilePrintService; +import com.hotwj.platform.resourceManagement.vehicleClaim.domain.bo.HotVehicleClaimBo; +import com.hotwj.platform.resourceManagement.vehicleClaim.domain.vo.HotVehicleClaimVo; +import com.hotwj.platform.resourceManagement.vehicleClaim.service.IHotVehicleClaimService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.springframework.stereotype.Component; + +import java.util.*; + +/** + * 车辆出险记录数据提供者 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class VehicleClaimRecordProvider implements IReportDataProvider { + + private final IHotVehicleClaimService claimService; + private final IHotVehicleService vehicleService; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/vehicle/claimRecord.html.vm"; + } + + @Override + public String getReportType() { + return IVehicleFilePrintService.TYPE_CLAIM_RECORD; + } + + @Override + public String getReportTitle() { + return "车辆出险记录"; + } + + @Override + public Map prepareData(Map params) { + Long vehicleId = MapUtil.getLong(params, "vehicleId"); + if (vehicleId == null) { + return new HashMap<>(); + } + + HotVehicleVo vehicle = vehicleService.queryById(vehicleId); + + String beginTime = MapUtil.getStr(params, "beginTime"); + String endTime = MapUtil.getStr(params, "endTime"); + Integer printEmptyData = MapUtil.getInt(params, "printEmptyData"); + boolean allowEmpty = printEmptyData == null || printEmptyData == 1; + Date startDate = null; + Date endDate = null; + if (StrUtil.isNotBlank(beginTime)) { + startDate = DateUtil.beginOfDay(DateUtil.parse(beginTime, "yyyy-MM-dd")); + } + if (StrUtil.isNotBlank(endTime)) { + endDate = DateUtil.endOfDay(DateUtil.parse(endTime, "yyyy-MM-dd")); + } + + HotVehicleClaimBo bo = new HotVehicleClaimBo(); + bo.setVehicleId(String.valueOf(vehicleId)); + bo.setIsDeleted(0L); + List list = claimService.queryList(bo); + + if (CollUtil.isNotEmpty(list) && (startDate != null || endDate != null)) { + Date finalStartDate = startDate; + Date finalEndDate = endDate; + list = list.stream() + .filter(item -> { + Date recordDate = item.getIncidentTime(); + if (recordDate == null) { + return false; + } + if (finalStartDate != null && recordDate.before(finalStartDate)) { + return false; + } + return finalEndDate == null || !recordDate.after(finalEndDate); + }) + .toList(); + } + + if (CollUtil.isEmpty(list)) { + if (!allowEmpty) { + return null; + } + list = new ArrayList<>(); + // 创建一个空对象,确保能渲染出一页 + HotVehicleClaimVo emptyRecord = new HotVehicleClaimVo(); + list.add(emptyRecord); + } else { + list = new ArrayList<>(list); + // 按照登记日期incidentTime降序排列 + list.sort((o1, o2) -> { + if (o1.getIncidentTime() == null && o2.getIncidentTime() == null) { + return 0; + } + if (o1.getIncidentTime() == null) { + return 1; + } + if (o2.getIncidentTime() == null) { + return -1; + } + return o2.getIncidentTime().compareTo(o1.getIncidentTime()); + }); + + // 合并所有记录的图片 + for (HotVehicleClaimVo vo : list) { + List recordImages = splitUrls(ossService.selectUrlByIds(vo.getImageUrls())); + vo.setImages(recordImages); + } + } + + Map ctx = new HashMap<>(); + ctx.put("vehicle", vehicle); + + // 放入列表数据 + ctx.put("records", list); + + return ctx; + } + + private List splitUrls(String urls) { + if (StrUtil.isBlank(urls)) { + return new ArrayList<>(); + } + List list = new ArrayList<>(); + String[] parts = urls.split(","); + for (String p : parts) { + String v = p.trim(); + if (StrUtil.isNotBlank(v)) { + list.add(v); + } + } + return list; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleInspectionReportProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleInspectionReportProvider.java new file mode 100644 index 0000000..eedb9cd --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleInspectionReportProvider.java @@ -0,0 +1,109 @@ +package com.hotwj.platform.reportStatistics.provider.vehicle; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.reportStatistics.service.IVehicleFilePrintService; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.vo.HotVehicleAnnualReviewVo; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.service.IHotVehicleAnnualReviewService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.springframework.stereotype.Component; + +import java.util.*; + +/** + * 车辆年审报告数据提供者 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class VehicleInspectionReportProvider implements IReportDataProvider { + + private final IHotVehicleAnnualReviewService annualReviewService; + private final IHotVehicleService vehicleService; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/vehicle/inspectionReport.html.vm"; + } + + @Override + public String getReportType() { + return IVehicleFilePrintService.TYPE_INSPECTION_REPORT; + } + + @Override + public String getReportTitle() { + return "安全技术检测报告"; + } + + @Override + public Map prepareData(Map params) { + Long vehicleId = MapUtil.getLong(params, "vehicleId"); + if (vehicleId == null) { + return new HashMap<>(); + } + + HotVehicleVo vehicle = vehicleService.queryById(vehicleId); + + HotVehicleAnnualReviewVo review = null; + Object reviewObj = params.get("review"); + if (reviewObj instanceof HotVehicleAnnualReviewVo) { + review = (HotVehicleAnnualReviewVo) reviewObj; + } + if (review == null) { + String inspectionStartDate = MapUtil.getStr(params, "inspectionStartDate"); + String inspectionEndDate = MapUtil.getStr(params, "inspectionEndDate"); + Date startDate = null; + Date endDate = null; + if (StrUtil.isNotBlank(inspectionStartDate)) { + startDate = DateUtil.beginOfDay(DateUtil.parse(inspectionStartDate, "yyyy-MM-dd")); + } + if (StrUtil.isNotBlank(inspectionEndDate)) { + endDate = DateUtil.endOfDay(DateUtil.parse(inspectionEndDate, "yyyy-MM-dd")); + } + + if (startDate != null || endDate != null) { + review = annualReviewService.queryLatestByVehicleIdAndReviewDateRange(String.valueOf(vehicleId), startDate, endDate); + } else { + review = annualReviewService.queryLatestByVehicleId(String.valueOf(vehicleId)); + } + } + + + Map ctx = new HashMap<>(); + + ctx.put("vehicle", vehicle); + ctx.put("review", review); + + List images = new ArrayList<>(); + if (review != null) { + List strings = splitUrls(ossService.selectUrlByIds(review.getImageUrls())); + images.addAll(strings); + } + ctx.put("images", images); + + return ctx; + } + + private List splitUrls(String urls) { + if (StrUtil.isBlank(urls)) { + return new ArrayList<>(); + } + List list = new ArrayList<>(); + String[] parts = urls.split(","); + for (String p : parts) { + String v = p.trim(); + if (StrUtil.isNotBlank(v)) { + list.add(v); + } + } + return list; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleLicenseProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleLicenseProvider.java new file mode 100644 index 0000000..2d14963 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleLicenseProvider.java @@ -0,0 +1,90 @@ +package com.hotwj.platform.reportStatistics.provider.vehicle; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.reportStatistics.service.IVehicleFilePrintService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 车辆证件信息(行驶证、运输证)数据提供者 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class VehicleLicenseProvider implements IReportDataProvider { + + private final IHotVehicleService hotVehicleService; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/vehicle/licenseInfo.html.vm"; + } + + @Override + public String getReportType() { + return IVehicleFilePrintService.TYPE_LICENSE; + } + + @Override + public String getReportTitle() { + return "车辆行驶证、运输证"; + } + + @Override + public Map prepareData(Map params) { + Long vehicleId = MapUtil.getLong(params, "vehicleId"); + if (vehicleId == null) { + return new HashMap<>(); + } + + HotVehicleVo vehicle = hotVehicleService.queryById(vehicleId); + if (vehicle == null) { + return new HashMap<>(); + } + + Map ctx = new HashMap<>(); + ctx.put("vehicle", vehicle); + + // Images + Map> images = new HashMap<>(); + // 主页 + images.put("dlMain", splitUrls(ossService.selectUrlByIds(vehicle.getDrivingLicenseMainUrls()))); + // 副页 + images.put("dlAux", splitUrls(ossService.selectUrlByIds(vehicle.getDrivingLicenseAuxUrls()))); + images.put("dlOther", splitUrls(ossService.selectUrlByIds(vehicle.getDrivingLicenseOtherUrl()))); + + // Transport License Images + images.put("tl", splitUrls(ossService.selectUrlByIds(vehicle.getTransportLicenseUrls()))); + + ctx.put("images", images); + + return ctx; + } + + private List splitUrls(String urls) { + if (StrUtil.isBlank(urls)) { + return new ArrayList<>(); + } + List list = new ArrayList<>(); + String[] parts = urls.split(","); + for (String p : parts) { + String v = p.trim(); + if (StrUtil.isNotBlank(v)) { + list.add(v); + } + } + return list; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleMaintenanceRecordProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleMaintenanceRecordProvider.java new file mode 100644 index 0000000..d05d6ac --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleMaintenanceRecordProvider.java @@ -0,0 +1,134 @@ +package com.hotwj.platform.reportStatistics.provider.vehicle; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.reportStatistics.service.IVehicleFilePrintService; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.bo.HotVehicleMaintenanceBo; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.vo.HotVehicleMaintenanceVo; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.service.IHotVehicleMaintenanceService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.OssService; +import org.springframework.stereotype.Component; + +import java.util.*; + +/** + * 车辆二级维护记录数据提供者 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class VehicleMaintenanceRecordProvider implements IReportDataProvider { + + private final IHotVehicleMaintenanceService maintenanceService; + private final IHotVehicleService vehicleService; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/vehicle/maintenanceRecord.html.vm"; + } + + @Override + public String getReportType() { + return IVehicleFilePrintService.TYPE_MAINTENANCE_RECORD; + } + + @Override + public String getReportTitle() { + return "二级维护记录"; + } + + @Override + public Map prepareData(Map params) { + Long vehicleId = MapUtil.getLong(params, "vehicleId"); + if (vehicleId == null) { + return new HashMap<>(); + } + + HotVehicleVo vehicle = vehicleService.queryById(vehicleId); + + String maintenanceStartDate = MapUtil.getStr(params, "maintenanceStartDate"); + String maintenanceEndDate = MapUtil.getStr(params, "maintenanceEndDate"); + Integer printEmptyData = MapUtil.getInt(params, "printEmptyData"); + boolean allowEmpty = printEmptyData == null || printEmptyData == 1; + Date startDate = null; + Date endDate = null; + if (StrUtil.isNotBlank(maintenanceStartDate)) { + startDate = DateUtil.beginOfDay(DateUtil.parse(maintenanceStartDate, "yyyy-MM-dd")); + } + if (StrUtil.isNotBlank(maintenanceEndDate)) { + endDate = DateUtil.endOfDay(DateUtil.parse(maintenanceEndDate, "yyyy-MM-dd")); + } + + HotVehicleMaintenanceBo bo = new HotVehicleMaintenanceBo(); + bo.setVehicleId(String.valueOf(vehicleId)); + // 这里可以根据需要过滤最近一次或特定记录,目前假设打印最近一次 + List list = maintenanceService.queryList(bo); + + Map ctx = new HashMap<>(); + ctx.put("vehicle", vehicle); + + if (CollUtil.isNotEmpty(list) && (startDate != null || endDate != null)) { + Date finalStartDate = startDate; + Date finalEndDate = endDate; + list = list.stream() + .filter(item -> { + Date recordDate = item.getFinishTime(); + if (recordDate == null) { + return false; + } + if (finalStartDate != null && recordDate.before(finalStartDate)) { + return false; + } + return finalEndDate == null || !recordDate.after(finalEndDate); + }) + .toList(); + } + + if (CollUtil.isEmpty(list)) { + if (!allowEmpty) { + return null; + } + // 如果没有维护记录,构造一个只有车牌号的空记录对象,以便模板显示车牌号 + HotVehicleMaintenanceVo emptyRecord = new HotVehicleMaintenanceVo(); + if (vehicle != null) { + emptyRecord.setPlateNumber(vehicle.getPlateNumber()); + } + ctx.put("record", emptyRecord); + return ctx; + } + + HotVehicleMaintenanceVo record = list.get(0); + + ctx.put("record", record); + + ctx.put("factoryCertImages", splitUrls(ossService.selectUrlByIds(record.getFactoryCertImageUrls()))); + ctx.put("finalInspectImages", splitUrls(ossService.selectUrlByIds(record.getFinalInspectImageUrls()))); + ctx.put("processInspectImages", splitUrls(ossService.selectUrlByIds(record.getProcessInspectImageUrls()))); + ctx.put("entryInspectImages", splitUrls(ossService.selectUrlByIds(record.getEntryInspectImageUrls()))); + + return ctx; + } + + private List splitUrls(String urls) { + if (StrUtil.isBlank(urls)) { + return new ArrayList<>(); + } + List list = new ArrayList<>(); + String[] parts = urls.split(","); + for (String p : parts) { + String v = p.trim(); + if (StrUtil.isNotBlank(v)) { + list.add(v); + } + } + return list; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleMileageRecordProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleMileageRecordProvider.java new file mode 100644 index 0000000..36010c8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleMileageRecordProvider.java @@ -0,0 +1,108 @@ +package com.hotwj.platform.reportStatistics.provider.vehicle; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.reportStatistics.service.IVehicleFilePrintService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import com.hotwj.platform.resourceManagement.vehicleMileage.domain.bo.HotVehicleMileageBo; +import com.hotwj.platform.resourceManagement.vehicleMileage.domain.vo.HotVehicleMileageVo; +import com.hotwj.platform.resourceManagement.vehicleMileage.service.IHotVehicleMileageService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 车辆行驶里程记录数据提供者 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class VehicleMileageRecordProvider implements IReportDataProvider { + + private final IHotVehicleMileageService mileageService; + private final IHotVehicleService vehicleService; + + @Override + public String getTemplateCode() { + return "templates/vehicle/mileageRecord.html.vm"; + } + + @Override + public String getReportType() { + return IVehicleFilePrintService.TYPE_MILEAGE_RECORD; + } + + @Override + public String getReportTitle() { + return "车辆行驶里程记录"; + } + + @Override + public Map prepareData(Map params) { + Long vehicleId = MapUtil.getLong(params, "vehicleId"); + if (vehicleId == null) { + return new HashMap<>(); + } + + String beginTime = MapUtil.getStr(params, "beginTime"); + String endTime = MapUtil.getStr(params, "endTime"); + Integer printEmptyData = MapUtil.getInt(params, "printEmptyData"); + boolean allowEmpty = printEmptyData == null || printEmptyData == 1; + Date startDate = null; + Date endDate = null; + if (StrUtil.isNotBlank(beginTime)) { + startDate = DateUtil.beginOfDay(DateUtil.parse(beginTime, "yyyy-MM-dd")); + } + if (StrUtil.isNotBlank(endTime)) { + endDate = DateUtil.endOfDay(DateUtil.parse(endTime, "yyyy-MM-dd")); + } + + HotVehicleMileageBo bo = new HotVehicleMileageBo(); + bo.setVehicleId(vehicleId); + // 去掉时间限制,查询所有 + // 增加 isDeleted=0 条件 + bo.setIsDeleted(0L); + + // 获取里程记录列表 + List list = mileageService.queryList(bo); + + if (list != null && (startDate != null || endDate != null)) { + Date finalStartDate = startDate; + Date finalEndDate = endDate; + list = list.stream() + .filter(item -> { + Date recordStart = item.getStartDate(); + Date recordEnd = item.getEndDate(); + if (recordStart == null || recordEnd == null) { + return false; + } + if (finalStartDate != null && recordStart.before(finalStartDate)) { + return false; + } + return finalEndDate == null || !recordEnd.after(finalEndDate); + }) + .toList(); + } + + if (!allowEmpty && (list == null || list.isEmpty())) { + return null; + } + + // 单独查询车辆信息 + HotVehicleVo vehicle = vehicleService.queryById(vehicleId); + + Map ctx = new HashMap<>(); + ctx.put("mileageList", list); + ctx.put("vehicle", vehicle); + + return ctx; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleRepairRecordProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleRepairRecordProvider.java new file mode 100644 index 0000000..cfd1590 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleRepairRecordProvider.java @@ -0,0 +1,205 @@ +package com.hotwj.platform.reportStatistics.provider.vehicle; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.reportStatistics.service.IVehicleFilePrintService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.vo.HotCompanySafetyManagerVo; +import com.hotwj.platform.resourceManagement.companySafetyManager.service.IHotCompanySafetyManagerService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import com.hotwj.platform.resourceManagement.vehicleRepair.domain.bo.HotVehicleRepairBo; +import com.hotwj.platform.resourceManagement.vehicleRepair.domain.vo.HotVehicleRepairVo; +import com.hotwj.platform.resourceManagement.vehicleRepair.service.IHotVehicleRepairService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.DictService; +import org.dromara.common.core.service.OssService; +import org.springframework.stereotype.Component; + +import java.util.*; + +/** + * 车辆维修记录数据提供者 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class VehicleRepairRecordProvider implements IReportDataProvider { + + private final IHotVehicleRepairService repairService; + private final IHotVehicleService vehicleService; + private final IHotCompanySafetyManagerService companySafetyManagerService; + private final IHotDriverService driverService; + private final DictService dictService; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/vehicle/repairRecord.html.vm"; + } + + @Override + public String getReportType() { + return IVehicleFilePrintService.TYPE_REPAIR_RECORD; + } + + @Override + public String getReportTitle() { + return "车辆维护与维修记录"; + } + + @Override + public Map prepareData(Map params) { + Long vehicleId = MapUtil.getLong(params, "vehicleId"); + if (vehicleId == null) { + return new HashMap<>(); + } + + String beginTime = MapUtil.getStr(params, "beginTime"); + String endTime = MapUtil.getStr(params, "endTime"); + Integer printEmptyData = MapUtil.getInt(params, "printEmptyData"); + boolean allowEmpty = printEmptyData == null || printEmptyData == 1; + Date startDate = null; + Date endDate = null; + if (StrUtil.isNotBlank(beginTime)) { + startDate = DateUtil.beginOfDay(DateUtil.parse(beginTime, "yyyy-MM-dd")); + } + if (StrUtil.isNotBlank(endTime)) { + endDate = DateUtil.endOfDay(DateUtil.parse(endTime, "yyyy-MM-dd")); + } + + HotVehicleRepairBo bo = new HotVehicleRepairBo(); + bo.setVehicleId(String.valueOf(vehicleId)); + bo.setIsDeleted(0L); + // 获取维修记录,这里取最近一次 + List list = repairService.queryList(bo); + + // 单独查询车辆信息 + HotVehicleVo vehicle = vehicleService.queryById(vehicleId); + + if (CollUtil.isNotEmpty(list) && (startDate != null || endDate != null)) { + Date finalStartDate = startDate; + Date finalEndDate = endDate; + list = list.stream() + .filter(item -> { + Date recordDate = item.getFinishDate(); + if (recordDate == null) { + return false; + } + if (finalStartDate != null && recordDate.before(finalStartDate)) { + return false; + } + return finalEndDate == null || !recordDate.after(finalEndDate); + }) + .toList(); + } + + // 如果没有记录,创建一个空对象,填充车辆信息,确保能渲染出一页 + if (CollUtil.isEmpty(list)) { + if (!allowEmpty) { + return null; + } + list = new ArrayList<>(); + HotVehicleRepairVo emptyRecord = new HotVehicleRepairVo(); + emptyRecord.setVehicle(vehicle); + if (vehicle != null) { + emptyRecord.setPlateNumber(vehicle.getPlateNumber()); + } + list.add(emptyRecord); + } else { + list = new ArrayList<>(list); + Map recorderNameCache = new HashMap<>(); + // 按照完成日期 finishDate 倒序 + list.sort((o1, o2) -> { + if (o1.getFinishDate() == null && o2.getFinishDate() == null) { + return 0; + } + if (o1.getFinishDate() == null) { + return 1; + } + if (o2.getFinishDate() == null) { + return -1; + } + return o2.getFinishDate().compareTo(o1.getFinishDate()); + }); + + // 处理图片并设置车辆信息 + for (HotVehicleRepairVo vo : list) { + List images = splitUrls(ossService.selectUrlByIds(vo.getProofImageUrls())); + vo.setImages(images); + vo.setVehicle(vehicle); + vo.setRepairCategory(resolveRepairCategory(vo.getRepairCategory())); + vo.setRecorder(resolveRecorderName(vo.getRecorder(), recorderNameCache)); + } + } + + Map ctx = new HashMap<>(); + ctx.put("records", list); + // 兼容外层引用(可选,根据需求决定是否保留) + ctx.put("vehicle", vehicle); + + return ctx; + } + + private List splitUrls(String urls) { + if (StrUtil.isBlank(urls)) { + return new ArrayList<>(); + } + List list = new ArrayList<>(); + String[] parts = urls.split(","); + for (String p : parts) { + String v = p.trim(); + if (StrUtil.isNotBlank(v)) { + list.add(v); + } + } + return list; + } + + private String resolveRepairCategory(String repairCategory) { + if (StrUtil.isBlank(repairCategory)) { + return repairCategory; + } + List labels = Arrays.stream(repairCategory.split(",")) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .map(code -> dictService.getDictLabel("hot_vehicle_repair", code)) + .map(label -> StrUtil.blankToDefault(label, "")) + .filter(StrUtil::isNotBlank) + .toList(); + if (CollUtil.isNotEmpty(labels)) { + return StrUtil.join(",", labels); + } + return repairCategory; + } + + private String resolveRecorderName(String recorder, Map cache) { + if (StrUtil.isBlank(recorder)) { + return recorder; + } + String cached = cache.get(recorder); + if (StrUtil.isNotBlank(cached)) { + return cached; + } + if (NumberUtil.isLong(recorder)) { + HotCompanySafetyManagerVo manager = companySafetyManagerService.queryById(Long.valueOf(recorder)); + if (manager != null && StrUtil.isNotBlank(manager.getName())) { + cache.put(recorder, manager.getName()); + return manager.getName(); + } + } + HotDriverVo driver = driverService.queryById(recorder); + if (driver != null && StrUtil.isNotBlank(driver.getName())) { + cache.put(recorder, driver.getName()); + return driver.getName(); + } + cache.put(recorder, recorder); + return recorder; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleTrafficAccidentRecordProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleTrafficAccidentRecordProvider.java new file mode 100644 index 0000000..df21101 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/provider/vehicle/VehicleTrafficAccidentRecordProvider.java @@ -0,0 +1,197 @@ +package com.hotwj.platform.reportStatistics.provider.vehicle; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.reportStatistics.service.IVehicleFilePrintService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.vo.HotCompanySafetyManagerVo; +import com.hotwj.platform.resourceManagement.companySafetyManager.service.IHotCompanySafetyManagerService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import com.hotwj.platform.securityManagement.accidentArchive.domain.bo.HotAccidentArchiveBo; +import com.hotwj.platform.securityManagement.accidentArchive.domain.vo.HotAccidentArchiveVo; +import com.hotwj.platform.securityManagement.accidentArchive.service.IHotAccidentArchiveService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.DictService; +import org.dromara.common.core.service.OssService; +import org.springframework.stereotype.Component; + +import java.util.*; + +/** + * 交通事故记录数据提供者 + * + * @author shihongwei + */ +@Component +@RequiredArgsConstructor +public class VehicleTrafficAccidentRecordProvider implements IReportDataProvider { + + private final IHotAccidentArchiveService accidentArchiveService; + private final IHotVehicleService vehicleService; + private final IHotCompanySafetyManagerService companySafetyManagerService; + private final DictService dictService; + private final OssService ossService; + + @Override + public String getTemplateCode() { + return "templates/vehicle/trafficAccidentRecord.html.vm"; + } + + @Override + public String getReportType() { + return IVehicleFilePrintService.TYPE_VEHICLE_ACCIDENT_RECORD; + } + + @Override + public String getReportTitle() { + return "交通事故记录"; + } + + @Override + public Map prepareData(Map params) { + Long vehicleId = MapUtil.getLong(params, "vehicleId"); + if (vehicleId == null) { + return new HashMap<>(); + } + + String beginTime = MapUtil.getStr(params, "beginTime"); + String endTime = MapUtil.getStr(params, "endTime"); + Integer printEmptyData = MapUtil.getInt(params, "printEmptyData"); + boolean allowEmpty = printEmptyData == null || printEmptyData == 1; + Date startDate = null; + Date endDate = null; + if (StrUtil.isNotBlank(beginTime)) { + startDate = DateUtil.beginOfDay(DateUtil.parse(beginTime, "yyyy-MM-dd")); + } + if (StrUtil.isNotBlank(endTime)) { + endDate = DateUtil.endOfDay(DateUtil.parse(endTime, "yyyy-MM-dd")); + } + + HotAccidentArchiveBo bo = new HotAccidentArchiveBo(); + bo.setVehicleId(vehicleId); + bo.setIsDeleted(0L); + // 获取事故记录 + List list = accidentArchiveService.queryList(bo); + + // 单独查询车辆信息 + HotVehicleVo vehicle = vehicleService.queryById(vehicleId); + + // 如果没有记录,创建一个空对象,填充车辆信息,确保能渲染出一页 + if (CollUtil.isNotEmpty(list) && (startDate != null || endDate != null)) { + Date finalStartDate = startDate; + Date finalEndDate = endDate; + list = list.stream() + .filter(item -> { + Date recordDate = item.getAccidentTime(); + if (recordDate == null) { + return false; + } + if (finalStartDate != null && recordDate.before(finalStartDate)) { + return false; + } + return finalEndDate == null || !recordDate.after(finalEndDate); + }) + .toList(); + } + + if (CollUtil.isEmpty(list)) { + if (!allowEmpty) { + return null; + } + list = new ArrayList<>(); + HotAccidentArchiveVo emptyRecord = new HotAccidentArchiveVo(); + // 填充车辆信息 + if (vehicle != null) { + emptyRecord.setPlateNumber(vehicle.getPlateNumber()); + emptyRecord.setVehicleId(vehicleId); + } + list.add(emptyRecord); + } else { + list = new ArrayList<>(list); + Map reporterNameCache = new HashMap<>(); + // 按照发生时间 accidentTime 倒序 + list.sort((o1, o2) -> { + if (o1.getAccidentTime() == null && o2.getAccidentTime() == null) { + return 0; + } + if (o1.getAccidentTime() == null) { + return 1; + } + if (o2.getAccidentTime() == null) { + return -1; + } + return o2.getAccidentTime().compareTo(o1.getAccidentTime()); + }); + + // 处理图片 + for (HotAccidentArchiveVo vo : list) { + List images = splitUrls(ossService.selectUrlByIds(vo.getResponsibilityCertificateUrls())); + vo.setImages(images); + vo.setAccidentPattern(resolveAccidentCategory(vo)); + vo.setReporterName(resolveReporterName(vo.getReporterName(), reporterNameCache)); + } + } + + Map ctx = new HashMap<>(); + ctx.put("records", list); + ctx.put("vehicle", vehicle); + + return ctx; + } + + private List splitUrls(String urls) { + if (StrUtil.isBlank(urls)) { + return new ArrayList<>(); + } + List list = new ArrayList<>(); + String[] parts = urls.split(","); + for (String p : parts) { + String v = p.trim(); + if (StrUtil.isNotBlank(v)) { + list.add(v); + } + } + return list; + } + + private String resolveAccidentCategory(HotAccidentArchiveVo vo) { + if (StrUtil.isNotBlank(vo.getAccidentForms())) { + List labels = Arrays.stream(vo.getAccidentForms().split(",")) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .map(code -> dictService.getDictLabel("hot_accident_archive_form", code)) + .map(label -> StrUtil.blankToDefault(label, "")) + .filter(StrUtil::isNotBlank) + .toList(); + if (CollUtil.isNotEmpty(labels)) { + return StrUtil.join(",", labels); + } + } + return vo.getAccidentPattern(); + } + + private String resolveReporterName(String reporterName, Map cache) { + if (StrUtil.isBlank(reporterName)) { + return reporterName; + } + String cached = cache.get(reporterName); + if (StrUtil.isNotBlank(cached)) { + return cached; + } + if (!NumberUtil.isLong(reporterName)) { + cache.put(reporterName, reporterName); + return reporterName; + } + HotCompanySafetyManagerVo manager = companySafetyManagerService.queryById(Long.valueOf(reporterName)); + if (manager != null && StrUtil.isNotBlank(manager.getName())) { + cache.put(reporterName, manager.getName()); + return manager.getName(); + } + cache.put(reporterName, reporterName); + return reporterName; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/service/IDriverFilePrintService.java b/src/main/java/com/hotwj/platform/reportStatistics/service/IDriverFilePrintService.java new file mode 100644 index 0000000..4a65198 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/service/IDriverFilePrintService.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.reportStatistics.service; + +import java.util.List; + +/** + * 驾驶员档案打印Service接口 + * + * @author shihongwei + * @date 2026-01-04 + */ +public interface IDriverFilePrintService { + + String TYPE_APPLICATION_FORM = "APPLICATION_FORM"; + String TYPE_ID_CARD_SCAN = "ID_CARD_SCAN"; + String TYPE_DRIVER_LICENSE_COPY = "DRIVER_LICENSE_COPY"; + String TYPE_QUALIFICATION_CERTIFICATE_COPY = "QUALIFICATION_CERTIFICATE_COPY"; + String TYPE_QUALIFICATION_REVIEW = "QUALIFICATION_REVIEW"; + String TYPE_ACCIDENT_RECORD = "ACCIDENT_RECORD"; + String TYPE_VIOLATION_RECORD = "VIOLATION_RECORD"; + String TYPE_ANNUAL_ASSESSMENT_RECORD = "ANNUAL_ASSESSMENT_RECORD"; + String TYPE_MEDICAL_EXAMINATION_REPORT = "MEDICAL_EXAMINATION_REPORT"; + String TYPE_REWARD_PUNISHMENT_RECORD = "REWARD_PUNISHMENT_RECORD"; + + /** + * 生成驾驶员应聘表PDF + * + * @param driverIds 驾驶员ID列表 + * @return PDF字节数组 + * @throws Exception 异常 + */ + byte[] generateApplicationForm(List driverIds) throws Exception; + + /** + * 生成身份证扫描件PDF + * + * @param driverIds 驾驶员ID列表 + * @return PDF字节数组 + * @throws Exception 异常 + */ + byte[] generateIdCardScan(List driverIds) throws Exception; + + /** + * 生成驾驶员机动车驾驶证复印件 + * + * @param driverIds 驾驶员ID列表 + * @return PDF字节数组 + */ + byte[] generateDriverLicenseCopy(List driverIds) throws Exception; + + /** + * 生成驾驶员从业资格证复印件 + * + * @param driverIds 驾驶员ID列表 + * @return PDF字节数组 + */ + byte[] generateQualificationCertificateCopy(List driverIds) throws Exception; + + /** + * 生成驾驶员资格审查及技能考核登记表PDF + * + * @param driverIds 驾驶员ID列表 + * @return PDF字节数组 + */ + byte[] generateQualificationReview(List driverIds) throws Exception; + + /** + * 生成驾驶员安全行车事故记录PDF + * + * @param driverIds 驾驶员ID列表 + * @return PDF字节数组 + */ + byte[] generateAccidentRecord(List driverIds) throws Exception; + + /** + * 生成驾驶员违章记录PDF + * + * @param driverIds 驾驶员ID列表 + * @return PDF字节数组 + */ + byte[] generateViolationRecord(List driverIds) throws Exception; + + /** + * 生成驾驶员年度考核记录PDF + * + * @param driverIds 驾驶员ID列表 + * @return PDF字节数组 + */ + byte[] generateAnnualAssessmentRecord(List driverIds) throws Exception; + + /** + * 生成驾驶员体检报告PDF + * + * @param driverIds 驾驶员ID列表 + * @return PDF字节数组 + */ + byte[] generateMedicalExaminationReport(List driverIds) throws Exception; + + /** + * 生成员工奖惩记录表PDF + * + * @param driverIds 驾驶员ID列表 + * @return PDF字节数组 + */ + byte[] generateRewardPunishmentRecord(List driverIds) throws Exception; + + /** + * 一键打印 + * + * @param driverIds 驾驶员ID列表 + * @param types 打印类型列表 + * @return PDF字节数组 + */ + byte[] generateOneClickPrintPdf(List driverIds, List types) throws Exception; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/service/ILedgerReportService.java b/src/main/java/com/hotwj/platform/reportStatistics/service/ILedgerReportService.java new file mode 100644 index 0000000..f46859c --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/service/ILedgerReportService.java @@ -0,0 +1,91 @@ +package com.hotwj.platform.reportStatistics.service; + +import com.hotwj.platform.reportStatistics.domain.bo.ReportLedgerQueryBo; +import com.hotwj.platform.reportStatistics.domain.vo.*; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.List; + +/** + * 驾驶员台账服务接口 + * + * @author shihongwei + * @date 2026-01-06 + */ +public interface ILedgerReportService { + + TableDataInfo queryDriverInfo(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery); + + List listDriverInfo(String districtCode, ReportLedgerQueryBo bo); + + TableDataInfo queryDriverHealth(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery); + + List listDriverHealth(String districtCode, ReportLedgerQueryBo bo); + + TableDataInfo queryProfCertScore(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery); + + List listProfCertScore(String districtCode, ReportLedgerQueryBo bo); + + TableDataInfo queryDriverIntegrity(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery); + + List listDriverIntegrity(String districtCode, ReportLedgerQueryBo bo); + + TableDataInfo queryVehicleInfo(ReportLedgerQueryBo bo, PageQuery pageQuery); + + List listVehicleInfo(ReportLedgerQueryBo bo); + + TableDataInfo queryGovVehicleInfo(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery); + + List listGovVehicleInfo(String districtCode, ReportLedgerQueryBo bo); + + TableDataInfo queryGovGpsViolation(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery); + + TableDataInfo queryAccident(ReportLedgerQueryBo bo, PageQuery pageQuery); + + List listAccident(ReportLedgerQueryBo bo); + + TableDataInfo queryGovAccident(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery); + + List listGovAccident(String districtCode, ReportLedgerQueryBo bo); + + TableDataInfo queryPreJobTraining(ReportLedgerQueryBo bo, PageQuery pageQuery); + + List listPreJobTraining(ReportLedgerQueryBo bo); + + TableDataInfo queryGovPreJobTraining(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery); + + List listGovPreJobTraining(String districtCode, ReportLedgerQueryBo bo); + + TableDataInfo queryGovPreJobTrainingDetail(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery); + + /** + * 查询安全生产教育培训记录-分页 + */ + TableDataInfo queryGovSafetyEducationTraining(ReportLedgerQueryBo bo, PageQuery pageQuery); + + /** + * 查询安全生产教育培训详情 + */ + GovSafetyEducationTrainingDetailVo queryGovSafetyEducationTrainingDetail(Long id); + + /** + * 查询违法违章信息排查记录-分页 + */ + TableDataInfo queryGovViolationInfoLedger(ReportLedgerQueryBo bo, PageQuery pageQuery); + + /** + * 查询违法违章信息排查记录-详情分页 + */ + TableDataInfo queryGovViolationInfoDetail(ReportLedgerQueryBo bo, PageQuery pageQuery); + + /** + * 查询违章处理记录-分页(政府端) + */ + TableDataInfo queryGovViolationHandlingLedger(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery); + + /** + * 查询安全生产会议记录-分页 + */ + TableDataInfo querySafetyProductionMeeting(ReportLedgerQueryBo bo, PageQuery pageQuery); +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/service/IReportDataProvider.java b/src/main/java/com/hotwj/platform/reportStatistics/service/IReportDataProvider.java new file mode 100644 index 0000000..24b9eb7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/service/IReportDataProvider.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.reportStatistics.service; + +import cn.hutool.core.date.DateUtil; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 报表数据提供者接口 + * + * @author shihongwei + */ +public interface IReportDataProvider { + + /** + * 获取模版编码 + * + * @return 模版编码,如 "templates/driver/driverApplicationForm.html.vm" + */ + String getTemplateCode(); + + /** + * 准备数据 + * + * @param params 请求参数 + * @return 模版所需的数据 Map + */ + Map prepareData(Map params); + + /** + * 获取布局模版编码 (可选) + * + * @param params 请求参数 + * @return 布局模版编码 + */ + default String getLayoutTemplateCode(Map params) { + return null; + } + + /** + * 获取 Mock 数据(用于预览) + * + * @return Mock 数据 Map + */ + default Map getMockData() { + return Map.of(); + } + + /** + * 获取报表标题 + * + * @return 标题,如 "驾驶员应聘表" + */ + default String getReportTitle() { + return ""; + } + + /** + * 获取报表标题 (支持动态参数) + * + * @param params 请求参数 + * @return 标题 + */ + default String getReportTitle(Map params) { + return getReportTitle(); + } + + /** + * 是否横向布局 + * + * @return true=横向, false=纵向(默认) + */ + default boolean isLandscape() { + return false; + } + + /** + * 获取报表类型标识 (用于一键打印等场景查找 Provider) + * 默认返回 Bean 名称或类名 + */ + default String getReportType() { + return this.getClass().getSimpleName(); + } + + /** + * 获取关联的封面 Provider 类型 + * + * @return 封面 Provider 类型,默认 "COMMON_COVER",返回 null 表示不使用封面 + */ + default String getCoverProviderType() { + return "COMMON_COVER"; + } + + /** + * 获取封面数据 + *

+ * 默认实现返回标题和当前日期。 + * 业务 Provider 可重写此方法以提供自定义的封面数据(如副标题、描述等)。 + *

+ * + * @param params 上下文参数 + * @return 封面数据 + */ + default Map getCoverData(Map params) { + Map data = new HashMap<>(); + data.put("title", getReportTitle()); + data.put("date", DateUtil.format(new Date(), "yyyy年MM月dd日")); + if (params != null) { + data.putAll(params); + } + return data; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/service/ISecurityManageFilePrintService.java b/src/main/java/com/hotwj/platform/reportStatistics/service/ISecurityManageFilePrintService.java new file mode 100644 index 0000000..e10d9a6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/service/ISecurityManageFilePrintService.java @@ -0,0 +1,35 @@ +package com.hotwj.platform.reportStatistics.service; + +import com.hotwj.platform.reportStatistics.domain.bo.ReportLedgerQueryBo; + +public interface ISecurityManageFilePrintService { + + byte[] generatePreJobTrainingPdf(ReportLedgerQueryBo bo); + + byte[] generateSafetyEducationTrainingPdf(ReportLedgerQueryBo bo); + + byte[] generateTrainingPlanMonthlyStatPdf(Long companyId, Long trainingId, java.util.List userIds); + + byte[] generateStudyRecordPdf(Long companyId, Long trainingId, java.util.List userIds); + + byte[] generateSafetyFireHazardInspectionPdf(ReportLedgerQueryBo bo); + + byte[] generateViolationInvestigationPdf(ReportLedgerQueryBo bo); + + byte[] generateViolationHandlingPdf(ReportLedgerQueryBo bo); + + byte[] generateGpsViolationPdf(ReportLedgerQueryBo bo); + + byte[] generateSafetyProductionInputPdf(ReportLedgerQueryBo bo); + + byte[] generateServiceQualityPdf(ReportLedgerQueryBo bo); + + byte[] generateSafetyMeetingPdf(ReportLedgerQueryBo bo); + + byte[] generateStudyDetailPdf(Long companyId, Long trainingId, java.util.List userIds); + + /** + * 生成线下培训/会议照片PDF (type=1:线下培训, type=2:线下会议) + */ + byte[] generateOfflineTrainingPhotoPdf(Long companyId, java.util.List ids, Integer type); +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/service/ISysReportTemplateService.java b/src/main/java/com/hotwj/platform/reportStatistics/service/ISysReportTemplateService.java new file mode 100644 index 0000000..cff20e2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/service/ISysReportTemplateService.java @@ -0,0 +1,63 @@ +package com.hotwj.platform.reportStatistics.service; + +import com.hotwj.platform.reportStatistics.domain.bo.SysReportTemplateBo; +import com.hotwj.platform.reportStatistics.domain.vo.SysReportTemplateVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 报表模板Service接口 + * + * @author shihongwei + * @date 2024-03-15 + */ +public interface ISysReportTemplateService { + + /** + * 查询报表模板 + */ + SysReportTemplateVo queryById(Long id); + + /** + * 查询报表模板 + */ + SysReportTemplateVo queryByCode(String code); + + /** + * 查询报表模板列表 + */ + TableDataInfo queryPageList(SysReportTemplateBo bo, PageQuery pageQuery); + + /** + * 查询报表模板列表 + */ + List queryList(SysReportTemplateBo bo); + + /** + * 新增报表模板 + */ + Boolean insertByBo(SysReportTemplateBo bo); + + /** + * 修改报表模板 + */ + Boolean updateByBo(SysReportTemplateBo bo); + + /** + * 校验并批量删除报表模板信息 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 同步 Classpath 下的模版到数据库 + */ + void syncTemplatesFromClasspath(); + + /** + * 根据模板编码查找对应的 Provider + */ + IReportDataProvider findProviderByTemplateCode(String templateCode); +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/service/IVehicleFilePrintService.java b/src/main/java/com/hotwj/platform/reportStatistics/service/IVehicleFilePrintService.java new file mode 100644 index 0000000..8ac6afe --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/service/IVehicleFilePrintService.java @@ -0,0 +1,75 @@ +package com.hotwj.platform.reportStatistics.service; + +import java.util.List; + +public interface IVehicleFilePrintService { + byte[] generateBaseInfoPdf(List vehicleIds) throws Exception; + + byte[] generateLicensePdf(List vehicleIds) throws Exception; + + /** + * 生成车辆年审报告PDF + * + * @param vehicleIds 车辆ID列表 + * @return PDF字节数组 + */ + byte[] generateInspectionReportPdf(List vehicleIds, String inspectionStartDate, String inspectionEndDate, Integer printEmptyData) throws Exception; + + /** + * 生成车辆二级维护记录PDF + * + * @param vehicleIds 车辆ID列表 + * @return PDF字节数组 + */ + byte[] generateMaintenanceRecordPdf(List vehicleIds, String maintenanceStartDate, String maintenanceEndDate, Integer printEmptyData) throws Exception; + + /** + * 生成车辆维修与维护记录PDF + * + * @param vehicleIds 车辆ID列表 + * @return PDF字节数组 + */ + byte[] generateRepairRecordPdf(List vehicleIds, String beginTime, String endTime, Integer printEmptyData) throws Exception; + + /** + * 生成交通事故记录PDF + * + * @param vehicleIds 车辆ID列表 + * @return PDF字节数组 + */ + byte[] generateTrafficAccidentRecordPdf(List vehicleIds, String beginTime, String endTime, Integer printEmptyData) throws Exception; + + /** + * 生成车辆出险记录PDF + * + * @param vehicleIds 车辆ID列表 + * @return PDF字节数组 + */ + byte[] generateClaimRecordPdf(List vehicleIds, String beginTime, String endTime, Integer printEmptyData) throws Exception; + + /** + * 生成车辆行驶里程记录PDF + * + * @param vehicleIds 车辆ID列表 + * @return PDF字节数组 + */ + byte[] generateMileageRecordPdf(List vehicleIds, String beginTime, String endTime, Integer printEmptyData) throws Exception; + + String TYPE_BASE_INFO = "BASE_INFO"; + String TYPE_LICENSE = "LICENSE"; + String TYPE_INSPECTION_REPORT = "INSPECTION_REPORT"; + String TYPE_MAINTENANCE_RECORD = "MAINTENANCE_RECORD"; + String TYPE_CLAIM_RECORD = "CLAIM_RECORD"; + String TYPE_MILEAGE_RECORD = "MILEAGE_RECORD"; + String TYPE_REPAIR_RECORD = "REPAIR_RECORD"; + String TYPE_VEHICLE_ACCIDENT_RECORD = "VEHICLE_ACCIDENT_RECORD"; + + /** + * 一键打印 + * + * @param vehicleIds 车辆ID列表 + * @param types 打印类型列表 + * @return PDF字节数组 + */ + byte[] generateOneClickPrintPdf(List vehicleIds, List types, Integer printEmptyData) throws Exception; +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/service/ReportPrintJob.java b/src/main/java/com/hotwj/platform/reportStatistics/service/ReportPrintJob.java new file mode 100644 index 0000000..c4dd7b8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/service/ReportPrintJob.java @@ -0,0 +1,168 @@ +package com.hotwj.platform.reportStatistics.service; + +import cn.hutool.core.util.StrUtil; +import lombok.Getter; +import org.dromara.common.core.utils.SpringUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +/** + * 报表打印任务构建器 + * + * @author shihongwei + */ +public class ReportPrintJob { + + @Getter + private final List segments = new ArrayList<>(); + // 辅助函数:根据类型获取 Provider + private final Function providerFinder; + @Getter + private Long companyId; + @Getter + private boolean useSeal = true; + @Getter + private boolean useWatermark = true; + @Getter + private String watermarkText; + @Getter + private boolean useFooterPageNumber = true; + + public ReportPrintJob(Function providerFinder) { + this.providerFinder = providerFinder; + } + + public ReportPrintJob() { + this(null); + } + + public ReportPrintJob withCompanyId(Long companyId) { + this.companyId = companyId; + return this; + } + + /** + * 是否使用公章 (默认 false) + */ + public ReportPrintJob useSeal(boolean useSeal) { + this.useSeal = useSeal; + return this; + } + + /** + * 是否使用水印 (默认 true) + */ + public ReportPrintJob useWatermark(boolean useWatermark) { + this.useWatermark = useWatermark; + return this; + } + + /** + * 设置自定义水印文字 (默认使用 appName) + */ + public ReportPrintJob watermarkText(String text) { + this.watermarkText = text; + return this; + } + + /** + * 添加封面 (基于指定业务 Provider 的配置) + * + * @param targetProvider 业务 Provider,将使用其 getCoverProviderType() 和 getCoverData() + * @param params 可选的覆盖参数 + */ + public ReportPrintJob addCover(IReportDataProvider targetProvider, Map params) { + if (targetProvider == null) { + return this; + } + + String coverType = targetProvider.getCoverProviderType(); + // 自动获取封面数据 + Map coverData = targetProvider.getCoverData(params); + return addCover(coverType, coverData); + } + + /** + * 添加指定类型的封面 + * + * @param coverType 封面 Provider 类型 + * @param params 封面参数 + */ + public ReportPrintJob addCover(String coverType, Map params) { + if (StrUtil.isBlank(coverType) || providerFinder == null) { + return this; + } + + IReportDataProvider coverProvider = providerFinder.apply(coverType); + if (coverProvider != null) { + add(coverProvider, params); + } + return this; + } + + public ReportPrintJob useFooterPageNumber(boolean useFooterPageNumber) { + this.useFooterPageNumber = useFooterPageNumber; + return this; + } + + /** + * 添加封面 (使用默认配置) + */ + public ReportPrintJob addCover(IReportDataProvider targetProvider) { + return addCover(targetProvider, null); + } + + /** + * 添加单个报表段 (如封面) + */ + public ReportPrintJob add(IReportDataProvider provider, Map params) { + if (provider != null) { + // 深拷贝 params 以防止外部修改,或者至少创建一个新 Map + Map safeParams = params != null ? new HashMap<>(params) : new HashMap<>(); + segments.add(new JobSegment(provider, safeParams)); + } + return this; + } + + /** + * 批量添加报表段 (如每个ID生成一份) + */ + public ReportPrintJob addBatch(IReportDataProvider provider, List ids, String idKey) { + if (provider != null && ids != null) { + // 防御性拷贝 ID 列表 + for (String id : ids) { + Map params = new HashMap<>(); + params.put(idKey, id); + segments.add(new JobSegment(provider, params)); + } + } + return this; + } + + /** + * 执行渲染 + */ + public byte[] render() { + ReportRenderService service = SpringUtils.getBean(ReportRenderService.class); + return service.renderJob(this); + } + + @Getter + public static class JobSegment { + private final IReportDataProvider provider; + private final Map params; + + public JobSegment(IReportDataProvider provider, Map params) { + this.provider = provider; + this.params = params; + } + + public boolean isLandscape() { + return provider.isLandscape(); + } + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/service/ReportRenderService.java b/src/main/java/com/hotwj/platform/reportStatistics/service/ReportRenderService.java new file mode 100644 index 0000000..99c77a2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/service/ReportRenderService.java @@ -0,0 +1,591 @@ +package com.hotwj.platform.reportStatistics.service; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.common.utils.PdfWatermarkUtil; +import com.hotwj.platform.reportStatistics.integration.GotenbergClient; +import com.hotwj.platform.reportStatistics.service.impl.DynamicVelocityTemplateLoader; +import com.hotwj.platform.reportStatistics.template.ReportVelocityTool; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import jakarta.annotation.PreDestroy; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.pdfbox.io.MemoryUsageSetting; +import org.apache.pdfbox.multipdf.PDFMergerUtility; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.runtime.RuntimeConstants; +import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; +import org.dromara.common.core.domain.dto.OssDTO; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.*; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ReportRenderService implements InitializingBean { + + private final GotenbergClient gotenbergClient; + private final OssService ossService; + private final ISysCompanyService sysCompanyService; + + @Value("${spring.application.name}") + private String appName; + @Value("${report.render.parallel-threshold:4}") + private int renderParallelThreshold; + @Value("${report.render.max-concurrency:4}") + private int renderMaxConcurrency; + + private VelocityEngine velocityEngine; + private ExecutorService renderExecutor; + /** + * 公司公章缓存,避免一键打印时重复下载同一公章 + */ + private final Map companySealCache = new ConcurrentHashMap<>(); + + @Override + public void afterPropertiesSet() { + initVelocityEngine(); + initRenderExecutor(); + } + + private void initVelocityEngine() { + Properties p = new Properties(); + // 设置资源加载器:db 优先,classpath 为后备 + p.setProperty(RuntimeConstants.RESOURCE_LOADER, "db,classpath"); + + // 配置 DB Loader + p.setProperty("resource.loader.db.class", DynamicVelocityTemplateLoader.class.getName()); + p.setProperty("resource.loader.db.cache", "false"); // 开发阶段关闭缓存 + p.setProperty("resource.loader.db.modification_check_interval", "0"); + + // 配置 Classpath Loader + p.setProperty("resource.loader.classpath.class", ClasspathResourceLoader.class.getName()); + + // 编码 + p.setProperty(RuntimeConstants.INPUT_ENCODING, "UTF-8"); + p.setProperty("output.encoding", "UTF-8"); + + velocityEngine = new VelocityEngine(p); + velocityEngine.init(); + log.info("ReportRenderService:Velocity引擎已通过数据库和类路径加载器完成初始化"); + } + + private void initRenderExecutor() { + int poolSize = Math.max(1, renderMaxConcurrency); + ThreadFactory threadFactory = new ThreadFactory() { + private final java.util.concurrent.atomic.AtomicInteger idx = new java.util.concurrent.atomic.AtomicInteger(1); + + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setName("report-render-" + idx.getAndIncrement()); + t.setDaemon(true); + return t; + } + }; + this.renderExecutor = Executors.newFixedThreadPool(poolSize, threadFactory); + log.info("ReportRenderService:渲染线程池初始化完成,poolSize={}", poolSize); + } + + @PreDestroy + public void destroy() { + if (renderExecutor != null) { + renderExecutor.shutdown(); + } + } + + /** + * 渲染报表 + * + * @param provider 数据提供者 + * @param params 请求参数 + * @return 渲染后的 HTML 字符串 + */ + public String render(IReportDataProvider provider, Map params) { + // 1. 准备数据 + Map data = provider.prepareData(params); + if (data == null) { + data = MapUtil.newHashMap(); + } + VelocityContext context = new VelocityContext(data); + // 注入工具类 + context.put("tool", new ReportVelocityTool()); + + // 2. 获取主模版 + String templateCode = provider.getTemplateCode(); + String content = renderTemplate(templateCode, context); + + // 3. 处理布局 + String layoutCode = provider.getLayoutTemplateCode(params); + if (StrUtil.isNotBlank(layoutCode)) { + // 将主内容放入 layout 上下文 + context.put("content", content); + context.put("title", provider.getReportTitle(params)); + return renderTemplate(layoutCode, context); + } + + return content; + } + + /** + * 创建一个新的打印任务构建器 + */ + public ReportPrintJob createJob(java.util.function.Function providerFinder) { + return new ReportPrintJob(providerFinder); + } + + /** + * 创建一个新的打印任务构建器 (不带 Provider 查找功能,无法使用 addCover) + */ + public ReportPrintJob createJob() { + return new ReportPrintJob(); + } + + /** + * 执行打印任务 + *

+ * 自动处理混合布局(横向/纵向)的合并逻辑。 + *

+ */ + public byte[] renderJob(ReportPrintJob job) { + if (job == null || job.getSegments().isEmpty()) { + throw new ServiceException("打印任务为空"); + } + + boolean allSameOrientation = true; + Boolean firstOrientation = null; + for (ReportPrintJob.JobSegment segment : job.getSegments()) { + if (firstOrientation == null) { + firstOrientation = segment.isLandscape(); + } else if (!firstOrientation.equals(segment.isLandscape())) { + allSameOrientation = false; + break; + } + } + if (allSameOrientation && firstOrientation != null) { + return renderBatchToPdfBytes(job.getSegments(), firstOrientation, job); + } + + boolean originUseWatermark = job.isUseWatermark(); + boolean originUseSeal = job.isUseSeal(); + String originWatermarkText = job.getWatermarkText(); + Long companyId = job.getCompanyId(); + + job.useFooterPageNumber(false); + job.useWatermark(false); + job.useSeal(false); + List pdfParts = new ArrayList<>(); + List currentBatch = new ArrayList<>(); + Boolean currentLandscape = null; + + for (ReportPrintJob.JobSegment segment : job.getSegments()) { + boolean isLandscape = segment.isLandscape(); + + // 如果当前批次为空,初始化方向 + if (currentLandscape == null) { + currentLandscape = isLandscape; + } + + // 如果方向改变,先渲染当前批次 + if (isLandscape != currentLandscape) { + if (!currentBatch.isEmpty()) { + pdfParts.add(renderBatchToPdfBytes(currentBatch, currentLandscape, job)); + currentBatch.clear(); + } + currentLandscape = isLandscape; + } + + currentBatch.add(segment); + } + + // 渲染剩余批次 + if (!currentBatch.isEmpty()) { + pdfParts.add(renderBatchToPdfBytes(currentBatch, currentLandscape, job)); + } + + byte[] merged; + if (pdfParts.size() == 1) { + merged = pdfParts.get(0); + } else { + merged = mergePdfs(pdfParts); + } + + byte[] pdf = PdfWatermarkUtil.addPageNumbers(merged); + + if (originUseWatermark) { + String text = StrUtil.isBlank(originWatermarkText) ? appName : originWatermarkText; + pdf = PdfWatermarkUtil.addWatermark(pdf, text); + } + + if (originUseSeal) { + byte[] sealBytes = loadCompanySeal(companyId); + if (sealBytes != null) { + pdf = PdfWatermarkUtil.addSealBottomRight(pdf, sealBytes); + } + } + + return pdf; + } + + public String renderJobToHtml(ReportPrintJob job) { + if (job == null || job.getSegments().isEmpty()) { + throw new ServiceException("打印任务为空"); + } + Boolean currentLandscape = null; + List currentBatch = new ArrayList<>(); + + for (ReportPrintJob.JobSegment segment : job.getSegments()) { + boolean isLandscape = segment.isLandscape(); + if (currentLandscape == null) { + currentLandscape = isLandscape; + } + if (isLandscape != currentLandscape) { + throw new ServiceException("验真快照暂不支持混合布局报表"); + } + currentBatch.add(segment); + } + if (currentLandscape == null) { + throw new ServiceException("打印任务为空"); + } + return renderBatchToHtml(currentBatch, currentLandscape); + } + + private byte[] renderBatchToPdfBytes(List segments, boolean isLandscape, ReportPrintJob job) { + if (segments.isEmpty()) return new byte[0]; + + String html = renderBatchToHtml(segments, isLandscape); + return generatePdf(html, job, isLandscape); + } + + private String renderBatchToHtml(List segments, boolean isLandscape) { + if (segments == null || segments.isEmpty()) { + return ""; + } + + // 先顺序计算标题,保证与原逻辑一致(第一个非空标题) + String batchTitle = ""; + for (ReportPrintJob.JobSegment segment : segments) { + String title = segment.getProvider().getReportTitle(segment.getParams()); + if (StrUtil.isNotBlank(title)) { + batchTitle = title; + break; + } + } + + // 小批量直接串行,避免线程切换和调度开销 + String[] renderedBodies = new String[segments.size()]; + if (segments.size() < Math.max(1, renderParallelThreshold) || renderExecutor == null) { + for (int i = 0; i < segments.size(); i++) { + renderedBodies[i] = renderSegmentBody(segments.get(i)); + } + } else { + List> futures = new ArrayList<>(segments.size()); + for (int i = 0; i < segments.size(); i++) { + final int index = i; + CompletableFuture future = CompletableFuture.supplyAsync(() -> renderSegmentBody(segments.get(index)), renderExecutor) + .thenAccept(body -> renderedBodies[index] = body); + futures.add(future); + } + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); + } + + List bodies = new ArrayList<>(segments.size()); + for (String body : renderedBodies) { + if (body != null) { + bodies.add(body); + } + } + + String layoutCode = isLandscape + ? "templates/layout/printLayoutLandscape.html.vm" + : "templates/layout/printLayout.html.vm"; + + VelocityContext layoutContext = new VelocityContext(); + layoutContext.put("tool", new ReportVelocityTool()); + layoutContext.put("title", batchTitle); + layoutContext.put("bodies", bodies); + + return renderTemplate(layoutCode, layoutContext); + } + + private String renderSegmentBody(ReportPrintJob.JobSegment segment) { + IReportDataProvider provider = segment.getProvider(); + Map data = provider.prepareData(segment.getParams()); + if (data == null) { + return null; + } + VelocityContext context = new VelocityContext(data); + context.put("tool", new ReportVelocityTool()); + String body = renderTemplate(provider.getTemplateCode(), context); + return extractBodyContent(body); + } + + private byte[] mergePdfs(List pdfBytesList) { + if (pdfBytesList == null || pdfBytesList.isEmpty()) { + return new byte[0]; + } + + PDFMergerUtility merger = new PDFMergerUtility(); + try (ByteArrayOutputStream mergedOut = new ByteArrayOutputStream()) { + merger.setDestinationStream(mergedOut); + + for (byte[] pdf : pdfBytesList) { + // PDFBox 2.0+ 推荐使用 load + merger.addSource(new ByteArrayInputStream(pdf)); + } + + // 使用内存合并,如果内存溢出风险大,可以改为 setupTempFileOnly() + merger.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly()); + return mergedOut.toByteArray(); + } catch (Exception e) { + log.error("Failed to merge PDFs", e); + throw new ServiceException("PDF合并失败: " + e.getMessage()); + } + } + + /** + * 批量渲染报表并合并 + *

+ * 使用 Layout 模版进行合并 + *

+ * + * @param provider 数据提供者 + * @param paramsList 参数列表(每个 Map 对应一个报表实例) + * @param layoutCode 布局模版编码(如 "templates/layout/printLayout.html.vm") + * @return 合并后的 HTML + */ + public String renderBatch(IReportDataProvider provider, List> paramsList, String layoutCode) { + List bodies = new ArrayList<>(); + String templateCode = provider.getTemplateCode(); + + for (Map params : paramsList) { + Map data = provider.prepareData(params); + if (data == null) { + continue; + } + VelocityContext context = new VelocityContext(data); + context.put("tool", new ReportVelocityTool()); + + // Render body only + String body = renderTemplate(templateCode, context); + bodies.add(extractBodyContent(body)); + } + + if (StrUtil.isBlank(layoutCode)) { + layoutCode = provider.isLandscape() + ? "templates/layout/printLayoutLandscape.html.vm" + : "templates/layout/printLayout.html.vm"; + } + + VelocityContext layoutContext = new VelocityContext(); + layoutContext.put("tool", new ReportVelocityTool()); + layoutContext.put("title", provider.getReportTitle()); + layoutContext.put("bodies", bodies); + + return renderTemplate(layoutCode, layoutContext); + } + + /** + * 批量渲染并生成 PDF (包含水印和公章) + * + * @param provider 数据提供者 + * @param ids 业务ID列表 + * @param idKey 业务ID参数名 + * @param companyId 公司ID (用于公章) + * @return PDF 字节数组 + */ + public byte[] renderBatchToPdf(IReportDataProvider provider, List ids, String idKey, Long companyId) { + // 创建临时 Job 以复用 generatePdf 逻辑 + ReportPrintJob tempJob = new ReportPrintJob().withCompanyId(companyId).useSeal(true).useWatermark(true); + + List> paramsList = new ArrayList<>(); + if (ids != null) { + for (String id : ids) { + paramsList.add(Map.of(idKey, id)); + } + } + + // 渲染 HTML + String html = renderBatch(provider, paramsList, null); + + // 转 PDF + return generatePdf(html, tempJob, provider.isLandscape()); + } + + /** + * 生成 PDF (私有方法) + */ + private byte[] generatePdf(String htmlContent, ReportPrintJob job, boolean isLandscape) { + try { + String footer = """ +
+ 平台信息依实为准,信息使用自判 + / +
+ """; + + // 性能优化:直接以内存HTML调用 Gotenberg,避免频繁临时文件 IO + byte[] pdf = gotenbergClient.convertHtmlToPdf(htmlContent, job.isUseFooterPageNumber() ? footer : null, isLandscape); + + // Add Watermark + if (job.isUseWatermark()) { + String text = StrUtil.isBlank(job.getWatermarkText()) ? appName : job.getWatermarkText(); + if (isLandscape) { + pdf = PdfWatermarkUtil.addWatermarkLandscape(pdf, text); + } else { + pdf = PdfWatermarkUtil.addWatermark(pdf, text); + } + } + + // Add Seal + if (job.isUseSeal()) { + byte[] sealBytes = loadCompanySeal(job.getCompanyId()); + if (sealBytes != null) { + return PdfWatermarkUtil.addSealBottomRight(pdf, sealBytes); + } + } + return pdf; + + } catch (Exception e) { + log.error("Failed to generate PDF", e); + throw new ServiceException("生成PDF失败: " + e.getMessage()); + } + } + + private byte[] loadCompanySeal(Long companyId) { + if (companyId == null) { + return null; + } + + byte[] cached = companySealCache.get(companyId); + if (cached != null) { + return cached; + } + + try { + SysCompanyVo company = sysCompanyService.queryById(companyId); + if (company == null) { + return null; + } + String seal = company.getSealUrl(); + if (StringUtils.isBlank(seal)) { + return null; + } + String trimmed = seal.trim(); + byte[] sealBytes; + if (trimmed.matches("^\\d+(,\\d+)*$")) { + List list = ossService.selectByIds(trimmed); + if (list == null || list.isEmpty()) { + return null; + } + String url = list.get(0).getUrl(); + sealBytes = downloadBytes(url); + } else { + sealBytes = downloadBytes(trimmed); + } + + if (sealBytes != null) { + companySealCache.put(companyId, sealBytes); + } + return sealBytes; + } catch (Exception e) { + log.warn("Failed to load company seal for companyId: {}", companyId, e); + // 公章加载失败不应阻塞主流程,返回 null 即可 + return null; + } + } + + private byte[] downloadBytes(String url) { + if (StringUtils.isBlank(url)) { + return null; + } + try { + java.net.URL u = new java.net.URL(url); + try (java.io.InputStream is = u.openStream()) { + return is.readAllBytes(); + } + } catch (Exception e) { + log.error("Failed to download seal image from url: {}", url, e); + return null; + } + } + + private String extractBodyContent(String html) { + if (StrUtil.isBlank(html)) return ""; + String lower = html.toLowerCase(); + int start = lower.indexOf(""); + if (start >= 0 && end >= 0) { + start = lower.indexOf(">", start) + 1; + return html.substring(start, end); + } + return html; + } + + /** + * 渲染指定模版 + */ + public String renderTemplate(String templateCode, VelocityContext context) { + try { + // 确保 context 中有 tool + if (!context.containsKey("tool")) { + context.put("tool", new ReportVelocityTool()); + } + + Template template = velocityEngine.getTemplate(templateCode); + StringWriter writer = new StringWriter(); + template.merge(context, writer); + return writer.toString(); + } catch (Exception e) { + log.error("Failed to render template: {}", templateCode, e); + throw new ServiceException("报表渲染失败: " + e.getMessage()); + } + } + + /** + * 仅渲染内容(用于预览等直接调用) + */ + public String renderContent(String templateCode, Map data) { + VelocityContext context = new VelocityContext(data); + return renderTemplate(templateCode, context); + } + + /** + * 渲染字符串模版(用于在线编辑预览) + * + * @param templateContent 模版内容 + * @param data 数据 + * @return 渲染结果 + */ + public String renderString(String templateContent, Map data) { + VelocityContext context = new VelocityContext(data); + if (!context.containsKey("tool")) { + context.put("tool", new ReportVelocityTool()); + } + + StringWriter writer = new StringWriter(); + try { + velocityEngine.evaluate(context, writer, "preview", templateContent); + return writer.toString(); + } catch (Exception e) { + log.error("Failed to render string template", e); + throw new ServiceException("预览渲染失败: " + e.getMessage()); + } + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/service/impl/DriverFilePrintServiceImpl.java b/src/main/java/com/hotwj/platform/reportStatistics/service/impl/DriverFilePrintServiceImpl.java new file mode 100644 index 0000000..6109314 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/service/impl/DriverFilePrintServiceImpl.java @@ -0,0 +1,156 @@ +package com.hotwj.platform.reportStatistics.service.impl; + +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.reportStatistics.service.IDriverFilePrintService; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.reportStatistics.service.ReportPrintJob; +import com.hotwj.platform.reportStatistics.service.ReportRenderService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 驾驶员档案打印 Service业务层处理 + * + * @author shihongwei + * @date 2026-01-04 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class DriverFilePrintServiceImpl implements IDriverFilePrintService, InitializingBean { + + private final IHotDriverService driverService; + private final ReportRenderService reportRenderService; + private final List allProviders; + + private Map providerMap; + + @Override + public void afterPropertiesSet() { + // 初始化 Provider Map + providerMap = allProviders.stream() + .collect(Collectors.toMap(IReportDataProvider::getReportType, p -> p)); + // 手动添加映射,以防 Provider 的 simpleName 不匹配 TYPE 常量 + // 如果 Provider 类名和常量一致则无需手动,这里为了保险起见可以显式映射 + // 例如:TYPE_APPLICATION_FORM -> DriverApplicationFormProvider + } + + @Override + public byte[] generateApplicationForm(List driverIds) throws Exception { + return renderSingleProviderJob(IDriverFilePrintService.TYPE_APPLICATION_FORM, driverIds, true); + } + + @Override + public byte[] generateIdCardScan(List driverIds) throws Exception { + return renderSingleProviderJob(IDriverFilePrintService.TYPE_ID_CARD_SCAN, driverIds, true); + } + + @Override + public byte[] generateDriverLicenseCopy(List driverIds) throws Exception { + return renderSingleProviderJob(IDriverFilePrintService.TYPE_DRIVER_LICENSE_COPY, driverIds, true); + } + + @Override + public byte[] generateQualificationCertificateCopy(List driverIds) throws Exception { + return renderSingleProviderJob(IDriverFilePrintService.TYPE_QUALIFICATION_CERTIFICATE_COPY, driverIds, true); + } + + @Override + public byte[] generateQualificationReview(List driverIds) throws Exception { + return renderSingleProviderJob(IDriverFilePrintService.TYPE_QUALIFICATION_REVIEW, driverIds, true); + } + + @Override + public byte[] generateAccidentRecord(List driverIds) throws Exception { + return renderSingleProviderJob(IDriverFilePrintService.TYPE_ACCIDENT_RECORD, driverIds, true); + } + + @Override + public byte[] generateViolationRecord(List driverIds) throws Exception { + return renderSingleProviderJob(IDriverFilePrintService.TYPE_VIOLATION_RECORD, driverIds, true); + } + + @Override + public byte[] generateAnnualAssessmentRecord(List driverIds) throws Exception { + return renderSingleProviderJob(IDriverFilePrintService.TYPE_ANNUAL_ASSESSMENT_RECORD, driverIds, true); + } + + @Override + public byte[] generateMedicalExaminationReport(List driverIds) throws Exception { + return renderSingleProviderJob(IDriverFilePrintService.TYPE_MEDICAL_EXAMINATION_REPORT, driverIds, true); + } + + @Override + public byte[] generateRewardPunishmentRecord(List driverIds) throws Exception { + return renderSingleProviderJob(IDriverFilePrintService.TYPE_REWARD_PUNISHMENT_RECORD, driverIds, true); + } + + @Override + public byte[] generateOneClickPrintPdf(List driverIds, List types) throws Exception { + Long companyId = resolveCompanyId(driverIds); + ReportPrintJob job = reportRenderService.createJob(this::getProviderByType) + .withCompanyId(companyId) + .useSeal(true); + + if (driverIds == null || types == null || driverIds.isEmpty() || types.isEmpty()) { + return job.render(); + } + + // 先把有效 provider 解析出来,避免在双层循环中重复查 Map + List providers = types.stream() + .map(this::getProviderByType) + .filter(java.util.Objects::nonNull) + .toList(); + + if (providers.isEmpty()) { + return job.render(); + } + + // 性能优化:这里只需要 driverId 作为参数,不再逐个查询驾驶员详情 + for (String id : driverIds) { + if (id == null) { + continue; + } + for (IReportDataProvider provider : providers) { + job.add(provider, Map.of("driverId", id)); + } + } + return job.render(); + } + + private byte[] renderSingleProviderJob(String type, List driverIds, boolean useSeal) throws Exception { + Long companyId = resolveCompanyId(driverIds); + IReportDataProvider provider = getProviderByType(type); + if (provider == null) { + throw new IllegalArgumentException("未找到报表类型对应的 Provider: " + type); + } + ReportPrintJob job = reportRenderService.createJob(this::getProviderByType) + .withCompanyId(companyId) + .useSeal(useSeal) + .addCover(provider, driverIds == null || driverIds.isEmpty() ? null : Map.of("driverId", driverIds.get(0))); + return job.addBatch(provider, driverIds, "driverId").render(); + } + + private IReportDataProvider getProviderByType(String type) { + return providerMap.get(type); + } + + private Long resolveCompanyId(List driverIds) { + if (driverIds == null || driverIds.isEmpty()) { + return null; + } + String firstId = driverIds.get(0); + HotDriverVo driver = driverService.queryById(firstId); + if (driver == null) { + return null; + } + return driver.getCompanyId(); + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/service/impl/DynamicVelocityTemplateLoader.java b/src/main/java/com/hotwj/platform/reportStatistics/service/impl/DynamicVelocityTemplateLoader.java new file mode 100644 index 0000000..40f0d4d --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/service/impl/DynamicVelocityTemplateLoader.java @@ -0,0 +1,94 @@ +package com.hotwj.platform.reportStatistics.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.reportStatistics.domain.vo.SysReportTemplateVo; +import com.hotwj.platform.reportStatistics.service.ISysReportTemplateService; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.runtime.resource.Resource; +import org.apache.velocity.runtime.resource.loader.ResourceLoader; +import org.apache.velocity.util.ExtProperties; +import org.dromara.common.core.utils.SpringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Reader; +import java.io.StringReader; + +/** + * 动态 Velocity 模版加载器 + *

+ * 优先从数据库加载模版,用于支持在线编辑和实时预览。 + *

+ * + * @author shihongwei + */ +public class DynamicVelocityTemplateLoader extends ResourceLoader { + + private static final Logger log = LoggerFactory.getLogger(DynamicVelocityTemplateLoader.class); + + private ISysReportTemplateService templateService; + + @Override + public void init(ExtProperties configuration) { + // 延迟初始化 + } + + private ISysReportTemplateService getTemplateService() { + if (templateService == null) { + try { + templateService = SpringUtils.getBean(ISysReportTemplateService.class); + } catch (Exception e) { + log.error("无法获取 ISysReportTemplateService Bean", e); + } + } + return templateService; + } + + @Override + public Reader getResourceReader(String source, String encoding) throws ResourceNotFoundException { + if (StrUtil.isBlank(source)) { + throw new ResourceNotFoundException("Template source is empty"); + } + + // 尝试从数据库获取 + ISysReportTemplateService service = getTemplateService(); + if (service != null) { + try { + // source 即为 template code + SysReportTemplateVo template = service.queryByCode(source); + if (template != null && StrUtil.isNotBlank(template.getContent())) { + log.info(">>>>>> 正在从数据库加载模板: {}", source); + return new StringReader(template.getContent()); + } + } catch (Exception e) { + log.warn("从数据库加载模板失败: {}", source, e); + } + } + + log.info(">>>>>> 数据库未找到模板,将降级到文件系统加载: {}", source); + throw new ResourceNotFoundException("Template not found in DB: " + source); + } + + @Override + public boolean isSourceModified(Resource resource) { + long lastModified = resource.getLastModified(); + long dbModified = getLastModified(resource); + return dbModified > lastModified; + } + + @Override + public long getLastModified(Resource resource) { + ISysReportTemplateService service = getTemplateService(); + if (service != null) { + try { + SysReportTemplateVo template = service.queryByCode(resource.getName()); + if (template != null && template.getUpdateTime() != null) { + return template.getUpdateTime().getTime(); + } + } catch (Exception e) { + // ignore + } + } + return 0; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/service/impl/LedgerReportServiceImpl.java b/src/main/java/com/hotwj/platform/reportStatistics/service/impl/LedgerReportServiceImpl.java new file mode 100644 index 0000000..f2b308c --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/service/impl/LedgerReportServiceImpl.java @@ -0,0 +1,330 @@ +package com.hotwj.platform.reportStatistics.service.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.reportStatistics.domain.bo.ReportLedgerQueryBo; +import com.hotwj.platform.reportStatistics.domain.vo.*; +import com.hotwj.platform.reportStatistics.mapper.LedgerReportMapper; +import com.hotwj.platform.reportStatistics.service.ILedgerReportService; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.time.YearMonth; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * 驾驶员台账服务接口实现类 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Service +@RequiredArgsConstructor +public class LedgerReportServiceImpl implements ILedgerReportService { + + private final LedgerReportMapper ledgerReportMapper; + private final ISysCompanyService sysCompanyService; + + @Override + public TableDataInfo queryDriverInfo(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery) { + Page page = pageQuery.build(); + List rows = ledgerReportMapper.selectDriverInfoPage(page, districtCode, bo); + page.setRecords(rows); + return TableDataInfo.build(page); + } + + @Override + public TableDataInfo queryGovViolationHandlingLedger(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery) { + Page page = pageQuery.build(); + List rows = ledgerReportMapper.selectGovViolationHandlingPage(page, districtCode, bo); + page.setRecords(rows); + return TableDataInfo.build(page); + } + + @Override + public List listDriverInfo(String districtCode, ReportLedgerQueryBo bo) { + return ledgerReportMapper.selectDriverInfoList(districtCode, bo); + } + + @Override + public TableDataInfo queryDriverHealth(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery) { + Page page = pageQuery.build(); + List rows = ledgerReportMapper.selectDriverHealthPage(page, districtCode, bo); + page.setRecords(rows); + return TableDataInfo.build(page); + } + + @Override + public List listDriverHealth(String districtCode, ReportLedgerQueryBo bo) { + return ledgerReportMapper.selectDriverHealthList(districtCode, bo); + } + + @Override + public TableDataInfo queryProfCertScore(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery) { + Page page = pageQuery.build(); + List rows = ledgerReportMapper.selectProfCertScorePage(page, districtCode, bo); + page.setRecords(rows); + return TableDataInfo.build(page); + } + + @Override + public List listProfCertScore(String districtCode, ReportLedgerQueryBo bo) { + return ledgerReportMapper.selectProfCertScoreList(districtCode, bo); + } + + @Override + public TableDataInfo queryDriverIntegrity(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery) { + Page page = pageQuery.build(); + List rows = ledgerReportMapper.selectDriverIntegrityPage(page, districtCode, bo); + page.setRecords(rows); + return TableDataInfo.build(page); + } + + @Override + public List listDriverIntegrity(String districtCode, ReportLedgerQueryBo bo) { + return ledgerReportMapper.selectDriverIntegrityList(districtCode, bo); + } + + @Override + public TableDataInfo queryVehicleInfo(ReportLedgerQueryBo bo, PageQuery pageQuery) { + Page page = pageQuery.build(); + List rows = ledgerReportMapper.selectVehicleInfoPage(page, bo); + page.setRecords(rows); + return TableDataInfo.build(page); + } + + @Override + public List listVehicleInfo(ReportLedgerQueryBo bo) { + List list = ledgerReportMapper.selectVehicleInfoList(bo); + if (list != null && !list.isEmpty()) { + for (int i = 0; i < list.size(); i++) { + list.get(i).setIndex(i + 1); + } + } + return list; + } + + @Override + public TableDataInfo queryGovVehicleInfo(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery) { + Page page = pageQuery.build(); + List rows = ledgerReportMapper.selectGovVehicleInfoPage(page, districtCode, bo); + page.setRecords(rows); + return TableDataInfo.build(page); + } + + @Override + public List listGovVehicleInfo(String districtCode, ReportLedgerQueryBo bo) { + return ledgerReportMapper.selectGovVehicleInfoList(districtCode, bo); + } + + @Override + public TableDataInfo queryGovGpsViolation(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery) { + bo.setNeedInterview(1); + bo.setProcessStatus(2); + Page page = pageQuery.build(); + List rows = ledgerReportMapper.selectGovGpsViolationPage(page, districtCode, bo); + page.setRecords(rows); + return TableDataInfo.build(page); + } + + @Override + public TableDataInfo queryAccident(ReportLedgerQueryBo bo, PageQuery pageQuery) { + Page page = pageQuery.build(); + List rows = ledgerReportMapper.selectAccidentPage(page, bo); + page.setRecords(rows); + return TableDataInfo.build(page); + } + + @Override + public List listAccident(ReportLedgerQueryBo bo) { + return ledgerReportMapper.selectAccidentList(bo); + } + + @Override + public TableDataInfo queryGovAccident(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery) { + Page page = pageQuery.build(); + List rows = ledgerReportMapper.selectGovAccidentPage(page, districtCode, bo); + page.setRecords(rows); + return TableDataInfo.build(page); + } + + @Override + public List listGovAccident(String districtCode, ReportLedgerQueryBo bo) { + return ledgerReportMapper.selectGovAccidentList(districtCode, bo); + } + + @Override + public TableDataInfo queryPreJobTraining(ReportLedgerQueryBo bo, PageQuery pageQuery) { + Page page = pageQuery.build(); + List rows = ledgerReportMapper.selectPreJobTrainingPage(page, bo); + page.setRecords(rows); + return TableDataInfo.build(page); + } + + @Override + public List listPreJobTraining(ReportLedgerQueryBo bo) { + return ledgerReportMapper.selectPreJobTrainingList(bo); + } + + @Override + public TableDataInfo queryGovPreJobTraining(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery) { + Page page = pageQuery.build(); + List rows = ledgerReportMapper.selectGovPreJobTrainingPage(page, districtCode, bo); + page.setRecords(rows); + return TableDataInfo.build(page); + } + + @Override + public List listGovPreJobTraining(String districtCode, ReportLedgerQueryBo bo) { + return ledgerReportMapper.selectGovPreJobTrainingList(districtCode, bo); + } + + @Override + public TableDataInfo queryGovPreJobTrainingDetail(String districtCode, ReportLedgerQueryBo bo, PageQuery pageQuery) { + Page page = pageQuery.build(); + List rows = ledgerReportMapper.selectGovPreJobTrainingDetailPage(page, districtCode, bo); + page.setRecords(rows); + return TableDataInfo.build(page); + } + + @Override + public TableDataInfo queryGovSafetyEducationTraining(ReportLedgerQueryBo bo, PageQuery pageQuery) { + Page page = pageQuery.build(); + List rows = ledgerReportMapper.selectGovSafetyEducationTrainingPage(page, bo); + page.setRecords(rows); + return TableDataInfo.build(page); + } + + @Override + public GovSafetyEducationTrainingDetailVo queryGovSafetyEducationTrainingDetail(Long id) { + // 1. Get main details + GovSafetyEducationTrainingDetailVo detail = ledgerReportMapper.selectGovSafetyEducationTrainingDetail(id); + if (detail == null) { + return null; + } + + // 2. Get participants + detail.setParticipantList(ledgerReportMapper.selectGovSafetyEducationTrainingParticipants(id)); + + // 3. Get course names and format content + List courseNames = ledgerReportMapper.selectGovSafetyEducationTrainingCourseNames(id); + if (courseNames != null && !courseNames.isEmpty()) { + String content = IntStream.range(0, courseNames.size()) + .mapToObj(i -> (i + 1) + ":" + courseNames.get(i)) + .collect(Collectors.joining("\n")); + detail.setTrainingContent(content); + } + + return detail; + } + + @Override + public TableDataInfo queryGovViolationInfoLedger(ReportLedgerQueryBo bo, PageQuery pageQuery) { + // 1. Get company creation time + SysCompanyVo company = sysCompanyService.queryById(bo.getCompanyId()); + if (company == null) { + return TableDataInfo.build(); + } + + // 2. Determine start and end YearMonth + LocalDate createDate = company.getCreateTime() != null + ? company.getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate() + : LocalDate.now().minusYears(1); // Fallback + YearMonth startYm = YearMonth.from(createDate); + YearMonth endYm = YearMonth.now(); + + if (startYm.isAfter(endYm)) { + startYm = endYm; + } + + // 3. Generate all YearMonths in descending order (newest first) + List allMonths = new ArrayList<>(); + YearMonth current = endYm; + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-M"); + + while (!current.isBefore(startYm)) { + allMonths.add(current.format(formatter)); + current = current.minusMonths(1); + } + + // 4. Pagination in memory + int total = allMonths.size(); + int pageNum = pageQuery.getPageNum(); + int pageSize = pageQuery.getPageSize(); + int start = (pageNum - 1) * pageSize; + int end = Math.min(start + pageSize, total); + + if (start >= total) { + TableDataInfo emptyPage = TableDataInfo.build(); + emptyPage.setTotal(total); + emptyPage.setRows(Collections.emptyList()); + return emptyPage; + } + + List pagedMonths = allMonths.subList(start, end); + + // 5. Query counts for the paged months + // Convert first and last month of the page to Date range for query optimization + // Note: pagedMonths is descending, so first element is the latest month (end of query range) + // last element is the oldest month (start of query range) + String latestMonthStr = pagedMonths.get(0); + String oldestMonthStr = pagedMonths.get(pagedMonths.size() - 1); + + YearMonth queryEndYm = YearMonth.parse(latestMonthStr, formatter); + YearMonth queryStartYm = YearMonth.parse(oldestMonthStr, formatter); + + Date queryStartDate = Date.from(queryStartYm.atDay(1).atStartOfDay(ZoneId.systemDefault()).toInstant()); + Date queryEndDate = Date.from(queryEndYm.atEndOfMonth().atTime(23, 59, 59).atZone(ZoneId.systemDefault()).toInstant()); + + List counts = ledgerReportMapper.selectViolationCountByDateRange(bo, queryStartDate, queryEndDate); + Map countMap = counts.stream() + .collect(Collectors.toMap(GovViolationInfoLedgerVo::getPlanName, GovViolationInfoLedgerVo::getViolationCount)); + + // 6. Build result list + List result = pagedMonths.stream() + .map(month -> { + GovViolationInfoLedgerVo vo = new GovViolationInfoLedgerVo(); + vo.setPlanName(month); + vo.setViolationCount(countMap.getOrDefault(month, 0L)); + return vo; + }) + .collect(Collectors.toList()); + + TableDataInfo tableData = TableDataInfo.build(); + tableData.setRows(result); + tableData.setTotal(total); + return tableData; + } + + @Override + public TableDataInfo queryGovViolationInfoDetail(ReportLedgerQueryBo bo, PageQuery pageQuery) { + if (bo.getEndDate() != null) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(bo.getEndDate()); + calendar.set(Calendar.HOUR_OF_DAY, 23); + calendar.set(Calendar.MINUTE, 59); + calendar.set(Calendar.SECOND, 59); + bo.setEndDate(calendar.getTime()); + } + Page page = pageQuery.build(); + List rows = ledgerReportMapper.selectGovViolationInfoDetailPage(page, bo); + page.setRecords(rows); + return TableDataInfo.build(page); + } + + @Override + public TableDataInfo querySafetyProductionMeeting(ReportLedgerQueryBo bo, PageQuery pageQuery) { + Page page = pageQuery.build(); + List list = ledgerReportMapper.selectSafetyProductionMeetingPage(page, bo); + page.setRecords(list); + return TableDataInfo.build(page); + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/service/impl/SecurityManageFilePrintServiceImpl.java b/src/main/java/com/hotwj/platform/reportStatistics/service/impl/SecurityManageFilePrintServiceImpl.java new file mode 100644 index 0000000..f793b81 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/service/impl/SecurityManageFilePrintServiceImpl.java @@ -0,0 +1,738 @@ +package com.hotwj.platform.reportStatistics.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.reportStatistics.domain.bo.ReportLedgerQueryBo; +import com.hotwj.platform.reportStatistics.domain.vo.*; +import com.hotwj.platform.reportStatistics.mapper.LedgerReportMapper; +import com.hotwj.platform.reportStatistics.pdf.OfficialInformationSealPostProcessor; +import com.hotwj.platform.reportStatistics.provider.cover.DailyTrainingCoverProvider; +import com.hotwj.platform.reportStatistics.provider.security.*; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.reportStatistics.service.ISecurityManageFilePrintService; +import com.hotwj.platform.reportStatistics.service.ReportPrintJob; +import com.hotwj.platform.reportStatistics.service.ReportRenderService; +import com.hotwj.platform.reportStatistics.verify.service.QrCodeService; +import com.hotwj.platform.reportStatistics.verify.service.ReportVerifyTicketService; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.vo.HotHiddenDangerInspectionVo; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.service.IHotHiddenDangerInspectionService; +import com.hotwj.platform.securityManagement.safetyInvestment.domain.vo.HotSafetyInvestmentVo; +import com.hotwj.platform.securityManagement.safetyInvestment.service.IHotSafetyInvestmentService; +import com.hotwj.platform.securityManagement.securityMeeting.domain.vo.HotSecurityMeetingVo; +import com.hotwj.platform.securityManagement.securityMeeting.service.IHotSecurityMeetingService; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.vo.HotServiceQualityComplaintVo; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.service.IHotServiceQualityComplaintService; +import com.hotwj.platform.securityManagement.training.service.IHotTrainingService; +import com.hotwj.platform.securityManagement.violationInfo.domain.vo.HotViolationInfoVo; +import com.hotwj.platform.securityManagement.violationInfo.service.IHotViolationInfoService; +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.YearMonth; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 安全生产管理档案打印服务实现类 + * + * @author shihongwei + * @date 2026-01-08 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class SecurityManageFilePrintServiceImpl implements ISecurityManageFilePrintService { + + private final LedgerReportMapper ledgerReportMapper; + private final ReportRenderService reportRenderService; + private final IHotTrainingService trainingService; + private final IHotViolationInfoService violationInfoService; + private final IHotHiddenDangerInspectionService hiddenDangerInspectionService; + private final IHotSecurityMeetingService securityMeetingService; + private final IHotSafetyInvestmentService safetyInvestmentService; + private final IHotServiceQualityComplaintService serviceQualityComplaintService; + private final OssService ossService; + private final OfficialInformationSealPostProcessor officialInformationSealPostProcessor; + private final ReportVerifyTicketService reportVerifyTicketService; + private final QrCodeService qrCodeService; + + private final List allProviders; + private Map, IReportDataProvider> providerMap; + private Map providerTypeMap; + + @PostConstruct + public void init() { + providerMap = new HashMap<>(); + providerTypeMap = new HashMap<>(); + for (IReportDataProvider provider : allProviders) { + providerMap.put(provider.getClass(), provider); + if (StringUtils.isNotBlank(provider.getReportType())) { + providerTypeMap.put(provider.getReportType(), provider); + } + } + } + + private T getProvider(Class clazz) { + return (T) providerMap.get(clazz); + } + + @Override + public byte[] generatePreJobTrainingPdf(ReportLedgerQueryBo bo) { + List list = ledgerReportMapper.selectPreJobTrainingList(bo); + if (list == null || list.isEmpty()) { + throw new RuntimeException("暂无数据可打印"); + } + + PreJobTrainingProvider provider = getProvider(PreJobTrainingProvider.class); + + ReportPrintJob job = reportRenderService.createJob(key -> providerTypeMap.get(key)) + .withCompanyId(bo.getCompanyId()) + .addCover(provider, Map.of("companyId", bo.getCompanyId(), "coverName", "从业人员岗前培训考核卡")) + .addBatch(provider, StringUtils.splitList(bo.getIds()), "driverId"); + + return job.render(); + } + + @Override + public byte[] generateSafetyEducationTrainingPdf(ReportLedgerQueryBo bo) { + List trainingIds = resolveSafetyEducationTrainingIds(bo); + if (trainingIds.isEmpty()) { + throw new RuntimeException("没有可打印的培训计划"); + } + + ReportPrintJob job = reportRenderService.createJob() + .withCompanyId(bo.getCompanyId()); + + // Cover (Only once) + DailyTrainingCoverProvider coverProvider = getProvider(DailyTrainingCoverProvider.class); + job.add(coverProvider, Map.of("companyId", bo.getCompanyId())); + + SafetyEducationTrainingProvider provider = getProvider(SafetyEducationTrainingProvider.class); + + for (Long trainingId : trainingIds) { + if (trainingId == null) continue; + + Map baseParams = new HashMap<>(); + baseParams.put("companyId", bo.getCompanyId()); + baseParams.put("trainingId", trainingId); + + // Detail + Map detailParams = new HashMap<>(baseParams); + detailParams.put("mode", "detail"); + job.add(provider, detailParams); + + // SignIn + Map signInParams = new HashMap<>(baseParams); + signInParams.put("mode", "signIn"); + job.add(provider, signInParams); + } + + return job.render(); + } + + @Override + public byte[] generateTrainingPlanMonthlyStatPdf(Long companyId, Long trainingId, List userIds) { + if (companyId == null) throw new RuntimeException("公司ID不能为空"); + if (trainingId == null) throw new RuntimeException("培训ID不能为空"); + + Long ticketId = reportVerifyTicketService.createTicketId(); + String verifyUrl = reportVerifyTicketService.buildVerifyUrl("/public/verify/training/monthly", ticketId); + String verifyQrBase64 = qrCodeService.generatePngDataUrl(verifyUrl, 300, 300); + + ReportPrintJob job = reportRenderService.createJob() + .withCompanyId(companyId); + + TrainingPlanMonthlyStatProvider provider = getProvider(TrainingPlanMonthlyStatProvider.class); + Map params = new HashMap<>(); + params.put("companyId", companyId); + params.put("trainingId", trainingId); + params.put("userIds", userIds); + params.put("verifyUrl", verifyUrl); + params.put("verifyQrBase64", verifyQrBase64); + job.add(provider, params); + job.useSeal(false); + + String snapshotHtml = reportRenderService.renderJobToHtml(job); + String businessParamsJson = cn.hutool.json.JSONUtil.toJsonStr(params); + reportVerifyTicketService.issueHtmlSnapshotTicket(ticketId, "TP_MONTHLY", companyId, businessParamsJson, snapshotHtml); + + return officialInformationSealPostProcessor.process(job.render()); + } + + @Override + public byte[] generateStudyRecordPdf(Long companyId, Long trainingId, List userIds) { + if (companyId == null) throw new RuntimeException("公司ID不能为空"); + if (trainingId == null) throw new RuntimeException("培训ID不能为空"); + if (userIds == null || userIds.isEmpty()) throw new RuntimeException("请先选择要打印的人员"); + + Long ticketId = reportVerifyTicketService.createTicketId(); + String verifyUrl = reportVerifyTicketService.buildVerifyUrl("/public/verify/training/studyRecord", ticketId); + String verifyQrBase64 = qrCodeService.generatePngDataUrl(verifyUrl, 300, 300); + + ReportPrintJob job = reportRenderService.createJob() + .withCompanyId(companyId); + + StudyRecordProvider provider = getProvider(StudyRecordProvider.class); + + for (String userId : userIds) { + if (StringUtils.isBlank(userId)) continue; + Map params = new HashMap<>(); + params.put("companyId", companyId); + params.put("trainingId", trainingId); + params.put("userId", userId.trim()); + params.put("verifyUrl", verifyUrl); + params.put("verifyQrBase64", verifyQrBase64); + job.add(provider, params); + } + + job.useSeal(false); + + Map businessParams = new HashMap<>(); + businessParams.put("companyId", companyId); + businessParams.put("trainingId", trainingId); + businessParams.put("userIds", userIds); + String snapshotHtml = reportRenderService.renderJobToHtml(job); + reportVerifyTicketService.issueHtmlSnapshotTicket(ticketId, "STUDY_RECORD", companyId, cn.hutool.json.JSONUtil.toJsonStr(businessParams), snapshotHtml); + + return officialInformationSealPostProcessor.process(job.render()); + } + + @Override + public byte[] generateSafetyFireHazardInspectionPdf(ReportLedgerQueryBo bo) { + if (bo.getCompanyId() == null) throw new RuntimeException("公司ID不能为空"); + List ids = parseIds(bo.getIds()); + if (ids.isEmpty()) throw new RuntimeException("请选择要打印的隐患排查项"); + + ReportPrintJob job = reportRenderService.createJob() + .withCompanyId(bo.getCompanyId()); + + SafetyFireHazardInspectionProvider provider = getProvider(SafetyFireHazardInspectionProvider.class); + + // Cover + Map coverParams = new HashMap<>(); + coverParams.put("mode", "cover"); + coverParams.put("companyId", bo.getCompanyId()); + job.add(provider, coverParams); + + boolean hasRecord = false; + for (Long id : ids) { + if (id == null) continue; + HotHiddenDangerInspectionVo inspection = hiddenDangerInspectionService.queryById(id); + if (inspection == null || Objects.equals(inspection.getIsDeleted(), 1L)) continue; + + Map params = new HashMap<>(); + params.put("companyId", bo.getCompanyId()); + params.put("inspection", inspection); + + // Detail + params.put("mode", "detail"); + job.add(provider, new HashMap<>(params)); + + // Photo + params.put("mode", "photo"); + job.add(provider, new HashMap<>(params)); + + hasRecord = true; + } + + if (!hasRecord) throw new RuntimeException("没有可打印的隐患排查记录"); + + return job.render(); + } + + @Override + public byte[] generateViolationInvestigationPdf(ReportLedgerQueryBo bo) { + if (bo.getCompanyId() == null) throw new RuntimeException("公司ID不能为空"); + + ReportPrintJob job = reportRenderService.createJob() + .withCompanyId(bo.getCompanyId()); + + ViolationInvestigationProvider provider = getProvider(ViolationInvestigationProvider.class); + + // Cover + Map coverParams = new HashMap<>(); + coverParams.put("mode", "cover"); + coverParams.put("companyId", bo.getCompanyId()); + job.add(provider, coverParams); + + boolean multiPlan = StringUtils.isNotBlank(bo.getPlanName()) && bo.getPlanName().contains(","); + + if (multiPlan) { + String[] plans = bo.getPlanName().split(","); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-M"); + ZoneId zoneId = ZoneId.systemDefault(); + Date originalStart = bo.getStartDate(); + Date originalEnd = bo.getEndDate(); + for (String raw : plans) { + String p = raw == null ? "" : raw.trim(); + if (p.isEmpty()) continue; + try { + YearMonth ym = YearMonth.parse(p, formatter); + LocalDate start = ym.atDay(1); + LocalDate end = ym.atEndOfMonth(); + Date startDate = Date.from(start.atStartOfDay(zoneId).toInstant()); + Date endDate = Date.from(end.atTime(23, 59, 59).atZone(zoneId).toInstant()); + bo.setStartDate(startDate); + bo.setEndDate(endDate); + addViolationInvestigationDetail(job, provider, bo); + } catch (Exception e) { + log.warn("解析违法违章排查记录计划名称失败: {}", p, e); + } + } + bo.setStartDate(originalStart); + bo.setEndDate(originalEnd); + } else { + if (bo.getStartDate() == null && bo.getEndDate() == null && StringUtils.isNotBlank(bo.getPlanName())) { + try { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-M"); + YearMonth ym = YearMonth.parse(bo.getPlanName(), formatter); + LocalDate start = ym.atDay(1); + LocalDate end = ym.atEndOfMonth(); + ZoneId zoneId = ZoneId.systemDefault(); + bo.setStartDate(Date.from(start.atStartOfDay(zoneId).toInstant())); + bo.setEndDate(Date.from(end.atTime(23, 59, 59).atZone(zoneId).toInstant())); + } catch (Exception e) { + log.warn("解析违法违章排查记录计划名称失败: {}", bo.getPlanName(), e); + } + } + addViolationInvestigationDetail(job, provider, bo); + } + + return job.render(); + } + + private void addViolationInvestigationDetail(ReportPrintJob job, ViolationInvestigationProvider provider, ReportLedgerQueryBo bo) { + String investigateDate = buildDateRangeLabel(bo.getStartDate(), bo.getEndDate()); + Page page = new Page<>(1, 9999); + List details = ledgerReportMapper.selectGovViolationInfoDetailPage(page, bo); + String recorderName = ""; + if (CollUtil.isNotEmpty(details)) { + recorderName = details + .stream() + .map(GovViolationInfoDetailVo::getRecorderName) + .filter(StringUtils::isNotBlank) + .distinct() + .collect(Collectors.joining("、")); + } + Map params = new HashMap<>(); + params.put("mode", "detail"); + params.put("companyId", bo.getCompanyId()); + params.put("managerName", recorderName); + params.put("investigateDate", investigateDate); + params.put("details", details); + job.add(provider, params); + } + + @Override + public byte[] generateSafetyProductionInputPdf(ReportLedgerQueryBo bo) { + if (bo.getCompanyId() == null) throw new RuntimeException("公司ID不能为空"); + List ids = parseIds(bo.getIds()); + if (ids.isEmpty()) throw new RuntimeException("请选择要打印的安全生产投入记录"); + + ReportPrintJob job = reportRenderService.createJob() + .withCompanyId(bo.getCompanyId()); + + SafetyProductionInputProvider provider = getProvider(SafetyProductionInputProvider.class); + + List records = new ArrayList<>(); + for (Long id : ids) { + if (id == null) continue; + HotSafetyInvestmentVo vo = safetyInvestmentService.queryById(id); + if (vo == null || Objects.equals(vo.getIsDeleted(), 1L)) continue; + records.add(vo); + } + if (records.isEmpty()) throw new RuntimeException("没有可打印的安全生产投入记录"); + + String year = bo.getKeyword(); + if (StringUtils.isBlank(year)) { + Date time = records.get(0).getInvestmentTime(); + if (time != null) { + year = cn.hutool.core.date.DateUtil.format(time, "yyyy"); + } else { + year = cn.hutool.core.date.DateUtil.format(new Date(), "yyyy"); + } + } + BigDecimal totalAmount = records.stream() + .map(HotSafetyInvestmentVo::getInvestmentAmount) + .filter(Objects::nonNull) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + StringBuilder receiptIds = new StringBuilder(); + for (HotSafetyInvestmentVo vo : records) { + if (StringUtils.isBlank(vo.getReceiptAttachments())) continue; + if (receiptIds.length() > 0) receiptIds.append(","); + receiptIds.append(vo.getReceiptAttachments()); + } + List receiptImages = resolveInspectionImages(receiptIds.toString()); + + Map baseParams = new HashMap<>(); + baseParams.put("companyId", bo.getCompanyId()); + baseParams.put("year", year); + + // Cover + Map coverParams = new HashMap<>(baseParams); + coverParams.put("mode", "cover"); + job.add(provider, coverParams); + + // Detail + Map detailParams = new HashMap<>(baseParams); + detailParams.put("mode", "detail"); + detailParams.put("totalAmount", totalAmount); + detailParams.put("records", records); + job.add(provider, detailParams); + + // Receipt + Map receiptParams = new HashMap<>(baseParams); + receiptParams.put("mode", "receipt"); + receiptParams.put("receiptImages", receiptImages); + job.add(provider, receiptParams); + + return job.render(); + } + + @Override + public byte[] generateViolationHandlingPdf(ReportLedgerQueryBo bo) { + if (bo.getCompanyId() == null) throw new RuntimeException("公司ID不能为空"); + + ReportPrintJob job = reportRenderService.createJob() + .withCompanyId(bo.getCompanyId()); + + ViolationHandlingProvider provider = getProvider(ViolationHandlingProvider.class); + + // Cover + Map coverParams = new HashMap<>(); + coverParams.put("mode", "cover"); + coverParams.put("companyId", bo.getCompanyId()); + job.add(provider, coverParams); + + List ids = parseIds(bo.getIds()); + List list = new ArrayList<>(); + if (!ids.isEmpty()) { + for (Long id : ids) { + if (id == null) continue; + GovViolationHandlingLedgerVo vo = new GovViolationHandlingLedgerVo(); + vo.setId(id); + list.add(vo); + } + } else { + Page page = new Page<>(1, 1000); + List rows = ledgerReportMapper.selectGovViolationHandlingPage(page, null, bo); + if (rows != null) list.addAll(rows); + } + + if (list.isEmpty()) throw new RuntimeException("没有可打印的违章处理记录"); + + for (GovViolationHandlingLedgerVo vo : list) { + if (vo == null || vo.getId() == null) continue; + HotViolationInfoVo info = violationInfoService.queryById(vo.getId()); + if (info == null) continue; + + Map params = new HashMap<>(); + params.put("companyId", bo.getCompanyId()); + params.put("info", info); + + // Detail + params.put("mode", "detail"); + job.add(provider, new HashMap<>(params)); + + // Attachment + params.put("mode", "attachment"); + job.add(provider, new HashMap<>(params)); + } + + return job.render(); + } + + @Override + public byte[] generateGpsViolationPdf(ReportLedgerQueryBo bo) { + if (bo.getCompanyId() == null) throw new RuntimeException("公司ID不能为空"); + + ReportPrintJob job = reportRenderService.createJob() + .withCompanyId(bo.getCompanyId()); + + GpsViolationProvider provider = getProvider(GpsViolationProvider.class); + + // Cover + Map coverParams = new HashMap<>(); + coverParams.put("mode", "cover"); + coverParams.put("companyId", bo.getCompanyId()); + job.add(provider, coverParams); + + List ids = parseIds(bo.getIds()); + List list = new ArrayList<>(); + if (!ids.isEmpty()) { + for (Long id : ids) { + if (id == null) continue; + GovGpsViolationLedgerVo vo = new GovGpsViolationLedgerVo(); + vo.setId(id); + list.add(vo); + } + } else { + Page page = new Page<>(1, 1000); + List rows = ledgerReportMapper.selectGovGpsViolationPage(page, null, bo); + if (rows != null) list.addAll(rows); + } + + if (list.isEmpty()) throw new RuntimeException("没有可打印的车辆动态监控违法行为记录"); + + for (GovGpsViolationLedgerVo vo : list) { + if (vo == null || vo.getId() == null) continue; + HotViolationInfoVo info = violationInfoService.queryById(vo.getId()); + if (info == null) continue; + + Map params = new HashMap<>(); + params.put("companyId", bo.getCompanyId()); + params.put("info", info); + + // Detail + params.put("mode", "detail"); + job.add(provider, new HashMap<>(params)); + + // Attachment + params.put("mode", "attachment"); + job.add(provider, new HashMap<>(params)); + } + + return job.render(); + } + + @Override + public byte[] generateServiceQualityPdf(ReportLedgerQueryBo bo) { + if (bo.getCompanyId() == null) throw new RuntimeException("公司ID不能为空"); + List ids = parseIds(bo.getIds()); + if (ids.isEmpty()) throw new RuntimeException("请选择要打印的服务质量记录"); + + ReportPrintJob job = reportRenderService.createJob() + .withCompanyId(bo.getCompanyId()); + + ServiceQualityProvider provider = getProvider(ServiceQualityProvider.class); + + // Cover + Map coverParams = new HashMap<>(); + coverParams.put("mode", "cover"); + coverParams.put("companyId", bo.getCompanyId()); + job.add(provider, coverParams); + + boolean hasRecord = false; + for (Long id : ids) { + if (id == null) continue; + HotServiceQualityComplaintVo complaint = serviceQualityComplaintService.queryById(id); + if (complaint == null || Objects.equals(complaint.getIsDeleted(), 1L)) continue; + + Map params = new HashMap<>(); + params.put("mode", "detail"); + params.put("companyId", bo.getCompanyId()); + params.put("complaint", complaint); + job.add(provider, params); + hasRecord = true; + } + + if (!hasRecord) throw new RuntimeException("没有可打印的服务质量记录"); + + return job.render(); + } + + @Override + public byte[] generateSafetyMeetingPdf(ReportLedgerQueryBo bo) { + if (bo.getCompanyId() == null) throw new RuntimeException("公司ID不能为空"); + List ids = parseIds(bo.getIds()); + if (ids.isEmpty()) throw new RuntimeException("会议ID不能为空"); + + ReportPrintJob job = reportRenderService.createJob() + .withCompanyId(bo.getCompanyId()); + + SafetyMeetingProvider provider = getProvider(SafetyMeetingProvider.class); + + // Cover + Map coverParams = new HashMap<>(); + coverParams.put("mode", "cover"); + coverParams.put("companyId", bo.getCompanyId()); + job.add(provider, coverParams); + + boolean hasMeeting = false; + for (Long meetingId : ids) { + if (meetingId == null) continue; + HotSecurityMeetingVo meeting = securityMeetingService.queryById(meetingId); + if (meeting == null) continue; + + Map params = new HashMap<>(); + params.put("companyId", bo.getCompanyId()); + params.put("meetingId", meetingId); + + // Detail + params.put("mode", "detail"); + job.add(provider, new HashMap<>(params)); + + // SignIn + params.put("mode", "signIn"); + job.add(provider, new HashMap<>(params)); + + hasMeeting = true; + } + + if (!hasMeeting) throw new RuntimeException("没有可打印的会议记录"); + + return job.render(); + } + + @Override + public byte[] generateStudyDetailPdf(Long companyId, Long trainingId, List userIds) { + if (companyId == null) throw new RuntimeException("公司ID不能为空"); + if (trainingId == null) throw new RuntimeException("培训ID不能为空"); + if (userIds == null || userIds.isEmpty()) throw new RuntimeException("请先选择要打印的人员"); + + Long ticketId = reportVerifyTicketService.createTicketId(); + String verifyUrl = reportVerifyTicketService.buildVerifyUrl("/public/verify/training/studyDetail", ticketId); + String verifyQrBase64 = qrCodeService.generatePngDataUrl(verifyUrl, 300, 300); + + ReportPrintJob job = reportRenderService.createJob() + .withCompanyId(companyId); + + StudyDetailProvider provider = getProvider(StudyDetailProvider.class); + + for (String userId : userIds) { + if (StringUtils.isBlank(userId)) continue; + Map params = new HashMap<>(); + params.put("companyId", companyId); + params.put("trainingId", trainingId); + params.put("userId", userId.trim()); + params.put("verifyUrl", verifyUrl); + params.put("verifyQrBase64", verifyQrBase64); + job.add(provider, params); + } + + job.useSeal(false); + + Map businessParams = new HashMap<>(); + businessParams.put("companyId", companyId); + businessParams.put("trainingId", trainingId); + businessParams.put("userIds", userIds); + String snapshotHtml = reportRenderService.renderJobToHtml(job); + reportVerifyTicketService.issueHtmlSnapshotTicket(ticketId, "STUDY_DETAIL", companyId, cn.hutool.json.JSONUtil.toJsonStr(businessParams), snapshotHtml); + + return officialInformationSealPostProcessor.process(job.render()); + } + + @Override + public byte[] generateOfflineTrainingPhotoPdf(Long companyId, List ids, Integer type) { + if (companyId == null) throw new RuntimeException("公司ID不能为空"); + if (ids == null || ids.isEmpty()) throw new RuntimeException("请先选择要打印的记录"); + + ReportPrintJob job = reportRenderService.createJob() + .withCompanyId(companyId); + + boolean hasData = false; + + if (type == 2) { + // 线下会议 + OfflineMeetingProvider provider = getProvider(OfflineMeetingProvider.class); + for (Long meetingId : ids) { + if (meetingId == null) continue; + Map params = new HashMap<>(); + params.put("meetingId", meetingId); + job.add(provider, params); + hasData = true; + } + } else { + // 默认线下培训 + OfflineTrainingProvider provider = getProvider(OfflineTrainingProvider.class); + for (Long trainingId : ids) { + if (trainingId == null) continue; + Map params = new HashMap<>(); + params.put("trainingId", trainingId); + job.add(provider, params); + hasData = true; + } + } + + if (!hasData) throw new RuntimeException("没有可打印的记录"); + + return job.render(); + } + + // Helper methods + + private List resolveSafetyEducationTrainingIds(ReportLedgerQueryBo bo) { + List result = new ArrayList<>(); + if (bo == null) return result; + if (StringUtils.isNotBlank(bo.getIds())) { + return parseIds(bo.getIds()); + } + Page page = new Page<>(1, 1000); + List rows = ledgerReportMapper.selectGovSafetyEducationTrainingPage(page, bo); + if (rows != null) { + for (GovSafetyEducationTrainingVo vo : rows) { + if (vo != null && vo.getId() != null) { + result.add(vo.getId()); + } + } + } + return result; + } + + private List parseIds(String ids) { + List result = new ArrayList<>(); + if (StringUtils.isBlank(ids)) return result; + for (String s : ids.split(",")) { + String t = s == null ? "" : s.trim(); + if (!t.isEmpty()) { + try { + result.add(Long.parseLong(t)); + } catch (Exception ignored) { + } + } + } + return result; + } + + private String buildDateRangeLabel(Date startDate, Date endDate) { + if (startDate == null && endDate == null) return ""; + String pattern = "yyyy-MM-dd"; + if (startDate != null && endDate != null) { + String start = cn.hutool.core.date.DateUtil.format(startDate, pattern); + String end = cn.hutool.core.date.DateUtil.format(endDate, pattern); + if (start.equals(end)) return start; + return start + " 至 " + end; + } + if (startDate != null) return cn.hutool.core.date.DateUtil.format(startDate, pattern); + return cn.hutool.core.date.DateUtil.format(endDate, pattern); + } + + private List resolveTrainingPhotoImages(String photoAttachments) { + List result = new ArrayList<>(); + if (StringUtils.isBlank(photoAttachments)) return result; + String urls = photoAttachments; + if (!(photoAttachments.contains("http://") || photoAttachments.contains("https://"))) { + urls = ossService.selectUrlByIds(photoAttachments); + } + return splitUrls(urls); + } + + private List resolveInspectionImages(String attachmentUrl) { + List result = new ArrayList<>(); + if (StringUtils.isBlank(attachmentUrl)) return result; + String urls = attachmentUrl; + if (!(attachmentUrl.contains("http://") || attachmentUrl.contains("https://"))) { + urls = ossService.selectUrlByIds(attachmentUrl); + } + return splitUrls(urls); + } + + private List splitUrls(String urls) { + List result = new ArrayList<>(); + if (StringUtils.isBlank(urls)) return result; + for (String s : urls.split(",")) { + String t = s == null ? "" : s.trim(); + if (!t.isEmpty()) result.add(t); + } + return result; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/service/impl/SysReportTemplateServiceImpl.java b/src/main/java/com/hotwj/platform/reportStatistics/service/impl/SysReportTemplateServiceImpl.java new file mode 100644 index 0000000..172071c --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/service/impl/SysReportTemplateServiceImpl.java @@ -0,0 +1,266 @@ +package com.hotwj.platform.reportStatistics.service.impl; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.reportStatistics.domain.SysReportTemplate; +import com.hotwj.platform.reportStatistics.domain.bo.SysReportTemplateBo; +import com.hotwj.platform.reportStatistics.domain.vo.SysReportTemplateVo; +import com.hotwj.platform.reportStatistics.mapper.SysReportTemplateMapper; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.reportStatistics.service.ISysReportTemplateService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 报表模板Service业务层处理 + * + * @author shihongwei + * @date 2024-03-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class SysReportTemplateServiceImpl implements ISysReportTemplateService { + + private final SysReportTemplateMapper baseMapper; + private final List reportDataProviders; + + /** + * 根据模板编码查找对应的 Provider + */ + @Override + public IReportDataProvider findProviderByTemplateCode(String templateCode) { + if (reportDataProviders == null || StringUtils.isBlank(templateCode)) { + return null; + } + for (IReportDataProvider provider : reportDataProviders) { + if (templateCode.equals(provider.getTemplateCode())) { + return provider; + } + } + return null; + } + + /** + * 查询报表模板 + */ + @Override + public SysReportTemplateVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 查询报表模板 + */ + @Override + public SysReportTemplateVo queryByCode(String code) { + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysReportTemplate::getTemplateCode, code)); + } + + /** + * 查询报表模板列表 + */ + @Override + public TableDataInfo queryPageList(SysReportTemplateBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询报表模板列表 + */ + @Override + public List queryList(SysReportTemplateBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysReportTemplateBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getTemplateCode()), SysReportTemplate::getTemplateCode, bo.getTemplateCode()); + lqw.like(StringUtils.isNotBlank(bo.getTemplateName()), SysReportTemplate::getTemplateName, bo.getTemplateName()); + lqw.eq(StringUtils.isNotBlank(bo.getRemark()), SysReportTemplate::getRemark, bo.getRemark()); + return lqw; + } + + /** + * 新增报表模板 + */ + @Override + public Boolean insertByBo(SysReportTemplateBo bo) { + SysReportTemplate add = MapstructUtils.convert(bo, SysReportTemplate.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改报表模板 + */ + @Override + public Boolean updateByBo(SysReportTemplateBo bo) { + SysReportTemplate update = MapstructUtils.convert(bo, SysReportTemplate.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(SysReportTemplate entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 批量删除报表模板 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 同步 Classpath 下的模版到数据库 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void syncTemplatesFromClasspath() { + // 1. Sync from Providers (Source of truth for Title and MockData) + if (reportDataProviders != null) { + for (IReportDataProvider provider : reportDataProviders) { + try { + String code = provider.getTemplateCode(); + if (StringUtils.isBlank(code)) continue; + + SysReportTemplateVo existing = queryByCode(code); + + // Read content + String content = null; + try { + ClassPathResource resource = new ClassPathResource(code); + if (resource.exists()) { + content = IoUtil.read(resource.getInputStream(), StandardCharsets.UTF_8); + } + } catch (Exception e) { + log.warn("Template file not found for provider: {}", provider.getClass().getSimpleName()); + } + + if (existing == null) { + if (StringUtils.isBlank(content)) { + log.warn("Skipping provider {} because template file {} not found", provider.getClass().getSimpleName(), code); + continue; + } + SysReportTemplate template = new SysReportTemplate(); + template.setTemplateCode(code); + template.setTemplateName(provider.getReportTitle()); + template.setContent(content); + template.setMockData(provider.getMockData()); + template.setVersion(1); + template.setRemark("Synced from Provider: " + provider.getClass().getSimpleName()); + baseMapper.insert(template); + log.info("Inserted template from provider: {}", code); + } else { + // Update metadata + SysReportTemplate update = new SysReportTemplate(); + update.setId(existing.getId()); + boolean changed = false; + + // Sync Title + String title = provider.getReportTitle(); + if (StringUtils.isNotBlank(title) && !title.equals(existing.getTemplateName())) { + update.setTemplateName(title); + changed = true; + } + + // Sync MockData if provided + Map mockData = provider.getMockData(); + if (mockData != null && !mockData.isEmpty()) { + update.setMockData(mockData); + changed = true; + } + + // Optional: Sync content if it was empty in DB (unlikely) + if (StringUtils.isBlank(existing.getContent()) && StringUtils.isNotBlank(content)) { + update.setContent(content); + changed = true; + } + + if (changed) { + baseMapper.updateById(update); + log.info("Updated template metadata from provider: {}", code); + } + } + } catch (Exception e) { + log.error("Error syncing provider template: " + provider.getClass().getSimpleName(), e); + } + } + } + + // 2. Scan remaining files (for layouts and fragments) + ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + try { + // Find all .vm files in templates directory + Resource[] resources = resolver.getResources("classpath*:templates/**/*.vm"); + for (Resource resource : resources) { + try { + String path = resource.getURL().getPath(); + // Normalize path separators + path = path.replace("\\", "/"); + + int index = path.lastIndexOf("templates/"); + if (index > -1) { + String code = path.substring(index); // Include "templates/" in the code + + // Check if exists + SysReportTemplateVo exist = queryByCode(code); + if (exist == null) { + String content = IoUtil.read(resource.getInputStream(), StandardCharsets.UTF_8); + SysReportTemplate template = new SysReportTemplate(); + template.setTemplateCode(code); + // Use filename as name + template.setTemplateName(FileUtil.mainName(resource.getFilename())); + template.setContent(content); + template.setVersion(1); + template.setRemark("Synced from classpath scan"); + baseMapper.insert(template); + log.info("Synced template from scan: {}", code); + } + } + } catch (Exception e) { + log.error("Error processing resource: " + resource, e); + } + } + } catch (IOException e) { + log.error("Failed to sync templates", e); + throw new ServiceException("同步模版失败"); + } + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/service/impl/VehicleFilePrintServiceImpl.java b/src/main/java/com/hotwj/platform/reportStatistics/service/impl/VehicleFilePrintServiceImpl.java new file mode 100644 index 0000000..0a1bb4e --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/service/impl/VehicleFilePrintServiceImpl.java @@ -0,0 +1,412 @@ +package com.hotwj.platform.reportStatistics.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.reportStatistics.service.IReportDataProvider; +import com.hotwj.platform.reportStatistics.service.IVehicleFilePrintService; +import com.hotwj.platform.reportStatistics.service.ReportRenderService; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.vo.HotVehicleAnnualReviewVo; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.service.IHotVehicleAnnualReviewService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 车辆档案打印 + * + * @author shihongwei + * @date 2026-01-02 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class VehicleFilePrintServiceImpl implements IVehicleFilePrintService { + + private final IHotVehicleService hotVehicleService; + private final IHotVehicleAnnualReviewService annualReviewService; + private final ReportRenderService reportRenderService; + + // 使用 Map 注入所有 Provider,key 为 BeanName,需要确保 Provider 注册时指定了名称或使用别名 + // 或者更好的是:使用 List 注入,然后构建 Map + // 这里我们假设已经有一个机制可以获取 Provider,类似于 DriverFilePrintServiceImpl + private final Map providerMap; + + // 为了支持 Map 注入,我们需要确保 Spring 能正确注入 + // 如果 IReportDataProvider 实现类没有指定 Bean Name,默认是类名首字母小写 + // 我们可以注入 List,然后在 PostConstruct 中构建 Map,或者使用构造器注入 List + // 让我们修改为注入 List 并构建 Map,这样更稳健 + + private final List allProviders; + private Map typeToProviderMap; + + @jakarta.annotation.PostConstruct + public void init() { + typeToProviderMap = new java.util.HashMap<>(); + for (IReportDataProvider provider : allProviders) { + typeToProviderMap.put(provider.getReportType(), provider); + } + } + + @Override + public byte[] generateBaseInfoPdf(List vehicleIds) throws Exception { + return generatePdf(vehicleIds, TYPE_BASE_INFO); + } + + @Override + public byte[] generateLicensePdf(List vehicleIds) throws Exception { + return generatePdf(vehicleIds, TYPE_LICENSE); + } + + @Override + public byte[] generateInspectionReportPdf(List vehicleIds, String inspectionStartDate, String inspectionEndDate, Integer printEmptyData) throws Exception { + Long companyId = resolveCompanyId(vehicleIds); + IReportDataProvider provider = typeToProviderMap.get(TYPE_INSPECTION_REPORT); + + var job = reportRenderService.createJob(typeToProviderMap::get) + .withCompanyId(companyId) + .useSeal(true); + + boolean allowEmpty = printEmptyData == null || printEmptyData == 1; + Date startDate = null; + Date endDate = null; + if (StrUtil.isNotBlank(inspectionStartDate)) { + startDate = DateUtil.beginOfDay(DateUtil.parse(inspectionStartDate, "yyyy-MM-dd")); + } + if (StrUtil.isNotBlank(inspectionEndDate)) { + endDate = DateUtil.endOfDay(DateUtil.parse(inspectionEndDate, "yyyy-MM-dd")); + } + + if (vehicleIds != null) { + for (Long id : vehicleIds) { + if (id == null) { + continue; + } + HotVehicleAnnualReviewVo review = null; + if (!allowEmpty) { + if (startDate != null || endDate != null) { + review = annualReviewService.queryLatestByVehicleIdAndReviewDateRange(String.valueOf(id), startDate, endDate); + } else { + review = annualReviewService.queryLatestByVehicleId(String.valueOf(id)); + } + if (review == null) { + continue; + } + } + Map params = new java.util.HashMap<>(); + params.put("vehicleId", id); + params.put("inspectionStartDate", inspectionStartDate); + params.put("inspectionEndDate", inspectionEndDate); + if (review != null) { + params.put("review", review); + } + job.add(provider, params); + } + } + + if (!allowEmpty && job.getSegments().isEmpty()) { + throw new RuntimeException("暂无检测报告可打印"); + } + + return job.render(); + } + + @Override + public byte[] generateMaintenanceRecordPdf(List vehicleIds, String maintenanceStartDate, String maintenanceEndDate, Integer printEmptyData) throws Exception { + Long companyId = resolveCompanyId(vehicleIds); + IReportDataProvider provider = typeToProviderMap.get(TYPE_MAINTENANCE_RECORD); + + var job = reportRenderService.createJob(typeToProviderMap::get) + .withCompanyId(companyId) + .useSeal(true); + + boolean allowEmpty = printEmptyData == null || printEmptyData == 1; + if (vehicleIds != null) { + for (Long id : vehicleIds) { + if (id == null) { + continue; + } + Map params = new java.util.HashMap<>(); + params.put("vehicleId", id); + params.put("maintenanceStartDate", maintenanceStartDate); + params.put("maintenanceEndDate", maintenanceEndDate); + params.put("printEmptyData", printEmptyData); + if (!allowEmpty) { + Map data = provider.prepareData(params); + if (data == null) { + continue; + } + } + job.add(provider, params); + } + } + + if (!allowEmpty && job.getSegments().isEmpty()) { + throw new RuntimeException("暂无可打印记录"); + } + + return job.render(); + } + + @Override + public byte[] generateRepairRecordPdf(List vehicleIds, String beginTime, String endTime, Integer printEmptyData) throws Exception { + Long companyId = resolveCompanyId(vehicleIds); + IReportDataProvider provider = typeToProviderMap.get(TYPE_REPAIR_RECORD); + + var job = reportRenderService.createJob(typeToProviderMap::get) + .withCompanyId(companyId) + .useSeal(true); + + boolean allowEmpty = printEmptyData == null || printEmptyData == 1; + if (vehicleIds != null) { + for (Long id : vehicleIds) { + if (id == null) { + continue; + } + Map params = new java.util.HashMap<>(); + params.put("vehicleId", id); + params.put("beginTime", beginTime); + params.put("endTime", endTime); + params.put("printEmptyData", printEmptyData); + if (!allowEmpty) { + Map data = provider.prepareData(params); + if (data == null) { + continue; + } + } + job.add(provider, params); + } + } + + if (!allowEmpty && job.getSegments().isEmpty()) { + throw new RuntimeException("暂无可打印记录"); + } + + return job.render(); + } + + @Override + public byte[] generateTrafficAccidentRecordPdf(List vehicleIds, String beginTime, String endTime, Integer printEmptyData) throws Exception { + Long companyId = resolveCompanyId(vehicleIds); + IReportDataProvider provider = typeToProviderMap.get(TYPE_VEHICLE_ACCIDENT_RECORD); + + var job = reportRenderService.createJob(typeToProviderMap::get) + .withCompanyId(companyId) + .useSeal(true); + + boolean allowEmpty = printEmptyData == null || printEmptyData == 1; + if (vehicleIds != null) { + for (Long id : vehicleIds) { + if (id == null) { + continue; + } + Map params = new java.util.HashMap<>(); + params.put("vehicleId", id); + params.put("beginTime", beginTime); + params.put("endTime", endTime); + params.put("printEmptyData", printEmptyData); + if (!allowEmpty) { + Map data = provider.prepareData(params); + if (data == null) { + continue; + } + } + job.add(provider, params); + } + } + + if (!allowEmpty && job.getSegments().isEmpty()) { + throw new RuntimeException("暂无可打印记录"); + } + + return job.render(); + } + + @Override + public byte[] generateClaimRecordPdf(List vehicleIds, String beginTime, String endTime, Integer printEmptyData) throws Exception { + Long companyId = resolveCompanyId(vehicleIds); + IReportDataProvider provider = typeToProviderMap.get(TYPE_CLAIM_RECORD); + + var job = reportRenderService.createJob(typeToProviderMap::get) + .withCompanyId(companyId) + .useSeal(true); + + boolean allowEmpty = printEmptyData == null || printEmptyData == 1; + if (vehicleIds != null) { + for (Long id : vehicleIds) { + if (id == null) { + continue; + } + Map params = new java.util.HashMap<>(); + params.put("vehicleId", id); + params.put("beginTime", beginTime); + params.put("endTime", endTime); + params.put("printEmptyData", printEmptyData); + if (!allowEmpty) { + Map data = provider.prepareData(params); + if (data == null) { + continue; + } + } + job.add(provider, params); + } + } + + if (!allowEmpty && job.getSegments().isEmpty()) { + throw new RuntimeException("暂无可打印记录"); + } + + return job.render(); + } + + @Override + public byte[] generateMileageRecordPdf(List vehicleIds, String beginTime, String endTime, Integer printEmptyData) throws Exception { + Long companyId = resolveCompanyId(vehicleIds); + IReportDataProvider provider = typeToProviderMap.get(TYPE_MILEAGE_RECORD); + + var job = reportRenderService.createJob(typeToProviderMap::get) + .withCompanyId(companyId) + .useSeal(true); + + boolean allowEmpty = printEmptyData == null || printEmptyData == 1; + if (vehicleIds != null) { + for (Long id : vehicleIds) { + if (id == null) { + continue; + } + Map params = new java.util.HashMap<>(); + params.put("vehicleId", id); + params.put("beginTime", beginTime); + params.put("endTime", endTime); + params.put("printEmptyData", printEmptyData); + if (!allowEmpty) { + Map data = provider.prepareData(params); + if (data == null) { + continue; + } + } + job.add(provider, params); + } + } + + if (!allowEmpty && job.getSegments().isEmpty()) { + throw new RuntimeException("暂无可打印记录"); + } + + return job.render(); + } + + @Override + public byte[] generateOneClickPrintPdf(List vehicleIds, List types, Integer printEmptyData) throws Exception { + Long companyId = resolveCompanyId(vehicleIds); + boolean allowEmpty = printEmptyData == null || printEmptyData == 1; + + var job = reportRenderService.createJob(typeToProviderMap::get) + .withCompanyId(companyId) + .useSeal(true); // 一键打印默认不盖章,或根据需求调整 + + for (Long id : vehicleIds) { + // 这里可以添加封面,目前暂未实现 VehicleCoverProvider,如有需要可添加 + // job.addCover("VEHICLE_COVER", Map.of("vehicleId", id)); + + for (String type : types) { + // 兼容处理:如果是车辆打印中的 ACCIDENT_RECORD,应指代 VEHICLE_ACCIDENT_RECORD + if ("ACCIDENT_RECORD".equals(type)) { + type = IVehicleFilePrintService.TYPE_VEHICLE_ACCIDENT_RECORD; + } + + IReportDataProvider provider = typeToProviderMap.get(type); + if (provider != null) { + Map params = new java.util.HashMap<>(); + params.put("vehicleId", id); + params.put("printEmptyData", printEmptyData); + if (!allowEmpty && shouldCheckEmptyData(type)) { + Map data = provider.prepareData(params); + if (data == null) { + continue; + } + } + job.add(provider, params); + } + } + } + if (!allowEmpty && job.getSegments().isEmpty()) { + throw new RuntimeException("暂无可打印记录"); + } + return job.render(); + } + + private boolean shouldCheckEmptyData(String type) { + return !TYPE_BASE_INFO.equals(type) && !TYPE_LICENSE.equals(type); + } + + private byte[] generatePdf(List vehicleIds, String reportType) { + Long companyId = resolveCompanyId(vehicleIds); + IReportDataProvider provider = typeToProviderMap.get(reportType); + + return reportRenderService.createJob(typeToProviderMap::get) + .withCompanyId(companyId) + .addBatch(provider, convertToStringList(vehicleIds), "vehicleId") + .useSeal(true) + .render(); + } + + private byte[] generatePdfWithParams(List vehicleIds, String reportType, String inspectionStartDate, String inspectionEndDate, Integer printEmptyData) { + Long companyId = resolveCompanyId(vehicleIds); + IReportDataProvider provider = typeToProviderMap.get(reportType); + + var job = reportRenderService.createJob(typeToProviderMap::get) + .withCompanyId(companyId) + .useSeal(true); + + boolean allowEmpty = printEmptyData == null || printEmptyData == 1; + if (vehicleIds != null) { + for (Long id : vehicleIds) { + if (id == null) { + continue; + } + Map params = new java.util.HashMap<>(); + params.put("vehicleId", id); + params.put("inspectionStartDate", inspectionStartDate); + params.put("inspectionEndDate", inspectionEndDate); + params.put("printEmptyData", printEmptyData); + if (!allowEmpty) { + Map data = provider.prepareData(params); + if (data == null) { + continue; + } + } + job.add(provider, params); + } + } + + if (!allowEmpty && job.getSegments().isEmpty()) { + throw new RuntimeException("暂无可打印记录"); + } + + return job.render(); + } + + private Long resolveCompanyId(List vehicleIds) { + if (CollUtil.isEmpty(vehicleIds)) { + return null; + } + Long firstId = vehicleIds.get(0); + HotVehicleVo vehicle = hotVehicleService.queryById(firstId); + if (vehicle == null) { + return null; + } + return vehicle.getCompanyId(); + } + + private List convertToStringList(List list) { + if (list == null) return null; + return list.stream().map(String::valueOf).toList(); + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/template/DateTool.java b/src/main/java/com/hotwj/platform/reportStatistics/template/DateTool.java new file mode 100644 index 0000000..429b910 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/template/DateTool.java @@ -0,0 +1,49 @@ +package com.hotwj.platform.reportStatistics.template; + +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; + +/** + * 日期工具类 + * + * @author shihongwei + * @date 2026-1-3 + */ +public class DateTool { + + /** + * 格式化日期 + * + * @param pattern 日期格式 + * @param dateObj 日期对象 + * @return 格式化后的日期字符串 + */ + public String format(String pattern, Object dateObj) { + if (dateObj == null) return ""; + Date date = toDate(dateObj); + if (date == null) return ""; + return new SimpleDateFormat(pattern).format(date); + } + + /** + * 将日期对象转换为Date类型 + * + * @param obj 日期对象 + * @return Date类型日期 + */ + private Date toDate(Object obj) { + if (obj instanceof Date) { + return (Date) obj; + } + if (obj instanceof LocalDate) { + return Date.from(((LocalDate) obj).atStartOfDay(ZoneId.systemDefault()).toInstant()); + } + if (obj instanceof LocalDateTime) { + return Date.from(((LocalDateTime) obj).atZone(ZoneId.systemDefault()).toInstant()); + } + return null; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/template/ReportVelocityTool.java b/src/main/java/com/hotwj/platform/reportStatistics/template/ReportVelocityTool.java new file mode 100644 index 0000000..eb3fa9b --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/template/ReportVelocityTool.java @@ -0,0 +1,120 @@ +package com.hotwj.platform.reportStatistics.template; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import org.dromara.common.core.service.DictService; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +/** + * Velocity 模版工具类 + *

+ * 提供日期格式化、字典翻译、OSS URL转换等常用功能,供模版直接调用。 + * 在模版中通过 $tool 调用,例如 $tool.formatDate($date) + *

+ * + * @author shihongwei + */ +public class ReportVelocityTool { + + private static final OssService ossService = SpringUtils.getBean(OssService.class); + private static final DictService dictService = SpringUtils.getBean(DictService.class); + + /** + * 格式化日期 + * + * @param date 日期对象 + * @param format 格式,如 "yyyy-MM-dd" + */ + public String formatDate(Date date, String format) { + if (date == null) { + return ""; + } + return DateUtil.format(date, format); + } + + /** + * 格式化日期 (默认 yyyy-MM-dd) + */ + public String formatDate(Date date) { + return formatDate(date, "yyyy-MM-dd"); + } + + /** + * 获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + */ + public String getDictLabel(String dictType, Object dictValue) { + if (dictValue == null) { + return ""; + } + String label = dictService.getDictLabel(dictType, String.valueOf(dictValue)); + return StringUtils.isBlank(label) ? "" : label; + } + + /** + * 获取字典标签,如果为空返回原值 + */ + public String getDictLabelOrValue(String dictType, Object dictValue) { + if (dictValue == null) { + return ""; + } + String val = String.valueOf(dictValue); + String label = dictService.getDictLabel(dictType, val); + return StringUtils.isBlank(label) ? val : label; + } + + /** + * 获取单个 OSS URL + * + * @param ossId OSS ID + */ + public String getOssUrl(Object ossId) { + if (ossId == null) { + return ""; + } + String idStr = String.valueOf(ossId); + if (StrUtil.isBlank(idStr)) { + return ""; + } + return ossService.selectUrlByIds(idStr); + } + + /** + * 获取 OSS URL 列表 (处理逗号分隔的 ID) + * + * @param ossIds OSS ID 字符串 (逗号分隔) + */ + public List getOssUrls(Object ossIds) { + if (ossIds == null) { + return Collections.emptyList(); + } + String idsStr = String.valueOf(ossIds); + if (StrUtil.isBlank(idsStr)) { + return Collections.emptyList(); + } + + String urls = ossService.selectUrlByIds(idsStr); + if (StrUtil.isBlank(urls)) { + return Collections.emptyList(); + } + + List list = new ArrayList<>(); + String[] parts = urls.split(","); + for (String p : parts) { + String v = p.trim(); + if (StrUtil.isNotBlank(v)) { + list.add(v); + } + } + return list; + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/template/VelocityTemplateRenderer.java b/src/main/java/com/hotwj/platform/reportStatistics/template/VelocityTemplateRenderer.java new file mode 100644 index 0000000..48cb433 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/template/VelocityTemplateRenderer.java @@ -0,0 +1,30 @@ +package com.hotwj.platform.reportStatistics.template; + +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.runtime.RuntimeConstants; +import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; +import org.springframework.stereotype.Component; + +import java.io.StringWriter; + +@Component +public class VelocityTemplateRenderer { + private final VelocityEngine engine; + + public VelocityTemplateRenderer() { + engine = new VelocityEngine(); + engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "class"); + engine.setProperty("class.resource.loader.class", ClasspathResourceLoader.class.getName()); + engine.setProperty(RuntimeConstants.INPUT_ENCODING, "UTF-8"); + engine.init(); + } + + public String render(String templatePath, VelocityContext ctx) { + Template tpl = engine.getTemplate(templatePath, "UTF-8"); + StringWriter sw = new StringWriter(); + tpl.merge(ctx, sw); + return sw.toString(); + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/verify/config/AppVerifyProperties.java b/src/main/java/com/hotwj/platform/reportStatistics/verify/config/AppVerifyProperties.java new file mode 100644 index 0000000..fd3ef64 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/verify/config/AppVerifyProperties.java @@ -0,0 +1,24 @@ +package com.hotwj.platform.reportStatistics.verify.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "app") +public class AppVerifyProperties { + + private String webBaseUrl; + + private Verify verify = new Verify(); + + @Data + public static class Verify { + private Boolean enabled = Boolean.TRUE; + private String secret; + private Integer ticketExpireDays = 1095; + private Boolean snapshotMode = Boolean.TRUE; + } +} + diff --git a/src/main/java/com/hotwj/platform/reportStatistics/verify/controller/ReportVerifyController.java b/src/main/java/com/hotwj/platform/reportStatistics/verify/controller/ReportVerifyController.java new file mode 100644 index 0000000..a8cf4c3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/verify/controller/ReportVerifyController.java @@ -0,0 +1,71 @@ +package com.hotwj.platform.reportStatistics.verify.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import com.hotwj.platform.reportStatistics.verify.domain.HotReportVerifyTicket; +import com.hotwj.platform.reportStatistics.verify.service.ReportVerifyTicketService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.exception.ServiceException; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@SaIgnore +@RestController +@RequiredArgsConstructor +@RequestMapping("/public/verify/training") +public class ReportVerifyController { + + private final ReportVerifyTicketService reportVerifyTicketService; + + @GetMapping(value = "/monthly", produces = MediaType.TEXT_HTML_VALUE + ";charset=UTF-8") + public String verifyTrainingMonthly(@RequestParam("ticket") Long ticket) { + try { + HotReportVerifyTicket t = reportVerifyTicketService.loadAndValidate(ticket, "TP_MONTHLY"); + return renderHtml(t); + } catch (ServiceException e) { + return renderError(e.getMessage()); + } + } + + @GetMapping(value = "/studyRecord", produces = MediaType.TEXT_HTML_VALUE + ";charset=UTF-8") + public String verifyStudyRecord(@RequestParam("ticket") Long ticket) { + try { + HotReportVerifyTicket t = reportVerifyTicketService.loadAndValidate(ticket, "STUDY_RECORD"); + return renderHtml(t); + } catch (ServiceException e) { + return renderError(e.getMessage()); + } + } + + @GetMapping(value = "/studyDetail", produces = MediaType.TEXT_HTML_VALUE + ";charset=UTF-8") + public String verifyStudyDetail(@RequestParam("ticket") Long ticket) { + try { + HotReportVerifyTicket t = reportVerifyTicketService.loadAndValidate(ticket, "STUDY_DETAIL"); + return renderHtml(t); + } catch (ServiceException e) { + return renderError(e.getMessage()); + } + } + + private String renderHtml(HotReportVerifyTicket ticket) { + if (ticket == null) { + throw new ServiceException("验真票据不存在"); + } + String html = ticket.getSnapshotContent(); + return reportVerifyTicketService.normalizeSnapshotHtmlForMobile(html); + } + + private String renderError(String message) { + String msg = message == null ? "验真失败" : message; + String html = "" + + "验真失败" + + "
" + + "

验真失败

" + + "
" + + msg + + "
"; + return reportVerifyTicketService.normalizeSnapshotHtmlForMobile(html); + } +} diff --git a/src/main/java/com/hotwj/platform/reportStatistics/verify/domain/HotReportVerifyTicket.java b/src/main/java/com/hotwj/platform/reportStatistics/verify/domain/HotReportVerifyTicket.java new file mode 100644 index 0000000..bd72506 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/verify/domain/HotReportVerifyTicket.java @@ -0,0 +1,50 @@ +package com.hotwj.platform.reportStatistics.verify.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_report_verify_ticket") +public class HotReportVerifyTicket extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.INPUT) + private Long id; + + private Long companyId; + + private String module; + + private String businessParams; + + private String snapshotType; + + private String snapshotContent; + + private String snapshotHash; + + private Date issuedAt; + + private Date expireAt; + + private Integer status; + + private String nonce; + + private String sign; + + @TableLogic + private Long isDeleted; +} + diff --git a/src/main/java/com/hotwj/platform/reportStatistics/verify/mapper/HotReportVerifyTicketMapper.java b/src/main/java/com/hotwj/platform/reportStatistics/verify/mapper/HotReportVerifyTicketMapper.java new file mode 100644 index 0000000..6d232a2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/verify/mapper/HotReportVerifyTicketMapper.java @@ -0,0 +1,10 @@ +package com.hotwj.platform.reportStatistics.verify.mapper; + +import com.hotwj.platform.reportStatistics.verify.domain.HotReportVerifyTicket; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +@Mapper +public interface HotReportVerifyTicketMapper extends BaseMapperPlus { +} + diff --git a/src/main/java/com/hotwj/platform/reportStatistics/verify/service/QrCodeService.java b/src/main/java/com/hotwj/platform/reportStatistics/verify/service/QrCodeService.java new file mode 100644 index 0000000..904f75d --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/verify/service/QrCodeService.java @@ -0,0 +1,21 @@ +package com.hotwj.platform.reportStatistics.verify.service; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.img.ImgUtil; +import cn.hutool.extra.qrcode.QrCodeUtil; +import org.springframework.stereotype.Service; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; + +@Service +public class QrCodeService { + + public String generatePngDataUrl(String content, int width, int height) { + BufferedImage image = QrCodeUtil.generate(content, width, height); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ImgUtil.writePng(image, out); + return "data:image/png;base64," + Base64.encode(out.toByteArray()); + } +} + diff --git a/src/main/java/com/hotwj/platform/reportStatistics/verify/service/ReportVerifyTicketService.java b/src/main/java/com/hotwj/platform/reportStatistics/verify/service/ReportVerifyTicketService.java new file mode 100644 index 0000000..707e7f4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/reportStatistics/verify/service/ReportVerifyTicketService.java @@ -0,0 +1,250 @@ +package com.hotwj.platform.reportStatistics.verify.service; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.hotwj.platform.reportStatistics.verify.config.AppVerifyProperties; +import com.hotwj.platform.reportStatistics.verify.domain.HotReportVerifyTicket; +import com.hotwj.platform.reportStatistics.verify.mapper.HotReportVerifyTicketMapper; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.exception.ServiceException; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; + +@Service +@RequiredArgsConstructor +public class ReportVerifyTicketService { + + private static final String SEAL_RESOURCE = "images/officialInformationSeal.png"; + private static volatile String cachedSealDataUrl; + private final HotReportVerifyTicketMapper ticketMapper; + private final AppVerifyProperties appVerifyProperties; + + public Long createTicketId() { + return IdWorker.getId(); + } + + public Long issueHtmlSnapshotTicket(Long ticketId, String module, Long companyId, String businessParamsJson, String snapshotHtml) { + if (Boolean.FALSE.equals(appVerifyProperties.getVerify().getEnabled())) { + throw new ServiceException("验真功能未开启"); + } + if (Boolean.FALSE.equals(appVerifyProperties.getVerify().getSnapshotMode())) { + throw new ServiceException("当前仅支持验真快照模式"); + } + if (StrUtil.isBlank(appVerifyProperties.getVerify().getSecret())) { + throw new ServiceException("验真签名密钥未配置"); + } + if (StrUtil.isBlank(module)) { + throw new ServiceException("验真模块不能为空"); + } + if (companyId == null) { + throw new ServiceException("公司ID不能为空"); + } + if (StrUtil.isBlank(snapshotHtml)) { + throw new ServiceException("快照内容不能为空"); + } + + Long id = ticketId == null ? IdWorker.getId() : ticketId; + Date issuedAt = truncateToSeconds(new Date()); + Date expireAt = buildExpireAt(issuedAt); + String nonce = cn.hutool.core.util.IdUtil.fastSimpleUUID(); + String snapshotHash = SecureUtil.sha256(snapshotHtml); + String sign = sign(module, companyId, businessParamsJson, snapshotHash, issuedAt, nonce); + + HotReportVerifyTicket entity = new HotReportVerifyTicket(); + entity.setId(id); + entity.setCompanyId(companyId); + entity.setModule(module); + entity.setBusinessParams(businessParamsJson); + entity.setSnapshotType("HTML"); + entity.setSnapshotContent(snapshotHtml); + entity.setSnapshotHash(snapshotHash); + entity.setIssuedAt(issuedAt); + entity.setExpireAt(expireAt); + entity.setStatus(1); + entity.setNonce(nonce); + entity.setSign(sign); + entity.setIsDeleted(0L); + + ticketMapper.insert(entity); + return id; + } + + public Long issueHtmlSnapshotTicket(String module, Long companyId, String businessParamsJson, String snapshotHtml) { + return issueHtmlSnapshotTicket(null, module, companyId, businessParamsJson, snapshotHtml); + } + + public HotReportVerifyTicket loadAndValidate(Long ticketId, String expectedModule) { + if (ticketId == null) { + throw new ServiceException("ticket不能为空"); + } + HotReportVerifyTicket ticket = ticketMapper.selectById(ticketId); + if (ticket == null || (ticket.getIsDeleted() != null && ticket.getIsDeleted() == 1L)) { + throw new ServiceException("验真票据不存在"); + } + if (ticket.getStatus() == null || ticket.getStatus() != 1) { + throw new ServiceException("验真票据已作废"); + } + if (StrUtil.isNotBlank(expectedModule) && !StrUtil.equals(expectedModule, ticket.getModule())) { + throw new ServiceException("验真票据类型不匹配"); + } + if (ticket.getExpireAt() != null && ticket.getExpireAt().before(new Date())) { + throw new ServiceException("验真票据已过期"); + } + if (StrUtil.isBlank(appVerifyProperties.getVerify().getSecret())) { + throw new ServiceException("验真签名密钥未配置"); + } + String snapshotHash = SecureUtil.sha256(ticket.getSnapshotContent() == null ? "" : ticket.getSnapshotContent()); + if (!StrUtil.equals(snapshotHash, ticket.getSnapshotHash())) { + throw new ServiceException("验真快照校验失败"); + } + String expectedSign = sign(ticket.getModule(), ticket.getCompanyId(), ticket.getBusinessParams(), ticket.getSnapshotHash(), ticket.getIssuedAt(), ticket.getNonce()); + if (!StrUtil.equals(expectedSign, ticket.getSign())) { + throw new ServiceException("验真签名校验失败"); + } + return ticket; + } + + public String buildVerifyUrl(String path, Long ticketId) { + String baseUrl = appVerifyProperties.getWebBaseUrl(); + if (StrUtil.isBlank(baseUrl)) { + throw new ServiceException("app.web-base-url 未配置"); + } + String normalizedBase = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl; + String normalizedPath = path.startsWith("/") ? path : ("/" + path); + return normalizedBase + normalizedPath + "?ticket=" + ticketId; + } + + public String normalizeSnapshotHtmlForMobile(String html) { + if (StrUtil.isBlank(html)) { + return html; + } + String out = ensureViewport(html); + return injectSealTopRight(out); + } + + private String ensureViewport(String html) { + if (html.contains("name=\"viewport\"")) { + return html; + } + int headIdx = html.indexOf(""); + if (headIdx >= 0) { + int insertPos = headIdx + "".length(); + return html.substring(0, insertPos) + + "" + + html.substring(insertPos); + } + return html; + } + + private String injectSealTopRight(String html) { + if (StrUtil.isBlank(html)) { + return html; + } + if (html.contains("id=\"verify-seal-img\"")) { + return html; + } + String sealDataUrl = getSealDataUrl(); + if (StrUtil.isBlank(sealDataUrl)) { + return html; + } + + String style = ""; + + String img = "\"seal\"/"; + + String out = html; + if (!out.contains("id=\"verify-seal-style\"")) { + int headIdx = out.indexOf(""); + if (headIdx >= 0) { + out = out.substring(0, headIdx) + style + out.substring(headIdx); + } else { + int headOpen = out.indexOf(""); + if (headOpen >= 0) { + int insertPos = headOpen + "".length(); + out = out.substring(0, insertPos) + style + out.substring(insertPos); + } + } + } + + int bodyIdx = out.indexOf("= 0) { + int bodyEnd = out.indexOf(">", bodyIdx); + if (bodyEnd >= 0) { + int insertPos = bodyEnd + 1; + out = out.substring(0, insertPos) + img + out.substring(insertPos); + return out; + } + } + + return "" + + style + + "" + + img + + out + + ""; + } + + private String getSealDataUrl() { + if (cachedSealDataUrl != null) { + return cachedSealDataUrl; + } + synchronized (ReportVerifyTicketService.class) { + if (cachedSealDataUrl != null) { + return cachedSealDataUrl; + } + try { + ClassPathResource resource = new ClassPathResource(SEAL_RESOURCE); + if (!resource.exists()) { + return null; + } + try (InputStream is = resource.getInputStream()) { + byte[] bytes = is.readAllBytes(); + cachedSealDataUrl = "data:image/png;base64," + Base64.encode(bytes); + return cachedSealDataUrl; + } + } catch (Exception e) { + return null; + } + } + } + + private Date buildExpireAt(Date issuedAt) { + Integer days = appVerifyProperties.getVerify().getTicketExpireDays(); + if (days == null || days <= 0) { + return null; + } + LocalDateTime expire = LocalDateTime.ofInstant(issuedAt.toInstant(), ZoneId.systemDefault()).plusDays(days); + return Date.from(expire.atZone(ZoneId.systemDefault()).toInstant()); + } + + private String sign(String module, Long companyId, String businessParamsJson, String snapshotHash, Date issuedAt, String nonce) { + String secret = appVerifyProperties.getVerify().getSecret(); + long issuedSeconds = issuedAt == null ? 0L : (issuedAt.getTime() / 1000L); // 以秒对齐,避免DATETIME毫秒精度丢失导致验签失败 + String payload = StrUtil.join("|", + StrUtil.blankToDefault(module, ""), + companyId == null ? "" : String.valueOf(companyId), + StrUtil.blankToDefault(businessParamsJson, ""), + StrUtil.blankToDefault(snapshotHash, ""), + String.valueOf(issuedSeconds), + StrUtil.blankToDefault(nonce, "") + ); + return SecureUtil.hmacSha256(secret.getBytes(StandardCharsets.UTF_8)).digestHex(payload); + } + + private Date truncateToSeconds(Date d) { + if (d == null) return null; + long sec = (d.getTime() / 1000L) * 1000L; + return new Date(sec); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/controller/HotCompanyBankAccountController.java b/src/main/java/com/hotwj/platform/resourceManagement/company/controller/HotCompanyBankAccountController.java new file mode 100644 index 0000000..0b5b00b --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/controller/HotCompanyBankAccountController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.resourceManagement.company.controller; + +import com.hotwj.platform.resourceManagement.company.domain.bo.HotCompanyBankAccountBo; +import com.hotwj.platform.resourceManagement.company.domain.vo.HotCompanyBankAccountVo; +import com.hotwj.platform.resourceManagement.company.service.IHotCompanyBankAccountService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 公司银行账户 + * + * @author shihongwei + * @date 2025-12-08 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/companyBankAccount/companyBankAccount") +@Tag(name = "公司银行账户", description = "公司银行账户增删改查") +public class HotCompanyBankAccountController extends BaseController { + + private final IHotCompanyBankAccountService hotCompanyBankAccountService; + + /** + * 查询公司银行账户列表 + */ + //@SaCheckPermission("companyBankAccount:companyBankAccount:list") + @GetMapping("/list") + @Operation(summary = "分页查询公司银行账户列表") + public TableDataInfo list(HotCompanyBankAccountBo bo, PageQuery pageQuery) { + return hotCompanyBankAccountService.queryPageList(bo, pageQuery); + } + + /** + * 导出公司银行账户列表 + */ + //@SaCheckPermission("companyBankAccount:companyBankAccount:export") + @Log(title = "公司银行账户", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出公司银行账户列表") + public void export(HotCompanyBankAccountBo bo, HttpServletResponse response) { + List list = hotCompanyBankAccountService.queryList(bo); + ExcelUtil.exportExcel(list, "公司银行账户", HotCompanyBankAccountVo.class, response); + } + + /** + * 获取公司银行账户详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("companyBankAccount:companyBankAccount:query") + @GetMapping("/{id}") + @Operation(summary = "查询公司银行账户详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotCompanyBankAccountService.queryById(id)); + } + + /** + * 新增公司银行账户 + */ + //@SaCheckPermission("companyBankAccount:companyBankAccount:add") + @Log(title = "公司银行账户", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增公司银行账户") + public R add(@Validated(AddGroup.class) @RequestBody HotCompanyBankAccountBo bo) { + return toAjax(hotCompanyBankAccountService.insertByBo(bo)); + } + + /** + * 修改公司银行账户 + */ + //@SaCheckPermission("companyBankAccount:companyBankAccount:edit") + @Log(title = "公司银行账户", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改公司银行账户") + public R edit(@Validated(EditGroup.class) @RequestBody HotCompanyBankAccountBo bo) { + return toAjax(hotCompanyBankAccountService.updateByBo(bo)); + } + + /** + * 删除公司银行账户 + * + * @param ids 主键串 + */ + //@SaCheckPermission("companyBankAccount:companyBankAccount:remove") + @Log(title = "公司银行账户", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除公司银行账户") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotCompanyBankAccountService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/controller/HotCompanyThirdPlatformController.java b/src/main/java/com/hotwj/platform/resourceManagement/company/controller/HotCompanyThirdPlatformController.java new file mode 100644 index 0000000..6de6fcb --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/controller/HotCompanyThirdPlatformController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.resourceManagement.company.controller; + +import com.hotwj.platform.resourceManagement.company.domain.bo.HotCompanyThirdPlatformBo; +import com.hotwj.platform.resourceManagement.company.domain.vo.HotCompanyThirdPlatformVo; +import com.hotwj.platform.resourceManagement.company.service.IHotCompanyThirdPlatformService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 公司第三方平台账号 + * + * @author shihongwei + * @date 2025-12-08 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/companyThirdPlatform/companyThirdPlatform") +@Tag(name = "公司第三方平台账号", description = "公司第三方平台账号增删改查") +public class HotCompanyThirdPlatformController extends BaseController { + + private final IHotCompanyThirdPlatformService hotCompanyThirdPlatformService; + + /** + * 查询公司第三方平台账号列表 + */ + //@SaCheckPermission("companyThirdPlatform:companyThirdPlatform:list") + @GetMapping("/list") + @Operation(summary = "分页查询公司第三方平台账号列表") + public TableDataInfo list(HotCompanyThirdPlatformBo bo, PageQuery pageQuery) { + return hotCompanyThirdPlatformService.queryPageList(bo, pageQuery); + } + + /** + * 导出公司第三方平台账号列表 + */ + //@SaCheckPermission("companyThirdPlatform:companyThirdPlatform:export") + @Log(title = "公司第三方平台账号", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出公司第三方平台账号列表") + public void export(HotCompanyThirdPlatformBo bo, HttpServletResponse response) { + List list = hotCompanyThirdPlatformService.queryList(bo); + ExcelUtil.exportExcel(list, "公司第三方平台账号", HotCompanyThirdPlatformVo.class, response); + } + + /** + * 获取公司第三方平台账号详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("公司第三方平台账号:companyThirdPlatform:query") + @GetMapping("/{id}") + @Operation(summary = "查询公司第三方平台账号详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotCompanyThirdPlatformService.queryById(id)); + } + + /** + * 新增公司第三方平台账号 + */ + //@SaCheckPermission("companyThirdPlatform:companyThirdPlatform:add") + @Log(title = "公司第三方平台账号", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增公司第三方平台账号") + public R add(@Validated(AddGroup.class) @RequestBody HotCompanyThirdPlatformBo bo) { + return toAjax(hotCompanyThirdPlatformService.insertByBo(bo)); + } + + /** + * 修改公司第三方平台账号 + */ + //@SaCheckPermission("companyThirdPlatform:companyThirdPlatform:edit") + @Log(title = "公司第三方平台账号", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改公司第三方平台账号") + public R edit(@Validated(EditGroup.class) @RequestBody HotCompanyThirdPlatformBo bo) { + return toAjax(hotCompanyThirdPlatformService.updateByBo(bo)); + } + + /** + * 删除公司第三方平台账号 + * + * @param ids 主键串 + */ + //@SaCheckPermission("companyThirdPlatform:companyThirdPlatform:remove") + @Log(title = "公司第三方平台账号", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除公司第三方平台账号") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotCompanyThirdPlatformService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/controller/SysCompanyController.java b/src/main/java/com/hotwj/platform/resourceManagement/company/controller/SysCompanyController.java new file mode 100644 index 0000000..97f9b92 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/controller/SysCompanyController.java @@ -0,0 +1,197 @@ +package com.hotwj.platform.resourceManagement.company.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.annotation.SaIgnore; +import cn.hutool.core.collection.CollUtil; +import com.hotwj.platform.resourceManagement.company.domain.bo.SysCompanyBo; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.translation.constant.TransConstant; +import org.dromara.common.translation.core.TranslationInterface; +import org.dromara.common.translation.core.handler.TranslationHandler; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 企业基本信息 + * + * @author shihongwei + * @date 2025-12-02 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/company") +@Tag(name = "企业基本信息", description = "企业基本信息增删改查") +public class SysCompanyController extends BaseController { + + private final ISysCompanyService sysCompanyService; + + /** + * 查询企业基本信息列表 + */ + //@SaCheckPermission("resourceManagement:company:list") + @GetMapping("/list") + @Operation(summary = "分页查询企业基本信息列表") + public TableDataInfo list(SysCompanyBo bo, PageQuery pageQuery) { + return sysCompanyService.queryPageList(bo, pageQuery); + } + + /** + * 根据邀请码查询企业名称 + */ + @SaIgnore + @GetMapping("/getCompanyNameByInviteCode") + @Operation(summary = "根据邀请码查询企业名称") + public R getCompanyNameByInviteCode(@RequestParam("companyId") Long companyId, @RequestParam("inviteCode") String inviteCode) { + return R.ok("操作成功", sysCompanyService.checkInviteCode(companyId, inviteCode)); + } + + /** + * 重置所有企业邀请码(管理员功能) + */ + //@SaCheckPermission("resourceManagement:company:resetInviteCode") + @Log(title = "重置所有企业邀请码", businessType = BusinessType.UPDATE) + @PostMapping("/resetAllInviteCodes") + @Operation(summary = "重置所有企业邀请码") + public R resetAllInviteCodes() { + sysCompanyService.resetAllInviteCodes(); + return R.ok("重置成功,请稍后查看企业列表"); + } + + + //@SaCheckPermission("resourceManagement:company:list") + @GetMapping("/listByRegion") + @Operation(summary = "按多个区划分页查询企业基本信息列表") + public TableDataInfo listByRegion(SysCompanyBo bo, + @RequestParam(value = "regionCodes", required = false) String regionCodes, + @RequestParam(value = "ids", required = false) String ids, + PageQuery pageQuery) { + java.util.List codes = null; + java.util.List idList = null; + if (org.dromara.common.core.utils.StringUtils.isNotBlank(regionCodes)) { + codes = java.util.Arrays.stream(regionCodes.split(",")) + .map(String::trim) + .filter(org.dromara.common.core.utils.StringUtils::isNotBlank) + .distinct() + .toList(); + bo.setRegionCode(null); + } + if (org.dromara.common.core.utils.StringUtils.isNotBlank(ids)) { + idList = java.util.Arrays.stream(ids.split(",")) + .map(String::trim) + .filter(org.dromara.common.core.utils.StringUtils::isNotBlank) + .map(Long::valueOf) + .distinct() + .toList(); + } + if ((codes != null && !codes.isEmpty()) || (idList != null && !idList.isEmpty())) { + return sysCompanyService.queryPageListByRegionAndIds(bo, pageQuery, codes, idList); + } else { + return sysCompanyService.queryPageList(bo, pageQuery); + } + } + + /** + * 导出企业基本信息列表 + */ + //@SaCheckPermission("resourceManagement:company:export") + @Log(title = "企业基本信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出企业基本信息列表") + public void export(SysCompanyBo bo, + @RequestParam(value = "ids", required = false) String ids, + HttpServletResponse response) { + List idList = null; + if (org.dromara.common.core.utils.StringUtils.isNotBlank(ids)) { + idList = java.util.Arrays.stream(ids.split(",")) + .map(String::trim) + .filter(org.dromara.common.core.utils.StringUtils::isNotBlank) + .map(Long::valueOf) + .distinct() + .toList(); + } + List list = sysCompanyService.queryList(bo, idList); + if (CollUtil.isNotEmpty(list)) { + TranslationInterface trans = TranslationHandler.TRANSLATION_MAPPER.get(TransConstant.REGION_CODE_TO_NAME); + for (int i = 0; i < list.size(); i++) { + SysCompanyVo vo = list.get(i); + vo.setIndex(i + 1); + if (trans != null && org.dromara.common.core.utils.StringUtils.isNotBlank(vo.getRegionCode())) { + String label = (String) trans.translation(vo.getRegionCode(), null); + vo.setRegionLabel(label); + } + } + } + ExcelUtil.exportExcel(list, "企业基本信息", SysCompanyVo.class, response); + } + + /** + * 获取企业基本信息详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:company:query") + @GetMapping("/{id}") + @Operation(summary = "查询企业基本信息详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(sysCompanyService.queryById(id)); + } + + /** + * 新增企业基本信息 + */ + //@SaCheckPermission("resourceManagement:company:add") + @Log(title = "企业基本信息", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增企业基本信息") + public R add(@Validated(AddGroup.class) @RequestBody SysCompanyBo bo) { + return toAjax(sysCompanyService.insertByBo(bo)); + } + + /** + * 修改企业基本信息 + */ + //@SaCheckPermission("resourceManagement:company:edit") + @Log(title = "企业基本信息", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改企业基本信息") + public R edit(@Validated(EditGroup.class) @RequestBody SysCompanyBo bo) { + return toAjax(sysCompanyService.updateByBo(bo)); + } + + /** + * 删除企业基本信息 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:company:remove") + @Log(title = "企业基本信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除企业基本信息") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(sysCompanyService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/controller/SysCompanyRoleController.java b/src/main/java/com/hotwj/platform/resourceManagement/company/controller/SysCompanyRoleController.java new file mode 100644 index 0000000..62a163b --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/controller/SysCompanyRoleController.java @@ -0,0 +1,72 @@ +package com.hotwj.platform.resourceManagement.company.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.hotwj.platform.resourceManagement.company.domain.bo.SysCompanyRoleBo; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyRoleVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyRoleService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/companyRole") +@Tag(name = "企业角色", description = "企业角色权限配置") +public class SysCompanyRoleController extends BaseController { + + private final ISysCompanyRoleService sysCompanyRoleService; + + // //@SaCheckPermission("resourceManagement:companyRole:list") + @GetMapping("/list") + @Operation(summary = "分页查询企业角色列表") + public TableDataInfo list(SysCompanyRoleBo bo, PageQuery pageQuery) { + return sysCompanyRoleService.queryPageList(bo, pageQuery); + } + + //@SaCheckPermission("resourceManagement:companyRole:query") + @GetMapping("/{id}") + @Operation(summary = "查询企业角色详情") + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { + return R.ok(sysCompanyRoleService.queryById(id)); + } + + //@SaCheckPermission("resourceManagement:companyRole:add") + @Log(title = "企业角色", businessType = BusinessType.INSERT) + @PostMapping() + @Operation(summary = "新增企业角色") + public R add(@Validated(AddGroup.class) @RequestBody SysCompanyRoleBo bo) { + return toAjax(sysCompanyRoleService.insertByBo(bo)); + } + + //@SaCheckPermission("resourceManagement:companyRole:edit") + @Log(title = "企业角色", businessType = BusinessType.UPDATE) + @PutMapping() + @Operation(summary = "修改企业角色") + public R edit(@Validated(EditGroup.class) @RequestBody SysCompanyRoleBo bo) { + return toAjax(sysCompanyRoleService.updateByBo(bo)); + } + + //@SaCheckPermission("resourceManagement:companyRole:remove") + @Log(title = "企业角色", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除企业角色") + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { + return toAjax(sysCompanyRoleService.deleteWithValidByIds(List.of(ids), true)); + } +} + diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/controller/SysUserLoginPortController.java b/src/main/java/com/hotwj/platform/resourceManagement/company/controller/SysUserLoginPortController.java new file mode 100644 index 0000000..0a5b93a --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/controller/SysUserLoginPortController.java @@ -0,0 +1,77 @@ +package com.hotwj.platform.resourceManagement.company.controller; + +import cn.hutool.core.io.FileUtil; +import com.hotwj.platform.resourceManagement.company.domain.SysUserLoginPort; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.file.MimeTypeUtils; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.dromara.system.domain.vo.SysOssVo; +import org.dromara.system.service.ISysOssService; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Arrays; + +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/userLoginPort") +@Tag(name = "用户登录端口", description = "用户登录端口管理") +public class SysUserLoginPortController extends BaseController { + + private final ISysUserLoginPortService sysUserLoginPortService; + private final ISysOssService ossService; + + /** + * 头像上传 + */ + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping(value = "/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @Operation(summary = "用户头像上传") + public R avatar(@RequestPart("avatarfile") MultipartFile avatarfile) { + if (!avatarfile.isEmpty()) { + String extension = FileUtil.extName(avatarfile.getOriginalFilename()); + if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) { + return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式"); + } + SysOssVo oss = ossService.upload(avatarfile); + Long avatarId = oss.getOssId(); + String avatarUrl = oss.getUrl(); + + // 获取当前登录用户的端口ID + Long portId = LoginHelper.getLoginUser().getCompanyRoleId(); + if (portId == null) { + return R.fail("无法获取当前登录端口信息"); + } + + SysUserLoginPort port = sysUserLoginPortService.getById(portId); + if (port != null) { + port.setAvatar(avatarId); + sysUserLoginPortService.updateById(port); + return R.ok(new AvatarVo(avatarUrl)); + } + } + return R.fail("上传图片异常,请联系管理员"); + } + + /** + * 用户头像信息 + * + * @param imgUrl 头像地址 + */ + public record AvatarVo(String imgUrl) { + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/domain/HotCompanyBankAccount.java b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/HotCompanyBankAccount.java new file mode 100644 index 0000000..036301c --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/HotCompanyBankAccount.java @@ -0,0 +1,59 @@ +package com.hotwj.platform.resourceManagement.company.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 公司银行账户对象 hot_company_bank_account + * + * @author shihongwei + * @date 2025-12-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_company_bank_account") +public class HotCompanyBankAccount extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 账户号 + */ + private String accountNumber; + + /** + * 银行类型(ICBC/ABC/CCB/BOC/CMB等) + */ + private String bankType; + + /** + * 开户行 + */ + private String bankBranch; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/domain/HotCompanyThirdPlatform.java b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/HotCompanyThirdPlatform.java new file mode 100644 index 0000000..5ee0ca9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/HotCompanyThirdPlatform.java @@ -0,0 +1,69 @@ +package com.hotwj.platform.resourceManagement.company.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 公司第三方平台账号对象 hot_company_third_platform + * + * @author shihongwei + * @date 2025-12-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_company_third_platform") +public class HotCompanyThirdPlatform extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 平台名称 + */ + private String platformName; + + /** + * 登录账号 + */ + private String account; + + /** + * 密码哈希/密文 + */ + private String password; + + /** + * 登录地址 + */ + private String loginUrl; + + /** + * 免登录配置(JSON) + */ + private String ssoConfig; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/domain/SysCompany.java b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/SysCompany.java new file mode 100644 index 0000000..522be76 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/SysCompany.java @@ -0,0 +1,215 @@ +package com.hotwj.platform.resourceManagement.company.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.time.LocalDate; + +/** + * 企业基本信息对象 sys_company + * + * @author shihongwei + * @date 2025-12-02 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_company") +public class SysCompany extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * + */ + @TableId(value = "id") + private Long id; + + /** + * 上级代理商id + */ + private Long agentId; + + /** + * 代理商业务员id + */ + private Long salesmanId; + + /** + * 企业名称 + */ + private String companyName; + + /** + * 企业简称 + */ + private String companyShortName; + + /** + * 企业类型(下拉选项值或字典编码) + */ + private String companyType; + + /** + * 所属行业(下拉选项值或字典编码) + */ + private String industry; + + /** + * 法人姓名 + */ + private String legalPersonName; + + /** + * 法人身份证号 + */ + private String legalPersonIdNo; + + /** + * 法人手机号 + */ + private String legalPersonPhone; + + /** + * 负责人姓名 + */ + private String responsibleName; + + /** + * 负责人手机号 + */ + private String responsibleMobile; + + /** + * 注册地址 + */ + private String registeredAddress; + + /** + * 办公地址 + */ + private String officeAddress; + + /** + * 办公电话 + */ + private String officePhone; + + /** + * 电子公章文件URL(或OSS地址) + */ + private String sealUrl; + + /** + * 行政区划代码 + */ + private String regionCode; + + /** + * 其他附件(逗号分隔) + */ + private String otherAttachments; + + /** + * 统一社会信用代码 + */ + private String creditCode; + + /** + * 成立日期 + */ + private LocalDate establishDate; + + + /** + * 注册资本(万元) + */ + private BigDecimal registeredCapital; + + /** + * 经营期限 + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS) + private LocalDate businessTerm; + + /** + * 是否长期经营 + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS) + private Boolean businessLongTerm; + + /** + * 经营范围 + */ + private String businessScope; + + /** + * 营业执照图片URL(或OSS地址) + */ + private String businessLicenseImage; + + /** + * 道路经营许可证号 + */ + private String roadPermitNumber; + + /** + * 道路经营许可证过期日期 + */ + private LocalDate roadExpireDate; + + /** + * 道路许可证经营范围 + */ + private String roadBusinessScope; + + /** + * 道路经营许可证文件URL(或OSS地址) 多个 + */ + private String roadPermitFiles; + + /** + * 状态:1=正常 0=禁用 + */ + private Long status; + + /** + * 省行政区划代码 + */ + private String provinceCode; + + /** + * 市行政区划代码 + */ + private String cityCode; + + /** + * 区县行政区划代码 + */ + private String districtCode; + + /** + * 驾驶员邀请码 + */ + private String driverInvitationCode; + + /** + * 驾驶员邀请码过期时间 + */ + private java.time.LocalDateTime driverInvitationCodeExpireTime; + + /** + * 驾驶员邀请码二维码URL + */ + private String driverInvitationQrCodeUrl; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic(value = "0", delval = "1") + private Long isDeleted; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/domain/SysCompanyRole.java b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/SysCompanyRole.java new file mode 100644 index 0000000..5b98ec2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/SysCompanyRole.java @@ -0,0 +1,40 @@ +package com.hotwj.platform.resourceManagement.company.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_company_role") +public class SysCompanyRole extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @TableId(value = "id") + private Long id; + + private Long companyId; + + private String roleName; + + private String roleKey; + + private Integer roleSort; + + private String status; + + private String remark; + + private Long creatorUserId; + + @TableLogic + private Long isDeleted; +} + diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/domain/SysCompanyRoleMenu.java b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/SysCompanyRoleMenu.java new file mode 100644 index 0000000..806c236 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/SysCompanyRoleMenu.java @@ -0,0 +1,18 @@ +package com.hotwj.platform.resourceManagement.company.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +@Data +@TableName("sys_company_role_menu") +public class SysCompanyRoleMenu { + + @TableId(value = "id") + private Long id; + + private Long companyRoleId; + + private Long menuId; +} + diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/domain/SysUserLoginPort.java b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/SysUserLoginPort.java new file mode 100644 index 0000000..e1c8037 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/SysUserLoginPort.java @@ -0,0 +1,81 @@ +package com.hotwj.platform.resourceManagement.company.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 用户-企业-登录端口关联关系对象 sys_user_login_port + * + * @author shihongwei + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_user_login_port") +public class SysUserLoginPort extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id") + private Long id; + + /** + * 用户ID + */ + @TableField(value = "user_id") + private Long userId; + + /** + * 用户名 + *

+ * 同一个手机号,可能会有多个不同的用户名 + */ + @TableField(value = "username") + private String username; + + /** + * 企业ID + */ + @TableField(value = "company_id") + private Long companyId; + + /** + * 登录端口:port-headquarters/port-government/port-company_admin/port-agent + */ + @TableField(value = "login_port") + private String loginPort; + + /** + * 是否默认企业(用于首次登录自动跳转) + */ + @TableField(value = "is_default") + private Integer isDefault; + + /** + * 状态:1=正常 0=禁用 + */ + @TableField(value = "status") + private Integer status; + + /** + * 子密码 + */ + @TableField(value = "sub_password") + private String subPassword; + + /** + * 头像 + */ + @TableField(value = "avatar") + private Long avatar; + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/domain/bo/HotCompanyBankAccountBo.java b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/bo/HotCompanyBankAccountBo.java new file mode 100644 index 0000000..02993ff --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/bo/HotCompanyBankAccountBo.java @@ -0,0 +1,61 @@ +package com.hotwj.platform.resourceManagement.company.domain.bo; + +import com.hotwj.platform.resourceManagement.company.domain.HotCompanyBankAccount; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 公司银行账户业务对象 hot_company_bank_account + * + * @author shihongwei + * @date 2025-12-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotCompanyBankAccount.class, reverseConvertGenerate = false) +public class HotCompanyBankAccountBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 账户号 + */ + @NotBlank(message = "账户号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String accountNumber; + + /** + * 银行类型(ICBC/ABC/CCB/BOC/CMB等) + */ + @NotBlank(message = "银行类型(ICBC/ABC/CCB/BOC/CMB等)不能为空", groups = {AddGroup.class, EditGroup.class}) + private String bankType; + + /** + * 开户行 + */ + @NotBlank(message = "开户行不能为空", groups = {AddGroup.class, EditGroup.class}) + private String bankBranch; + + /** + * 0=正常, 1=已删除 + */ + @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/domain/bo/HotCompanyThirdPlatformBo.java b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/bo/HotCompanyThirdPlatformBo.java new file mode 100644 index 0000000..14f227c --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/bo/HotCompanyThirdPlatformBo.java @@ -0,0 +1,72 @@ +package com.hotwj.platform.resourceManagement.company.domain.bo; + +import com.hotwj.platform.resourceManagement.company.domain.HotCompanyThirdPlatform; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 公司第三方平台账号业务对象 hot_company_third_platform + * + * @author shihongwei + * @date 2025-12-08 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotCompanyThirdPlatform.class, reverseConvertGenerate = false) +public class HotCompanyThirdPlatformBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 平台名称 + */ + @NotBlank(message = "平台名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String platformName; + + /** + * 登录账号 + */ + @NotBlank(message = "登录账号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String account; + + /** + * 密码哈希/密文 + */ + @NotBlank(message = "密码哈希/密文不能为空", groups = {AddGroup.class, EditGroup.class}) + private String password; + + /** + * 登录地址 + */ + @NotBlank(message = "登录地址不能为空", groups = {AddGroup.class, EditGroup.class}) + private String loginUrl; + + /** + * 免登录配置(JSON) + */ + private String ssoConfig; + + /** + * 0=正常, 1=已删除 + */ + @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/domain/bo/SysCompanyBo.java b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/bo/SysCompanyBo.java new file mode 100644 index 0000000..676e171 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/bo/SysCompanyBo.java @@ -0,0 +1,232 @@ +package com.hotwj.platform.resourceManagement.company.domain.bo; + +import com.hotwj.platform.resourceManagement.company.domain.SysCompany; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.List; + +/** + * 企业基本信息业务对象 sys_company + * + * @author shihongwei + * @date 2025-12-02 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysCompany.class, reverseConvertGenerate = false) +public class SysCompanyBo extends BaseEntity { + + /** + * + */ + @NotNull(message = "不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 上级代理商id + */ + private Long agentId; + + /** + * 代理商业务员id + */ + private Long salesmanId; + + /** + * 企业名称 + */ + @NotBlank(message = "企业名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String companyName; + + /** + * 企业简称 + */ + private String companyShortName; + + /** + * 企业类型(下拉选项值或字典编码) + */ + @NotBlank(message = "企业类型不能为空", groups = {AddGroup.class, EditGroup.class}) + private String companyType; + + /** + * 所属行业(下拉选项值或字典编码) + */ + @NotBlank(message = "所属行业不能为空", groups = {AddGroup.class, EditGroup.class}) + private String industry; + + /** + * 法人姓名 + */ + @NotBlank(message = "法人姓名不能为空", groups = {AddGroup.class, EditGroup.class}) + private String legalPersonName; + + /** + * 法人手机号 + */ + @NotBlank(message = "法人手机号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String legalPersonPhone; + + /** + * 负责人姓名 + */ + @NotBlank(message = "负责人姓名不能为空", groups = {AddGroup.class, EditGroup.class}) + private String responsibleName; + + /** + * 负责人手机号 + */ + @NotBlank(message = "负责人手机号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String responsibleMobile; + + /** + * 注册地址 + */ + @NotBlank(message = "注册地址不能为空", groups = {AddGroup.class, EditGroup.class}) + private String registeredAddress; + + /** + * 办公地址 + */ + @NotBlank(message = "办公地址不能为空", groups = {AddGroup.class, EditGroup.class}) + private String officeAddress; + + /** + * 办公电话 + */ + private String officePhone; + + /** + * 电子公章文件URL(或OSS地址) + */ + private String sealUrl; + + /** + * 省份代码 + */ + private String provinceCode; + + /** + * 城市代码 + */ + private String cityCode; + + /** + * 行政区划代码 + */ + @NotBlank(message = "行政区划代码不能为空", groups = {AddGroup.class, EditGroup.class}) + private String regionCode; + + /** + * 其他附件(逗号分隔) + */ + private String otherAttachments; + + /** + * 统一社会信用代码 + */ + @NotBlank(message = "统一社会信用代码不能为空", groups = {AddGroup.class, EditGroup.class}) + private String creditCode; + + /** + * 成立日期 + */ + @NotNull(message = "成立日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private LocalDate establishDate; + + /** + * 注册资本(万元) + */ + private BigDecimal registeredCapital; + + /** + * 经营期限 + */ + private LocalDate businessTerm; + + /** + * 是否长期经营 + */ + private Boolean businessLongTerm; + + /** + * 经营范围 + */ + private String businessScope; + + /** + * 营业执照图片URL(或OSS地址) + */ + private String businessLicenseImage; + + /** + * 道路经营许可证号 + */ + private String roadPermitNumber; + + /** + * 道路经营许可证过期日期 + */ + private LocalDate roadExpireDate; + + /** + * 道路许可证经营范围 + */ + private String roadBusinessScope; + + /** + * 道路经营许可证文件URL(或OSS地址) 多个 + */ + private String roadPermitFiles; + + // 其他字段 + + /** + * 状态:1=正常 0=禁用 + */ + @NotNull(message = "状态:1=正常 0=禁用不能为空", groups = {EditGroup.class}) + private Long status; + + /** + * 0=正常, 1=已删除 + */ + @NotNull(message = "0=正常, 1=已删除不能为空", groups = {EditGroup.class}) + private Long isDeleted; + + /** + * 驾驶员邀请码 + */ + private String driverInvitationCode; + + /** + * 驾驶员邀请码过期时间 + */ + private java.time.LocalDateTime driverInvitationCodeExpireTime; + + /** + * 驾驶员邀请码二维码URL + */ + private String driverInvitationQrCodeUrl; + + // -- 其他子表 + + /** + * 公司银行账号 + */ + private List bankAccounts; + + /** + * 公司第三方平台账号 + */ + private List thirdPlatforms; + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/domain/bo/SysCompanyRoleBo.java b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/bo/SysCompanyRoleBo.java new file mode 100644 index 0000000..c425133 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/bo/SysCompanyRoleBo.java @@ -0,0 +1,41 @@ +package com.hotwj.platform.resourceManagement.company.domain.bo; + +import com.hotwj.platform.resourceManagement.company.domain.SysCompanyRole; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = SysCompanyRole.class, reverseConvertGenerate = false) +public class SysCompanyRoleBo extends BaseEntity { + + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + @NotBlank(message = "角色名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String roleName; + + @NotBlank(message = "角色标识不能为空", groups = {AddGroup.class, EditGroup.class}) + private String roleKey; + + @NotNull(message = "显示顺序不能为空", groups = {AddGroup.class, EditGroup.class}) + private Integer roleSort; + + private String status; + + private String remark; + + private Long creatorUserId; + + private Long[] menuIds; +} + diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/domain/vo/HotCompanyBankAccountVo.java b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/vo/HotCompanyBankAccountVo.java new file mode 100644 index 0000000..59bbe2f --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/vo/HotCompanyBankAccountVo.java @@ -0,0 +1,64 @@ +package com.hotwj.platform.resourceManagement.company.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.company.domain.HotCompanyBankAccount; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 公司银行账户视图对象 hot_company_bank_account + * + * @author shihongwei + * @date 2025-12-08 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotCompanyBankAccount.class) +public class HotCompanyBankAccountVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 账户号 + */ + @ExcelProperty(value = "账户号") + private String accountNumber; + + /** + * 银行类型(ICBC/ABC/CCB/BOC/CMB等) + */ + @ExcelProperty(value = "银行类型(ICBC/ABC/CCB/BOC/CMB等)") + private String bankType; + + /** + * 开户行 + */ + @ExcelProperty(value = "开户行") + private String bankBranch; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/domain/vo/HotCompanyThirdPlatformVo.java b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/vo/HotCompanyThirdPlatformVo.java new file mode 100644 index 0000000..4e8cc11 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/vo/HotCompanyThirdPlatformVo.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.resourceManagement.company.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.company.domain.HotCompanyThirdPlatform; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 公司第三方平台账号视图对象 hot_company_third_platform + * + * @author shihongwei + * @date 2025-12-08 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotCompanyThirdPlatform.class) +public class HotCompanyThirdPlatformVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 平台名称 + */ + @ExcelProperty(value = "平台名称") + private String platformName; + + /** + * 登录账号 + */ + @ExcelProperty(value = "登录账号") + private String account; + + /** + * 密码哈希/密文 + */ + @ExcelProperty(value = "密码哈希/密文") + private String password; + + /** + * 登录地址 + */ + @ExcelProperty(value = "登录地址") + private String loginUrl; + + /** + * 免登录配置(JSON) + */ + @ExcelProperty(value = "免登录配置(JSON)") + private String ssoConfig; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/domain/vo/SysCompanyRoleVo.java b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/vo/SysCompanyRoleVo.java new file mode 100644 index 0000000..2b2d7a3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/vo/SysCompanyRoleVo.java @@ -0,0 +1,51 @@ +package com.hotwj.platform.resourceManagement.company.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.company.domain.SysCompanyRole; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysCompanyRole.class) +public class SysCompanyRoleVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + + @ExcelProperty(value = "公司ID") + private Long companyId; + + @ExcelProperty(value = "角色名称") + private String roleName; + + @ExcelProperty(value = "角色标识") + private String roleKey; + + @ExcelProperty(value = "排序") + private Integer roleSort; + + @ExcelProperty(value = "状态") + private String status; + + private String remark; + + private Date createTime; + + private Long createBy; + + private String createByName; + + private Long creatorUserId; + + private List menuIds; + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/domain/vo/SysCompanyVo.java b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/vo/SysCompanyVo.java new file mode 100644 index 0000000..11515f1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/vo/SysCompanyVo.java @@ -0,0 +1,222 @@ +package com.hotwj.platform.resourceManagement.company.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.company.domain.SysCompany; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.Date; + + +/** + * 企业基本信息视图对象 sys_company + * + * @author shihongwei + * @date 2025-12-02 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = SysCompany.class) +public class SysCompanyVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + + /** + * 上级代理商id + */ + private Long agentId; + + /** + * 代理商业务员id + */ + private Long salesmanId; + + @ExcelProperty(value = "序号", index = 0) + private Integer index; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 企业名称 + */ + @ExcelProperty(value = "企业名称", index = 1) + private String companyName; + + /** + * 企业简称 + */ + private String companyShortName; + + /** + * 企业类型(下拉选项值或字典编码) + */ + @ExcelDictFormat(dictType = "hot_enterprise_type") + private String companyType; + + /** + * 所属行业(下拉选项值或字典编码) + */ + @ExcelProperty(value = "所属行业", index = 2, converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_industry") + private String industry; + + /** + * 法人姓名 + */ + private String legalPersonName; + + /** + * 法人手机号 + */ + private String legalPersonPhone; + + /** + * 负责人姓名 + */ + @ExcelProperty(value = "负责人姓名", index = 4) + private String responsibleName; + + /** + * 负责人手机号 + */ + @ExcelProperty(value = "负责人手机号", index = 5) + private String responsibleMobile; + + /** + * 注册地址 + */ + private String registeredAddress; + + /** + * 办公地址 + */ + private String officeAddress; + + /** + * 办公电话 + */ + private String officePhone; + + /** + * 电子公章文件URL(或OSS地址) + */ + @ExcelDictFormat(readConverterExp = "或=OSS地址") + private String sealUrl; + + /** + * 状态:1=正常 0=禁用 + */ + @ExcelProperty(value = "企业状态", index = 6, converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_company_status") + private Long status; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + /** + * 省份代码 + */ + private String provinceCode; + + /** + * 城市代码 + */ + private String cityCode; + + /** + * 行政区划代码 + */ + private String regionCode; + + @Translation(type = TransConstant.REGION_CODE_TO_NAME, mapper = "regionCode") + @ExcelProperty(value = "区划", index = 3) + private String regionLabel; + + /** + * 统一社会信用代码 + */ + private String creditCode; + + /** + * 成立日期 + */ + private LocalDate establishDate; + + /** + * 注册资本(万元) + */ + private BigDecimal registeredCapital; + + /** + * 经营期限 + */ + private LocalDate businessTerm; + + /** + * 是否长期经营 + */ + private Boolean businessLongTerm; + + /** + * 经营范围 + */ + private String businessScope; + + /** + * 营业执照图片URL(或OSS地址) + */ + private String businessLicenseImage; + + /** + * 道路经营许可证号 + */ + private String roadPermitNumber; + + /** + * 道路经营许可证过期日期 + */ + private LocalDate roadExpireDate; + + /** + * 道路许可证经营范围 + */ + private String roadBusinessScope; + + /** + * 道路经营许可证文件URL(或OSS地址) 多个 + */ + private String roadPermitFiles; + + /** + * 驾驶员邀请码 + */ + private String driverInvitationCode; + + + /** + * 驾驶员邀请码二维码URL + */ + private String driverInvitationQrCodeUrl; + + /** + * 其他附件(逗号分隔) + */ + private String otherAttachments; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/domain/vo/SysUserLoginPortVo.java b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/vo/SysUserLoginPortVo.java new file mode 100644 index 0000000..182bd05 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/domain/vo/SysUserLoginPortVo.java @@ -0,0 +1,65 @@ +package com.hotwj.platform.resourceManagement.company.domain.vo; + +import lombok.Data; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +@Data +public class SysUserLoginPortVo { + + /** + * 主键 + */ + private Long id; + + /** + * 用户id + */ + private Long userId; + + /** + * 用户名 + */ + private String username; + + /** + * 企业id + */ + private Long companyId; + + /** + * 企业名称 + */ + private String companyName; + + /** + * 业务角色 + */ + private Long roleId; + + /** + * 角色名称 + */ + private String roleName; + + /** + * 登录端口 + */ + private String loginPort; + + /** + * 身份类型 + */ + private String roleType; + + /** + * 角色模块 + */ + private String roleMod; + + /** + * 头像 + */ + @Translation(type = TransConstant.OSS_ID_TO_URL) + private Long avatar; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/HotCompanyBankAccountMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/HotCompanyBankAccountMapper.java new file mode 100644 index 0000000..877a2e8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/HotCompanyBankAccountMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.company.mapper; + +import com.hotwj.platform.resourceManagement.company.domain.HotCompanyBankAccount; +import com.hotwj.platform.resourceManagement.company.domain.vo.HotCompanyBankAccountVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 公司银行账户Mapper接口 + * + * @author shihongwei + * @date 2025-12-08 + */ +@Mapper +public interface HotCompanyBankAccountMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/HotCompanyThirdPlatformMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/HotCompanyThirdPlatformMapper.java new file mode 100644 index 0000000..fa11cd9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/HotCompanyThirdPlatformMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.company.mapper; + +import com.hotwj.platform.resourceManagement.company.domain.HotCompanyThirdPlatform; +import com.hotwj.platform.resourceManagement.company.domain.vo.HotCompanyThirdPlatformVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 公司第三方平台账号Mapper接口 + * + * @author shihongwei + * @date 2025-12-08 + */ +@Mapper +public interface HotCompanyThirdPlatformMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/SysCompanyMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/SysCompanyMapper.java new file mode 100644 index 0000000..1582b87 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/SysCompanyMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.company.mapper; + +import com.hotwj.platform.resourceManagement.company.domain.SysCompany; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 企业基本信息Mapper接口 + * + * @author shihongwei + * @date 2025-12-02 + */ +@Mapper +public interface SysCompanyMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/SysCompanyRoleMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/SysCompanyRoleMapper.java new file mode 100644 index 0000000..de69b04 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/SysCompanyRoleMapper.java @@ -0,0 +1,11 @@ +package com.hotwj.platform.resourceManagement.company.mapper; + +import com.hotwj.platform.resourceManagement.company.domain.SysCompanyRole; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyRoleVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +@Mapper +public interface SysCompanyRoleMapper extends BaseMapperPlus { +} + diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/SysCompanyRoleMenuMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/SysCompanyRoleMenuMapper.java new file mode 100644 index 0000000..60f8f72 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/SysCompanyRoleMenuMapper.java @@ -0,0 +1,10 @@ +package com.hotwj.platform.resourceManagement.company.mapper; + +import com.hotwj.platform.resourceManagement.company.domain.SysCompanyRoleMenu; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +@Mapper +public interface SysCompanyRoleMenuMapper extends BaseMapperPlus { +} + diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/SysUserLoginPortMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/SysUserLoginPortMapper.java new file mode 100644 index 0000000..6850274 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/mapper/SysUserLoginPortMapper.java @@ -0,0 +1,25 @@ +package com.hotwj.platform.resourceManagement.company.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.hotwj.platform.resourceManagement.company.domain.SysUserLoginPort; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysUserLoginPortVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 用户-企业-登录端口关联关系Mapper接口 + * + * @author shihongwei + */ +@Mapper +public interface SysUserLoginPortMapper extends BaseMapper { + + /** + * 查询用户-企业-登录端口关联关系列表(根据端口) + * + * @param userId 用户ID + * @return 用户-企业-登录端口关联关系列表 + */ + List selectLoginPortListByPort(Long userId, String port); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/service/IHotCompanyBankAccountService.java b/src/main/java/com/hotwj/platform/resourceManagement/company/service/IHotCompanyBankAccountService.java new file mode 100644 index 0000000..ef0d78c --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/service/IHotCompanyBankAccountService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.resourceManagement.company.service; + +import com.hotwj.platform.resourceManagement.company.domain.bo.HotCompanyBankAccountBo; +import com.hotwj.platform.resourceManagement.company.domain.vo.HotCompanyBankAccountVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 公司银行账户Service接口 + * + * @author shihongwei + * @date 2025-12-08 + */ +public interface IHotCompanyBankAccountService { + + /** + * 查询公司银行账户 + * + * @param id 主键 + * @return 公司银行账户 + */ + HotCompanyBankAccountVo queryById(Long id); + + /** + * 分页查询公司银行账户列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 公司银行账户分页列表 + */ + TableDataInfo queryPageList(HotCompanyBankAccountBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的公司银行账户列表 + * + * @param bo 查询条件 + * @return 公司银行账户列表 + */ + List queryList(HotCompanyBankAccountBo bo); + + /** + * 新增公司银行账户 + * + * @param bo 公司银行账户 + * @return 是否新增成功 + */ + Boolean insertByBo(HotCompanyBankAccountBo bo); + + /** + * 修改公司银行账户 + * + * @param bo 公司银行账户 + * @return 是否修改成功 + */ + Boolean updateByBo(HotCompanyBankAccountBo bo); + + /** + * 校验并批量删除公司银行账户信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/service/IHotCompanyThirdPlatformService.java b/src/main/java/com/hotwj/platform/resourceManagement/company/service/IHotCompanyThirdPlatformService.java new file mode 100644 index 0000000..ed92618 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/service/IHotCompanyThirdPlatformService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.resourceManagement.company.service; + +import com.hotwj.platform.resourceManagement.company.domain.bo.HotCompanyThirdPlatformBo; +import com.hotwj.platform.resourceManagement.company.domain.vo.HotCompanyThirdPlatformVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 公司第三方平台账号Service接口 + * + * @author shihongwei + * @date 2025-12-08 + */ +public interface IHotCompanyThirdPlatformService { + + /** + * 查询公司第三方平台账号 + * + * @param id 主键 + * @return 公司第三方平台账号 + */ + HotCompanyThirdPlatformVo queryById(Long id); + + /** + * 分页查询公司第三方平台账号列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 公司第三方平台账号分页列表 + */ + TableDataInfo queryPageList(HotCompanyThirdPlatformBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的公司第三方平台账号列表 + * + * @param bo 查询条件 + * @return 公司第三方平台账号列表 + */ + List queryList(HotCompanyThirdPlatformBo bo); + + /** + * 新增公司第三方平台账号 + * + * @param bo 公司第三方平台账号 + * @return 是否新增成功 + */ + Boolean insertByBo(HotCompanyThirdPlatformBo bo); + + /** + * 修改公司第三方平台账号 + * + * @param bo 公司第三方平台账号 + * @return 是否修改成功 + */ + Boolean updateByBo(HotCompanyThirdPlatformBo bo); + + /** + * 校验并批量删除公司第三方平台账号信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/service/ISysCompanyRoleService.java b/src/main/java/com/hotwj/platform/resourceManagement/company/service/ISysCompanyRoleService.java new file mode 100644 index 0000000..1569180 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/service/ISysCompanyRoleService.java @@ -0,0 +1,28 @@ +package com.hotwj.platform.resourceManagement.company.service; + +import com.hotwj.platform.resourceManagement.company.domain.bo.SysCompanyRoleBo; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyRoleVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +public interface ISysCompanyRoleService { + + SysCompanyRoleVo queryById(Long id); + + TableDataInfo queryPageList(SysCompanyRoleBo bo, PageQuery pageQuery); + + List queryList(SysCompanyRoleBo bo); + + Boolean insertByBo(SysCompanyRoleBo bo); + + Boolean updateByBo(SysCompanyRoleBo bo); + + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + Set selectRoleKeys(Long userId, Long companyId); +} + diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/service/ISysCompanyService.java b/src/main/java/com/hotwj/platform/resourceManagement/company/service/ISysCompanyService.java new file mode 100644 index 0000000..cc404ad --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/service/ISysCompanyService.java @@ -0,0 +1,119 @@ +package com.hotwj.platform.resourceManagement.company.service; + +import com.hotwj.platform.resourceManagement.company.domain.bo.SysCompanyBo; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 企业基本信息Service接口 + * + * @author shihongwei + * @date 2025-12-02 + */ +public interface ISysCompanyService { + + /** + * 查询企业基本信息 + * + * @param id 主键 + * @return 企业基本信息 + */ + SysCompanyVo queryById(Long id); + + /** + * 分页查询企业基本信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 企业基本信息分页列表 + */ + TableDataInfo queryPageList(SysCompanyBo bo, PageQuery pageQuery); + + /** + * 按多个区划分页查询企业基本信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @param regionCodes 区代码列表 + * @return 企业基本信息分页列表 + */ + TableDataInfo queryPageListByRegionCodes(SysCompanyBo bo, PageQuery pageQuery, List regionCodes); + + /** + * 按多个区和多个ID分页查询企业基本信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @param regionCodes 区代码列表 + * @param ids 企业ID列表 + * @return 企业基本信息分页列表 + */ + TableDataInfo queryPageListByRegionAndIds(SysCompanyBo bo, PageQuery pageQuery, List regionCodes, List ids); + /** + * 查询符合条件的企业基本信息列表 + * + * @param bo 查询条件 + * @return 企业基本信息列表 + */ + List queryList(SysCompanyBo bo); + + /** + * 查询符合条件的企业基本信息列表 + * + * @param bo 查询条件 + * @param ids ID列表 + * @return 企业基本信息列表 + */ + List queryList(SysCompanyBo bo, List ids); + + /** + * 新增企业基本信息 + * + * @param bo 企业基本信息 + * @return 是否新增成功 + */ + Boolean insertByBo(SysCompanyBo bo); + + /** + * 修改企业基本信息 + * + * @param bo 企业基本信息 + * @return 是否修改成功 + */ + Boolean updateByBo(SysCompanyBo bo); + + /** + * 校验并批量删除企业基本信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 结果 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 根据邀请码校验并返回企业名称 + * + * @param companyId 企业ID + * @param inviteCode 邀请码 + * @return 企业名称 + */ + String checkInviteCode(Long companyId, String inviteCode); + + /** + * 重置所有企业的邀请码 + */ + void resetAllInviteCodes(); + + + /** + * 根据邀请码查询企业信息 + * @param code 邀请码 + * @return 企业信息 + */ + SysCompanyVo queryByInviteCode(String code); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/service/ISysUserLoginPortService.java b/src/main/java/com/hotwj/platform/resourceManagement/company/service/ISysUserLoginPortService.java new file mode 100644 index 0000000..21e4c15 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/service/ISysUserLoginPortService.java @@ -0,0 +1,51 @@ +package com.hotwj.platform.resourceManagement.company.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.hotwj.platform.resourceManagement.company.domain.SysUserLoginPort; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysUserLoginPortVo; + +import java.util.List; + +/** + * 用户-企业-登录端口关联关系Service接口 + * + * @author shihongwei + */ +public interface ISysUserLoginPortService extends IService { + + + /** + * 以下角色都是空权限,只负责用户能够登录该端口 + */ + // 总部端角色id + Long HEADQUARTERS_ROLE_ID = 1999302654074122241L; + // 政府端角色id + Long GOVERNMENT_ROLE_ID = 1999302783325794305L; + // 代理商端角色id + Long AGENT_ROLE_ID = 1999302903249334273L; + + Long BUSINESS_ADMIN_ROLE_ID = 1999311629767241729L; + + Long DRIVER_ROLE_ID = 2010339247320825857L; +// Long ESCORT_ROLE_ID = 1995523648844247042L; + + // 企业端业务角色 (登录端口) + String HEADQUARTERS_PORT = "port-headquarters"; + String GOVERNMENT_PORT = "port-government"; + String ENTERPRISE_PORT = "port-company_admin"; + String AGENT_PORT = "port-agent"; + String DRIVER_PORT = "port-driver"; + +// String DRIVER_ROLE = "driver"; +// String ESCORT_ROLE = "escort"; + + /** + * 查询用户-企业-登录端口关联关系列表 + * + * @param userId 用户ID + * @return 用户-企业-登录端口关联关系集合 + */ + List selectLoginPortListByPort(Long userId, String port); + + List selectUserLoginPorts(Long userId); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/service/impl/HotCompanyBankAccountServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/company/service/impl/HotCompanyBankAccountServiceImpl.java new file mode 100644 index 0000000..8f7b770 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/service/impl/HotCompanyBankAccountServiceImpl.java @@ -0,0 +1,134 @@ +package com.hotwj.platform.resourceManagement.company.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.company.domain.HotCompanyBankAccount; +import com.hotwj.platform.resourceManagement.company.domain.bo.HotCompanyBankAccountBo; +import com.hotwj.platform.resourceManagement.company.domain.vo.HotCompanyBankAccountVo; +import com.hotwj.platform.resourceManagement.company.mapper.HotCompanyBankAccountMapper; +import com.hotwj.platform.resourceManagement.company.service.IHotCompanyBankAccountService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 公司银行账户Service业务层处理 + * + * @author shihongwei + * @date 2025-12-08 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotCompanyBankAccountServiceImpl implements IHotCompanyBankAccountService { + + private final HotCompanyBankAccountMapper baseMapper; + + /** + * 查询公司银行账户 + * + * @param id 主键 + * @return 公司银行账户 + */ + @Override + public HotCompanyBankAccountVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询公司银行账户列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 公司银行账户分页列表 + */ + @Override + public TableDataInfo queryPageList(HotCompanyBankAccountBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的公司银行账户列表 + * + * @param bo 查询条件 + * @return 公司银行账户列表 + */ + @Override + public List queryList(HotCompanyBankAccountBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotCompanyBankAccountBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotCompanyBankAccount::getId); + lqw.eq(bo.getCompanyId() != null, HotCompanyBankAccount::getCompanyId, bo.getCompanyId()); + lqw.eq(StringUtils.isNotBlank(bo.getAccountNumber()), HotCompanyBankAccount::getAccountNumber, bo.getAccountNumber()); + lqw.eq(StringUtils.isNotBlank(bo.getBankType()), HotCompanyBankAccount::getBankType, bo.getBankType()); + lqw.eq(StringUtils.isNotBlank(bo.getBankBranch()), HotCompanyBankAccount::getBankBranch, bo.getBankBranch()); + lqw.eq(bo.getIsDeleted() != null, HotCompanyBankAccount::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增公司银行账户 + * + * @param bo 公司银行账户 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotCompanyBankAccountBo bo) { + HotCompanyBankAccount add = MapstructUtils.convert(bo, HotCompanyBankAccount.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改公司银行账户 + * + * @param bo 公司银行账户 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotCompanyBankAccountBo bo) { + HotCompanyBankAccount update = MapstructUtils.convert(bo, HotCompanyBankAccount.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotCompanyBankAccount entity) { + } + + /** + * 校验并批量删除公司银行账户信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/service/impl/HotCompanyThirdPlatformServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/company/service/impl/HotCompanyThirdPlatformServiceImpl.java new file mode 100644 index 0000000..c7e6689 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/service/impl/HotCompanyThirdPlatformServiceImpl.java @@ -0,0 +1,137 @@ +package com.hotwj.platform.resourceManagement.company.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.company.domain.HotCompanyThirdPlatform; +import com.hotwj.platform.resourceManagement.company.domain.bo.HotCompanyThirdPlatformBo; +import com.hotwj.platform.resourceManagement.company.domain.vo.HotCompanyThirdPlatformVo; +import com.hotwj.platform.resourceManagement.company.mapper.HotCompanyThirdPlatformMapper; +import com.hotwj.platform.resourceManagement.company.service.IHotCompanyThirdPlatformService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 公司第三方平台账号Service业务层处理 + * + * @author shihongwei + * @date 2025-12-08 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotCompanyThirdPlatformServiceImpl implements IHotCompanyThirdPlatformService { + + private final HotCompanyThirdPlatformMapper baseMapper; + + /** + * 查询公司第三方平台账号 + * + * @param id 主键 + * @return 公司第三方平台账号 + */ + @Override + public HotCompanyThirdPlatformVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询公司第三方平台账号列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 公司第三方平台账号分页列表 + */ + @Override + public TableDataInfo queryPageList(HotCompanyThirdPlatformBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的公司第三方平台账号列表 + * + * @param bo 查询条件 + * @return 公司第三方平台账号列表 + */ + @Override + public List queryList(HotCompanyThirdPlatformBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotCompanyThirdPlatformBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotCompanyThirdPlatform::getId); + lqw.eq(bo.getCompanyId() != null, HotCompanyThirdPlatform::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getPlatformName()), HotCompanyThirdPlatform::getPlatformName, bo.getPlatformName()); + lqw.eq(StringUtils.isNotBlank(bo.getAccount()), HotCompanyThirdPlatform::getAccount, bo.getAccount()); + lqw.eq(StringUtils.isNotBlank(bo.getPassword()), HotCompanyThirdPlatform::getPassword, bo.getPassword()); + lqw.eq(StringUtils.isNotBlank(bo.getLoginUrl()), HotCompanyThirdPlatform::getLoginUrl, bo.getLoginUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getSsoConfig()), HotCompanyThirdPlatform::getSsoConfig, bo.getSsoConfig()); + lqw.eq(bo.getIsDeleted() != null, HotCompanyThirdPlatform::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增公司第三方平台账号 + * + * @param bo 公司第三方平台账号 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotCompanyThirdPlatformBo bo) { + HotCompanyThirdPlatform add = MapstructUtils.convert(bo, HotCompanyThirdPlatform.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改公司第三方平台账号 + * + * @param bo 公司第三方平台账号 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotCompanyThirdPlatformBo bo) { + HotCompanyThirdPlatform update = MapstructUtils.convert(bo, HotCompanyThirdPlatform.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotCompanyThirdPlatform entity) { + } + + /** + * 校验并批量删除公司第三方平台账号信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/service/impl/SysCompanyRoleServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/company/service/impl/SysCompanyRoleServiceImpl.java new file mode 100644 index 0000000..73577a2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/service/impl/SysCompanyRoleServiceImpl.java @@ -0,0 +1,243 @@ +package com.hotwj.platform.resourceManagement.company.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.company.domain.SysCompanyRole; +import com.hotwj.platform.resourceManagement.company.domain.SysCompanyRoleMenu; +import com.hotwj.platform.resourceManagement.company.domain.bo.SysCompanyRoleBo; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyRoleVo; +import com.hotwj.platform.resourceManagement.company.mapper.SysCompanyRoleMapper; +import com.hotwj.platform.resourceManagement.company.mapper.SysCompanyRoleMenuMapper; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyRoleService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.system.domain.bo.SysMenuBo; +import org.dromara.system.domain.vo.SysMenuVo; +import org.dromara.system.service.ISysMenuService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +import static com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService.ENTERPRISE_PORT; + +@Slf4j +@RequiredArgsConstructor +@Service +public class SysCompanyRoleServiceImpl implements ISysCompanyRoleService { + + private final SysCompanyRoleMapper baseMapper; + private final SysCompanyRoleMenuMapper roleMenuMapper; + private final HotCompanySafetyManagerMapper safetyManagerMapper; + private final ISysMenuService menuService; + + @Override + public SysCompanyRoleVo queryById(Long id) { + SysCompanyRoleVo vo = baseMapper.selectVoById(id); + if (vo == null) { + return null; + } + ensureCurrentUserCanAccessRole(vo.getCreatorUserId()); + List menuIds = roleMenuMapper.selectList(Wrappers.lambdaQuery() + .eq(SysCompanyRoleMenu::getCompanyRoleId, id)) + .stream() + .map(SysCompanyRoleMenu::getMenuId) + .filter(Objects::nonNull) + .toList(); + if (!menuIds.isEmpty()) { + vo.setMenuIds(menuIds); + } + return vo; + } + + @Override + public TableDataInfo queryPageList(SysCompanyRoleBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + @Override + public List queryList(SysCompanyRoleBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysCompanyRoleBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(SysCompanyRole::getCompanyId).orderByAsc(SysCompanyRole::getRoleSort); + lqw.eq(bo.getCompanyId() != null, SysCompanyRole::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getRoleName()), SysCompanyRole::getRoleName, bo.getRoleName()); + lqw.eq(StringUtils.isNotBlank(bo.getRoleKey()), SysCompanyRole::getRoleKey, bo.getRoleKey()); + lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysCompanyRole::getStatus, bo.getStatus()); + lqw.eq(bo.getCreatorUserId() != null, SysCompanyRole::getCreatorUserId, bo.getCreatorUserId()); + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser != null && ENTERPRISE_PORT.equals(loginUser.getLoginPort()) && !LoginHelper.isSuperAdmin()) { + lqw.eq(SysCompanyRole::getCompanyId, loginUser.getCompanyId()); + if (isCurrentCompanyHead(loginUser)) { + lqw.and(w -> w.eq(SysCompanyRole::getCreatorUserId, loginUser.getUserId()) + .or() + .isNull(SysCompanyRole::getCreatorUserId)); + } else { + lqw.eq(SysCompanyRole::getCreatorUserId, loginUser.getUserId()); + } + } + return lqw; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(SysCompanyRoleBo bo) { + validateAssignableMenus(bo.getMenuIds(), bo.getCompanyId()); + SysCompanyRole add = MapstructUtils.convert(bo, SysCompanyRole.class); + add.setCreatorUserId(LoginHelper.getUserId()); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + insertRoleMenu(bo); + } + return flag; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(SysCompanyRoleBo bo) { + SysCompanyRole dbRole = baseMapper.selectById(bo.getId()); + if (dbRole == null) { + return false; + } + ensureCurrentUserCanAccessRole(dbRole.getCreatorUserId()); + validateAssignableMenus(bo.getMenuIds(), dbRole.getCompanyId()); + SysCompanyRole update = MapstructUtils.convert(bo, SysCompanyRole.class); + update.setCreatorUserId(dbRole.getCreatorUserId()); + boolean flag = baseMapper.updateById(update) > 0; + if (flag) { + roleMenuMapper.delete(Wrappers.lambdaQuery() + .eq(SysCompanyRoleMenu::getCompanyRoleId, bo.getId())); + insertRoleMenu(bo); + } + return flag; + } + + private void insertRoleMenu(SysCompanyRoleBo bo) { + Long[] menuIds = bo.getMenuIds(); + if (menuIds == null || menuIds.length == 0) { + return; + } + List list = new ArrayList<>(); + for (Long menuId : menuIds) { + if (menuId == null) { + continue; + } + SysCompanyRoleMenu rm = new SysCompanyRoleMenu(); + rm.setCompanyRoleId(bo.getId()); + rm.setMenuId(menuId); + list.add(rm); + } + if (!list.isEmpty()) { + roleMenuMapper.insertBatch(list); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (ids == null || ids.isEmpty()) { + return false; + } + if (needLimitByCurrentCreator()) { + Long currentUserId = LoginHelper.getUserId(); + long ownedCount = baseMapper.selectCount(Wrappers.lambdaQuery() + .in(SysCompanyRole::getId, ids) + .eq(SysCompanyRole::getCreatorUserId, currentUserId)); + if (ownedCount != ids.size()) { + throw new ServiceException("仅允许删除当前账号创建的权限类型"); + } + } + roleMenuMapper.delete(Wrappers.lambdaQuery() + .in(SysCompanyRoleMenu::getCompanyRoleId, ids)); + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + public Set selectRoleKeys(Long userId, Long companyId) { + // Query HotCompanySafetyManager to get role ID + HotCompanySafetyManager manager = safetyManagerMapper.selectOne(Wrappers.lambdaQuery() + .select(HotCompanySafetyManager::getPermissionRoleId) + .eq(HotCompanySafetyManager::getUserId, userId) + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getIsDeleted, 0) + .eq(HotCompanySafetyManager::getStatus, 1) + .and(w -> w.eq(HotCompanySafetyManager::getIsResigned, 0).or().isNull(HotCompanySafetyManager::getIsResigned)) + ); + + if (manager == null || manager.getPermissionRoleId() == null) { + return Collections.emptySet(); + } + + SysCompanyRole role = baseMapper.selectOne(Wrappers.lambdaQuery() + .select(SysCompanyRole::getRoleKey) + .eq(SysCompanyRole::getId, manager.getPermissionRoleId()) + .eq(SysCompanyRole::getStatus, "0")); + + if (role != null && StringUtils.isNotBlank(role.getRoleKey())) { + return Set.of(role.getRoleKey()); + } + return Collections.emptySet(); + } + + private void validateAssignableMenus(Long[] menuIds, Long companyId) { + if (!needLimitByCurrentCreator() || menuIds == null || menuIds.length == 0) { + return; + } + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null) { + throw new ServiceException("登录信息失效,请重新登录"); + } + Long resolvedCompanyId = companyId != null ? companyId : loginUser.getCompanyId(); + List currentMenus = menuService.selectMenuList(new SysMenuBo(), loginUser.getUserId(), resolvedCompanyId, loginUser.getLoginPort()); + Set currentMenuIds = currentMenus.stream().map(SysMenuVo::getMenuId).filter(Objects::nonNull).collect(java.util.stream.Collectors.toSet()); + boolean hasOutOfScopeMenu = Arrays.stream(menuIds).filter(Objects::nonNull).anyMatch(menuId -> !currentMenuIds.contains(menuId)); + if (hasOutOfScopeMenu) { + throw new ServiceException("权限树仅允许选择当前账号已拥有的菜单权限"); + } + } + + private void ensureCurrentUserCanAccessRole(Long creatorUserId) { + if (!needLimitByCurrentCreator()) { + return; + } + Long currentUserId = LoginHelper.getUserId(); + if (!Objects.equals(currentUserId, creatorUserId)) { + throw new ServiceException("仅允许访问当前账号创建的权限类型"); + } + } + + private boolean needLimitByCurrentCreator() { + LoginUser loginUser = LoginHelper.getLoginUser(); + return loginUser != null && ENTERPRISE_PORT.equals(loginUser.getLoginPort()) && !LoginHelper.isSuperAdmin(); + } + + private boolean isCurrentCompanyHead(LoginUser loginUser) { + if (loginUser == null || loginUser.getCompanyId() == null || loginUser.getUserId() == null) { + return false; + } + return safetyManagerMapper.exists(Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, loginUser.getCompanyId()) + .eq(HotCompanySafetyManager::getUserId, loginUser.getUserId()) + .eq(HotCompanySafetyManager::getCompanyHead, 1L) + .eq(HotCompanySafetyManager::getStatus, 1L) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .and(w -> w.eq(HotCompanySafetyManager::getIsResigned, 0L).or().isNull(HotCompanySafetyManager::getIsResigned))); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/service/impl/SysCompanyServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/company/service/impl/SysCompanyServiceImpl.java new file mode 100644 index 0000000..7303d1d --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/service/impl/SysCompanyServiceImpl.java @@ -0,0 +1,603 @@ +package com.hotwj.platform.resourceManagement.company.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.img.ImgUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.qrcode.QrCodeUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.common.utils.PasswordUtils; +import com.hotwj.platform.resourceManagement.company.domain.*; +import com.hotwj.platform.resourceManagement.company.domain.bo.SysCompanyBo; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.mapper.*; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.oss.core.OssClient; +import org.dromara.common.oss.factory.OssFactory; +import org.dromara.system.domain.SysRoleMenu; +import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.SysUserRole; +import org.dromara.system.mapper.SysRoleMenuMapper; +import org.dromara.system.mapper.SysUserMapper; +import org.dromara.system.mapper.SysUserRoleMapper; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.ByteArrayOutputStream; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService.BUSINESS_ADMIN_ROLE_ID; +import static com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService.ENTERPRISE_PORT; + +/** + * 企业基本信息Service业务层处理 + * + * @author shihongwei + * @date 2025-12-02 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class SysCompanyServiceImpl implements ISysCompanyService { + + @Value("${company.register-url}") + private String companyRegisterUrl; + + private final SysCompanyMapper baseMapper; + private final HotCompanyBankAccountMapper hotCompanyBankAccountMapper; + private final HotCompanyThirdPlatformMapper hotCompanyThirdPlatformMapper; + private final SysCompanyRoleMapper sysCompanyRoleMapper; + private final SysCompanyRoleMenuMapper sysCompanyRoleMenuMapper; + private final SysUserLoginPortMapper sysUserLoginPortMapper; + private final SysUserMapper sysUserMapper; + private final SysUserRoleMapper sysUserRoleMapper; + private final SysRoleMenuMapper sysRoleMenuMapper; + private final HotCompanySafetyManagerMapper hotCompanySafetyManagerMapper; + + /** + * 查询企业基本信息 + * + * @param id 主键 + * @return 企业基本信息 + */ + @Override + public SysCompanyVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询企业基本信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 企业基本信息分页列表 + */ + @Override + public TableDataInfo queryPageList(SysCompanyBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 按多个区和多个ID分页查询企业基本信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @param regionCodes 区代码列表 + * @param ids 企业ID列表 + * @return 企业基本信息分页列表 + */ + @Override + public TableDataInfo queryPageListByRegionAndIds(SysCompanyBo bo, PageQuery pageQuery, List regionCodes, List ids) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + if (CollUtil.isNotEmpty(regionCodes)) { + lqw.in(SysCompany::getRegionCode, regionCodes); + } + if (CollUtil.isNotEmpty(ids)) { + lqw.in(SysCompany::getId, ids); + } + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 按多个区划分页查询企业基本信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @param regionCodes 区代码列表 + * @return 企业基本信息分页列表 + */ + @Override + public TableDataInfo queryPageListByRegionCodes(SysCompanyBo bo, PageQuery pageQuery, List regionCodes) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + if (CollUtil.isNotEmpty(regionCodes)) { + lqw.in(SysCompany::getRegionCode, regionCodes); + } + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的企业基本信息列表 + * + * @param bo 查询条件 + * @return 企业基本信息列表 + */ + @Override + public List queryList(SysCompanyBo bo) { + return queryList(bo, null); + } + + /** + * 查询符合条件的企业基本信息列表 + * + * @param bo 查询条件 + * @param ids ID列表 + * @return 企业基本信息列表 + */ + @Override + public List queryList(SysCompanyBo bo, List ids) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + if (CollUtil.isNotEmpty(ids)) { + lqw.in(SysCompany::getId, ids); + } + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(SysCompanyBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(SysCompany::getCreateTime); + lqw.like(StringUtils.isNotBlank(bo.getCompanyName()), SysCompany::getCompanyName, bo.getCompanyName()); + lqw.like(StringUtils.isNotBlank(bo.getCompanyShortName()), SysCompany::getCompanyShortName, bo.getCompanyShortName()); + lqw.eq(StringUtils.isNotBlank(bo.getCompanyType()), SysCompany::getCompanyType, bo.getCompanyType()); + lqw.eq(StringUtils.isNotBlank(bo.getIndustry()), SysCompany::getIndustry, bo.getIndustry()); + lqw.like(StringUtils.isNotBlank(bo.getLegalPersonName()), SysCompany::getLegalPersonName, bo.getLegalPersonName()); + lqw.like(StringUtils.isNotBlank(bo.getResponsibleName()), SysCompany::getResponsibleName, bo.getResponsibleName()); + lqw.like(StringUtils.isNotBlank(bo.getResponsibleMobile()), SysCompany::getResponsibleMobile, bo.getResponsibleMobile()); + lqw.eq(StringUtils.isNotBlank(bo.getRegisteredAddress()), SysCompany::getRegisteredAddress, bo.getRegisteredAddress()); + lqw.eq(StringUtils.isNotBlank(bo.getOfficeAddress()), SysCompany::getOfficeAddress, bo.getOfficeAddress()); + lqw.eq(StringUtils.isNotBlank(bo.getOfficePhone()), SysCompany::getOfficePhone, bo.getOfficePhone()); + lqw.eq(StringUtils.isNotBlank(bo.getSealUrl()), SysCompany::getSealUrl, bo.getSealUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getRegionCode()), SysCompany::getRegionCode, bo.getRegionCode()); + lqw.eq(StringUtils.isNotBlank(bo.getProvinceCode()), SysCompany::getProvinceCode, bo.getProvinceCode()); + lqw.eq(StringUtils.isNotBlank(bo.getCityCode()), SysCompany::getCityCode, bo.getCityCode()); + lqw.eq(bo.getStatus() != null, SysCompany::getStatus, bo.getStatus()); + lqw.eq(bo.getIsDeleted() != null, SysCompany::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增企业基本信息 + * + * @param bo 企业基本信息 + * @return 是否新增成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(SysCompanyBo bo) { + SysCompany add = MapstructUtils.convert(bo, SysCompany.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + + // -- 插入子表数据 + insertBankAccounts(bo, add); + + insertThirdAccounts(bo, add); + + // 添加负责人到用户表 + addResponsibleUser(bo); + + // 生成驾驶员邀请码 + if (flag) { + boolean updated = ensureInvitationCode(add); + if (updated) { + baseMapper.updateById(add); + } + } + return flag; + } + + /** + * 确保企业有有效的驾驶员邀请码 + */ + private boolean ensureInvitationCode(SysCompany company) { + // 检查邀请码是否存在 + boolean needGenerate = false; + if (StrUtil.isBlank(company.getDriverInvitationCode())) { + needGenerate = true; + } + + if (needGenerate) { + // 1. 生成邀请码 + String code = IdUtil.fastSimpleUUID(); + company.setDriverInvitationCode(code); + // 邀请码长期有效,不设置过期时间 + company.setDriverInvitationCodeExpireTime(null); + + // 2. 生成二维码 + // 注册页面路径 + String registerUrl = companyRegisterUrl + "/pages/register?inviteCode=" + code + "&companyId=" + company.getId(); + + try { + // 生成二维码图片 + java.awt.image.BufferedImage image = QrCodeUtil.generate(registerUrl, 300, 300); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ImgUtil.writePng(image, out); + byte[] bytes = out.toByteArray(); + + // 上传OSS + OssClient ossClient = OssFactory.instance(); + String suffix = ".png"; + String contentType = "image/png"; + var result = ossClient.uploadSuffix(bytes, suffix, contentType); + company.setDriverInvitationQrCodeUrl(result.getUrl()); + } catch (Exception e) { + log.error("生成驾驶员邀请码二维码失败", e); + } + return true; + } + return false; + } + + public void insertThirdAccounts(SysCompanyBo bo, SysCompany add) { + if (CollUtil.isNotEmpty(bo.getThirdPlatforms())) { + List thirdPlatforms = MapstructUtils.convert(bo.getThirdPlatforms(), HotCompanyThirdPlatform.class); + thirdPlatforms.forEach(thirdPlatform -> thirdPlatform.setCompanyId(add.getId())); + hotCompanyThirdPlatformMapper.insertBatch(thirdPlatforms); + } + } + + public void updateBankAccounts(SysCompanyBo bo, SysCompany update) { + if (CollUtil.isNotEmpty(bo.getBankAccounts())) { + List bankAccounts = MapstructUtils.convert(bo.getBankAccounts(), HotCompanyBankAccount.class); + bankAccounts.forEach(bankAccount -> { + bankAccount.setCompanyId(update.getId()); + if (bankAccount.getId() == null) { + hotCompanyBankAccountMapper.insert(bankAccount); + } else { + if (bankAccount.getIsDeleted() != null && bankAccount.getIsDeleted() == 1) { + hotCompanyBankAccountMapper.deleteById(bankAccount); + } else { + hotCompanyBankAccountMapper.updateById(bankAccount); + } + } + }); + + } + } + + public void updateThirdAccounts(SysCompanyBo bo, SysCompany update) { + if (CollUtil.isNotEmpty(bo.getThirdPlatforms())) { + List thirdPlatforms = MapstructUtils.convert(bo.getThirdPlatforms(), HotCompanyThirdPlatform.class); + thirdPlatforms.forEach(thirdPlatform -> { + thirdPlatform.setCompanyId(update.getId()); + if (thirdPlatform.getId() == null) { + hotCompanyThirdPlatformMapper.insert(thirdPlatform); + } else { + if (thirdPlatform.getIsDeleted() != null && thirdPlatform.getIsDeleted() == 1) { + hotCompanyThirdPlatformMapper.deleteById(thirdPlatform); + } else { + hotCompanyThirdPlatformMapper.updateById(thirdPlatform); + } + } + }); + } + } + + public void insertBankAccounts(SysCompanyBo bo, SysCompany add) { + if (CollUtil.isNotEmpty(bo.getBankAccounts())) { + List bankAccounts = MapstructUtils.convert(bo.getBankAccounts(), HotCompanyBankAccount.class); + bankAccounts.forEach(bankAccount -> bankAccount.setCompanyId(add.getId())); + hotCompanyBankAccountMapper.insertBatch(bankAccounts); + } + } + + /** + * 添加负责人到用户表 + * + * @param bo 企业基本信息 + */ + public void addResponsibleUser(SysCompanyBo bo) { + Long companyId = bo.getId(); + String responsibleMobile = bo.getResponsibleMobile(); + String responsibleName = bo.getResponsibleName(); + + List sysUsers = sysUserMapper.selectList(Wrappers.lambdaQuery().eq(SysUser::getPhonenumber, responsibleMobile)); + Long userId = null; + + // 就算是多个企业管理员共用一个手机号,SysUser应该只有一个 + if (CollUtil.isNotEmpty(sysUsers)) { + if (sysUsers.size() > 1) { + throw new IllegalArgumentException("手机号已存在多个用户"); + } + userId = sysUsers.get(0).getUserId(); + } + + // 用户不存在,创建用户并设置角色 + if (userId == null) { + SysUser sysUser = new SysUser(); + sysUser.setPhonenumber(responsibleMobile); + sysUser.setNickName(responsibleName); + sysUser.setUserName(responsibleMobile); + // 主密码保留(兼容旧逻辑,或作为默认密码),但也需要设置子密码 + sysUser.setPassword(PasswordUtils.createDefaultPassword(responsibleMobile)); + sysUser.setUserType("sys_user"); + sysUser.setStatus("0"); + sysUserMapper.insert(sysUser); + userId = sysUser.getUserId(); + } + + // 无论用户是新创建还是已存在,都需要确保有该企业的登录端口,并初始化子密码 + // 检查是否存在登录端口 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysUserLoginPort::getCompanyId, companyId) + .eq(SysUserLoginPort::getUserId, userId) + .eq(SysUserLoginPort::getLoginPort, ENTERPRISE_PORT); + SysUserLoginPort sysUserLoginPort = sysUserLoginPortMapper.selectOne(wrapper); + + if (sysUserLoginPort == null) { + // 不存在,创建新的登录端口 + sysUserLoginPort = new SysUserLoginPort(); + sysUserLoginPort.setCompanyId(companyId); + sysUserLoginPort.setUserId(userId); + sysUserLoginPort.setUsername(responsibleName); + sysUserLoginPort.setLoginPort(ENTERPRISE_PORT); + sysUserLoginPort.setIsDefault(1); + sysUserLoginPort.setStatus(1); + // 初始化子密码:手机号 + "hot" + sysUserLoginPort.setSubPassword(PasswordUtils.createDefaultPassword(responsibleMobile)); + sysUserLoginPortMapper.insert(sysUserLoginPort); + } else { + // 已存在,检查是否有子密码,如果没有则补充 + if (StringUtils.isBlank(sysUserLoginPort.getSubPassword())) { + sysUserLoginPort.setSubPassword(PasswordUtils.createDefaultPassword(responsibleMobile)); + sysUserLoginPortMapper.updateById(sysUserLoginPort); + } + } + + // 确保有企业管理员角色 + Long roleId = createEnterpriseAdminRole(companyId, userId, responsibleName); + + // 查找是否已存在该人员记录 + LambdaQueryWrapper smWrapper = new LambdaQueryWrapper<>(); + smWrapper.eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getPhone, responsibleMobile); + HotCompanySafetyManager safetyManager = hotCompanySafetyManagerMapper.selectOne(smWrapper); + + if (safetyManager == null) { + safetyManager = new HotCompanySafetyManager(); + safetyManager.setCompanyId(companyId); + safetyManager.setName(responsibleName); + safetyManager.setPhone(responsibleMobile); + safetyManager.setUserId(userId); + // 设置为企业负责人 + safetyManager.setCompanyHead(1L); + // 企业负责人权限变更 + safetyManager.setPermissionType("company_admin"); + safetyManager.setPermissionRoleId(roleId); + + safetyManager.setIsResigned(0L); + safetyManager.setStatus(1L); + hotCompanySafetyManagerMapper.insert(safetyManager); + log.info("[企业负责人] 创建安全管理人员记录 companyId={}, userId={}, managerId={}", companyId, userId, safetyManager.getId()); + } else { + // 更新现有记录 + safetyManager.setName(responsibleName); + safetyManager.setUserId(userId); + safetyManager.setCompanyHead(1L); + safetyManager.setPermissionType("company_admin"); + safetyManager.setPermissionRoleId(roleId); + safetyManager.setIsResigned(0L); + safetyManager.setStatus(1L); + hotCompanySafetyManagerMapper.updateById(safetyManager); + log.info("[企业负责人] 更新安全管理人员记录 companyId={}, userId={}, managerId={}", companyId, userId, safetyManager.getId()); + } + } + + public Long createEnterpriseAdminRole(Long companyId, Long userId, String username) { + // 1. Check/Create SysCompanyRole + LambdaQueryWrapper roleWrapper = new LambdaQueryWrapper<>(); + roleWrapper.eq(SysCompanyRole::getCompanyId, companyId) + .eq(SysCompanyRole::getRoleKey, "company_admin"); + SysCompanyRole role = sysCompanyRoleMapper.selectOne(roleWrapper); + + if (role == null) { + role = new SysCompanyRole(); + role.setCompanyId(companyId); + role.setRoleName("企业管理员(默认)"); + role.setRoleKey("company_admin"); + role.setRoleSort(1); + role.setStatus("0"); // 0 normal + role.setRemark("自动生成的企业管理员角色"); + role.setCreatorUserId(null); + sysCompanyRoleMapper.insert(role); + // 赋予所有菜单权限 + List sysRoleMenus = sysRoleMenuMapper.selectList( + new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, BUSINESS_ADMIN_ROLE_ID) + ); + if (CollUtil.isNotEmpty(sysRoleMenus)) { + Long roleId = role.getId(); + List companyRoleMenus = sysRoleMenus.stream().map(sysRoleMenu -> { + SysCompanyRoleMenu companyRoleMenu = new SysCompanyRoleMenu(); + companyRoleMenu.setCompanyRoleId(roleId); + companyRoleMenu.setMenuId(sysRoleMenu.getMenuId()); + return companyRoleMenu; + }).toList(); + sysCompanyRoleMenuMapper.insertBatch(companyRoleMenus); + } + } + Long roleId = role.getId(); + + return roleId; + } + + /** + * 修改企业基本信息 + * + * @param bo 企业基本信息 + * @return 是否修改成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(SysCompanyBo bo) { + // 获取旧的企业信息以比对负责人是否变更 + SysCompany oldCompany = baseMapper.selectById(bo.getId()); + SysCompany update = MapstructUtils.convert(bo, SysCompany.class); + + if (oldCompany != null) { + if (update.getDriverInvitationCode() == null) { + update.setDriverInvitationCode(oldCompany.getDriverInvitationCode()); + update.setDriverInvitationCodeExpireTime(oldCompany.getDriverInvitationCodeExpireTime()); + update.setDriverInvitationQrCodeUrl(oldCompany.getDriverInvitationQrCodeUrl()); + } + } + + ensureInvitationCode(update); + + validEntityBeforeSave(update); + boolean flag = baseMapper.updateById(update) > 0; + + // 更新子表的操作 + updateBankAccounts(bo, update); + updateThirdAccounts(bo, update); + + // 修改企业负责人是否用户信息也需要变更?? + if (oldCompany != null) { + // 1. 处理旧负责人:移除相关权限,标记离职(仅当负责人手机号变更时) + String oldMobile = oldCompany.getResponsibleMobile(); + if (StrUtil.isNotBlank(oldMobile) && !StrUtil.equals(oldMobile, bo.getResponsibleMobile())) { + SysUser oldUser = sysUserMapper.selectOne(new LambdaQueryWrapper().eq(SysUser::getPhonenumber, oldMobile)); + if (oldUser != null) { + Long oldUserId = oldUser.getUserId(); + // 移除该用户在当前企业的角色关联 + sysUserRoleMapper.delete(new LambdaQueryWrapper() + .eq(SysUserRole::getUserId, oldUserId) + .eq(SysUserRole::getLoginPort, ENTERPRISE_PORT) + .eq(SysUserRole::getCompanyId, bo.getId())); + + // 移除该用户与企业的关联关系 + sysUserLoginPortMapper.delete(new LambdaQueryWrapper() + .eq(SysUserLoginPort::getUserId, oldUserId) + .eq(SysUserLoginPort::getLoginPort, ENTERPRISE_PORT) + .eq(SysUserLoginPort::getCompanyId, bo.getId())); + + hotCompanySafetyManagerMapper.deleteCompanyHeadByPhone(bo.getId(), oldMobile); + } + } + + // 2. 添加新负责人:创建用户(如果不存在)、赋予权限、添加安全管理人员记录 + addResponsibleUser(bo); + } + + return flag; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(SysCompany entity) { + } + + /** + * 校验并批量删除企业基本信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + public String checkInviteCode(Long companyId, String inviteCode) { + if (companyId == null) { + throw new ServiceException("企业ID不能为空"); + } + if (StringUtils.isBlank(inviteCode)) { + throw new ServiceException("邀请码不能为空"); + } + SysCompany company = baseMapper.selectById(companyId); + if (company == null) { + throw new ServiceException("企业不存在"); + } + if (!inviteCode.equals(company.getDriverInvitationCode())) { + throw new ServiceException("邀请码无效"); + } + // 校验是否过期(如果有过期时间的话) + // if (company.getDriverInvitationCodeExpireTime() != null && company.getDriverInvitationCodeExpireTime().before(new Date())) { + // throw new ServiceException("邀请码已过期"); + // } + return company.getCompanyName(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void resetAllInviteCodes() { + // 1. 查出所有未删除的企业 + List companyList = baseMapper.selectList(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(SysCompany::getIsDeleted, 0)); + + if (CollUtil.isEmpty(companyList)) { + return; + } + + log.info("开始重置所有企业邀请码,共 {} 家", companyList.size()); + + // 2. 遍历重置 + for (SysCompany company : companyList) { + try { + // 清空现有邀请码,强制 ensureInvitationCode 重新生成 + company.setDriverInvitationCode(null); + boolean updated = ensureInvitationCode(company); + if (updated) { + baseMapper.updateById(company); + } + } catch (Exception e) { + log.error("重置企业邀请码失败: companyId={}, name={}", company.getId(), company.getCompanyName(), e); + } + } + log.info("重置所有企业邀请码完成"); + } + + /** + * 根据邀请码查询企业信息 + * + * @param code 邀请码 + * @return 企业信息 + */ + @Override + public SysCompanyVo queryByInviteCode(String code) { + if (StringUtils.isBlank(code)) { + return null; + } + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(SysCompany::getDriverInvitationCode, code); + + return baseMapper.selectVoOne(lqw); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/company/service/impl/SysUserLoginPortServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/company/service/impl/SysUserLoginPortServiceImpl.java new file mode 100644 index 0000000..e521d5a --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/company/service/impl/SysUserLoginPortServiceImpl.java @@ -0,0 +1,53 @@ +package com.hotwj.platform.resourceManagement.company.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.hotwj.platform.resourceManagement.company.domain.SysUserLoginPort; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysUserLoginPortVo; +import com.hotwj.platform.resourceManagement.company.mapper.SysUserLoginPortMapper; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 用户-企业-登录端口关联关系Service业务层处理 + * + * @author shihongwei + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class SysUserLoginPortServiceImpl extends ServiceImpl implements ISysUserLoginPortService { + + /** + * 查询用户-企业-登录端口关联关系列表(根据端口) + * + * @param userId 用户ID + * @return 用户-企业-登录端口关联关系 + */ + @Override + public List selectLoginPortListByPort(Long userId, String port) { + List sysUserLoginPortVos = baseMapper.selectLoginPortListByPort(userId, port); + + return sysUserLoginPortVos; + } + + @Override + public List selectUserLoginPorts(Long userId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.select(SysUserLoginPort::getLoginPort) + .eq(SysUserLoginPort::getUserId, userId); + List sysUserLoginPorts = baseMapper.selectList(wrapper); + return sysUserLoginPorts.stream() + .map(SysUserLoginPort::getLoginPort) + .filter(StringUtils::isNotBlank) + .distinct() + .collect(Collectors.toList()); + } + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/controller/HotCompanyChangeHistoryController.java b/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/controller/HotCompanyChangeHistoryController.java new file mode 100644 index 0000000..702e838 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/controller/HotCompanyChangeHistoryController.java @@ -0,0 +1,57 @@ +package com.hotwj.platform.resourceManagement.companyChangeHistory.controller; + +import com.hotwj.platform.resourceManagement.companyChangeHistory.domain.bo.HotCompanyChangeHistoryBo; +import com.hotwj.platform.resourceManagement.companyChangeHistory.domain.vo.HotCompanyChangeHistoryVo; +import com.hotwj.platform.resourceManagement.companyChangeHistory.service.IHotCompanyChangeHistoryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 企业人员信息变更历史 + * + * @author shihongwei + * @date 2025-12-10 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/companyChangeHistory") +@Tag(name = "企业人员信息变更历史", description = "人员信息变更历史查询") +public class HotCompanyChangeHistoryController extends BaseController { + + private final IHotCompanyChangeHistoryService changeHistoryService; + + /** + * 查询安全管理人员列表 + */ + //@SaCheckPermission("resourceManagement:changeHistory:list") + @GetMapping("/list") + @Operation(summary = "分页查询安全管理人员变更历史列表") + public TableDataInfo list(HotCompanyChangeHistoryBo bo, PageQuery pageQuery) { + return changeHistoryService.queryPageList(bo, pageQuery); + } + + /** + * 获取安全管理人员详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:changeHistory:query") + @GetMapping("/{id}") + @Operation(summary = "查询安全管理人员变更历史详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(changeHistoryService.queryById(id)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/domain/HotCompanyChangeHistory.java b/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/domain/HotCompanyChangeHistory.java new file mode 100644 index 0000000..312fd28 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/domain/HotCompanyChangeHistory.java @@ -0,0 +1,105 @@ +package com.hotwj.platform.resourceManagement.companyChangeHistory.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 人员变更历史记录 hot_company_change_history + * + * @author shihongwei + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_company_change_history") +public class HotCompanyChangeHistory extends BaseEntity { + + @TableId(value = "id") + private Long id; + + private Long companyId; + + /** + * 用户名称 + */ + private String name; + + /** + * 用户手机号 + */ + private String phone; + + /** + * 员工ID(安全管理人员ID) + */ + private Long employeeId; + + /** + * 变更前岗位 + */ + private String beforeJobName; + + /** + * 变更后岗位 + */ + private String afterJobName; + + /** + * 变更前部门ID + */ + private Long beforeDeptId; + + /** + * 变更后部门ID + */ + private Long afterDeptId; + + /** + * 变更类型:调岗位/调部门/调岗调部门/离职 + */ + private String changeType; + + /** + * 变更时间 + */ + private Date changeTime; + + /** + * 操作人ID + */ + private Long operatorId; + + /** + * 操作人名称 + */ + private String operatorName; + + /** + * 变更原因(可选) + */ + private String changeReason; + + /** + * 变动前负责业务 + */ + private String beforeBusiness; + + /** + * 变更前任命书附件(可选) + */ + private String beforeAppointmentUrl; + + /** + * 变更后任命书附件(可选) + */ + private String afterAppointmentUrl; + + /** + * 逻辑删除标记:0正常,1删除 + */ + private Integer isDeleted; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/domain/bo/HotCompanyChangeHistoryBo.java b/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/domain/bo/HotCompanyChangeHistoryBo.java new file mode 100644 index 0000000..fed248c --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/domain/bo/HotCompanyChangeHistoryBo.java @@ -0,0 +1,57 @@ +package com.hotwj.platform.resourceManagement.companyChangeHistory.domain.bo; + +import com.hotwj.platform.resourceManagement.companyChangeHistory.domain.HotCompanyChangeHistory; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 人员变更历史记录 BO + * + * @author shihongwei + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotCompanyChangeHistory.class, reverseConvertGenerate = false) +public class HotCompanyChangeHistoryBo extends BaseEntity { + + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + @NotNull(message = "员工ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long employeeId; + + /** + * 用户名称 + */ + @NotNull(message = "用户名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String name; + /** + * 用户手机号 + */ + @NotNull(message = "用户手机号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String phone; + + private String beforeJobName; + private String afterJobName; + private Long beforeDeptId; + private Long afterDeptId; + private String changeType; + private Date changeTime; + private Long operatorId; + private String operatorName; + private String changeReason; + private String beforeBusiness; + private String beforeAppointmentUrl; + private String afterAppointmentUrl; + private Integer isDeleted; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/domain/vo/HotCompanyChangeHistoryVo.java b/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/domain/vo/HotCompanyChangeHistoryVo.java new file mode 100644 index 0000000..059d1ea --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/domain/vo/HotCompanyChangeHistoryVo.java @@ -0,0 +1,58 @@ +package com.hotwj.platform.resourceManagement.companyChangeHistory.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import com.hotwj.platform.resourceManagement.companyChangeHistory.domain.HotCompanyChangeHistory; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 人员变更历史记录 VO + * + * @author shihongwei + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotCompanyChangeHistory.class) +public class HotCompanyChangeHistoryVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + private Long companyId; + /** 用户名称 */ + private String name; + /** 用户手机号 */ + private String phone; + /** 员工ID(安全管理人员ID) */ + private Long employeeId; + private String beforeJobName; + private String afterJobName; + private Long beforeDeptId; + private Long afterDeptId; + private String changeType; + private Date changeTime; + private Long operatorId; + private String operatorName; + private String changeReason; + /** + * 变动前负责业务 + */ + private String beforeBusiness; + private String beforeAppointmentUrl; + private String afterAppointmentUrl; + + /** + * 变更前部门名称 + */ + private String beforeDeptName; + + /** + * 变更后部门名称 + */ + private String afterDeptName; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/mapper/HotCompanyChangeHistoryMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/mapper/HotCompanyChangeHistoryMapper.java new file mode 100644 index 0000000..ce24a42 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/mapper/HotCompanyChangeHistoryMapper.java @@ -0,0 +1,15 @@ +package com.hotwj.platform.resourceManagement.companyChangeHistory.mapper; + +import com.hotwj.platform.resourceManagement.companyChangeHistory.domain.HotCompanyChangeHistory; +import com.hotwj.platform.resourceManagement.companyChangeHistory.domain.vo.HotCompanyChangeHistoryVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 人员变更历史记录 Mapper + * + * @author shihongwei + */ +@Mapper +public interface HotCompanyChangeHistoryMapper extends BaseMapperPlus { +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/service/IHotCompanyChangeHistoryService.java b/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/service/IHotCompanyChangeHistoryService.java new file mode 100644 index 0000000..3d9d5dc --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/service/IHotCompanyChangeHistoryService.java @@ -0,0 +1,29 @@ +package com.hotwj.platform.resourceManagement.companyChangeHistory.service; + +import com.hotwj.platform.resourceManagement.companyChangeHistory.domain.bo.HotCompanyChangeHistoryBo; +import com.hotwj.platform.resourceManagement.companyChangeHistory.domain.vo.HotCompanyChangeHistoryVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 人员变更历史记录 Service 接口 + * + * @author shihongwei + */ +public interface IHotCompanyChangeHistoryService { + + HotCompanyChangeHistoryVo queryById(Long id); + + TableDataInfo queryPageList(HotCompanyChangeHistoryBo bo, PageQuery pageQuery); + + List queryList(HotCompanyChangeHistoryBo bo); + + Boolean insertByBo(HotCompanyChangeHistoryBo bo); + + Boolean updateByBo(HotCompanyChangeHistoryBo bo); + + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/service/impl/HotCompanyChangeHistoryServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/service/impl/HotCompanyChangeHistoryServiceImpl.java new file mode 100644 index 0000000..b55628a --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyChangeHistory/service/impl/HotCompanyChangeHistoryServiceImpl.java @@ -0,0 +1,172 @@ +package com.hotwj.platform.resourceManagement.companyChangeHistory.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.config.companyDeptConfig.domain.HotCompanyDeptConfig; +import com.hotwj.platform.config.companyDeptConfig.mapper.HotCompanyDeptConfigMapper; +import com.hotwj.platform.resourceManagement.companyChangeHistory.domain.HotCompanyChangeHistory; +import com.hotwj.platform.resourceManagement.companyChangeHistory.domain.bo.HotCompanyChangeHistoryBo; +import com.hotwj.platform.resourceManagement.companyChangeHistory.domain.vo.HotCompanyChangeHistoryVo; +import com.hotwj.platform.resourceManagement.companyChangeHistory.mapper.HotCompanyChangeHistoryMapper; +import com.hotwj.platform.resourceManagement.companyChangeHistory.service.IHotCompanyChangeHistoryService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * 人员变更历史记录 Service + * + * @author shihongwei + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotCompanyChangeHistoryServiceImpl implements IHotCompanyChangeHistoryService { + + private final HotCompanyChangeHistoryMapper baseMapper; + private final HotCompanyDeptConfigMapper deptConfigMapper; + + @Override + public HotCompanyChangeHistoryVo queryById(Long id) { + HotCompanyChangeHistoryVo vo = baseMapper.selectVoById(id); + if (vo != null) { + fillDeptInfo(java.util.Collections.singletonList(vo)); + } + return vo; + } + + @Override + public TableDataInfo queryPageList(HotCompanyChangeHistoryBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + fillDeptInfo(result.getRecords()); + return TableDataInfo.build(result); + } + + @Override + public List queryList(HotCompanyChangeHistoryBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + List list = baseMapper.selectVoList(lqw); + fillDeptInfo(list); + return list; + } + + private void fillDeptInfo(List list) { + if (list == null || list.isEmpty()) { + return; + } + Set deptIds = new HashSet<>(); + for (HotCompanyChangeHistoryVo vo : list) { + if (vo.getBeforeDeptId() != null) { + deptIds.add(vo.getBeforeDeptId()); + } + if (vo.getAfterDeptId() != null) { + deptIds.add(vo.getAfterDeptId()); + } + } + Map deptMap = new HashMap<>(); + if (!deptIds.isEmpty()) { + List depts = deptConfigMapper.selectBatchIds(deptIds); + for (HotCompanyDeptConfig dept : depts) { + deptMap.put(dept.getId(), dept.getDeptName()); + } + } + for (HotCompanyChangeHistoryVo vo : list) { + String beforeDeptName = deptMap.get(vo.getBeforeDeptId()); + String afterDeptName = deptMap.get(vo.getAfterDeptId()); + vo.setBeforeDeptName(beforeDeptName); + vo.setAfterDeptName(afterDeptName); + + if ("调岗调部门".equals(vo.getChangeType())) { + if (StringUtils.isNotBlank(beforeDeptName)) { + vo.setBeforeJobName(buildJobNameWithDept(vo.getBeforeJobName(), beforeDeptName)); + } + if (StringUtils.isNotBlank(afterDeptName)) { + vo.setAfterJobName(buildJobNameWithDept(vo.getAfterJobName(), afterDeptName)); + } + } + } + } + + private String buildJobNameWithDept(String jobName, String deptName) { + String normalizedJobName = StringUtils.isNotBlank(jobName) ? jobName.trim() : ""; + String fullSuffix = "(" + deptName + ")"; + String fullSuffixWithSpace = " (" + deptName + ")"; + String halfSuffix = "(" + deptName + ")"; + String halfSuffixWithSpace = " (" + deptName + ")"; + String slashSuffix = "/" + deptName; + String slashSuffixWithSpace = " /" + deptName; + while (normalizedJobName.endsWith(fullSuffix) + || normalizedJobName.endsWith(fullSuffixWithSpace) + || normalizedJobName.endsWith(halfSuffix) + || normalizedJobName.endsWith(halfSuffixWithSpace) + || normalizedJobName.endsWith(slashSuffix) + || normalizedJobName.endsWith(slashSuffixWithSpace)) { + if (normalizedJobName.endsWith(fullSuffix)) { + normalizedJobName = normalizedJobName.substring(0, normalizedJobName.length() - fullSuffix.length()).trim(); + continue; + } + if (normalizedJobName.endsWith(fullSuffixWithSpace)) { + normalizedJobName = normalizedJobName.substring(0, normalizedJobName.length() - fullSuffixWithSpace.length()).trim(); + continue; + } + if (normalizedJobName.endsWith(halfSuffix)) { + normalizedJobName = normalizedJobName.substring(0, normalizedJobName.length() - halfSuffix.length()).trim(); + continue; + } + if (normalizedJobName.endsWith(halfSuffixWithSpace)) { + normalizedJobName = normalizedJobName.substring(0, normalizedJobName.length() - halfSuffixWithSpace.length()).trim(); + continue; + } + if (normalizedJobName.endsWith(slashSuffix)) { + normalizedJobName = normalizedJobName.substring(0, normalizedJobName.length() - slashSuffix.length()).trim(); + continue; + } + normalizedJobName = normalizedJobName.substring(0, normalizedJobName.length() - slashSuffixWithSpace.length()).trim(); + } + return normalizedJobName; + } + + private LambdaQueryWrapper buildQueryWrapper(HotCompanyChangeHistoryBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(bo.getCompanyId() != null, HotCompanyChangeHistory::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getEmployeeId() != null, HotCompanyChangeHistory::getEmployeeId, bo.getEmployeeId()); + lqw.like(StringUtils.isNotBlank(bo.getName()), HotCompanyChangeHistory::getName, bo.getName()); + lqw.like(StringUtils.isNotBlank(bo.getPhone()), HotCompanyChangeHistory::getPhone, bo.getPhone()); + lqw.eq(StringUtils.isNotBlank(bo.getChangeType()), HotCompanyChangeHistory::getChangeType, bo.getChangeType()); + lqw.orderByDesc(HotCompanyChangeHistory::getChangeTime); + return lqw; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(HotCompanyChangeHistoryBo bo) { + HotCompanyChangeHistory add = MapstructUtils.convert(bo, HotCompanyChangeHistory.class); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(HotCompanyChangeHistoryBo bo) { + HotCompanyChangeHistory update = MapstructUtils.convert(bo, HotCompanyChangeHistory.class); + return baseMapper.updateById(update) > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/controller/HotCompanyInsuranceController.java b/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/controller/HotCompanyInsuranceController.java new file mode 100644 index 0000000..765410a --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/controller/HotCompanyInsuranceController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.resourceManagement.companyInsurance.controller; + +import com.hotwj.platform.resourceManagement.companyInsurance.domain.bo.HotCompanyInsuranceBo; +import com.hotwj.platform.resourceManagement.companyInsurance.domain.vo.HotCompanyInsuranceVo; +import com.hotwj.platform.resourceManagement.companyInsurance.service.IHotCompanyInsuranceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 公司企业保险信息 + * + * @author shihongwei + * @date 2025-12-10 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/companyInsurance") +@Tag(name = "公司企业保险信息", description = "公司企业保险信息增删改查") +public class HotCompanyInsuranceController extends BaseController { + + private final IHotCompanyInsuranceService hotCompanyInsuranceService; + + /** + * 查询公司企业保险信息列表 + */ + //@SaCheckPermission("resourceManagement:companyInsurance:list") + @GetMapping("/list") + @Operation(summary = "分页查询公司企业保险信息列表") + public TableDataInfo list(HotCompanyInsuranceBo bo, PageQuery pageQuery) { + return hotCompanyInsuranceService.queryPageList(bo, pageQuery); + } + + /** + * 导出公司企业保险信息列表 + */ + //@SaCheckPermission("resourceManagement:companyInsurance:export") + @Log(title = "公司企业保险信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出公司企业保险信息列表") + public void export(HotCompanyInsuranceBo bo, HttpServletResponse response) { + List list = hotCompanyInsuranceService.queryList(bo); + ExcelUtil.exportExcel(list, "公司企业保险信息", HotCompanyInsuranceVo.class, response); + } + + /** + * 获取公司企业保险信息详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:companyInsurance:query") + @GetMapping("/{id}") + @Operation(summary = "查询公司企业保险信息详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotCompanyInsuranceService.queryById(id)); + } + + /** + * 新增公司企业保险信息 + */ + //@SaCheckPermission("resourceManagement:companyInsurance:add") + @Log(title = "公司企业保险信息", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增公司企业保险信息") + public R add(@Validated(AddGroup.class) @RequestBody HotCompanyInsuranceBo bo) { + return toAjax(hotCompanyInsuranceService.insertByBo(bo)); + } + + /** + * 修改公司企业保险信息 + */ + //@SaCheckPermission("resourceManagement:companyInsurance:edit") + @Log(title = "公司企业保险信息", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改公司企业保险信息") + public R edit(@Validated(EditGroup.class) @RequestBody HotCompanyInsuranceBo bo) { + return toAjax(hotCompanyInsuranceService.updateByBo(bo)); + } + + /** + * 删除公司企业保险信息 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:companyInsurance:remove") + @Log(title = "公司企业保险信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除公司企业保险信息") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotCompanyInsuranceService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/domain/HotCompanyInsurance.java b/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/domain/HotCompanyInsurance.java new file mode 100644 index 0000000..b80d61d --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/domain/HotCompanyInsurance.java @@ -0,0 +1,91 @@ +package com.hotwj.platform.resourceManagement.companyInsurance.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 公司企业保险信息对象 hot_company_insurance + * + * @author shihongwei + * @date 2025-12-10 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_company_insurance") +public class HotCompanyInsurance extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 险种 + */ + private String insuranceType; + + /** + * 保险公司 + */ + private String insurerName; + + /** + * 起保日期 + */ + private Date startDate; + + /** + * 终保日期 + */ + private Date endDate; + + /** + * 渠道人 + */ + private String channelPerson; + + /** + * 渠道人手机号 + */ + private String channelPhone; + + /** + * 保险费用 + */ + private BigDecimal premiumAmount; + + /** + * 图片附件URL(多个用逗号分隔) + */ + private String annexUrl; + + /** + * 0=未归档, 1=已归档 + */ + private Long isArchived; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/domain/bo/HotCompanyInsuranceBo.java b/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/domain/bo/HotCompanyInsuranceBo.java new file mode 100644 index 0000000..c3698b8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/domain/bo/HotCompanyInsuranceBo.java @@ -0,0 +1,100 @@ +package com.hotwj.platform.resourceManagement.companyInsurance.domain.bo; + +import com.hotwj.platform.resourceManagement.companyInsurance.domain.HotCompanyInsurance; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 公司企业保险信息业务对象 hot_company_insurance + * + * @author shihongwei + * @date 2025-12-10 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotCompanyInsurance.class, reverseConvertGenerate = false) +public class HotCompanyInsuranceBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 险种 + */ + @NotBlank(message = "险种不能为空", groups = {AddGroup.class, EditGroup.class}) + private String insuranceType; + + /** + * 保险公司 + */ + @NotBlank(message = "保险公司不能为空", groups = {AddGroup.class, EditGroup.class}) + private String insurerName; + + /** + * 起保日期 + */ + @NotNull(message = "起保日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date startDate; + + /** + * 终保日期 + */ + @NotNull(message = "终保日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date endDate; + + /** + * 渠道人 + */ + private String channelPerson; + + /** + * 渠道人手机号 + */ + private String channelPhone; + + /** + * 保险费用 + */ + @NotNull(message = "保险费用不能为空", groups = {AddGroup.class, EditGroup.class}) + private BigDecimal premiumAmount; + + /** + * 图片附件URL(多个用逗号分隔) + */ + private String annexUrl; + + /** + * 0=未归档, 1=已归档 + */ + private Long isArchived; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + /** + * 导出/批量查询的保险IDs(逗号分隔) + */ + private String ids; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/domain/vo/HotCompanyInsuranceVo.java b/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/domain/vo/HotCompanyInsuranceVo.java new file mode 100644 index 0000000..54f2549 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/domain/vo/HotCompanyInsuranceVo.java @@ -0,0 +1,104 @@ +package com.hotwj.platform.resourceManagement.companyInsurance.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.companyInsurance.domain.HotCompanyInsurance; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 公司企业保险信息视图对象 hot_company_insurance + * + * @author shihongwei + * @date 2025-12-10 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotCompanyInsurance.class) +public class HotCompanyInsuranceVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 险种 + */ + @ExcelProperty(value = "险种", index = 0) + private String insuranceType; + + /** + * 保险公司 + */ + @ExcelProperty(value = "保险公司", index = 1) + private String insurerName; + + /** + * 起保日期 + */ + private Date startDate; + + /** + * 终保日期 + */ + @ExcelProperty(value = "终保日期", index = 2) + private Date endDate; + + /** + * 渠道人 + */ + @ExcelProperty(value = "渠道人", index = 3) + private String channelPerson; + + /** + * 渠道人手机号 + */ + @ExcelProperty(value = "渠道人手机号", index = 4) + private String channelPhone; + + /** + * 保险费用 + */ + private BigDecimal premiumAmount; + + /** + * 图片附件URL(多个用逗号分隔) + */ + private String annexUrl; + + /** + * 图片附件URL(多个用逗号分隔)Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "annexUrl") + private String annexUrlUrl; + /** + * 0=未归档, 1=已归档 + */ + @ExcelProperty(value = "是否归档", index = 5, converter = org.dromara.common.excel.convert.ExcelDictConvert.class) + @org.dromara.common.excel.annotation.ExcelDictFormat(readConverterExp = "1=是,0=否") + private Long isArchived; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/mapper/HotCompanyInsuranceMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/mapper/HotCompanyInsuranceMapper.java new file mode 100644 index 0000000..0f7c776 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/mapper/HotCompanyInsuranceMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.companyInsurance.mapper; + +import com.hotwj.platform.resourceManagement.companyInsurance.domain.HotCompanyInsurance; +import com.hotwj.platform.resourceManagement.companyInsurance.domain.vo.HotCompanyInsuranceVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 公司企业保险信息Mapper接口 + * + * @author shihongwei + * @date 2025-12-10 + */ +@Mapper +public interface HotCompanyInsuranceMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/service/IHotCompanyInsuranceService.java b/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/service/IHotCompanyInsuranceService.java new file mode 100644 index 0000000..8872b6e --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/service/IHotCompanyInsuranceService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.resourceManagement.companyInsurance.service; + +import com.hotwj.platform.resourceManagement.companyInsurance.domain.bo.HotCompanyInsuranceBo; +import com.hotwj.platform.resourceManagement.companyInsurance.domain.vo.HotCompanyInsuranceVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 公司企业保险信息Service接口 + * + * @author shihongwei + * @date 2025-12-10 + */ +public interface IHotCompanyInsuranceService { + + /** + * 查询公司企业保险信息 + * + * @param id 主键 + * @return 公司企业保险信息 + */ + HotCompanyInsuranceVo queryById(Long id); + + /** + * 分页查询公司企业保险信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 公司企业保险信息分页列表 + */ + TableDataInfo queryPageList(HotCompanyInsuranceBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的公司企业保险信息列表 + * + * @param bo 查询条件 + * @return 公司企业保险信息列表 + */ + List queryList(HotCompanyInsuranceBo bo); + + /** + * 新增公司企业保险信息 + * + * @param bo 公司企业保险信息 + * @return 是否新增成功 + */ + Boolean insertByBo(HotCompanyInsuranceBo bo); + + /** + * 修改公司企业保险信息 + * + * @param bo 公司企业保险信息 + * @return 是否修改成功 + */ + Boolean updateByBo(HotCompanyInsuranceBo bo); + + /** + * 校验并批量删除公司企业保险信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/service/impl/HotCompanyInsuranceServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/service/impl/HotCompanyInsuranceServiceImpl.java new file mode 100644 index 0000000..7481904 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyInsurance/service/impl/HotCompanyInsuranceServiceImpl.java @@ -0,0 +1,152 @@ +package com.hotwj.platform.resourceManagement.companyInsurance.service.impl; + +import cn.hutool.core.convert.Convert; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.companyInsurance.domain.HotCompanyInsurance; +import com.hotwj.platform.resourceManagement.companyInsurance.domain.bo.HotCompanyInsuranceBo; +import com.hotwj.platform.resourceManagement.companyInsurance.domain.vo.HotCompanyInsuranceVo; +import com.hotwj.platform.resourceManagement.companyInsurance.mapper.HotCompanyInsuranceMapper; +import com.hotwj.platform.resourceManagement.companyInsurance.service.IHotCompanyInsuranceService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 公司企业保险信息Service业务层处理 + * + * @author shihongwei + * @date 2025-12-10 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotCompanyInsuranceServiceImpl implements IHotCompanyInsuranceService { + + private final HotCompanyInsuranceMapper baseMapper; + + /** + * 查询公司企业保险信息 + * + * @param id 主键 + * @return 公司企业保险信息 + */ + @Override + public HotCompanyInsuranceVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询公司企业保险信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 公司企业保险信息分页列表 + */ + @Override + public TableDataInfo queryPageList(HotCompanyInsuranceBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的公司企业保险信息列表 + * + * @param bo 查询条件 + * @return 公司企业保险信息列表 + */ + @Override + public List queryList(HotCompanyInsuranceBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotCompanyInsuranceBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + Object beginEndDate = params == null ? null : params.get("beginEndDate"); + Object endEndDate = params == null ? null : params.get("endEndDate"); + lqw.orderByAsc(HotCompanyInsurance::getId); + lqw.in(StringUtils.isNotBlank(bo.getIds()), HotCompanyInsurance::getId, StringUtils.splitTo(bo.getIds(), Convert::toLong)); + lqw.eq(bo.getCompanyId() != null, HotCompanyInsurance::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getInsuranceType()), HotCompanyInsurance::getInsuranceType, bo.getInsuranceType()); + lqw.like(StringUtils.isNotBlank(bo.getInsurerName()), HotCompanyInsurance::getInsurerName, bo.getInsurerName()); + lqw.eq(bo.getStartDate() != null, HotCompanyInsurance::getStartDate, bo.getStartDate()); + lqw.eq(bo.getEndDate() != null, HotCompanyInsurance::getEndDate, bo.getEndDate()); + lqw.between(beginEndDate != null && endEndDate != null, HotCompanyInsurance::getEndDate, beginEndDate, endEndDate); + lqw.like(StringUtils.isNotBlank(bo.getChannelPerson()), HotCompanyInsurance::getChannelPerson, bo.getChannelPerson()); + lqw.like(StringUtils.isNotBlank(bo.getChannelPhone()), HotCompanyInsurance::getChannelPhone, bo.getChannelPhone()); + lqw.eq(bo.getPremiumAmount() != null, HotCompanyInsurance::getPremiumAmount, bo.getPremiumAmount()); + lqw.eq(StringUtils.isNotBlank(bo.getAnnexUrl()), HotCompanyInsurance::getAnnexUrl, bo.getAnnexUrl()); + lqw.eq(bo.getIsArchived() != null, HotCompanyInsurance::getIsArchived, bo.getIsArchived()); + lqw.eq(bo.getIsDeleted() != null, HotCompanyInsurance::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增公司企业保险信息 + * + * @param bo 公司企业保险信息 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotCompanyInsuranceBo bo) { + HotCompanyInsurance add = MapstructUtils.convert(bo, HotCompanyInsurance.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改公司企业保险信息 + * + * @param bo 公司企业保险信息 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotCompanyInsuranceBo bo) { + HotCompanyInsurance update = MapstructUtils.convert(bo, HotCompanyInsurance.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotCompanyInsurance entity) { + // 校验日期:终保日期必须大于起保日期 + if (entity.getStartDate() != null && entity.getEndDate() != null) { + if (!entity.getEndDate().after(entity.getStartDate())) { + throw new IllegalArgumentException("终保日期必须大于起保日期"); + } + } + } + + /** + * 校验并批量删除公司企业保险信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/controller/HotCompanyLaborContractController.java b/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/controller/HotCompanyLaborContractController.java new file mode 100644 index 0000000..ff71614 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/controller/HotCompanyLaborContractController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.resourceManagement.companyLaborContract.controller; + +import com.hotwj.platform.resourceManagement.companyLaborContract.domain.bo.HotCompanyLaborContractBo; +import com.hotwj.platform.resourceManagement.companyLaborContract.domain.vo.HotCompanyLaborContractVo; +import com.hotwj.platform.resourceManagement.companyLaborContract.service.IHotCompanyLaborContractService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 公司劳动合同 + * + * @author shihongwei + * @date 2025-12-10 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/companyLaborContract") +@Tag(name = "公司劳动合同", description = "公司劳动合同增删改查") +public class HotCompanyLaborContractController extends BaseController { + + private final IHotCompanyLaborContractService hotCompanyLaborContractService; + + /** + * 查询公司劳动合同列表 + */ + //@SaCheckPermission("resourceManagement:companyLaborContract:list") + @GetMapping("/list") + @Operation(summary = "分页查询公司劳动合同列表") + public TableDataInfo list(HotCompanyLaborContractBo bo, PageQuery pageQuery) { + return hotCompanyLaborContractService.queryPageList(bo, pageQuery); + } + + /** + * 导出公司劳动合同列表 + */ + //@SaCheckPermission("resourceManagement:companyLaborContract:export") + @Log(title = "公司劳动合同", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出公司劳动合同列表") + public void export(HotCompanyLaborContractBo bo, HttpServletResponse response) { + List list = hotCompanyLaborContractService.queryList(bo); + ExcelUtil.exportExcel(list, "公司劳动合同", HotCompanyLaborContractVo.class, response); + } + + /** + * 获取公司劳动合同详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:companyLaborContract:query") + @GetMapping("/{id}") + @Operation(summary = "查询公司劳动合同详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotCompanyLaborContractService.queryById(id)); + } + + /** + * 新增公司劳动合同 + */ + //@SaCheckPermission("resourceManagement:companyLaborContract:add") + @Log(title = "公司劳动合同", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增公司劳动合同") + public R add(@Validated(AddGroup.class) @RequestBody HotCompanyLaborContractBo bo) { + return toAjax(hotCompanyLaborContractService.insertByBo(bo)); + } + + /** + * 修改公司劳动合同 + */ + //@SaCheckPermission("resourceManagement:companyLaborContract:edit") + @Log(title = "公司劳动合同", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改公司劳动合同") + public R edit(@Validated(EditGroup.class) @RequestBody HotCompanyLaborContractBo bo) { + return toAjax(hotCompanyLaborContractService.updateByBo(bo)); + } + + /** + * 删除公司劳动合同 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:companyLaborContract:remove") + @Log(title = "公司劳动合同", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除公司劳动合同") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotCompanyLaborContractService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/domain/HotCompanyLaborContract.java b/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/domain/HotCompanyLaborContract.java new file mode 100644 index 0000000..1547a10 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/domain/HotCompanyLaborContract.java @@ -0,0 +1,75 @@ +package com.hotwj.platform.resourceManagement.companyLaborContract.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 公司劳动合同对象 hot_company_labor_contract + * + * @author shihongwei + * @date 2025-12-10 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_company_labor_contract") +public class HotCompanyLaborContract extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 安全管理人员ID + */ + private Long safeUserId; + + /** + * 生效日期 + */ + private Date effectiveDate; + + /** + * 截至日期 + */ + private Date expireDate; + + /** + * 图片附件URL(多个用逗号分隔) + */ + private String imageUrls; + + /** + * 备注 + */ + private String remark; + + /** + * 0=未归档, 1=已归档 + */ + private Long isArchived; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/domain/bo/HotCompanyLaborContractBo.java b/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/domain/bo/HotCompanyLaborContractBo.java new file mode 100644 index 0000000..2509253 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/domain/bo/HotCompanyLaborContractBo.java @@ -0,0 +1,79 @@ +package com.hotwj.platform.resourceManagement.companyLaborContract.domain.bo; + +import com.hotwj.platform.resourceManagement.companyLaborContract.domain.HotCompanyLaborContract; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 公司劳动合同业务对象 hot_company_labor_contract + * + * @author shihongwei + * @date 2025-12-10 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotCompanyLaborContract.class, reverseConvertGenerate = false) +public class HotCompanyLaborContractBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 安全管理人员ID + */ + @NotNull(message = "安全管理人员ID不能为空", groups = {AddGroup.class}) + private Long safeUserId; + + + + /** + * 生效日期 + */ + @NotNull(message = "生效日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date effectiveDate; + + /** + * 截至日期 + */ + @NotNull(message = "截至日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date expireDate; + + /** + * 图片附件URL(多个用逗号分隔) + */ + private String imageUrls; + + /** + * 备注 + */ + private String remark; + + /** + * 0=未归档, 1=已归档 + */ + private Long isArchived; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/domain/vo/HotCompanyLaborContractVo.java b/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/domain/vo/HotCompanyLaborContractVo.java new file mode 100644 index 0000000..65c07d8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/domain/vo/HotCompanyLaborContractVo.java @@ -0,0 +1,89 @@ +package com.hotwj.platform.resourceManagement.companyLaborContract.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.companyLaborContract.domain.HotCompanyLaborContract; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 公司劳动合同视图对象 hot_company_labor_contract + * + * @author shihongwei + * @date 2025-12-10 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotCompanyLaborContract.class) +public class HotCompanyLaborContractVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 安全管理人员ID + */ + private Long safeUserId; + + /** + * 生效日期 + */ + @ExcelProperty(value = "生效日期") + private Date effectiveDate; + + /** + * 截至日期 + */ + @ExcelProperty(value = "截至日期") + private Date expireDate; + + /** + * 图片附件URL(多个用逗号分隔) + */ + @ExcelProperty(value = "图片附件URL(多个用逗号分隔)") + private String imageUrls; + + /** + * 图片附件URL(多个用逗号分隔)Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "imageUrls") + private String imageUrlsUrl; + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 0=未归档, 1=已归档 + */ + @ExcelProperty(value = "0=未归档, 1=已归档") + private Long isArchived; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/mapper/HotCompanyLaborContractMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/mapper/HotCompanyLaborContractMapper.java new file mode 100644 index 0000000..7df4ef3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/mapper/HotCompanyLaborContractMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.companyLaborContract.mapper; + +import com.hotwj.platform.resourceManagement.companyLaborContract.domain.HotCompanyLaborContract; +import com.hotwj.platform.resourceManagement.companyLaborContract.domain.vo.HotCompanyLaborContractVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 公司劳动合同Mapper接口 + * + * @author shihongwei + * @date 2025-12-10 + */ +@Mapper +public interface HotCompanyLaborContractMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/service/IHotCompanyLaborContractService.java b/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/service/IHotCompanyLaborContractService.java new file mode 100644 index 0000000..12d4c73 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/service/IHotCompanyLaborContractService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.resourceManagement.companyLaborContract.service; + +import com.hotwj.platform.resourceManagement.companyLaborContract.domain.bo.HotCompanyLaborContractBo; +import com.hotwj.platform.resourceManagement.companyLaborContract.domain.vo.HotCompanyLaborContractVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 公司劳动合同Service接口 + * + * @author shihongwei + * @date 2025-12-10 + */ +public interface IHotCompanyLaborContractService { + + /** + * 查询公司劳动合同 + * + * @param id 主键 + * @return 公司劳动合同 + */ + HotCompanyLaborContractVo queryById(Long id); + + /** + * 分页查询公司劳动合同列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 公司劳动合同分页列表 + */ + TableDataInfo queryPageList(HotCompanyLaborContractBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的公司劳动合同列表 + * + * @param bo 查询条件 + * @return 公司劳动合同列表 + */ + List queryList(HotCompanyLaborContractBo bo); + + /** + * 新增公司劳动合同 + * + * @param bo 公司劳动合同 + * @return 是否新增成功 + */ + Boolean insertByBo(HotCompanyLaborContractBo bo); + + /** + * 修改公司劳动合同 + * + * @param bo 公司劳动合同 + * @return 是否修改成功 + */ + Boolean updateByBo(HotCompanyLaborContractBo bo); + + /** + * 校验并批量删除公司劳动合同信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/service/impl/HotCompanyLaborContractServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/service/impl/HotCompanyLaborContractServiceImpl.java new file mode 100644 index 0000000..09247cd --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyLaborContract/service/impl/HotCompanyLaborContractServiceImpl.java @@ -0,0 +1,157 @@ +package com.hotwj.platform.resourceManagement.companyLaborContract.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.companyLaborContract.domain.HotCompanyLaborContract; +import com.hotwj.platform.resourceManagement.companyLaborContract.domain.bo.HotCompanyLaborContractBo; +import com.hotwj.platform.resourceManagement.companyLaborContract.domain.vo.HotCompanyLaborContractVo; +import com.hotwj.platform.resourceManagement.companyLaborContract.mapper.HotCompanyLaborContractMapper; +import com.hotwj.platform.resourceManagement.companyLaborContract.service.IHotCompanyLaborContractService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 公司劳动合同Service业务层处理 + * + * @author shihongwei + * @date 2025-12-10 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotCompanyLaborContractServiceImpl implements IHotCompanyLaborContractService { + + private final HotCompanyLaborContractMapper baseMapper; + + /** + * 查询公司劳动合同 + * + * @param id 主键 + * @return 公司劳动合同 + */ + @Override + public HotCompanyLaborContractVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询公司劳动合同列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 公司劳动合同分页列表 + */ + @Override + public TableDataInfo queryPageList(HotCompanyLaborContractBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的公司劳动合同列表 + * + * @param bo 查询条件 + * @return 公司劳动合同列表 + */ + @Override + public List queryList(HotCompanyLaborContractBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotCompanyLaborContractBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotCompanyLaborContract::getId); + lqw.eq(bo.getCompanyId() != null, HotCompanyLaborContract::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getEffectiveDate() != null, HotCompanyLaborContract::getEffectiveDate, bo.getEffectiveDate()); + lqw.eq(bo.getExpireDate() != null, HotCompanyLaborContract::getExpireDate, bo.getExpireDate()); + lqw.eq(bo.getIsArchived() != null, HotCompanyLaborContract::getIsArchived, bo.getIsArchived()); + lqw.eq(StringUtils.isNotBlank(bo.getImageUrls()), HotCompanyLaborContract::getImageUrls, bo.getImageUrls()); + lqw.eq(bo.getIsDeleted() != null, HotCompanyLaborContract::getIsDeleted, bo.getIsDeleted()); + lqw.eq(bo.getSafeUserId() != null, HotCompanyLaborContract::getSafeUserId, bo.getSafeUserId()); + return lqw; + } + + /** + * 新增公司劳动合同 + * + * @param bo 公司劳动合同 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotCompanyLaborContractBo bo) { + HotCompanyLaborContract add = MapstructUtils.convert(bo, HotCompanyLaborContract.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改公司劳动合同 + * + * @param bo 公司劳动合同 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotCompanyLaborContractBo bo) { + // 检查是否已归档 + HotCompanyLaborContract old = baseMapper.selectById(bo.getId()); + if (old != null && Long.valueOf(1).equals(old.getIsArchived())) { + throw new ServiceException("该劳动合同已归档,无法修改"); + } + + HotCompanyLaborContract update = MapstructUtils.convert(bo, HotCompanyLaborContract.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotCompanyLaborContract entity) { + if (entity.getEffectiveDate() != null + && entity.getExpireDate() != null + && !entity.getExpireDate().after(entity.getEffectiveDate())) { + throw new ServiceException("截至日期必须大于生效日期"); + } + } + + /** + * 校验并批量删除公司劳动合同信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // 检查是否已归档 + if (ids != null && !ids.isEmpty()) { + List list = baseMapper.selectBatchIds(ids); + for (HotCompanyLaborContract contract : list) { + if (Long.valueOf(1).equals(contract.getIsArchived())) { + throw new ServiceException("存在已归档的劳动合同,无法删除"); + } + } + } + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/controller/HotCompanyPermissionGrantController.java b/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/controller/HotCompanyPermissionGrantController.java new file mode 100644 index 0000000..5fe1cf3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/controller/HotCompanyPermissionGrantController.java @@ -0,0 +1,50 @@ +package com.hotwj.platform.resourceManagement.companyPermissionGrant.controller; + +import com.hotwj.platform.resourceManagement.companyPermissionGrant.domain.bo.HotCompanyPermissionGrantBo; +import com.hotwj.platform.resourceManagement.companyPermissionGrant.domain.vo.HotCompanyPermissionGrantVo; +import com.hotwj.platform.resourceManagement.companyPermissionGrant.service.IHotCompanyPermissionGrantService; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/companyPermissionGrant") +public class HotCompanyPermissionGrantController extends BaseController { + + private final IHotCompanyPermissionGrantService hotCompanyPermissionGrantService; + + @GetMapping("/list") + public TableDataInfo list(HotCompanyPermissionGrantBo bo, PageQuery pageQuery) { + return hotCompanyPermissionGrantService.queryPageList(bo, pageQuery); + } + + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { + return R.ok(hotCompanyPermissionGrantService.queryById(id)); + } + + @PostMapping + public R add(@Validated(AddGroup.class) @RequestBody HotCompanyPermissionGrantBo bo) { + return toAjax(hotCompanyPermissionGrantService.insertByBo(bo)); + } + + @PutMapping + public R edit(@Validated(EditGroup.class) @RequestBody HotCompanyPermissionGrantBo bo) { + return toAjax(hotCompanyPermissionGrantService.updateByBo(bo)); + } + + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) { + return toAjax(hotCompanyPermissionGrantService.deleteWithValidByIds(java.util.List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/domain/HotCompanyPermissionGrant.java b/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/domain/HotCompanyPermissionGrant.java new file mode 100644 index 0000000..aaaa8e3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/domain/HotCompanyPermissionGrant.java @@ -0,0 +1,46 @@ +package com.hotwj.platform.resourceManagement.companyPermissionGrant.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_company_permission_grant") +public class HotCompanyPermissionGrant extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @TableId(value = "id") + private Long id; + + private Long companyId; + + private Long grantorManagerId; + + private Long grantorUserId; + + private Long granteeManagerId; + + private Long granteeUserId; + + private String permissionCode; + + private Long menuId; + + private Long status; + + private Date effectiveTime; + + private Date expireTime; + + private String remark; + + private Long isDeleted; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/domain/bo/HotCompanyPermissionGrantBo.java b/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/domain/bo/HotCompanyPermissionGrantBo.java new file mode 100644 index 0000000..1ed5972 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/domain/bo/HotCompanyPermissionGrantBo.java @@ -0,0 +1,48 @@ +package com.hotwj.platform.resourceManagement.companyPermissionGrant.domain.bo; + +import com.hotwj.platform.resourceManagement.companyPermissionGrant.domain.HotCompanyPermissionGrant; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotCompanyPermissionGrant.class, reverseConvertGenerate = false) +public class HotCompanyPermissionGrantBo extends BaseEntity { + + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + private Long companyId; + + private Long grantorManagerId; + + private Long grantorUserId; + + @NotNull(message = "被授权人安全管理人员ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long granteeManagerId; + + private Long granteeUserId; + + @NotBlank(message = "权限标识不能为空", groups = {AddGroup.class, EditGroup.class}) + private String permissionCode; + + private Long menuId; + + private Long status; + + private Date effectiveTime; + + private Date expireTime; + + private String remark; + + private Long isDeleted; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/domain/vo/HotCompanyPermissionGrantVo.java b/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/domain/vo/HotCompanyPermissionGrantVo.java new file mode 100644 index 0000000..440d33b --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/domain/vo/HotCompanyPermissionGrantVo.java @@ -0,0 +1,47 @@ +package com.hotwj.platform.resourceManagement.companyPermissionGrant.domain.vo; + +import com.hotwj.platform.resourceManagement.companyPermissionGrant.domain.HotCompanyPermissionGrant; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +@Data +@AutoMapper(target = HotCompanyPermissionGrant.class) +public class HotCompanyPermissionGrantVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + + private Long companyId; + + private Long grantorManagerId; + + private Long grantorUserId; + + private Long granteeManagerId; + + private Long granteeUserId; + + private String permissionCode; + + private Long menuId; + + private Long status; + + private Date effectiveTime; + + private Date expireTime; + + private String remark; + + private Date createTime; + + private Date updateTime; + + private Long isDeleted; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/mapper/HotCompanyPermissionGrantMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/mapper/HotCompanyPermissionGrantMapper.java new file mode 100644 index 0000000..fac365f --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/mapper/HotCompanyPermissionGrantMapper.java @@ -0,0 +1,10 @@ +package com.hotwj.platform.resourceManagement.companyPermissionGrant.mapper; + +import com.hotwj.platform.resourceManagement.companyPermissionGrant.domain.HotCompanyPermissionGrant; +import com.hotwj.platform.resourceManagement.companyPermissionGrant.domain.vo.HotCompanyPermissionGrantVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +@Mapper +public interface HotCompanyPermissionGrantMapper extends BaseMapperPlus { +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/service/IHotCompanyPermissionGrantService.java b/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/service/IHotCompanyPermissionGrantService.java new file mode 100644 index 0000000..4c8e7a6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/service/IHotCompanyPermissionGrantService.java @@ -0,0 +1,24 @@ +package com.hotwj.platform.resourceManagement.companyPermissionGrant.service; + +import com.hotwj.platform.resourceManagement.companyPermissionGrant.domain.bo.HotCompanyPermissionGrantBo; +import com.hotwj.platform.resourceManagement.companyPermissionGrant.domain.vo.HotCompanyPermissionGrantVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.Set; + +public interface IHotCompanyPermissionGrantService { + + HotCompanyPermissionGrantVo queryById(Long id); + + TableDataInfo queryPageList(HotCompanyPermissionGrantBo bo, PageQuery pageQuery); + + Boolean insertByBo(HotCompanyPermissionGrantBo bo); + + Boolean updateByBo(HotCompanyPermissionGrantBo bo); + + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + Set selectActivePermissionCodes(Long companyId, Long granteeUserId); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/service/impl/HotCompanyPermissionGrantServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/service/impl/HotCompanyPermissionGrantServiceImpl.java new file mode 100644 index 0000000..29723a5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyPermissionGrant/service/impl/HotCompanyPermissionGrantServiceImpl.java @@ -0,0 +1,262 @@ +package com.hotwj.platform.resourceManagement.companyPermissionGrant.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.companyPermissionGrant.domain.HotCompanyPermissionGrant; +import com.hotwj.platform.resourceManagement.companyPermissionGrant.domain.bo.HotCompanyPermissionGrantBo; +import com.hotwj.platform.resourceManagement.companyPermissionGrant.domain.vo.HotCompanyPermissionGrantVo; +import com.hotwj.platform.resourceManagement.companyPermissionGrant.mapper.HotCompanyPermissionGrantMapper; +import com.hotwj.platform.resourceManagement.companyPermissionGrant.service.IHotCompanyPermissionGrantService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.system.domain.SysMenu; +import org.dromara.system.mapper.SysMenuMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class HotCompanyPermissionGrantServiceImpl implements IHotCompanyPermissionGrantService { + + private final HotCompanyPermissionGrantMapper baseMapper; + private final HotCompanySafetyManagerMapper safetyManagerMapper; + private final SysMenuMapper sysMenuMapper; + + @Override + public HotCompanyPermissionGrantVo queryById(Long id) { + LoginUser loginUser = LoginHelper.getLoginUser(); + Long companyId = loginUser.getCompanyId(); + getCurrentOperator(companyId, loginUser.getUserId()); + return baseMapper.selectVoOne(Wrappers.lambdaQuery() + .eq(HotCompanyPermissionGrant::getId, id) + .eq(HotCompanyPermissionGrant::getCompanyId, companyId) + .eq(HotCompanyPermissionGrant::getIsDeleted, 0L)); + } + + @Override + public TableDataInfo queryPageList(HotCompanyPermissionGrantBo bo, PageQuery pageQuery) { + LoginUser loginUser = LoginHelper.getLoginUser(); + getCurrentOperator(loginUser.getCompanyId(), loginUser.getUserId()); + bo.setCompanyId(loginUser.getCompanyId()); + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(HotCompanyPermissionGrantBo bo) { + LoginUser loginUser = LoginHelper.getLoginUser(); + Long companyId = bo.getCompanyId() == null ? loginUser.getCompanyId() : bo.getCompanyId(); + if (companyId == null) { + throw new ServiceException("公司ID不能为空"); + } + HotCompanySafetyManager grantor = getCurrentOperator(companyId, loginUser.getUserId()); + HotCompanySafetyManager grantee = getGrantee(companyId, bo.getGranteeManagerId()); + checkGrantRelation(grantor, grantee); + Long menuId = checkPermissionCode(bo.getPermissionCode(), bo.getMenuId()); + checkTimeRange(bo.getEffectiveTime(), bo.getExpireTime()); + resetGranteePermissions(companyId, grantee.getId(), grantee.getUserId(), grantor.getId(), grantor.getUserId(), null); + + HotCompanyPermissionGrant entity = new HotCompanyPermissionGrant(); + entity.setCompanyId(companyId); + entity.setGranteeManagerId(grantee.getId()); + entity.setGranteeUserId(grantee.getUserId()); + entity.setPermissionCode(bo.getPermissionCode()); + entity.setMenuId(menuId); + entity.setStatus(bo.getStatus() == null ? 1L : bo.getStatus()); + entity.setEffectiveTime(bo.getEffectiveTime()); + entity.setExpireTime(bo.getExpireTime()); + entity.setRemark(bo.getRemark()); + entity.setGrantorManagerId(grantor.getId()); + entity.setGrantorUserId(grantor.getUserId()); + entity.setIsDeleted(0L); + boolean flag = baseMapper.insert(entity) > 0; + if (flag) { + bo.setId(entity.getId()); + } + return flag; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(HotCompanyPermissionGrantBo bo) { + LoginUser loginUser = LoginHelper.getLoginUser(); + HotCompanyPermissionGrant old = baseMapper.selectById(bo.getId()); + if (old == null || Long.valueOf(1L).equals(old.getIsDeleted())) { + throw new ServiceException("授权记录不存在"); + } + HotCompanySafetyManager grantor = getCurrentOperator(old.getCompanyId(), loginUser.getUserId()); + checkTimeRange(bo.getEffectiveTime(), bo.getExpireTime()); + HotCompanySafetyManager grantee = bo.getGranteeManagerId() == null + ? getGrantee(old.getCompanyId(), old.getGranteeManagerId()) + : getGrantee(old.getCompanyId(), bo.getGranteeManagerId()); + checkGrantRelation(grantor, grantee); + resetGranteePermissions(old.getCompanyId(), grantee.getId(), grantee.getUserId(), grantor.getId(), grantor.getUserId(), old.getId()); + + HotCompanyPermissionGrant update = MapstructUtils.convert(bo, HotCompanyPermissionGrant.class); + String permissionCode = StringUtils.isNotBlank(bo.getPermissionCode()) ? bo.getPermissionCode() : old.getPermissionCode(); + Long menuId = checkPermissionCode(permissionCode, bo.getMenuId()); + update.setPermissionCode(permissionCode); + update.setMenuId(menuId); + update.setGranteeManagerId(grantee.getId()); + update.setGranteeUserId(grantee.getUserId()); + update.setGrantorManagerId(grantor.getId()); + update.setGrantorUserId(grantor.getUserId()); + update.setCompanyId(old.getCompanyId()); + update.setStatus(bo.getStatus() == null ? 1L : bo.getStatus()); + update.setIsDeleted(0L); + return baseMapper.updateById(update) > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (CollUtil.isEmpty(ids)) { + return false; + } + LoginUser loginUser = LoginHelper.getLoginUser(); + HotCompanySafetyManager grantor = getCurrentOperator(loginUser.getCompanyId(), loginUser.getUserId()); + LambdaUpdateWrapper luw = Wrappers.lambdaUpdate(); + luw.in(HotCompanyPermissionGrant::getId, ids) + .eq(HotCompanyPermissionGrant::getCompanyId, loginUser.getCompanyId()) + .eq(HotCompanyPermissionGrant::getIsDeleted, 0L) + .set(HotCompanyPermissionGrant::getStatus, 0L) + .set(HotCompanyPermissionGrant::getIsDeleted, 1L) + .set(HotCompanyPermissionGrant::getGrantorManagerId, grantor.getId()) + .set(HotCompanyPermissionGrant::getGrantorUserId, grantor.getUserId()); + return baseMapper.update(null, luw) > 0; + } + + @Override + public Set selectActivePermissionCodes(Long companyId, Long granteeUserId) { + Date now = new Date(); + List objs = baseMapper.selectObjs(Wrappers.lambdaQuery() + .select(HotCompanyPermissionGrant::getPermissionCode) + .eq(HotCompanyPermissionGrant::getCompanyId, companyId) + .eq(HotCompanyPermissionGrant::getGranteeUserId, granteeUserId) + .eq(HotCompanyPermissionGrant::getStatus, 1L) + .eq(HotCompanyPermissionGrant::getIsDeleted, 0L) + .and(w -> w.isNull(HotCompanyPermissionGrant::getEffectiveTime) + .or() + .le(HotCompanyPermissionGrant::getEffectiveTime, now)) + .and(w -> w.isNull(HotCompanyPermissionGrant::getExpireTime) + .or() + .ge(HotCompanyPermissionGrant::getExpireTime, now))); + return objs.stream() + .map(String::valueOf) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toSet()); + } + + private LambdaQueryWrapper buildQueryWrapper(HotCompanyPermissionGrantBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(bo.getCompanyId() != null, HotCompanyPermissionGrant::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getGrantorManagerId() != null, HotCompanyPermissionGrant::getGrantorManagerId, bo.getGrantorManagerId()); + lqw.eq(bo.getGrantorUserId() != null, HotCompanyPermissionGrant::getGrantorUserId, bo.getGrantorUserId()); + lqw.eq(bo.getGranteeManagerId() != null, HotCompanyPermissionGrant::getGranteeManagerId, bo.getGranteeManagerId()); + lqw.eq(bo.getGranteeUserId() != null, HotCompanyPermissionGrant::getGranteeUserId, bo.getGranteeUserId()); + lqw.eq(StringUtils.isNotBlank(bo.getPermissionCode()), HotCompanyPermissionGrant::getPermissionCode, bo.getPermissionCode()); + lqw.eq(bo.getStatus() != null, HotCompanyPermissionGrant::getStatus, bo.getStatus()); + lqw.eq(HotCompanyPermissionGrant::getIsDeleted, 0L); + lqw.orderByDesc(HotCompanyPermissionGrant::getCreateTime); + return lqw; + } + + private HotCompanySafetyManager getCurrentOperator(Long companyId, Long userId) { + HotCompanySafetyManager manager = safetyManagerMapper.selectOne(Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getUserId, userId) + .eq(HotCompanySafetyManager::getStatus, 1L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getIsDeleted, 0L)); + if (manager == null) { + throw new ServiceException("仅企业安全管理人员可以分发权限"); + } + return manager; + } + + private HotCompanySafetyManager getGrantee(Long companyId, Long granteeManagerId) { + HotCompanySafetyManager manager = safetyManagerMapper.selectOne(Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getId, granteeManagerId) + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getStatus, 1L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getIsDeleted, 0L)); + if (manager == null) { + throw new ServiceException("被授权人不存在或状态不可用"); + } + if (Long.valueOf(1L).equals(manager.getCompanyHead())) { + throw new ServiceException("不允许给企业负责人分配权限"); + } + return manager; + } + + private void checkGrantRelation(HotCompanySafetyManager grantor, HotCompanySafetyManager grantee) { + if (grantor != null && grantee != null && grantor.getId() != null && grantor.getId().equals(grantee.getId())) { + throw new ServiceException("不允许给自己分配权限"); + } + if (isCompanyHead(grantee)) { + throw new ServiceException("不允许给企业负责人分配权限"); + } + if (!isCompanyHead(grantor) && isCompanyHead(grantee)) { + throw new ServiceException("企业安全管理人员不可以给企业负责人分配权限"); + } + } + + private boolean isCompanyHead(HotCompanySafetyManager manager) { + return manager != null && Long.valueOf(1L).equals(manager.getCompanyHead()); + } + + private void resetGranteePermissions(Long companyId, Long granteeManagerId, Long granteeUserId, + Long grantorManagerId, Long grantorUserId, Long excludeId) { + LambdaUpdateWrapper luw = Wrappers.lambdaUpdate(); + luw.eq(HotCompanyPermissionGrant::getCompanyId, companyId) + .eq(HotCompanyPermissionGrant::getGranteeManagerId, granteeManagerId) + .eq(HotCompanyPermissionGrant::getGranteeUserId, granteeUserId) + .eq(HotCompanyPermissionGrant::getIsDeleted, 0L) + .ne(excludeId != null, HotCompanyPermissionGrant::getId, excludeId) + .set(HotCompanyPermissionGrant::getStatus, 0L) + .set(HotCompanyPermissionGrant::getIsDeleted, 1L) + .set(HotCompanyPermissionGrant::getGrantorManagerId, grantorManagerId) + .set(HotCompanyPermissionGrant::getGrantorUserId, grantorUserId); + baseMapper.update(null, luw); + } + + private Long checkPermissionCode(String permissionCode, Long menuId) { + SysMenu menu = sysMenuMapper.selectOne(Wrappers.lambdaQuery() + .eq(SysMenu::getPerms, permissionCode) + .eq(SysMenu::getStatus, "0") + .last("limit 1")); + if (menu == null) { + throw new ServiceException("权限标识不存在或菜单已停用"); + } + if (menuId != null && !menuId.equals(menu.getMenuId())) { + throw new ServiceException("menuId 与 permissionCode 不匹配"); + } + return menu.getMenuId(); + } + + private void checkTimeRange(Date effectiveTime, Date expireTime) { + if (effectiveTime != null && expireTime != null && effectiveTime.after(expireTime)) { + throw new ServiceException("失效时间不能早于生效时间"); + } + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/controller/HotCompanyPolicyController.java b/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/controller/HotCompanyPolicyController.java new file mode 100644 index 0000000..8ec6615 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/controller/HotCompanyPolicyController.java @@ -0,0 +1,149 @@ +package com.hotwj.platform.resourceManagement.companyPolicy.controller; + +import cn.hutool.core.collection.CollUtil; +import com.hotwj.platform.regulationManagement.policyRevision.domain.bo.HotPolicyRevisionBo; +import com.hotwj.platform.regulationManagement.policyRevision.service.IHotPolicyRevisionService; +import com.hotwj.platform.resourceManagement.companyPolicy.domain.bo.HotCompanyPolicyBo; +import com.hotwj.platform.resourceManagement.companyPolicy.domain.vo.HotCompanyPolicyVo; +import com.hotwj.platform.resourceManagement.companyPolicy.service.IHotCompanyPolicyService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 公司制度资料档案 + * + * @author shihongwei + * @date 2025-12-10 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/companyPolicy") +@Tag(name = "公司制度资料档案", description = "公司制度资料档案增删改查") +public class HotCompanyPolicyController extends BaseController { + + private final IHotCompanyPolicyService hotCompanyPolicyService; + private final IHotPolicyRevisionService hotPolicyRevisionService; + + /** + * 查询公司制度资料档案列表 + */ + //@SaCheckPermission("resourceManagement:companyPolicy:list") + @GetMapping("/list") + @Operation(summary = "分页查询公司制度资料档案列表") + public TableDataInfo list(HotCompanyPolicyBo bo, PageQuery pageQuery) { + return hotCompanyPolicyService.queryPageList(bo, pageQuery); + } + + /** + * 导出公司制度资料档案列表 + */ + //@SaCheckPermission("resourceManagement:companyPolicy:export") + @Log(title = "公司制度资料档案", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出公司制度资料档案列表") + public void export(HotCompanyPolicyBo bo, HttpServletResponse response) { + List list = hotCompanyPolicyService.queryList(bo); + // 设置导出的排序号 + if (CollUtil.isNotEmpty(list)) { + for (int i = 0; i < list.size(); i++) { + list.get(i).setSort(i + 1); + } + } + + ExcelUtil.exportExcel(list, "公司制度资料档案", HotCompanyPolicyVo.class, response); + } + + /** + * 获取公司制度资料档案详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:companyPolicy:query") + @GetMapping("/{id}") + @Operation(summary = "查询公司制度资料档案详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotCompanyPolicyService.queryById(id)); + } + + /** + * 新增公司制度资料档案 + */ + //@SaCheckPermission("resourceManagement:companyPolicy:add") + @Log(title = "公司制度资料档案", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增公司制度资料档案") + public R add(@Validated(AddGroup.class) @RequestBody HotCompanyPolicyBo bo) { + return toAjax(hotCompanyPolicyService.insertByBo(bo)); + } + + /** + * 修改公司制度资料档案 + */ + //@SaCheckPermission("resourceManagement:companyPolicy:edit") + @Log(title = "公司制度资料档案", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改公司制度资料档案") + public R edit(@Validated(EditGroup.class) @RequestBody HotCompanyPolicyBo bo) { + HotCompanyPolicyVo before = hotCompanyPolicyService.queryById(bo.getId()); + boolean ok = hotCompanyPolicyService.updateByBo(bo); + if (ok) { + String beforeHtml = before != null ? before.getContentHtml() : null; + String afterHtml = bo.getContentHtml(); + boolean changed = !org.dromara.common.core.utils.StringUtils.equals(beforeHtml, afterHtml); + if (changed) { + Long versionNo = hotPolicyRevisionService.nextVersionNo(bo.getId()); + HotPolicyRevisionBo rev = new HotPolicyRevisionBo(); + rev.setCompanyId(bo.getCompanyId()); + rev.setPolicyId(bo.getId()); + rev.setVersionNo(versionNo); + Map params = bo.getParams(); + String changeNote = params != null ? String.valueOf(params.getOrDefault("changeNote", "")) : ""; + rev.setChangeNote(changeNote); + rev.setContent(afterHtml); + rev.setCorrection(beforeHtml); + rev.setAttachmentUrl(bo.getAttachmentUrl()); + rev.setCreatedTime(new Date()); + hotPolicyRevisionService.createRevision(rev); + } + } + return toAjax(ok); + } + + /** + * 删除公司制度资料档案 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:companyPolicy:remove") + @Log(title = "公司制度资料档案", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除公司制度资料档案") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotCompanyPolicyService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/domain/HotCompanyPolicy.java b/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/domain/HotCompanyPolicy.java new file mode 100644 index 0000000..dbc5501 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/domain/HotCompanyPolicy.java @@ -0,0 +1,79 @@ +package com.hotwj.platform.resourceManagement.companyPolicy.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 公司制度资料档案对象 hot_company_policy + * + * @author shihongwei + * @date 2025-12-10 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_company_policy") +public class HotCompanyPolicy extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 制度名称 + */ + private String policyTitle; + + /** + * 制度类型 + */ + private String policyType; + + /** + * 附件URL(docx,pdf) + */ + private String attachmentUrl; + + /** + * 制度内容(修改正后内容)HTML(富文本) + */ + private String contentHtml; + + /** + * 修正前内容 + */ + private String correctionHtml; + + /** + * 0=未发布, 1=已发布 + */ + private Long isPublished; + + /** + * 排序号 + */ + private Long orderNum; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/domain/bo/HotCompanyPolicyBo.java b/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/domain/bo/HotCompanyPolicyBo.java new file mode 100644 index 0000000..1caf41f --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/domain/bo/HotCompanyPolicyBo.java @@ -0,0 +1,82 @@ +package com.hotwj.platform.resourceManagement.companyPolicy.domain.bo; + +import com.hotwj.platform.resourceManagement.companyPolicy.domain.HotCompanyPolicy; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 公司制度资料档案业务对象 hot_company_policy + * + * @author shihongwei + * @date 2025-12-10 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotCompanyPolicy.class, reverseConvertGenerate = false) +public class HotCompanyPolicyBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 制度名称 + */ + @NotBlank(message = "制度名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String policyTitle; + + /** + * 制度类型 + */ + @NotBlank(message = "制度类型不能为空", groups = { AddGroup.class, EditGroup.class }) + private String policyType; + + /** + * 附件URL(docx,pdf) + */ + private String attachmentUrl; + + /** + * 制度内容(修改正后内容)HTML(富文本) + */ + private String contentHtml; + + /** + * 修正前内容 + */ + private String correctionHtml; + + /** + * 0=未发布, 1=已发布 + */ + @NotNull(message = "0=未发布, 1=已发布不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isPublished; + + /** + * 排序号 + */ +// @NotNull(message = "排序号不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long orderNum; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/domain/vo/HotCompanyPolicyVo.java b/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/domain/vo/HotCompanyPolicyVo.java new file mode 100644 index 0000000..1ea87a8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/domain/vo/HotCompanyPolicyVo.java @@ -0,0 +1,67 @@ +package com.hotwj.platform.resourceManagement.companyPolicy.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.companyPolicy.domain.HotCompanyPolicy; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 公司制度资料档案视图对象 hot_company_policy + * + * @author shihongwei + * @date 2025-12-10 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotCompanyPolicy.class) +public class HotCompanyPolicyVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty(value = "排序", index = 0) + private Integer sort; + + private Long id; + private Long companyId; + + /** + * 制度名称 + */ + @ExcelProperty(value = "制度名称", index = 1) + private String policyTitle; + + private String attachmentUrl; + + private String contentHtml; + + /** + * 是否发布 + */ + @ExcelProperty(value = "是否发布", index = 2, converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_yes_no") + private Long isPublished; + + /** + * 制度类型 + */ + private String policyType; + + private Long isDeleted; + + private Date createTime; + + /** + * 排序号 + */ + @ExcelProperty(value = "排序号") + private Long orderNum; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/mapper/HotCompanyPolicyMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/mapper/HotCompanyPolicyMapper.java new file mode 100644 index 0000000..480e800 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/mapper/HotCompanyPolicyMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.companyPolicy.mapper; + +import com.hotwj.platform.resourceManagement.companyPolicy.domain.HotCompanyPolicy; +import com.hotwj.platform.resourceManagement.companyPolicy.domain.vo.HotCompanyPolicyVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 公司制度资料档案Mapper接口 + * + * @author shihongwei + * @date 2025-12-10 + */ +@Mapper +public interface HotCompanyPolicyMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/service/IHotCompanyPolicyService.java b/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/service/IHotCompanyPolicyService.java new file mode 100644 index 0000000..a09b041 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/service/IHotCompanyPolicyService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.resourceManagement.companyPolicy.service; + +import com.hotwj.platform.resourceManagement.companyPolicy.domain.bo.HotCompanyPolicyBo; +import com.hotwj.platform.resourceManagement.companyPolicy.domain.vo.HotCompanyPolicyVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 公司制度资料档案Service接口 + * + * @author shihongwei + * @date 2025-12-10 + */ +public interface IHotCompanyPolicyService { + + /** + * 查询公司制度资料档案 + * + * @param id 主键 + * @return 公司制度资料档案 + */ + HotCompanyPolicyVo queryById(Long id); + + /** + * 分页查询公司制度资料档案列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 公司制度资料档案分页列表 + */ + TableDataInfo queryPageList(HotCompanyPolicyBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的公司制度资料档案列表 + * + * @param bo 查询条件 + * @return 公司制度资料档案列表 + */ + List queryList(HotCompanyPolicyBo bo); + + /** + * 新增公司制度资料档案 + * + * @param bo 公司制度资料档案 + * @return 是否新增成功 + */ + Boolean insertByBo(HotCompanyPolicyBo bo); + + /** + * 修改公司制度资料档案 + * + * @param bo 公司制度资料档案 + * @return 是否修改成功 + */ + Boolean updateByBo(HotCompanyPolicyBo bo); + + /** + * 校验并批量删除公司制度资料档案信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/service/impl/HotCompanyPolicyServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/service/impl/HotCompanyPolicyServiceImpl.java new file mode 100644 index 0000000..24a323a --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companyPolicy/service/impl/HotCompanyPolicyServiceImpl.java @@ -0,0 +1,267 @@ +package com.hotwj.platform.resourceManagement.companyPolicy.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.companyPolicy.domain.HotCompanyPolicy; +import com.hotwj.platform.resourceManagement.companyPolicy.domain.bo.HotCompanyPolicyBo; +import com.hotwj.platform.resourceManagement.companyPolicy.domain.vo.HotCompanyPolicyVo; +import com.hotwj.platform.resourceManagement.companyPolicy.mapper.HotCompanyPolicyMapper; +import com.hotwj.platform.resourceManagement.companyPolicy.service.IHotCompanyPolicyService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 公司制度资料档案Service业务层处理 + * + * @author shihongwei + * @date 2025-12-10 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotCompanyPolicyServiceImpl implements IHotCompanyPolicyService { + + private final HotCompanyPolicyMapper baseMapper; + private final HotCompanySafetyManagerMapper managerMapper; + private final IHotSystemNotificationService notificationService; + private final HotDriverMapper driverMapper; + + /** + * 查询公司制度资料档案 + * + * @param id 主键 + * @return 公司制度资料档案 + */ + @Override + public HotCompanyPolicyVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询公司制度资料档案列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 公司制度资料档案分页列表 + */ + @Override + public TableDataInfo queryPageList(HotCompanyPolicyBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的公司制度资料档案列表 + * + * @param bo 查询条件 + * @return 公司制度资料档案列表 + */ + @Override + public List queryList(HotCompanyPolicyBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotCompanyPolicyBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotCompanyPolicy::getId); + lqw.eq(StringUtils.isNotBlank(bo.getPolicyType()), HotCompanyPolicy::getPolicyType, bo.getPolicyType()); + lqw.eq(bo.getCompanyId() != null, HotCompanyPolicy::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getPolicyTitle()), HotCompanyPolicy::getPolicyTitle, bo.getPolicyTitle()); + lqw.eq(StringUtils.isNotBlank(bo.getAttachmentUrl()), HotCompanyPolicy::getAttachmentUrl, bo.getAttachmentUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getContentHtml()), HotCompanyPolicy::getContentHtml, bo.getContentHtml()); + lqw.like(bo.getIsPublished() != null, HotCompanyPolicy::getIsPublished, bo.getIsPublished()); + lqw.eq(bo.getIsDeleted() != null, HotCompanyPolicy::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增公司制度资料档案 + * + * @param bo 公司制度资料档案 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotCompanyPolicyBo bo) { + HotCompanyPolicy add = MapstructUtils.convert(bo, HotCompanyPolicy.class); + // 获取当前公司最大的排序号 + HotCompanyPolicy maxOrderPolicy = baseMapper.selectOne(new LambdaQueryWrapper() + .select(HotCompanyPolicy::getOrderNum) + .eq(HotCompanyPolicy::getCompanyId, bo.getCompanyId()) + .eq(HotCompanyPolicy::getPolicyType, bo.getPolicyType()) + .orderByDesc(HotCompanyPolicy::getOrderNum) + .last("limit 1")); + long maxOrder = (maxOrderPolicy != null && maxOrderPolicy.getOrderNum() != null) ? maxOrderPolicy.getOrderNum() : 0L; + add.setOrderNum(maxOrder + 1); + + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + if (add.getCompanyId() != null && add.getIsPublished() != null && Long.valueOf(1L).equals(add.getIsPublished())) { + java.util.List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, add.getCompanyId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + java.util.List drivers = driverMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotDriver::getCompanyId, add.getCompanyId()) + .eq(HotDriver::getIsDeleted, 0L) + .eq(HotDriver::getStatus, 1L) + ); + String title = StringUtils.blankToDefault(add.getPolicyTitle(), "新制度"); + String content = "公司制度【" + title + "】已发布,请及时查阅。"; + if (managers != null && !managers.isEmpty()) { + java.util.List managerIds = managers.stream().map(m -> String.valueOf(m.getId())).toList(); + HotSystemNotificationGroupBo bosMgr = new HotSystemNotificationGroupBo(); + bosMgr.setLevel("普通"); + bosMgr.setContent(content); + bosMgr.setSourceType("制度与会议"); + bosMgr.setSenderType("SYSTEM"); + bosMgr.setReceiverType("管理员"); + bosMgr.setReceiverIds(managerIds); + bosMgr.setIsDeleted(0L); + try { + notificationService.insertByBo(bosMgr); + } catch (Exception e) { + log.warn("发送新增制度通知失败(管理员) companyId={} policyId={}", add.getCompanyId(), add.getId(), e); + } + } + if (drivers != null && !drivers.isEmpty()) { + java.util.List driverIds = drivers.stream().map(d -> String.valueOf(d.getId())).toList(); + HotSystemNotificationGroupBo bosDrv = new HotSystemNotificationGroupBo(); + bosDrv.setLevel("普通"); + bosDrv.setContent(content); + bosDrv.setSourceType("制度与会议"); + bosDrv.setSenderType("SYSTEM"); + bosDrv.setReceiverType("驾驶员"); + bosDrv.setReceiverIds(driverIds); + bosDrv.setIsDeleted(0L); + try { + notificationService.insertByBo(bosDrv); + } catch (Exception e) { + log.warn("发送新增制度通知失败(驾驶员) companyId={} policyId={}", add.getCompanyId(), add.getId(), e); + } + } + } + } + return flag; + } + + /** + * 修改公司制度资料档案 + * + * @param bo 公司制度资料档案 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotCompanyPolicyBo bo) { + HotCompanyPolicy before = null; + if (bo.getId() != null) { + before = baseMapper.selectById(bo.getId()); + } + HotCompanyPolicy update = MapstructUtils.convert(bo, HotCompanyPolicy.class); + validEntityBeforeSave(update); + boolean ok = baseMapper.updateById(update) > 0; + if (ok) { + Long companyId = update.getCompanyId() != null ? update.getCompanyId() : (before != null ? before.getCompanyId() : null); + Long isPublished = update.getIsPublished() != null ? update.getIsPublished() : (before != null ? before.getIsPublished() : null); + if (companyId != null && isPublished != null && Long.valueOf(1L).equals(isPublished)) { + java.util.List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + java.util.List drivers = driverMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotDriver::getCompanyId, companyId) + .eq(HotDriver::getIsDeleted, 0L) + .eq(HotDriver::getStatus, 1L) + ); + String title = org.dromara.common.core.utils.StringUtils.blankToDefault( + update.getPolicyTitle() != null ? update.getPolicyTitle() : (before != null ? before.getPolicyTitle() : null), + "制度" + ); + String content = "公司制度【" + title + "】已更新,请及时查阅。"; + if (managers != null && !managers.isEmpty()) { + java.util.List managerIds = managers.stream().map(m -> String.valueOf(m.getId())).toList(); + HotSystemNotificationGroupBo bosMgr = new HotSystemNotificationGroupBo(); + bosMgr.setLevel("普通"); + bosMgr.setContent(content); + bosMgr.setSourceType("制度与会议"); + bosMgr.setSenderType("SYSTEM"); + bosMgr.setReceiverType("管理员"); + bosMgr.setReceiverIds(managerIds); + bosMgr.setIsDeleted(0L); + try { + notificationService.insertByBo(bosMgr); + } catch (Exception e) { + log.warn("发送更新制度通知失败(管理员) companyId={} policyId={}", companyId, update.getId(), e); + } + } + if (drivers != null && !drivers.isEmpty()) { + java.util.List driverIds = drivers.stream().map(d -> String.valueOf(d.getId())).toList(); + HotSystemNotificationGroupBo bosDrv = new HotSystemNotificationGroupBo(); + bosDrv.setLevel("普通"); + bosDrv.setContent(content); + bosDrv.setSourceType("制度与会议"); + bosDrv.setSenderType("SYSTEM"); + bosDrv.setReceiverType("驾驶员"); + bosDrv.setReceiverIds(driverIds); + bosDrv.setIsDeleted(0L); + try { + notificationService.insertByBo(bosDrv); + } catch (Exception e) { + log.warn("发送更新制度通知失败(驾驶员) companyId={} policyId={}", companyId, update.getId(), e); + } + } + } + } + return ok; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotCompanyPolicy entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除公司制度资料档案信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/controller/HotCompanySafetyManagerController.java b/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/controller/HotCompanySafetyManagerController.java new file mode 100644 index 0000000..bfa2fb7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/controller/HotCompanySafetyManagerController.java @@ -0,0 +1,172 @@ +package com.hotwj.platform.resourceManagement.companySafetyManager.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.bo.HotCompanySafetyManagerBo; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.bo.HotCompanySafetyManagerChangeReasonBo; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.vo.HotCompanySafetyManagerVo; +import com.hotwj.platform.resourceManagement.companySafetyManager.service.IHotCompanySafetyManagerService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.core.validate.QueryGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 安全管理人员 + * + * @author shihongwei + * @date 2025-12-09 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/companySafetyManager") +@Tag(name = "安全管理人员", description = "安全管理人员增删改查与同步") +public class HotCompanySafetyManagerController extends BaseController { + + private final IHotCompanySafetyManagerService hotCompanySafetyManagerService; + + /** + * 查询安全管理人员列表 + */ + //@SaCheckPermission("resourceManagement:companySafetyManager:list") + @GetMapping("/list") + @Operation(summary = "分页查询安全管理人员列表") + public TableDataInfo list(HotCompanySafetyManagerBo bo, PageQuery pageQuery) { + return hotCompanySafetyManagerService.queryPageList(bo, pageQuery); + } + + /** + * 查询部门负责人列表 + */ + //@SaCheckPermission("resourceManagement:companySafetyManager:list") + @GetMapping("/headList") + @Operation(summary = "分页查询该企业下所有人员列表") + public TableDataInfo headList(@Validated(QueryGroup.class) HotCompanySafetyManagerBo bo, PageQuery pageQuery) { + return hotCompanySafetyManagerService.queryCompanyUserList(bo, pageQuery); + } + + /** + * 获取上级管理人员候选人列表 + */ + //@SaCheckPermission("resourceManagement:companySafetyManager:list") + @GetMapping("/superiorCandidates") + @Operation(summary = "获取上级管理人员候选人列表") + public R> getSuperiorCandidates(@NotNull(message = "公司ID不能为空") @RequestParam Long companyId, + @RequestParam(required = false) Long deptId, + @RequestParam(required = false) Long currentId) { + return R.ok(hotCompanySafetyManagerService.getSuperiorCandidates(companyId, deptId, currentId)); + } + + /** + * 导出安全管理人员列表 + */ + //@SaCheckPermission("resourceManagement:companySafetyManager:export") + @Log(title = "安全管理人员", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出安全管理人员列表") + public void export(HotCompanySafetyManagerBo bo, HttpServletResponse response) { + List list = hotCompanySafetyManagerService.queryList(bo); + ExcelUtil.exportExcel(list, "安全管理人员", HotCompanySafetyManagerVo.class, response); + } + + /** + * 获取安全管理人员详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:companySafetyManager:query") + @GetMapping("/{id}") + @Operation(summary = "查询安全管理人员详情") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotCompanySafetyManagerService.queryById(id)); + } + + /** + * 新增安全管理人员 + */ + //@SaCheckPermission("resourceManagement:companySafetyManager:add") + @Log(title = "安全管理人员", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增安全管理人员并同步系统用户") + public R add(@Validated(AddGroup.class) @RequestBody HotCompanySafetyManagerBo bo) { + return toAjax(hotCompanySafetyManagerService.insertByBo(bo)); + } + + /** + * 修改安全管理人员 + */ + //@SaCheckPermission("resourceManagement:companySafetyManager:edit") + @Log(title = "安全管理人员", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改安全管理人员并同步系统用户") + public R edit(@Validated(EditGroup.class) @RequestBody HotCompanySafetyManagerChangeReasonBo bo) { + return toAjax(hotCompanySafetyManagerService.updateByBo(bo)); + } + + //@SaCheckPermission("resourceManagement:companySafetyManager:resetPwd") + @Log(title = "安全管理人员", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/{id}/resetPwd") + @Operation(summary = "重置安全管理人员登录密码") + public R resetPwd(@PathVariable Long id) { + return toAjax(hotCompanySafetyManagerService.resetPassword(id)); + } + + /** + * 删除安全管理人员 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:companySafetyManager:remove") + @Log(title = "安全管理人员", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除安全管理人员并禁用系统用户") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotCompanySafetyManagerService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 查询所有安全管理人员列表(不分页) + * + * @param companyId 企业ID + */ + @SaCheckPermission("resourceManagement:companySafetyManager:list") + @GetMapping("/allList") + @Operation(summary = "查询所有安全管理人员列表(不分页)") + public R> allList(@NotNull(message = "企业ID不能为空") @RequestParam Long companyId) { + return R.ok(hotCompanySafetyManagerService.queryAllList(companyId)); + } + + /** + * 查询有效安全管理人员列表(不分页,未离职、已启用、已通过审核) + * + * @param companyId 企业ID + */ + @SaCheckPermission("resourceManagement:companySafetyManager:list") + @GetMapping("/validList") + @Operation(summary = "查询有效安全管理人员列表(不分页,未离职、已启用、已通过审核)") + public R> validList(@NotNull(message = "企业ID不能为空") @RequestParam Long companyId) { + return R.ok(hotCompanySafetyManagerService.queryValidList(companyId)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/domain/HotCompanySafetyManager.java b/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/domain/HotCompanySafetyManager.java new file mode 100644 index 0000000..e45d35f --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/domain/HotCompanySafetyManager.java @@ -0,0 +1,191 @@ +package com.hotwj.platform.resourceManagement.companySafetyManager.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 安全管理人员对象 hot_company_safety_manager + * + * @author shihongwei + * @date 2025-12-09 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_company_safety_manager") +public class HotCompanySafetyManager extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * sys_user表的id + */ + private Long userId; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 上级管理人员ID + */ + private Long reporterId; + + /** + * 姓名 + */ + private String name; + + /** + * 身份证号 + */ + private String idCardNo; + + /** + * 手机号 + */ + private String phone; + + /** + * 登录密码 + */ + private String loginPassword; + + /** + * 是否党员(0否,1是) + */ + private Long isPartyMember; + + /** + * 政治面貌 + */ + private String politicalStatus; + + /** + * 是否部门负责人(0否,1是) + */ + private Long isDepartmentHead; + + /** + * 是否公司负责人(0否,1是) + */ + private Long companyHead; + + /** + * 入职日期 + */ + private Date entryDate; + + /** + * 岗位名称 + */ + private String jobName; + + /** + * 岗位职责 + */ + private String jobDuty; + + /** + * 所属部门ID + */ + private Long deptId; + + /** + * 权限类型 + */ + private String permissionType; + + /** + * 角色id + */ + private Long permissionRoleId; + + + /** + * 家庭住址 + */ + private String address; + + /** + * 微信号 + */ + private String wechatId; + + /** + * 身份证正面URL + */ + private String idcardFrontUrl; + + /** + * 身份证反面URL + */ + private String idcardBackUrl; + + /** + * 头像URL + */ + private String avatarUrl; + + /** + * 任命书URL + */ + private String appointmentDocUrl; + + /** + * 上岗资格证编号 + */ + private String certificateNo; + + /** + * 上岗资格证到期日期 + */ + private Date certificateExpireDate; + + /** + * 发证机关 + */ + private String issuingAuthority; + + /** + * 证书附件URL + */ + private String certificateAttachmentUrl; + + /** + * 电子签名URL + */ + private String signatureUrl; + + /** + * 0=正常,1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 是否离职(0否 1是) + */ + private Long isResigned; + + /** + * 状态(0否 1是) + */ + private Long status; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/domain/bo/HotCompanySafetyManagerBo.java b/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/domain/bo/HotCompanySafetyManagerBo.java new file mode 100644 index 0000000..5fe969d --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/domain/bo/HotCompanySafetyManagerBo.java @@ -0,0 +1,195 @@ +package com.hotwj.platform.resourceManagement.companySafetyManager.domain.bo; + +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.core.validate.QueryGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 安全管理人员业务对象 hot_company_safety_manager + * + * @author shihongwei + * @date 2025-12-09 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotCompanySafetyManager.class, reverseConvertGenerate = false) +public class HotCompanySafetyManagerBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class, QueryGroup.class}) + private Long companyId; + + /** + * 上级管理人员ID + */ +// @NotNull(message = "上级管理人员ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long reporterId; + + /** + * 姓名 + */ + @NotBlank(message = "姓名不能为空", groups = {AddGroup.class, EditGroup.class}) + private String name; + + /** + * 身份证号 + */ + @NotBlank(message = "身份证号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String idCardNo; + + /** + * 手机号 + */ + @NotBlank(message = "手机号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String phone; + + /** + * 登录密码 + */ + private String loginPassword; + + /** + * 是否党员(0否,1是) + */ + private Long isPartyMember; + + /** + * 政治面貌 + */ + private String politicalStatus; + + /** + * 是否部门负责人(0否,1是) + */ + private Long isDepartmentHead; + + /** + * 是否公司负责人(0否,1是) + */ + private Long companyHead; + + /** + * 入职日期 + */ + private Date entryDate; + + /** + * 岗位名称 + */ + private String jobName; + + /** + * 岗位职责 + */ + private String jobDuty; + + /** + * 所属部门ID + */ +// @NotNull(message = "所属部门ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long deptId; + + /** + * 权限类型 + */ + @NotBlank(message = "权限类型不能为空", groups = {AddGroup.class, EditGroup.class}) + private String permissionType; + + @NotNull(message = "权限类型代码不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long permissionRoleId; + + /** + * 家庭住址 + */ + private String address; + + /** + * 微信号 + */ + private String wechatId; + + /** + * 身份证正面URL + */ + private String idcardFrontUrl; + + /** + * 身份证反面URL + */ + private String idcardBackUrl; + + /** + * 头像URL + */ + private String avatarUrl; + + /** + * 任命书URL + */ + private String appointmentDocUrl; + + /** + * 上岗资格证编号 + */ + private String certificateNo; + + /** + * 上岗资格证到期日期 + */ + private Date certificateExpireDate; + + /** + * 发证机关 + */ + private String issuingAuthority; + + /** + * 证书附件URL + */ + private String certificateAttachmentUrl; + + /** + * 电子签名URL + */ + private String signatureUrl; + + /** + * 0=正常,1=已删除 + */ + @NotNull(message = "0=正常,1=已删除不能为空", groups = {EditGroup.class}) + private Long isDeleted; + + /** + * 是否离职(0否 1是) + */ + private Long isResigned; + + /** + * 状态(0否 1是) + */ + private Long status; + + /** + * 导出/批量查询的人员IDs(逗号分隔) + */ + private String ids; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/domain/bo/HotCompanySafetyManagerChangeReasonBo.java b/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/domain/bo/HotCompanySafetyManagerChangeReasonBo.java new file mode 100644 index 0000000..3ca2fb4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/domain/bo/HotCompanySafetyManagerChangeReasonBo.java @@ -0,0 +1,198 @@ +package com.hotwj.platform.resourceManagement.companySafetyManager.domain.bo; + +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.core.validate.QueryGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 安全管理人员业务对象 hot_company_safety_manager + * + * @author shihongwei + * @date 2025-12-09 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotCompanySafetyManager.class, reverseConvertGenerate = false) +public class HotCompanySafetyManagerChangeReasonBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class, QueryGroup.class}) + private Long companyId; + + /** + * 上级管理人员ID + */ +// @NotNull(message = "上级管理人员ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long reporterId; + + /** + * 姓名 + */ + @NotBlank(message = "姓名不能为空", groups = {AddGroup.class, EditGroup.class}) + private String name; + + /** + * 身份证号 + */ + @NotBlank(message = "身份证号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String idCardNo; + + /** + * 手机号 + */ + @NotBlank(message = "手机号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String phone; + + /** + * 登录密码 + */ + private String loginPassword; + + /** + * 是否党员(0否,1是) + */ + private Long isPartyMember; + + /** + * 政治面貌 + */ + private String politicalStatus; + + /** + * 是否部门负责人(0否,1是) + */ + private Long isDepartmentHead; + + /** + * 是否公司负责人(0否,1是) + */ + private Long companyHead; + + /** + * 入职日期 + */ + private Date entryDate; + + /** + * 岗位名称 + */ + private String jobName; + + /** + * 岗位职责 + */ + private String jobDuty; + + /** + * 所属部门ID + */ +// @NotNull(message = "所属部门ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long deptId; + + /** + * 权限类型 + */ + @NotBlank(message = "权限类型不能为空", groups = {AddGroup.class, EditGroup.class}) + private String permissionType; + + @NotNull(message = "权限类型代码不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long permissionRoleId; + + /** + * 家庭住址 + */ + private String address; + + /** + * 微信号 + */ + private String wechatId; + + /** + * 身份证正面URL + */ + private String idcardFrontUrl; + + /** + * 身份证反面URL + */ + private String idcardBackUrl; + + /** + * 头像URL + */ + private String avatarUrl; + + /** + * 任命书URL + */ + private String appointmentDocUrl; + + /** + * 上岗资格证编号 + */ + private String certificateNo; + + /** + * 上岗资格证到期日期 + */ + private Date certificateExpireDate; + + /** + * 发证机关 + */ + private String issuingAuthority; + + /** + * 证书附件URL + */ + private String certificateAttachmentUrl; + + /** + * 电子签名URL + */ + private String signatureUrl; + + /** + * 0=正常,1=已删除 + */ + @NotNull(message = "0=正常,1=已删除不能为空", groups = {EditGroup.class}) + private Long isDeleted; + + /** + * 是否离职(0否 1是) + */ + private Long isResigned; + + /** + * 状态(0否 1是) + */ + private Long status; + + /** + * 变更原因(可选) + */ + private String changeReason; + + /** + * 变动前负责业务 + */ + private String beforeBusiness; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/domain/vo/HotCompanySafetyManagerVo.java b/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/domain/vo/HotCompanySafetyManagerVo.java new file mode 100644 index 0000000..866b69c --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/domain/vo/HotCompanySafetyManagerVo.java @@ -0,0 +1,257 @@ +package com.hotwj.platform.resourceManagement.companySafetyManager.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 安全管理人员视图对象 hot_company_safety_manager + * + * @author shihongwei + * @date 2025-12-09 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotCompanySafetyManager.class) +public class HotCompanySafetyManagerVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 上级管理人员ID + */ + private Long reporterId; + + /** + * 上级管理人员 + */ + @Translation(type = TransConstant.SAFETY_MANAGER_ID_TO_NAME, mapper = "reporterId") + @ExcelProperty(value = "上级管理人员", index = 5) + private String reporterName; + + /** + * 姓名 + */ + @ExcelProperty(value = "姓名", index = 0) + private String name; + + /** + * 身份证号 + */ + private String idCardNo; + + /** + * 手机号 + */ + @ExcelProperty(value = "手机号", index = 1) + private String phone; + + /** + * 登录密码 + */ + private String loginPassword; + + /** + * 是否党员(0否,1是) + */ + private Long isPartyMember; + + /** + * 政治面貌 + */ + private String politicalStatus; + + /** + * 是否部门负责人(0否,1是) + */ + @ExcelProperty(value = "是否部门负责人", index = 4, converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1=是,0=否") + private Long isDepartmentHead; + + /** + * 是否公司负责人(0否,1是) + */ + private Long companyHead; + + /** + * 入职日期 + */ + private Date entryDate; + + /** + * 岗位名称 + */ + @ExcelProperty(value = "岗位名称", index = 3) + private String jobName; + + /** + * 岗位职责 + */ + private String jobDuty; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 登录IP + */ + private String loginIp; + + /** + * 最近登录时间 + */ + private Date lastLoginTime; + + /** + * sys_user表的id + */ + private Long userId; + + /** + * 所属部门ID + */ + private Long deptId; + + /** + * 部门名称 + */ + @ExcelProperty(value = "部门", index = 2) + private String deptName; + + /** + * 权限类型 + */ + private String permissionType; + + private Long permissionRoleId; + + /** + * 家庭住址 + */ + private String address; + + /** + * 微信号 + */ + private String wechatId; + + /** + * 身份证正面URL + */ + private String idcardFrontUrl; + + /** + * 身份证正面URLUrl + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "idcardFrontUrl") + private String idcardFrontUrlUrl; + /** + * 身份证反面URL + */ + private String idcardBackUrl; + + /** + * 身份证反面URLUrl + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "idcardBackUrl") + private String idcardBackUrlUrl; + /** + * 头像URL + */ + private String avatarUrl; + + /** + * 头像URLUrl + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "avatarUrl") + private String avatarUrlUrl; + /** + * 任命书URL + */ + private String appointmentDocUrl; + + /** + * 任命书URLUrl + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "appointmentDocUrl") + private String appointmentDocUrlUrl; + /** + * 上岗资格证编号 + */ + private String certificateNo; + + /** + * 上岗资格证到期日期 + */ + private Date certificateExpireDate; + + /** + * 发证机关 + */ + private String issuingAuthority; + + /** + * 证书附件URL + */ + private String certificateAttachmentUrl; + + /** + * 证书附件URLUrl + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "certificateAttachmentUrl") + private String certificateAttachmentUrlUrl; + /** + * 电子签名URL + */ + private String signatureUrl; + + /** + * 电子签名URLUrl + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "signatureUrl") + private String signatureUrlUrl; + /** + * 0=正常,1=已删除 + */ + private Long isDeleted; + + /** + * 是否离职(0否 1是) + */ + @ExcelProperty(value = "是否离职", index = 7, converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1=是,0=否") + private Long isResigned; + + /** + * 状态(0否 1是) + */ + @ExcelProperty(value = "是否启用", index = 6, converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1=是,0=否") + private Long status; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/mapper/HotCompanySafetyManagerMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/mapper/HotCompanySafetyManagerMapper.java new file mode 100644 index 0000000..7e6e560 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/mapper/HotCompanySafetyManagerMapper.java @@ -0,0 +1,21 @@ +package com.hotwj.platform.resourceManagement.companySafetyManager.mapper; + +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.vo.HotCompanySafetyManagerVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 安全管理人员Mapper接口 + * + * @author shihongwei + * @date 2025-12-09 + */ +@Mapper +public interface HotCompanySafetyManagerMapper extends BaseMapperPlus { + + @Delete("DELETE FROM hot_company_safety_manager WHERE company_id = #{companyId} AND phone = #{phone} AND company_head = 1") + int deleteCompanyHeadByPhone(@Param("companyId") Long companyId, @Param("phone") String phone); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/service/IHotCompanySafetyManagerService.java b/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/service/IHotCompanySafetyManagerService.java new file mode 100644 index 0000000..3220c82 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/service/IHotCompanySafetyManagerService.java @@ -0,0 +1,116 @@ +package com.hotwj.platform.resourceManagement.companySafetyManager.service; + +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.bo.HotCompanySafetyManagerBo; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.bo.HotCompanySafetyManagerChangeReasonBo; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.vo.HotCompanySafetyManagerVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 安全管理人员Service接口 + * + * @author shihongwei + * @date 2025-12-09 + */ + public interface IHotCompanySafetyManagerService { + + /** + * 构建系统管理员组织树 + * + * @param bo 查询条件(支持 companyId、deptId) + * @return 组织树 + */ + cn.hutool.core.lang.tree.Tree buildSystemAdminTree(HotCompanySafetyManagerBo bo); + + /** + * 查询安全管理人员 + * + * @param id 主键 + * @return 安全管理人员 + */ + HotCompanySafetyManagerVo queryById(Long id); + + /** + * 分页查询安全管理人员列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 安全管理人员分页列表 + */ + TableDataInfo queryPageList(HotCompanySafetyManagerBo bo, PageQuery pageQuery); + + /** + * 分页查询该企业下所有人员列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 部门负责人分页列表 + */ + TableDataInfo queryCompanyUserList(HotCompanySafetyManagerBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的安全管理人员列表 + * + * @param bo 查询条件 + * @return 安全管理人员列表 + */ + List queryList(HotCompanySafetyManagerBo bo); + + /** + * 获取上级管理人员候选人列表 + * + * @param companyId 公司ID + * @param deptId 部门ID(可选,如果有部门限制则传) + * @param currentId 当前人员ID(可选,编辑时传) + * @return 候选人列表 + */ + List getSuperiorCandidates(Long companyId, Long deptId, Long currentId); + + /** + * 新增安全管理人员 + * + * @param bo 安全管理人员 + * @return 是否新增成功 + */ + Boolean insertByBo(HotCompanySafetyManagerBo bo); + + /** + * 修改安全管理人员 + * + * @param bo 安全管理人员 + * @return 是否修改成功 + */ + Boolean updateByBo(HotCompanySafetyManagerChangeReasonBo bo); + + Boolean resetPassword(Long id); + + /** + * 校验并批量删除安全管理人员信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + boolean updateUserFaceInitialOssId(String businessUserId, Long ossId); + + /** + * 查询所有安全管理人员列表 + * + * @param companyId 企业ID + * @return 安全管理人员列表 + */ + List queryAllList(Long companyId); + + /** + * 查询特定的安全管理人员列表(未离职、已启用、已通过审核) + * + * @param companyId 企业ID + * @return 安全管理人员列表 + */ + List queryValidList(Long companyId); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/service/impl/HotCompanySafetyManagerServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/service/impl/HotCompanySafetyManagerServiceImpl.java new file mode 100644 index 0000000..105a3ae --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/companySafetyManager/service/impl/HotCompanySafetyManagerServiceImpl.java @@ -0,0 +1,1112 @@ +package com.hotwj.platform.resourceManagement.companySafetyManager.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.lang.tree.TreeNodeConfig; +import cn.hutool.core.lang.tree.TreeUtil; +import cn.hutool.crypto.digest.BCrypt; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.common.utils.PasswordUtils; +import com.hotwj.platform.config.companyDeptConfig.domain.HotCompanyDeptConfig; +import com.hotwj.platform.config.companyDeptConfig.mapper.HotCompanyDeptConfigMapper; +import com.hotwj.platform.config.signAuditPermissionConfig.domain.HotSignAuditPermissionConfig; +import com.hotwj.platform.config.signAuditPermissionConfig.mapper.HotSignAuditPermissionConfigMapper; +import com.hotwj.platform.driverManagement.driverGroup.domain.HotDriverGroup; +import com.hotwj.platform.driverManagement.driverGroup.mapper.HotDriverGroupMapper; +import com.hotwj.platform.noticeManagerment.noticeSignDocument.service.IHotNoticeSignDocumentService; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.company.domain.SysCompany; +import com.hotwj.platform.resourceManagement.company.domain.SysCompanyRole; +import com.hotwj.platform.resourceManagement.company.domain.SysUserLoginPort; +import com.hotwj.platform.resourceManagement.company.mapper.SysCompanyMapper; +import com.hotwj.platform.resourceManagement.company.mapper.SysCompanyRoleMapper; +import com.hotwj.platform.resourceManagement.company.mapper.SysUserLoginPortMapper; +import com.hotwj.platform.resourceManagement.companyChangeHistory.domain.HotCompanyChangeHistory; +import com.hotwj.platform.resourceManagement.companyChangeHistory.mapper.HotCompanyChangeHistoryMapper; +import com.hotwj.platform.resourceManagement.companyPermissionGrant.domain.HotCompanyPermissionGrant; +import com.hotwj.platform.resourceManagement.companyPermissionGrant.mapper.HotCompanyPermissionGrantMapper; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.bo.HotCompanySafetyManagerBo; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.bo.HotCompanySafetyManagerChangeReasonBo; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.vo.HotCompanySafetyManagerVo; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import com.hotwj.platform.resourceManagement.companySafetyManager.service.IHotCompanySafetyManagerService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.service.DictService; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.system.domain.SysUser; +import org.dromara.system.mapper.SysUserMapper; +import org.dromara.system.service.ISysUserService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService.ENTERPRISE_PORT; +import static com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService.HEADQUARTERS_ROLE_ID; + +/** + * 安全管理人员Service业务层处理 + * + * @author shihongwei + * @date 2025-12-09 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotCompanySafetyManagerServiceImpl implements IHotCompanySafetyManagerService { + + private final HotCompanySafetyManagerMapper baseMapper; + private final HotCompanyDeptConfigMapper hotCompanyDeptConfigMapper; + private final SysUserMapper sysUserMapper; + private final SysCompanyRoleMapper sysCompanyRoleMapper; + private final SysUserLoginPortMapper sysUserLoginPortMapper; + private final SysCompanyMapper sysCompanyMapper; + private final ISysUserService userService; + private final DictService dictService; + private final HotCompanyChangeHistoryMapper changeHistoryMapper; + private final IHotSystemNotificationService notificationService; + private final HotCompanyPermissionGrantMapper hotCompanyPermissionGrantMapper; + private final HotSignAuditPermissionConfigMapper signAuditPermissionConfigMapper; + private final HotDriverGroupMapper groupMapper; + private final IHotNoticeSignDocumentService noticeSignDocumentService; + /** + * 查询安全管理人员 + * + * @param id 主键 + * @return 安全管理人员 + */ + @Override + public HotCompanySafetyManagerVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 构建系统管理员组织树 + * + * @param bo 查询条件(支持 companyId、deptId) + * @return 组织树 + */ + @Override + public Tree buildSystemAdminTree(HotCompanySafetyManagerBo bo) { + if (bo.getCompanyId() == null) { + bo.setCompanyId(LoginHelper.getLoginUser().getCompanyId()); + } + + if (bo.getCompanyId() == null) { + return new Tree<>(); + } + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(HotCompanySafetyManager::getCompanyId, bo.getCompanyId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0) + .eq(HotCompanySafetyManager::getIsResigned, 0) + .eq(HotCompanySafetyManager::getStatus, 1) + .orderByAsc(HotCompanySafetyManager::getId); + lqw.eq(bo.getDeptId() != null, HotCompanySafetyManager::getDeptId, bo.getDeptId()); + List list = baseMapper.selectVoList(lqw); + if (list == null || list.isEmpty()) { + Tree root = new Tree<>(); + root.setId(0L); + root.setName("系统管理员组织图"); + root.setChildren(CollUtil.newArrayList()); + return root; + } + + // 填充额外字段 + Map deptNameMap = new java.util.HashMap<>(); + if (CollUtil.isNotEmpty(list)) { + // 1. 填充上级人员名称 + Map idNameMap = list.stream() + .collect(Collectors.toMap(HotCompanySafetyManagerVo::getId, HotCompanySafetyManagerVo::getName)); + list.forEach(vo -> { + if (vo.getReporterId() != null) { + vo.setReporterName(idNameMap.get(vo.getReporterId())); + } + }); + + // 2. 填充登录IP和最近登录时间(从sys_user表获取) + Set userIds = list.stream() + .map(HotCompanySafetyManagerVo::getUserId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + if (CollUtil.isNotEmpty(userIds)) { + List users = sysUserMapper.selectList( + Wrappers.lambdaQuery() + .in(SysUser::getUserId, userIds) + .select(SysUser::getUserId, SysUser::getLoginIp, SysUser::getLoginDate) + ); + Map userMap = users.stream() + .collect(Collectors.toMap(SysUser::getUserId, Function.identity())); + + list.forEach(vo -> { + if (vo.getUserId() != null) { + SysUser user = userMap.get(vo.getUserId()); + if (user != null) { + vo.setLoginIp(user.getLoginIp()); + vo.setLastLoginTime(user.getLoginDate()); + } + } + }); + } + + // 3. 准备部门名称Map + Set deptIds = list.stream() + .map(HotCompanySafetyManagerVo::getDeptId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + if (CollUtil.isNotEmpty(deptIds)) { + List depts = hotCompanyDeptConfigMapper.selectBatchIds(deptIds); + if (CollUtil.isNotEmpty(depts)) { + deptNameMap.putAll(depts.stream().collect(Collectors.toMap(HotCompanyDeptConfig::getId, HotCompanyDeptConfig::getDeptName))); + } + } + } + + Map finalDeptNameMap = deptNameMap; + TreeNodeConfig config = new TreeNodeConfig(); + config.setNameKey("label"); + config.setIdKey("id"); + config.setParentIdKey("reporterId"); + List> trees = TreeUtil.build( + list, + null, + config, + (node, treeNode) -> { + treeNode.setId(node.getId()); + treeNode.setParentId(node.getReporterId()); + treeNode.setName(node.getName()); + treeNode.putExtra("deptId", node.getDeptId()); + if (node.getDeptId() != null) { + treeNode.putExtra("deptName", finalDeptNameMap.get(node.getDeptId())); + } + treeNode.putExtra("jobName", node.getJobName()); + + // 扩展字段 + treeNode.putExtra("isDepartmentHead", node.getIsDepartmentHead()); + treeNode.putExtra("mobile", node.getPhone()); + treeNode.putExtra("reporterName", node.getReporterName()); + treeNode.putExtra("createTime", node.getCreateTime()); + treeNode.putExtra("loginIp", node.getLoginIp()); + treeNode.putExtra("lastLoginTime", node.getLastLoginTime()); + } + ); + + // 兜底逻辑:如果构建出的树为空,但列表不为空(通常是因为存在循环引用导致无法确定根节点) + // 则强制将所有节点作为根节点返回,确保前端能看到数据 + if (CollUtil.isEmpty(trees) && CollUtil.isNotEmpty(list)) { + trees = list.stream().map(node -> { + Tree treeNode = new Tree<>(); + treeNode.setId(node.getId()); + treeNode.setParentId(0L); + treeNode.setName(node.getName()); + treeNode.putExtra("deptId", node.getDeptId()); + if (node.getDeptId() != null) { + treeNode.putExtra("deptName", finalDeptNameMap.get(node.getDeptId())); + } + treeNode.putExtra("jobName", node.getJobName()); + + // 扩展字段 + treeNode.putExtra("isDepartmentHead", node.getIsDepartmentHead()); + treeNode.putExtra("mobile", node.getPhone()); + treeNode.putExtra("reporterName", node.getReporterName()); + treeNode.putExtra("createTime", node.getCreateTime()); + treeNode.putExtra("loginIp", node.getLoginIp()); + treeNode.putExtra("lastLoginTime", node.getLastLoginTime()); + return treeNode; + }).toList(); + } + + if (CollUtil.isEmpty(trees)) { + Tree root = new Tree<>(); + root.setId(0L); + root.setName("系统管理员组织图"); + root.setChildren(CollUtil.newArrayList()); + return root; + } + + // 如果只有一个根节点,直接返回;否则包裹虚拟根节点 + if (trees.size() == 1) { + return trees.get(0); + } else { + Tree root = new Tree<>(); + root.setId(0L); + root.setName("系统管理员组织图"); + root.putExtra("label", "系统管理员组织图"); + root.setChildren(trees); + return root; + } + } + + /** + * 分页查询安全管理人员列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 安全管理人员分页列表 + */ + @Override + public TableDataInfo queryPageList(HotCompanySafetyManagerBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + List records = result.getRecords(); + if (CollUtil.isNotEmpty(records)) { + Set deptIds = records.stream() + .map(HotCompanySafetyManagerVo::getDeptId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + if (CollUtil.isNotEmpty(deptIds)) { + List depts = hotCompanyDeptConfigMapper.selectBatchIds(deptIds); + Map deptNameMap = depts.stream() + .collect(Collectors.toMap(HotCompanyDeptConfig::getId, HotCompanyDeptConfig::getDeptName)); + records.forEach(vo -> { + if (vo.getDeptId() != null) { + vo.setDeptName(deptNameMap.get(vo.getDeptId())); + } + }); + } + } + return TableDataInfo.build(result); + } + + /** + * 分页查询部门负责人列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 部门负责人分页列表 + */ + @Override + public TableDataInfo queryCompanyUserList(HotCompanySafetyManagerBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.eq(HotCompanySafetyManager::getCompanyId, bo.getCompanyId()) + .eq(HotCompanySafetyManager::getIsResigned, 0) + .eq(HotCompanySafetyManager::getStatus, 1) + .eq(HotCompanySafetyManager::getIsDeleted, 0); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的安全管理人员列表 + * + * @param bo 查询条件 + * @return 安全管理人员列表 + */ + @Override + public List queryList(HotCompanySafetyManagerBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + List list = baseMapper.selectVoList(lqw); + if (CollUtil.isNotEmpty(list)) { + List reporterIds = list.stream() + .map(HotCompanySafetyManagerVo::getReporterId) + .filter(id -> id != null && id > 0) + .distinct() + .toList(); + if (CollUtil.isNotEmpty(reporterIds)) { + List reporters = baseMapper.selectVoList( + Wrappers.lambdaQuery().in(HotCompanySafetyManager::getId, reporterIds) + ); + Map idNameMap = reporters.stream() + .collect(Collectors.toMap(HotCompanySafetyManagerVo::getId, HotCompanySafetyManagerVo::getName)); + list.forEach(vo -> { + if (vo.getReporterId() != null) { + vo.setReporterName(idNameMap.get(vo.getReporterId())); + } + }); + } + + // 填充部门名称 + Set deptIds = list.stream() + .map(HotCompanySafetyManagerVo::getDeptId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + if (CollUtil.isNotEmpty(deptIds)) { + List depts = hotCompanyDeptConfigMapper.selectBatchIds(deptIds); + Map deptNameMap = depts.stream() + .collect(Collectors.toMap(HotCompanyDeptConfig::getId, HotCompanyDeptConfig::getDeptName)); + list.forEach(vo -> { + if (vo.getDeptId() != null) { + vo.setDeptName(deptNameMap.get(vo.getDeptId())); + } + }); + } + } + return list; + } + + private LambdaQueryWrapper buildQueryWrapper(HotCompanySafetyManagerBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotCompanySafetyManager::getId); + lqw.in(StringUtils.isNotBlank(bo.getIds()), HotCompanySafetyManager::getId, StringUtils.splitTo(bo.getIds(), Convert::toLong)); + if (bo.getCompanyId() == null) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser != null && !loginUser.getIsAdmin() && loginUser.getRoleId() != HEADQUARTERS_ROLE_ID) { + lqw.eq(HotCompanySafetyManager::getCompanyId, loginUser.getCompanyId()); + } else if (loginUser.getIsAdmin() || loginUser.getRoleId() == HEADQUARTERS_ROLE_ID) { + + } else { + lqw.eq(HotCompanySafetyManager::getCompanyId, loginUser.getCompanyId()); + } + } else { + lqw.eq(HotCompanySafetyManager::getCompanyId, bo.getCompanyId()); + } + + lqw.eq(bo.getReporterId() != null, HotCompanySafetyManager::getReporterId, bo.getReporterId()); + lqw.eq(bo.getId() != null, HotCompanySafetyManager::getId, bo.getId()); + lqw.like(StringUtils.isNotBlank(bo.getName()), HotCompanySafetyManager::getName, bo.getName()); + lqw.eq(StringUtils.isNotBlank(bo.getIdCardNo()), HotCompanySafetyManager::getIdCardNo, bo.getIdCardNo()); + lqw.like(StringUtils.isNotBlank(bo.getPhone()), HotCompanySafetyManager::getPhone, bo.getPhone()); + lqw.eq(StringUtils.isNotBlank(bo.getLoginPassword()), HotCompanySafetyManager::getLoginPassword, bo.getLoginPassword()); + lqw.eq(bo.getIsPartyMember() != null, HotCompanySafetyManager::getIsPartyMember, bo.getIsPartyMember()); + lqw.eq(bo.getIsDepartmentHead() != null, HotCompanySafetyManager::getIsDepartmentHead, bo.getIsDepartmentHead()); + lqw.eq(bo.getEntryDate() != null, HotCompanySafetyManager::getEntryDate, bo.getEntryDate()); + lqw.eq(StringUtils.isNotBlank(bo.getJobName()), HotCompanySafetyManager::getJobName, bo.getJobName()); + lqw.eq(StringUtils.isNotBlank(bo.getJobDuty()), HotCompanySafetyManager::getJobDuty, bo.getJobDuty()); + lqw.like(bo.getDeptId() != null, HotCompanySafetyManager::getDeptId, bo.getDeptId()); + lqw.eq(StringUtils.isNotBlank(bo.getPermissionType()), HotCompanySafetyManager::getPermissionType, bo.getPermissionType()); + lqw.eq(StringUtils.isNotBlank(bo.getAddress()), HotCompanySafetyManager::getAddress, bo.getAddress()); + lqw.eq(StringUtils.isNotBlank(bo.getWechatId()), HotCompanySafetyManager::getWechatId, bo.getWechatId()); + lqw.eq(StringUtils.isNotBlank(bo.getIdcardFrontUrl()), HotCompanySafetyManager::getIdcardFrontUrl, bo.getIdcardFrontUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getIdcardBackUrl()), HotCompanySafetyManager::getIdcardBackUrl, bo.getIdcardBackUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getAvatarUrl()), HotCompanySafetyManager::getAvatarUrl, bo.getAvatarUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getAppointmentDocUrl()), HotCompanySafetyManager::getAppointmentDocUrl, bo.getAppointmentDocUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getCertificateNo()), HotCompanySafetyManager::getCertificateNo, bo.getCertificateNo()); + lqw.eq(bo.getCertificateExpireDate() != null, HotCompanySafetyManager::getCertificateExpireDate, bo.getCertificateExpireDate()); + lqw.eq(StringUtils.isNotBlank(bo.getIssuingAuthority()), HotCompanySafetyManager::getIssuingAuthority, bo.getIssuingAuthority()); + lqw.eq(StringUtils.isNotBlank(bo.getCertificateAttachmentUrl()), HotCompanySafetyManager::getCertificateAttachmentUrl, bo.getCertificateAttachmentUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getSignatureUrl()), HotCompanySafetyManager::getSignatureUrl, bo.getSignatureUrl()); + lqw.eq(bo.getIsResigned() != null, HotCompanySafetyManager::getIsResigned, bo.getIsResigned()); + lqw.eq(bo.getStatus() != null, HotCompanySafetyManager::getStatus, bo.getStatus()); + lqw.eq(HotCompanySafetyManager::getIsDeleted, 0); // 只能查询未删除的安全管理人员 + return lqw; + } + + /** + * 新增安全管理人员 + * + * @param bo 安全管理人员 + * @return 是否新增成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(HotCompanySafetyManagerBo bo) { + HotCompanySafetyManager add = MapstructUtils.convert(bo, HotCompanySafetyManager.class); + if (add.getCompanyId() == null) { + add.setCompanyId(LoginHelper.getLoginUser().getCompanyId()); + } + + // 校验:如果当前部门没有负责人,且添加的不是负责人,则不允许添加 + if (add.getDeptId() != null && !Long.valueOf(1L).equals(add.getIsDepartmentHead())) { + Long headCount = baseMapper.selectCount(Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, add.getCompanyId()) + .eq(HotCompanySafetyManager::getDeptId, add.getDeptId()) + .eq(HotCompanySafetyManager::getIsDepartmentHead, 1) + .eq(HotCompanySafetyManager::getIsDeleted, 0) + .eq(HotCompanySafetyManager::getIsResigned, 0) + .eq(HotCompanySafetyManager::getStatus, 1) + ); + + if (headCount == 0) { + throw new ServiceException("当前部门暂无负责人,请先添加部门负责人"); + } + } + + add.setIsDeleted(0L); + validEntityBeforeSave(add); + validatePermissionAssignment(add.getCompanyId(), add.getCompanyHead()); + + // 如果该手机号已在其他企业被注册,禁止创建SysUser (业务规则:一个手机号对应一个自然人,可以在多个企业任职,但是有且仅有一个SysUser) + + + SysUser sysUser = findUserByPhone(add.getPhone()); + Long userId = null; + if (sysUser == null) { + sysUser = new SysUser(); + sysUser.setPhonenumber(add.getPhone()); + sysUser.setNickName(add.getName()); + sysUser.setUserName(add.getPhone()); + // 设置主密码(默认) + sysUser.setPassword(PasswordUtils.createDefaultPassword(add.getPhone())); + sysUser.setUserType("sys_user"); + sysUser.setStatus("0"); + sysUserMapper.insert(sysUser); + userId = sysUser.getUserId(); + } else { + userId = sysUser.getUserId(); + } + + // 初始化或更新子密码(特定企业的登录密码) + String subPassword; + if (StringUtils.isNotBlank(add.getLoginPassword())) { + validatePassword(add.getLoginPassword()); + subPassword = BCrypt.hashpw(add.getLoginPassword()); + } else { + subPassword = PasswordUtils.createDefaultPassword(add.getPhone()); + } + + // 确保有登录端口并设置子密码 + ensureLoginPort(userId, add.getCompanyId(), add.getName(), subPassword); + + log.info("[安全管理人员已创建] managerId={}, userId={}, companyId={}, operator={}", add.getId(), userId, add.getCompanyId(), add.getCreateBy()); + applyRoleByPermissionType(userId, add); + + add.setUserId(userId); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + noticeSignDocumentService.issueDefaultForNewManager(add); + } + + return flag; + } + + private void ensureLoginPort(Long userId, Long companyId, String username, String subPassword) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysUserLoginPort::getUserId, userId) + .eq(SysUserLoginPort::getCompanyId, companyId) + .eq(SysUserLoginPort::getLoginPort, ENTERPRISE_PORT); + + SysUserLoginPort port = sysUserLoginPortMapper.selectOne(wrapper); + if (port == null) { + port = new SysUserLoginPort(); + port.setUserId(userId); + port.setCompanyId(companyId); + port.setUsername(username); + port.setLoginPort(ENTERPRISE_PORT); + port.setStatus(1); + port.setSubPassword(subPassword); + sysUserLoginPortMapper.insert(port); + } else { + // 如果端口已存在但没有子密码,或者需要更新子密码(通常新建时如果是新密码则覆盖,否则保留?这里假设新建时传入的密码是初始化的) + // 如果是已存在的员工被重新录入,可能不应重置密码。但如果是“新增安全管理人员”,通常意味着新入职或新岗位。 + // 这里逻辑:如果传入了明确的密码,则更新;如果是默认密码且原密码为空,则更新。 + // 简化逻辑:新增人员操作,如果该人员在该公司已有端口,是否重置密码? + // 通常“新增”意味着从无到有。如果已存在SysUser但无此公司端口,是新开端口。 + // 如果已存在此公司端口,可能是数据错误或重复添加。 + // 假设是新开端口或修复数据: + if (StringUtils.isBlank(port.getSubPassword())) { + port.setSubPassword(subPassword); + sysUserLoginPortMapper.updateById(port); + } + } + } + + /** + * 修改安全管理人员 + * + * @param bo 安全管理人员 + * @return 是否修改成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(HotCompanySafetyManagerChangeReasonBo bo) { + HotCompanySafetyManager update = MapstructUtils.convert(bo, HotCompanySafetyManager.class); + HotCompanySafetyManager before = baseMapper.selectById(update.getId()); + if (before == null) { + throw new ServiceException("安全管理人员不存在"); + } + // 参考新增:编辑时如果更换部门,且目标部门暂无负责人、当前人员又不是负责人,则不允许变更 + boolean deptChanged = update.getDeptId() != null && !Objects.equals(update.getDeptId(), before.getDeptId()); + Long finalIsDepartmentHead = update.getIsDepartmentHead() != null ? update.getIsDepartmentHead() : before.getIsDepartmentHead(); + if (deptChanged + && !Long.valueOf(1L).equals(finalIsDepartmentHead) + && update.getCompanyId() != null + && countActiveDeptHead(update.getCompanyId(), update.getDeptId()) == 0) { + throw new ServiceException("当前部门暂无负责人,请先添加部门负责人"); + } + validEntityBeforeSave(update); + boolean permissionChanged = !Objects.equals(update.getPermissionRoleId(), before.getPermissionRoleId()) + || !StringUtils.equals(update.getPermissionType(), before.getPermissionType()); + if (permissionChanged) { + validatePermissionAssignment(before.getCompanyId(), update.getCompanyHead() == null ? before.getCompanyHead() : update.getCompanyHead()); + } + + // 如果是离职,将部门置空 + boolean isResigning = update.getIsResigned() != null + && Long.valueOf(1L).equals(update.getIsResigned()) + && (before.getIsResigned() == null || !Long.valueOf(1L).equals(before.getIsResigned())); + + if (isResigning) { + update.setDeptId(null); + } + + boolean ok = baseMapper.updateById(update) > 0; + + if (ok && isResigning) { + baseMapper.update(null, Wrappers.lambdaUpdate() + .set(HotCompanySafetyManager::getDeptId, null) + .eq(HotCompanySafetyManager::getId, update.getId())); + } + + // TODO 是否需要考虑修改密码 + if (before != null && ok) { + syncUserAndRecordHistory(update, before, bo.getChangeReason(), bo.getBeforeBusiness()); + } + return ok; + } + + private long countActiveDeptHead(Long companyId, Long deptId) { + if (companyId == null || deptId == null) { + return 0L; + } + return baseMapper.selectCount(Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getDeptId, deptId) + .eq(HotCompanySafetyManager::getIsDepartmentHead, 1) + .eq(HotCompanySafetyManager::getIsDeleted, 0) + .eq(HotCompanySafetyManager::getIsResigned, 0) + .eq(HotCompanySafetyManager::getStatus, 1) + ); + } + + private void syncUserAndRecordHistory(HotCompanySafetyManager update, HotCompanySafetyManager before, String changeReason, String beforeBusiness) { + SysUser sysUser = findUserByPhone(before.getPhone()); + if (sysUser == null) { + return; + } + + boolean userChanged = false; + + // 1. Handle Phone Change + if (handlePhoneChange(update, before, sysUser)) { + userChanged = true; + } + + // 2. Handle Name Change + if (handleNameChange(update, before, sysUser)) { + userChanged = true; + } + + // 3. Handle Department Change + if (handleDeptChange(update, before, sysUser)) { + userChanged = true; + } + + // 4. Handle Status Change (Disable/Resign) + handleStatusChange(update, before, sysUser); + + // 5. Handle Password Change + if (handlePasswordChange(update, before, sysUser)) { + userChanged = true; + } + + // 6. Handle Permission Change + boolean permissionChanged = !Objects.equals(update.getPermissionRoleId(), before.getPermissionRoleId()) + || !StringUtils.equals(update.getPermissionType(), before.getPermissionType()); + if (permissionChanged) { + applyRoleByPermissionType(sysUser.getUserId(), update); + } + + // 7. Record Change History + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHead = Long.valueOf(1L).equals(before.getCompanyHead()) || Long.valueOf(1L).equals(before.getIsDepartmentHead()); + boolean operatorIsManagerSelf = loginUser != null && before.getUserId() != null && java.util.Objects.equals(loginUser.getUserId(), before.getUserId()); + boolean sameCompany = loginUser != null && java.util.Objects.equals(loginUser.getCompanyId(), before.getCompanyId()); + if (!(isHead && operatorIsManagerSelf && sameCompany)) { + recordChangeHistory(update, before, changeReason, beforeBusiness); + } + boolean resignChanged = update.getIsResigned() != null && Long.valueOf(1L).equals(update.getIsResigned()) + && (before.getIsResigned() == null || !Long.valueOf(1L).equals(before.getIsResigned())); + boolean disabledChanged = update.getStatus() != null && !Long.valueOf(1L).equals(update.getStatus()) + && (before.getStatus() == null || Long.valueOf(1L).equals(before.getStatus())); + if (resignChanged && before.getCompanyId() != null) { + List heads = baseMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, before.getCompanyId()) + .eq(HotCompanySafetyManager::getCompanyHead, 1L) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (CollUtil.isNotEmpty(heads)) { + List receiverIds = heads.stream().map(h -> String.valueOf(h.getId())).collect(java.util.stream.Collectors.toList()); + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent("企业管理人员【" + StringUtils.blankToDefault(update.getName(), "该人员") + "】已经离职。"); + bos.setSourceType("企业管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送企业管理人员离职通知失败 companyId={} managerId={}", before.getCompanyId(), before.getId(), e); + } + } + } + if ((resignChanged || disabledChanged) && before.getCompanyId() != null) { + signAuditPermissionConfigMapper.delete( + Wrappers.lambdaQuery() + .eq(HotSignAuditPermissionConfig::getCompanyId, before.getCompanyId()) + .eq(HotSignAuditPermissionConfig::getAuditorId, before.getId()) + ); + log.info("[SafetyManager-AuditPerm] removed sign audit permissions auditorId={}, companyId={}", before.getId(), before.getCompanyId()); + } + + // 8. Handle Group Deletion on Resign/Disable + if ((resignChanged || disabledChanged) && before.getCompanyId() != null) { + groupMapper.delete( + Wrappers.lambdaQuery() + .eq(HotDriverGroup::getCompanyId, before.getCompanyId()) + .eq(HotDriverGroup::getLeaderId, before.getId()) + ); + log.info("[SafetyManager-Group] removed groups where leaderId={}, companyId={}", before.getId(), before.getCompanyId()); + } + + // 9. Update SysUser if needed + if (userChanged) { + sysUserMapper.updateById(sysUser); + log.info("[SafetyManager-Update] synced fields for userId={}, managerId={}, operator={}", sysUser.getUserId(), update.getId(), update.getUpdateBy()); + } + } + + private boolean handlePasswordChange(HotCompanySafetyManager update, HotCompanySafetyManager before, SysUser sysUser) { + String newPwd = update.getLoginPassword(); + String oldPwd = before.getLoginPassword(); + if (StringUtils.isBlank(newPwd) || StringUtils.equals(newPwd, oldPwd)) { + return false; + } + validatePassword(newPwd); + + // 修改子密码 + userService.resetUserPwd(sysUser.getUserId(), BCrypt.hashpw(newPwd), before.getCompanyId()); + + // 不需要单独更新 sysUser 密码,resetUserPwd 已经处理 + return false; + } + + private boolean handlePhoneChange(HotCompanySafetyManager update, HotCompanySafetyManager before, SysUser sysUser) { + if (StringUtils.isBlank(update.getPhone()) || StringUtils.equals(update.getPhone(), before.getPhone())) { + return false; + } + + // Check if new phone exists in sys_user + SysUser existUser = sysUserMapper.selectOne(new LambdaQueryWrapper() + .eq(SysUser::getPhonenumber, update.getPhone())); + + if (existUser != null) { + validatePhoneUsage(existUser); + } + + sysUser.setPhonenumber(update.getPhone()); + return true; + } + + private void validatePhoneUsage(SysUser existUser) { + // Check if used in same port + Long samePortCount = sysUserLoginPortMapper.selectCount(new LambdaQueryWrapper() + .eq(SysUserLoginPort::getUserId, existUser.getUserId()) + .eq(SysUserLoginPort::getLoginPort, ENTERPRISE_PORT)); + if (samePortCount > 0) { + throw new org.dromara.common.core.exception.ServiceException("该手机号已在当前端口被使用"); + } + + // Check if used in any other port + Long anyPortCount = sysUserLoginPortMapper.selectCount(new LambdaQueryWrapper() + .eq(SysUserLoginPort::getUserId, existUser.getUserId())); + if (anyPortCount > 0) { + throw new org.dromara.common.core.exception.ServiceException("该手机号已被其他端口使用"); + } + + // Exists in sys_user but not used in any port? + throw new org.dromara.common.core.exception.ServiceException("该手机号已被注册"); + } + + private boolean handleNameChange(HotCompanySafetyManager update, HotCompanySafetyManager before, SysUser sysUser) { + if (StringUtils.isBlank(update.getName()) || StringUtils.equals(update.getName(), before.getName())) { + return false; + } + + sysUser.setNickName(update.getName()); + + // Sync port username + SysUserLoginPort portEntry = getLoginPortEntry(sysUser.getUserId(), before.getCompanyId()); + if (portEntry != null) { + portEntry.setUsername(update.getName()); + sysUserLoginPortMapper.updateById(portEntry); + } + return true; + } + + private boolean handleDeptChange(HotCompanySafetyManager update, HotCompanySafetyManager before, SysUser sysUser) { + if (update.getDeptId() != null && !update.getDeptId().equals(before.getDeptId())) { + sysUser.setDeptId(update.getDeptId()); + return true; + } + return false; + } + + private void handleStatusChange(HotCompanySafetyManager update, HotCompanySafetyManager before, SysUser sysUser) { + if (update.getStatus() == null && update.getIsResigned() == null) { + return; + } + + boolean toDisable = (update.getStatus() != null && !Long.valueOf(1L).equals(update.getStatus())) + || (update.getIsResigned() != null && Long.valueOf(1L).equals(update.getIsResigned())); + + SysUserLoginPort portEntry = getLoginPortEntry(sysUser.getUserId(), before.getCompanyId()); + if (portEntry != null) { + portEntry.setStatus(toDisable ? 0 : 1); + sysUserLoginPortMapper.updateById(portEntry); + + String statusAction = toDisable ? "disabled" : "enabled"; + log.info("[安全管理人员状态] {} port access userId={}, managerId={}, operator={}", + statusAction, sysUser.getUserId(), update.getId(), update.getUpdateBy()); + } + } + + private void recordChangeHistory(HotCompanySafetyManager update, HotCompanySafetyManager before, String changeReason, String beforeBusiness) { + boolean resignChanged = update.getIsResigned() != null && Long.valueOf(1L).equals(update.getIsResigned()) + && (before.getIsResigned() == null || !Long.valueOf(1L).equals(before.getIsResigned())); + + boolean jobChanged = StringUtils.isNotBlank(update.getJobName()) && !StringUtils.equals(update.getJobName(), before.getJobName()); + // 如果是离职,且原部门不为空,则认为部门变更(变为空) + boolean deptChanged = (update.getDeptId() != null && !update.getDeptId().equals(before.getDeptId())) + || (resignChanged && before.getDeptId() != null); + + boolean appointmentDocChanged = StringUtils.isNotBlank(update.getAppointmentDocUrl()) && !StringUtils.equals(update.getAppointmentDocUrl(), before.getAppointmentDocUrl()); + + if (!jobChanged && !deptChanged && !resignChanged && !appointmentDocChanged) { + return; + } + + String changeType = determineChangeType(jobChanged, deptChanged, resignChanged, appointmentDocChanged); + + HotCompanyChangeHistory history = new HotCompanyChangeHistory(); + history.setCompanyId(before.getCompanyId()); + history.setName(update.getName()); + history.setPhone(update.getPhone()); + history.setEmployeeId(before.getId()); + history.setBeforeJobName(before.getJobName()); + history.setAfterJobName(jobChanged ? update.getJobName() : before.getJobName()); + history.setBeforeDeptId(before.getDeptId()); + history.setAfterDeptId(deptChanged ? update.getDeptId() : before.getDeptId()); + history.setChangeType(changeType); + history.setChangeTime(new java.util.Date()); + history.setOperatorId(LoginHelper.getUserId()); + history.setOperatorName(LoginHelper.getUsername()); + history.setIsDeleted(0); + history.setChangeReason(changeReason); + history.setBeforeBusiness(StringUtils.isNotBlank(beforeBusiness) ? beforeBusiness : before.getJobDuty()); + history.setBeforeAppointmentUrl(before.getAppointmentDocUrl()); + history.setAfterAppointmentUrl(update.getAppointmentDocUrl()); + changeHistoryMapper.insert(history); + + log.info("[安全管理人员变更] type={}, managerId={}, operator={}", changeType, before.getId(), update.getUpdateBy()); + } + + private String determineChangeType(boolean jobChanged, boolean deptChanged, boolean resignChanged, boolean appointmentDocChanged) { + if (resignChanged) return "离职"; + if (jobChanged && deptChanged) return "调岗调部门"; + if (jobChanged) return "调岗位"; + if (deptChanged) return "调部门"; + if (appointmentDocChanged) return "任命书变更"; + return "信息变更"; + } + + private SysUserLoginPort getLoginPortEntry(Long userId, Long companyId) { + return sysUserLoginPortMapper.selectOne(new LambdaQueryWrapper() + .eq(SysUserLoginPort::getUserId, userId) + .eq(SysUserLoginPort::getLoginPort, ENTERPRISE_PORT) + .eq(SysUserLoginPort::getCompanyId, companyId)); + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotCompanySafetyManager entity) { + } + + /** + * 校验并批量删除安全管理人员信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + } + boolean ok = baseMapper.deleteByIds(ids) > 0; + if (ok) { + ids.forEach(id -> { + HotCompanySafetyManager mgr = baseMapper.selectById(id); + if (mgr != null) { + SysUser sysUser = findUserByPhone(mgr.getPhone()); + if (sysUser != null) { + // 应该停用此端口,此用户的登录就行了,不能删除用户 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysUserLoginPort::getUserId, id); + wrapper.eq(SysUserLoginPort::getCompanyId, mgr.getCompanyId()); + wrapper.eq(SysUserLoginPort::getLoginPort, ENTERPRISE_PORT); + List loginPorts = sysUserLoginPortMapper.selectList(wrapper); + if (CollUtil.isEmpty(loginPorts)) { + sysUserLoginPortMapper.deleteByIds(loginPorts); + } + } + } + }); + } + return ok; + } + + @Override + public boolean updateUserFaceInitialOssId(String businessUserId, Long ossId) { + return baseMapper.update(null, + new LambdaUpdateWrapper() + .set(HotCompanySafetyManager::getAvatarUrl, ossId) + .eq(HotCompanySafetyManager::getId, businessUserId) + ) > 0; + } + + /** + * 查询所有安全管理人员列表 + * + * @param companyId 企业ID + * @return 安全管理人员列表 + */ + @Override + public List queryAllList(Long companyId) { + return baseMapper.selectVoList(Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getIsDeleted, 0)); + } + + /** + * 查询特定的安全管理人员列表(未离职、已启用、已通过审核) + * + * @param companyId 企业ID + * @return 安全管理人员列表 + */ + @Override + public List queryValidList(Long companyId) { + return baseMapper.selectVoList(Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getIsDeleted, 0) + .eq(HotCompanySafetyManager::getIsResigned, 0) + .eq(HotCompanySafetyManager::getStatus, 1)); + } + + private SysUser findUserByPhone(String phone) { + if (StringUtils.isBlank(phone)) { + return null; + } + List users = sysUserMapper.selectList(Wrappers.lambdaQuery().eq(SysUser::getPhonenumber, phone)); + if (users == null || users.isEmpty()) { + return null; + } + return users.get(0); + } + + + private void validatePassword(String raw) { + if (raw.length() < 8) { + throw new IllegalArgumentException("密码长度至少8位"); + } + boolean hasLetter = raw.chars().anyMatch(Character::isLetter); + boolean hasDigit = raw.chars().anyMatch(Character::isDigit); + if (!hasLetter || !hasDigit) { + throw new IllegalArgumentException("密码需包含字母和数字"); + } + } + + @Override + public List getSuperiorCandidates(Long companyId, Long deptId, Long currentId) { + // 1. 获取该企业下所有在职未删除的安全管理人员,并确保包括企业负责人(即使状态可能有问题) + SysCompany company = sysCompanyMapper.selectById(companyId); + String responsibleMobile = (company != null) ? company.getResponsibleMobile() : null; + + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getIsDeleted, 0) + .and(w -> w + // 情况1:是企业负责人(不限部门,不论状态) + .or(StringUtils.isNotBlank(responsibleMobile), n -> n.eq(HotCompanySafetyManager::getPhone, responsibleMobile)) + // 情况2:其他人员(必须正常在职) + .or(n -> { + n.eq(HotCompanySafetyManager::getIsResigned, 0) + .eq(HotCompanySafetyManager::getStatus, 1); + // 如果传入了部门ID,则必须是同部门 + if (deptId != null) { + n.eq(HotCompanySafetyManager::getDeptId, deptId); + } + }) + ); + + List allManagers = baseMapper.selectList(lqw); + + if (CollUtil.isEmpty(allManagers)) { + return Collections.emptyList(); + } + + // 如果是新增(currentId 为空),所有人都是候选人 + if (currentId == null) { + return MapstructUtils.convert(allManagers, HotCompanySafetyManagerVo.class); + } + + // 2. 过滤掉自己 + List candidates = allManagers.stream() + .filter(m -> !m.getId().equals(currentId)) + .collect(Collectors.toList()); + + // 3. 构建上下级关系映射 (Parent ID -> List) + Map> childrenMap = allManagers.stream() + .filter(m -> m.getReporterId() != null) + .collect(Collectors.groupingBy(HotCompanySafetyManager::getReporterId, + Collectors.mapping(HotCompanySafetyManager::getId, Collectors.toList()))); + + // 4. 找出 currentId 的所有下级(递归) + Set descendants = new HashSet<>(); + findDescendants(currentId, childrenMap, descendants); + + // 5. 过滤掉所有下级 + return candidates.stream() + .filter(m -> !descendants.contains(m.getId())) + .map(m -> { + HotCompanySafetyManagerVo vo = new HotCompanySafetyManagerVo(); + vo.setId(m.getId()); + vo.setName(m.getName()); + return vo; + }) + .collect(Collectors.toList()); + } + + private void findDescendants(Long currentId, Map> childrenMap, Set descendants) { + List children = childrenMap.get(currentId); + if (CollUtil.isNotEmpty(children)) { + for (Long childId : children) { + // 防止数据本身有环导致死循环 + if (descendants.add(childId)) { + findDescendants(childId, childrenMap, descendants); + } + } + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean resetPassword(Long id) { + HotCompanySafetyManager mgr = baseMapper.selectById(id); + if (mgr == null) { + log.warn("[安全管理人员重置密码] 未找到安全管理人员 id={}", id); + return false; + } + SysUser sysUser = findUserByPhone(mgr.getPhone()); + if (sysUser == null) { + log.warn("[安全管理人员重置密码] 未找到系统用户 phone={} (managerId={})", mgr.getPhone(), id); + return false; + } + String phoneNumber = sysUser.getPhonenumber(); + if (StringUtils.isBlank(phoneNumber)) { + log.warn("[SafetyManager-ResetPwd] 系统用户手机号为空 userId={}", sysUser.getUserId()); + return false; + } + + String defaultPassword = PasswordUtils.createDefaultPassword(phoneNumber); + + userService.checkUserAllowed(sysUser.getUserId()); + userService.checkUserDataScope(sysUser.getUserId()); + int rows = userService.resetUserPwd(sysUser.getUserId(), defaultPassword, mgr.getCompanyId()); + if (rows > 0) { + log.info("[安全管理人员重置密码] managerId={}, userId={}, operator={}", id, sysUser.getUserId(), LoginHelper.getUserId()); + return true; + } + return false; + } + + // 需要插入两个角色,一个是进入企业端,一个是其他权限业务权限,如果不存在,则创建 + private void applyRoleByPermissionType(Long userId, HotCompanySafetyManager bo) { + Long companyId = bo.getCompanyId(); + Long permissionRoleId = bo.getPermissionRoleId(); + String name = bo.getName(); + + if (userId == null || companyId == null || permissionRoleId == null) { + log.warn("[安全管理人员角色] 参数无效,跳过设置 userId={}, companyId={}, permissionRoleId={}", userId, companyId, permissionRoleId); + return; + } + + // 使用企业角色 + SysCompanyRole role = sysCompanyRoleMapper.selectOne(Wrappers.lambdaQuery() + .eq(SysCompanyRole::getId, permissionRoleId) + .eq(SysCompanyRole::getStatus, "0")); + + if (role == null) { + throw new org.dromara.common.core.exception.ServiceException("未找到匹配的企业角色,请检查角色配置"); + } + + Long roleId = role.getId(); + clearGrantedPermissions(companyId, userId); + + List list = sysUserLoginPortMapper.selectList(Wrappers.lambdaQuery() + .eq(SysUserLoginPort::getCompanyId, companyId) + .eq(SysUserLoginPort::getUserId, userId) + .eq(SysUserLoginPort::getLoginPort, ENTERPRISE_PORT) // 插入企业端端口 + .eq(SysUserLoginPort::getStatus, 1)); + + if (CollUtil.isEmpty(list)) { + // 取保新增的用户能进入企业端 + SysUserLoginPort ucr = new SysUserLoginPort(); + ucr.setCompanyId(companyId); + ucr.setUserId(userId); + ucr.setLoginPort(ENTERPRISE_PORT); + ucr.setUsername(name); + ucr.setIsDefault(0); + ucr.setStatus(1); + sysUserLoginPortMapper.insert(ucr); + log.info("[安全管理人员角色] 关联企业角色 userId={}, companyId={}, roleId={} ({})", userId, companyId, roleId, permissionRoleId); + } else { + // 用户已关联企业角色,无需重复添加 + log.info("[安全管理人员角色] 用户已关联企业角色,跳过端口添加 userId={}, companyId={}", userId, companyId); + } + } + + private void validatePermissionAssignment(Long companyId, Long targetCompanyHead) { + if (companyId == null) { + return; + } + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || loginUser.getUserId() == null) { + throw new ServiceException("登录状态异常,请重新登录后重试"); + } + HotCompanySafetyManager operator = baseMapper.selectOne(Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getUserId, loginUser.getUserId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L)); + if (operator == null) { + throw new ServiceException("仅企业负责人或安全管理人员可以分配权限"); + } + if (Long.valueOf(1L).equals(targetCompanyHead)) { + throw new ServiceException("不允许给企业负责人分配权限"); + } + } + + private void clearGrantedPermissions(Long companyId, Long userId) { + hotCompanyPermissionGrantMapper.update(null, Wrappers.lambdaUpdate() + .eq(HotCompanyPermissionGrant::getCompanyId, companyId) + .eq(HotCompanyPermissionGrant::getGranteeUserId, userId) + .eq(HotCompanyPermissionGrant::getIsDeleted, 0L) + .set(HotCompanyPermissionGrant::getStatus, 0L) + .set(HotCompanyPermissionGrant::getIsDeleted, 1L)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/V20240601.001__add_driver_query_index.sql b/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/V20240601.001__add_driver_query_index.sql new file mode 100644 index 0000000..5df6f82 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/V20240601.001__add_driver_query_index.sql @@ -0,0 +1,21 @@ +-- 驾驶员综合查询相关索引优化 + +-- hot_driver 表索引 +CREATE INDEX idx_hot_driver_company_id ON hot_driver (company_id); +CREATE INDEX idx_hot_driver_vehicle_id ON hot_driver (vehicle_id); +CREATE INDEX idx_hot_driver_name ON hot_driver (name); +CREATE INDEX idx_hot_driver_phone ON hot_driver (phone); +CREATE INDEX idx_hot_driver_id_card ON hot_driver (id_card_no); +CREATE INDEX idx_hot_driver_plate_number ON hot_driver (plate_number); + +-- hot_driver_license 表索引 +CREATE INDEX idx_hot_driver_license_driver_id ON hot_driver_license (driver_id); + +-- hot_driver_prof_cert 表索引 +CREATE INDEX idx_hot_driver_prof_cert_driver_id ON hot_driver_prof_cert (driver_id); + +-- hot_vehicle 表索引 +CREATE INDEX idx_hot_vehicle_plate_number ON hot_vehicle (plate_number); + +-- sys_company 表索引 (通常已有,补充 company_name) +CREATE INDEX idx_sys_company_name ON sys_company (company_name); diff --git a/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/controller/DriverPageController.java b/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/controller/DriverPageController.java new file mode 100644 index 0000000..0aeb130 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/controller/DriverPageController.java @@ -0,0 +1,31 @@ +package com.hotwj.platform.resourceManagement.driverInfo.controller; + +import com.hotwj.platform.resourceManagement.driverInfo.domain.bo.DriverPageBo; +import com.hotwj.platform.resourceManagement.driverInfo.domain.vo.DriverPageVo; +import com.hotwj.platform.resourceManagement.driverInfo.service.IDriverPageService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/driver-info") +@Tag(name = "驾驶员综合信息管理", description = "驾驶员综合信息管理相关接口") +public class DriverPageController { + + private final IDriverPageService driverPageService; + + //@SaCheckPermission("resourceManagement:driverInfo:list") + @GetMapping("/page") + @Operation(summary = "分页查询驾驶员信息", description = "获取驾驶员综合信息列表,包含车辆绑定、驾驶证、从业资格证等关联信息") + public TableDataInfo page(DriverPageBo bo, PageQuery pageQuery) { + return driverPageService.queryPage(pageQuery, bo); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/domain/bo/DriverPageBo.java b/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/domain/bo/DriverPageBo.java new file mode 100644 index 0000000..efeaa5d --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/domain/bo/DriverPageBo.java @@ -0,0 +1,43 @@ +package com.hotwj.platform.resourceManagement.driverInfo.domain.bo; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class DriverPageBo implements Serializable { + /** + * 公司名称 + */ + private String companyName; + + /** + * 驾驶员姓名 + */ + private String driverName; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 手机号码 + */ + private String driverPhone; + + /** + * 身份证号 + */ + private String idCard; + + /** + * 是否在职 + */ + private Boolean isActive; + + /** + * 审核状态:0=待审核 1=审核通过 2=审核驳回 + */ + private Integer auditStatus; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/domain/vo/DriverPageVo.java b/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/domain/vo/DriverPageVo.java new file mode 100644 index 0000000..1ae73d2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/domain/vo/DriverPageVo.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.resourceManagement.driverInfo.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +@Data +public class DriverPageVo implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 所属公司名称 + */ + private String companyName; + + /** + * 驾驶员姓名 + */ + private String driverName; + + /** + * 手机号码(脱敏) + */ + private String phone; + + /** + * 身份证号(脱敏) + */ + private String idCard; + + /** + * 是否在职 + */ + private Boolean isActive; + + /** + * 入职日期 + */ + @JsonFormat(pattern = "yyyy-MM-dd") + private Date hireDate; + + /** + * 现住址 + */ + private String currentAddress; + + /** + * 绑定车辆车牌号 + */ + private String plateNumber; + + /** + * 驾驶证号(脱敏) + */ + private String licenseNumber; + + /** + * 档案编号 + */ + private String licenseFileNo; + + /** + * 准驾车型 + */ + private String allowedVehicleType; + + /** + * 初次领证日期 + */ + @JsonFormat(pattern = "yyyy-MM-dd") + private Date licenseFirstIssueDate; + + /** + * 有效期止 + */ + @JsonFormat(pattern = "yyyy-MM-dd") + private Date licenseExpireDate; + + /** + * 从业资格证号 + */ + private String qualificationNo; + + /** + * 从业资格类别 + */ + private String qualificationType; + + /** + * 初次领证日期(从业资格) + */ + @JsonFormat(pattern = "yyyy-MM-dd") + private Date qualificationFirstIssueDate; + + /** + * 有效期止(从业资格) + */ + @JsonFormat(pattern = "yyyy-MM-dd") + private Date qualificationExpireDate; + + /** + * 驾驶证是否长期 0否 1是 + */ + private Long driverLicenseLongTerm; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/mapper/DriverPageMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/mapper/DriverPageMapper.java new file mode 100644 index 0000000..68dd3ed --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/mapper/DriverPageMapper.java @@ -0,0 +1,22 @@ +package com.hotwj.platform.resourceManagement.driverInfo.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.driverInfo.domain.bo.DriverPageBo; +import com.hotwj.platform.resourceManagement.driverInfo.domain.vo.DriverPageVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface DriverPageMapper { + /** + * 分页查询驾驶员综合信息 + * + * @param page 分页对象 + * @param districtCode 行政区划代码 + * @param bo 查询条件 + * @return 驾驶员信息列表 + */ + List selectDriverPage(Page page, @Param("districtCode") String districtCode, @Param("bo") DriverPageBo bo); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/service/IDriverPageService.java b/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/service/IDriverPageService.java new file mode 100644 index 0000000..8706aea --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/service/IDriverPageService.java @@ -0,0 +1,20 @@ +package com.hotwj.platform.resourceManagement.driverInfo.service; + +import com.hotwj.platform.resourceManagement.driverInfo.domain.bo.DriverPageBo; +import com.hotwj.platform.resourceManagement.driverInfo.domain.vo.DriverPageVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * 驾驶员信息综合查询Service接口 + */ +public interface IDriverPageService { + /** + * 分页查询驾驶员信息 + * + * @param pageQuery 分页参数 + * @param bo 查询条件 + * @return 分页结果 + */ + TableDataInfo queryPage(PageQuery pageQuery, DriverPageBo bo); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/service/impl/DriverPageServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/service/impl/DriverPageServiceImpl.java new file mode 100644 index 0000000..20967c1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/driverInfo/service/impl/DriverPageServiceImpl.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.resourceManagement.driverInfo.service.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import com.hotwj.platform.resourceManagement.driverInfo.domain.bo.DriverPageBo; +import com.hotwj.platform.resourceManagement.driverInfo.domain.vo.DriverPageVo; +import com.hotwj.platform.resourceManagement.driverInfo.mapper.DriverPageMapper; +import com.hotwj.platform.resourceManagement.driverInfo.service.IDriverPageService; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.vo.HotGovEnterpriseUnitVo; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.service.IHotGovEnterpriseUnitService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class DriverPageServiceImpl implements IDriverPageService { + + private final DriverPageMapper driverPageMapper; + private final IHotGovEnterpriseUnitService hotGovEnterpriseUnitService; + + @Override + public TableDataInfo queryPage(PageQuery pageQuery, DriverPageBo bo) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return TableDataInfo.build(); + } + HotGovEnterpriseUnitVo unitVo = hotGovEnterpriseUnitService.queryById(loginUser.getCompanyId()); + if (unitVo == null || unitVo.getDistrictCode() == null) { + return TableDataInfo.build(); + } + + Page page = pageQuery.build(); + List list = driverPageMapper.selectDriverPage(page, unitVo.getDistrictCode(), bo); + // 数据脱敏处理 + /* for (DriverPageVo vo : list) { + if (vo.getIdCard() != null) { + vo.setIdCard(DesensitizedUtil.idCardNum(vo.getIdCard(), 4, 4)); + } + if (vo.getPhone() != null) { + vo.setPhone(DesensitizedUtil.mobilePhone(vo.getPhone())); + } + if (vo.getLicenseNumber() != null && vo.getLicenseNumber().length() > 8) { + vo.setLicenseNumber(maskString(vo.getLicenseNumber(), 4, 4)); + } + }*/ + page.setRecords(list); + return TableDataInfo.build(page); + } + + /** + * 自定义脱敏方法 + * + * @param str 原始字符串 + * @param prefix 保留前几位 + * @param suffix 保留后几位 + * @return 脱敏后的字符串 + */ + private String maskString(String str, int prefix, int suffix) { + if (str == null || str.length() <= prefix + suffix) { + return str; + } + StringBuilder sb = new StringBuilder(); + sb.append(str.substring(0, prefix)); + for (int i = 0; i < str.length() - prefix - suffix; i++) { + sb.append("*"); + } + sb.append(str.substring(str.length() - suffix)); + return sb.toString(); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/controller/HotGovEnterpriseUnitController.java b/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/controller/HotGovEnterpriseUnitController.java new file mode 100644 index 0000000..28deb8f --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/controller/HotGovEnterpriseUnitController.java @@ -0,0 +1,128 @@ +package com.hotwj.platform.resourceManagement.govEnterpriseUnit.controller; + +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.bo.HotGovEnterpriseUnitBo; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.vo.HotGovEnterpriseUnitVo; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.service.IHotGovEnterpriseUnitService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 政企单位 + * + * @author shihongwei + * @date 2026-01-12 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/govEnterpriseUnit") +@Tag(name = "政企单位", description = "政企单位管理") +public class HotGovEnterpriseUnitController extends BaseController { + + private final IHotGovEnterpriseUnitService hotGovEnterpriseUnitService; + + /** + * 查询政企单位列表 + */ + //@SaCheckPermission("resourceManagement:govEnterpriseUnit:list") + @GetMapping("/list") + @Operation(summary = "分页查询政企单位列表") + public TableDataInfo list(HotGovEnterpriseUnitBo bo, PageQuery pageQuery) { + return hotGovEnterpriseUnitService.queryPageList(bo, pageQuery); + } + + /** + * 查询可选的上级单位列表 + */ + //@SaCheckPermission("resourceManagement:govEnterpriseUnit:list") + @GetMapping("/listParent") + @Operation(summary = "查询可选的上级单位列表") + public R> listParent(@Parameter(name = "id", description = "当前单位ID", required = false) @RequestParam(required = false) Long id, + @Parameter(name = "unitType", description = "单位类型", required = false) @RequestParam(required = false) Long unitType) { + return R.ok(hotGovEnterpriseUnitService.queryParentList(id, unitType)); + } + + /** + * 导出政企单位列表 + */ + //@SaCheckPermission("resourceManagement:govEnterpriseUnit:export") + @Log(title = "政企单位", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出政企单位列表") + public void export(HotGovEnterpriseUnitBo bo, HttpServletResponse response) { + List list = hotGovEnterpriseUnitService.queryList(bo); + ExcelUtil.exportExcel(list, "政企单位", HotGovEnterpriseUnitVo.class, response); + } + + /** + * 获取政企单位详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:govEnterpriseUnit:query") + @GetMapping("/{id}") + @Operation(summary = "获取政企单位详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotGovEnterpriseUnitService.queryById(id)); + } + + /** + * 新增政企单位 + */ + //@SaCheckPermission("resourceManagement:govEnterpriseUnit:add") + @Log(title = "政企单位", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增政企单位") + public R add(@Validated(AddGroup.class) @RequestBody HotGovEnterpriseUnitBo bo) { + return toAjax(hotGovEnterpriseUnitService.insertByBo(bo)); + } + + /** + * 修改政企单位 + */ + //@SaCheckPermission("resourceManagement:govEnterpriseUnit:edit") + @Log(title = "政企单位", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改政企单位") + public R edit(@Validated(EditGroup.class) @RequestBody HotGovEnterpriseUnitBo bo) { + return toAjax(hotGovEnterpriseUnitService.updateByBo(bo)); + } + + /** + * 删除政企单位 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:govEnterpriseUnit:remove") + @Log(title = "政企单位", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除政企单位") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotGovEnterpriseUnitService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/domain/HotGovEnterpriseUnit.java b/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/domain/HotGovEnterpriseUnit.java new file mode 100644 index 0000000..6f25cb4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/domain/HotGovEnterpriseUnit.java @@ -0,0 +1,99 @@ +package com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 政企单位对象 hot_gov_enterprise_unit + * + * @author shihongwei + * @date 2026-01-12 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_gov_enterprise_unit") +public class HotGovEnterpriseUnit extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 单位类型(1=政企 2=代理商) + */ + private Long unitType; + + /** + * 上级单位ID + */ + private Long parentUnitId; + + /** + * 单位名称 + */ + private String unitName; + + /** + * 省编码 + */ + private String provinceCode; + + /** + * 市编码 + */ + private String cityCode; + + /** + * 区/县编码 + */ + private String districtCode; + + /** + * 联系人 + */ + private String contactName; + + /** + * 联系人电话 + */ + private String contactPhone; + + /** + * 备用联系人 + */ + private String backupContactName; + + /** + * 备用联系人电话 + */ + private String backupContactPhone; + + /** + * 办公地址 + */ + private String officeAddress; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/domain/bo/HotGovEnterpriseUnitBo.java b/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/domain/bo/HotGovEnterpriseUnitBo.java new file mode 100644 index 0000000..4c9de28 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/domain/bo/HotGovEnterpriseUnitBo.java @@ -0,0 +1,104 @@ +package com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.bo; + +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.HotGovEnterpriseUnit; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 政企单位业务对象 hot_gov_enterprise_unit + * + * @author shihongwei + * @date 2026-01-12 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotGovEnterpriseUnit.class, reverseConvertGenerate = false) +public class HotGovEnterpriseUnitBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 单位类型(1=政企 2=代理商) + */ + @NotNull(message = "单位类型(1=政企 2=代理商)不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long unitType; + + /** + * 上级单位ID + */ + private Long parentUnitId; + + /** + * 单位名称 + */ + @NotBlank(message = "单位名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String unitName; + + /** + * 省编码 + */ + @NotBlank(message = "省编码不能为空", groups = {AddGroup.class, EditGroup.class}) + private String provinceCode; + + /** + * 市编码 + */ + @NotBlank(message = "市编码不能为空", groups = {AddGroup.class, EditGroup.class}) + private String cityCode; + + /** + * 区/县编码 + */ + @NotBlank(message = "区/县编码不能为空", groups = {AddGroup.class, EditGroup.class}) + private String districtCode; + + /** + * 联系人 + */ + @NotBlank(message = "联系人不能为空", groups = {AddGroup.class, EditGroup.class}) + private String contactName; + + /** + * 联系人电话 + */ + @NotBlank(message = "联系人电话不能为空", groups = {AddGroup.class, EditGroup.class}) + private String contactPhone; + + /** + * 备用联系人 + */ + private String backupContactName; + + /** + * 备用联系人电话 + */ + private String backupContactPhone; + + /** + * 办公地址 + */ + private String officeAddress; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + private Long createBy; + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/domain/vo/HotGovEnterpriseUnitVo.java b/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/domain/vo/HotGovEnterpriseUnitVo.java new file mode 100644 index 0000000..083b5c8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/domain/vo/HotGovEnterpriseUnitVo.java @@ -0,0 +1,123 @@ +package com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.HotGovEnterpriseUnit; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 政企单位视图对象 hot_gov_enterprise_unit + * + * @author shihongwei + * @date 2026-01-12 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotGovEnterpriseUnit.class) +public class HotGovEnterpriseUnitVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 单位类型(1=政企 2=代理商) + */ + @ExcelProperty(value = "单位类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1==政企,2==代理商") + private Long unitType; + + /** + * 上级单位ID + */ + @ExcelProperty(value = "上级单位ID") + private Long parentUnitId; + + /** + * 上级单位名称 + */ + @ExcelProperty(value = "上级单位名称") + private String parentUnitName; + + /** + * 单位名称 + */ + @ExcelProperty(value = "单位名称") + private String unitName; + + /** + * 省编码 + */ + @ExcelProperty(value = "省编码") + private String provinceCode; + + /** + * 市编码 + */ + @ExcelProperty(value = "市编码") + private String cityCode; + + /** + * 区/县编码 + */ + @ExcelProperty(value = "区/县编码") + private String districtCode; + + /** + * 联系人 + */ + @ExcelProperty(value = "联系人") + private String contactName; + + /** + * 联系人电话 + */ + @ExcelProperty(value = "联系人电话") + private String contactPhone; + + /** + * 备用联系人 + */ + @ExcelProperty(value = "备用联系人") + private String backupContactName; + + /** + * 备用联系人电话 + */ + @ExcelProperty(value = "备用联系人电话") + private String backupContactPhone; + + /** + * 办公地址 + */ + @ExcelProperty(value = "办公地址") + private String officeAddress; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + private Long createBy; + + private String createByName; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/mapper/HotGovEnterpriseUnitMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/mapper/HotGovEnterpriseUnitMapper.java new file mode 100644 index 0000000..2b47a60 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/mapper/HotGovEnterpriseUnitMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.govEnterpriseUnit.mapper; + +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.HotGovEnterpriseUnit; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.vo.HotGovEnterpriseUnitVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 政企单位Mapper接口 + * + * @author shihongwei + * @date 2026-01-12 + */ +@Mapper +public interface HotGovEnterpriseUnitMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/service/IHotGovEnterpriseUnitService.java b/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/service/IHotGovEnterpriseUnitService.java new file mode 100644 index 0000000..7e73d70 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/service/IHotGovEnterpriseUnitService.java @@ -0,0 +1,77 @@ +package com.hotwj.platform.resourceManagement.govEnterpriseUnit.service; + +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.bo.HotGovEnterpriseUnitBo; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.vo.HotGovEnterpriseUnitVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 政企单位Service接口 + * + * @author shihongwei + * @date 2026-01-12 + */ +public interface IHotGovEnterpriseUnitService { + + /** + * 查询政企单位 + * + * @param id 主键 + * @return 政企单位 + */ + HotGovEnterpriseUnitVo queryById(Long id); + + /** + * 分页查询政企单位列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 政企单位分页列表 + */ + TableDataInfo queryPageList(HotGovEnterpriseUnitBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的政企单位列表 + * + * @param bo 查询条件 + * @return 政企单位列表 + */ + List queryList(HotGovEnterpriseUnitBo bo); + + /** + * 查询可选的上级单位列表(排除自身及子孙节点) + * + * @param id 当前单位ID + * @param unitType 单位类型 + * @return 可选的上级单位列表 + */ + List queryParentList(Long id, Long unitType); + + /** + * 新增政企单位 + * + * @param bo 政企单位 + * @return 是否新增成功 + */ + Boolean insertByBo(HotGovEnterpriseUnitBo bo); + + /** + * 修改政企单位 + * + * @param bo 政企单位 + * @return 是否修改成功 + */ + Boolean updateByBo(HotGovEnterpriseUnitBo bo); + + /** + * 校验并批量删除政企单位信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/service/impl/HotGovEnterpriseUnitServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/service/impl/HotGovEnterpriseUnitServiceImpl.java new file mode 100644 index 0000000..7aab28a --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/govEnterpriseUnit/service/impl/HotGovEnterpriseUnitServiceImpl.java @@ -0,0 +1,231 @@ +package com.hotwj.platform.resourceManagement.govEnterpriseUnit.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.HotGovEnterpriseUnit; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.bo.HotGovEnterpriseUnitBo; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.vo.HotGovEnterpriseUnitVo; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.mapper.HotGovEnterpriseUnitMapper; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.service.IHotGovEnterpriseUnitService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 政企单位Service业务层处理 + * + * @author shihongwei + * @date 2026-01-12 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotGovEnterpriseUnitServiceImpl implements IHotGovEnterpriseUnitService { + + private final HotGovEnterpriseUnitMapper baseMapper; + + /** + * 查询政企单位 + * + * @param id 主键 + * @return 政企单位 + */ + @Override + public HotGovEnterpriseUnitVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询政企单位列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 政企单位分页列表 + */ + @Override + public TableDataInfo queryPageList(HotGovEnterpriseUnitBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + + // 填充上级单位名称 + if (!result.getRecords().isEmpty()) { + // 收集所有 parentUnitId + java.util.Set parentIds = result.getRecords().stream() + .map(HotGovEnterpriseUnitVo::getParentUnitId) + .filter(id -> id != null && id != 0) + .collect(java.util.stream.Collectors.toSet()); + + if (!parentIds.isEmpty()) { + // 批量查询父级单位 + List parents = baseMapper.selectBatchIds(parentIds); + Map parentNameMap = parents.stream() + .collect(java.util.stream.Collectors.toMap(HotGovEnterpriseUnit::getId, HotGovEnterpriseUnit::getUnitName)); + + // 设置 parentUnitName + for (HotGovEnterpriseUnitVo vo : result.getRecords()) { + if (vo.getParentUnitId() != null && parentNameMap.containsKey(vo.getParentUnitId())) { + vo.setParentUnitName(parentNameMap.get(vo.getParentUnitId())); + } + } + } + } + + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的政企单位列表 + * + * @param bo 查询条件 + * @return 政企单位列表 + */ + @Override + public List queryList(HotGovEnterpriseUnitBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + /** + * 查询可选的上级单位列表(排除自身及子孙节点) + * + * @param id 当前单位ID + * @param unitType 单位类型 + * @return 可选的上级单位列表 + */ + @Override + public List queryParentList(Long id, Long unitType) { + // 查询所有单位 + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + List allUnits = baseMapper.selectVoList(wrapper); + + // 排除自身及子孙节点 + java.util.Set excludeIds = new java.util.HashSet<>(); + if (id != null) { + excludeIds.add(id); + findChildren(allUnits, id, excludeIds); + } + + // 过滤 + return allUnits.stream() + .filter(unit -> !excludeIds.contains(unit.getId())) + .filter(unit -> unitType == null || unitType.equals(unit.getUnitType())) + .collect(java.util.stream.Collectors.toList()); + } + + private void findChildren(List allUnits, Long parentId, java.util.Set excludeIds) { + for (HotGovEnterpriseUnitVo unit : allUnits) { + if (parentId.equals(unit.getParentUnitId()) && !excludeIds.contains(unit.getId())) { + excludeIds.add(unit.getId()); + findChildren(allUnits, unit.getId(), excludeIds); + } + } + } + + private LambdaQueryWrapper buildQueryWrapper(HotGovEnterpriseUnitBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotGovEnterpriseUnit::getId); + lqw.eq(bo.getUnitType() != null, HotGovEnterpriseUnit::getUnitType, bo.getUnitType()); + lqw.eq(bo.getParentUnitId() != null, HotGovEnterpriseUnit::getParentUnitId, bo.getParentUnitId()); + lqw.like(StringUtils.isNotBlank(bo.getUnitName()), HotGovEnterpriseUnit::getUnitName, bo.getUnitName()); + lqw.eq(StringUtils.isNotBlank(bo.getProvinceCode()), HotGovEnterpriseUnit::getProvinceCode, bo.getProvinceCode()); + lqw.eq(StringUtils.isNotBlank(bo.getCityCode()), HotGovEnterpriseUnit::getCityCode, bo.getCityCode()); + lqw.eq(StringUtils.isNotBlank(bo.getDistrictCode()), HotGovEnterpriseUnit::getDistrictCode, bo.getDistrictCode()); + lqw.like(StringUtils.isNotBlank(bo.getContactName()), HotGovEnterpriseUnit::getContactName, bo.getContactName()); + lqw.like(StringUtils.isNotBlank(bo.getContactPhone()), HotGovEnterpriseUnit::getContactPhone, bo.getContactPhone()); + lqw.like(StringUtils.isNotBlank(bo.getBackupContactName()), HotGovEnterpriseUnit::getBackupContactName, bo.getBackupContactName()); + lqw.like(StringUtils.isNotBlank(bo.getBackupContactPhone()), HotGovEnterpriseUnit::getBackupContactPhone, bo.getBackupContactPhone()); + lqw.eq(StringUtils.isNotBlank(bo.getOfficeAddress()), HotGovEnterpriseUnit::getOfficeAddress, bo.getOfficeAddress()); + lqw.eq(bo.getIsDeleted() != null, HotGovEnterpriseUnit::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增政企单位 + * + * @param bo 政企单位 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotGovEnterpriseUnitBo bo) { + HotGovEnterpriseUnit add = MapstructUtils.convert(bo, HotGovEnterpriseUnit.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改政企单位 + * + * @param bo 政企单位 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotGovEnterpriseUnitBo bo) { + HotGovEnterpriseUnit update = MapstructUtils.convert(bo, HotGovEnterpriseUnit.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotGovEnterpriseUnit entity) { + // 校验上级单位不能是自己 + if (entity.getParentUnitId() != null && entity.getId() != null && entity.getParentUnitId().equals(entity.getId())) { + throw new ServiceException("上级单位不能是自己"); + } + + // 校验上级单位不能包含本单位,产生循环引用 + if (entity.getParentUnitId() != null) { + checkRecursion(entity.getId(), entity.getParentUnitId()); + } + } + + private void checkRecursion(Long currentId, Long parentId) { + Long nextParentId = parentId; + int depth = 0; + while (nextParentId != null && nextParentId != 0L) { + if (depth > 100) break; // 防止死循环 + + if (currentId != null && nextParentId.equals(currentId)) { + throw new ServiceException("上级单位不能包含本单位,产生循环引用"); + } + + HotGovEnterpriseUnit parent = baseMapper.selectById(nextParentId); + if (parent == null) { + break; + } + nextParentId = parent.getParentUnitId(); + depth++; + } + } + + /** + * 校验并批量删除政企单位信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/package-info.java b/src/main/java/com/hotwj/platform/resourceManagement/package-info.java new file mode 100644 index 0000000..7565090 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/package-info.java @@ -0,0 +1,6 @@ +/** + * 资源管理模块 + * + * @author shihongwei + */ +package com.hotwj.platform.resourceManagement; diff --git a/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/controller/HotPersonnelConfigController.java b/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/controller/HotPersonnelConfigController.java new file mode 100644 index 0000000..c15d625 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/controller/HotPersonnelConfigController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.resourceManagement.personnelConfig.controller; + +import com.hotwj.platform.resourceManagement.personnelConfig.domain.bo.HotPersonnelConfigBo; +import com.hotwj.platform.resourceManagement.personnelConfig.domain.vo.HotPersonnelConfigVo; +import com.hotwj.platform.resourceManagement.personnelConfig.service.IHotPersonnelConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 人员配置 + * + * @author shihongwei + * @date 2026-01-12 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/personnelConfig") +@Tag(name = "人员配置", description = "人员配置管理") +public class HotPersonnelConfigController extends BaseController { + + private final IHotPersonnelConfigService hotPersonnelConfigService; + + /** + * 查询人员配置列表 + */ + //@SaCheckPermission("resourceManagement:personnelConfig:list") + @GetMapping("/list") + @Operation(summary = "分页查询人员配置列表") + public TableDataInfo list(HotPersonnelConfigBo bo, PageQuery pageQuery) { + return hotPersonnelConfigService.queryPageList(bo, pageQuery); + } + + /** + * 导出人员配置列表 + */ + //@SaCheckPermission("resourceManagement:personnelConfig:export") + @Log(title = "人员配置", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出人员配置列表") + public void export(HotPersonnelConfigBo bo, HttpServletResponse response) { + List list = hotPersonnelConfigService.queryList(bo); + ExcelUtil.exportExcel(list, "人员配置", HotPersonnelConfigVo.class, response); + } + + /** + * 获取人员配置详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:personnelConfig:query") + @GetMapping("/{id}") + @Operation(summary = "获取人员配置详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotPersonnelConfigService.queryById(id)); + } + + /** + * 新增人员配置 + */ + //@SaCheckPermission("resourceManagement:personnelConfig:add") + @Log(title = "人员配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增人员配置") + public R add(@Validated(AddGroup.class) @RequestBody HotPersonnelConfigBo bo) { + return toAjax(hotPersonnelConfigService.insertByBo(bo)); + } + + /** + * 修改人员配置 + */ + //@SaCheckPermission("resourceManagement:personnelConfig:edit") + @Log(title = "人员配置", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改人员配置") + public R edit(@Validated(EditGroup.class) @RequestBody HotPersonnelConfigBo bo) { + return toAjax(hotPersonnelConfigService.updateByBo(bo)); + } + + /** + * 删除人员配置 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:personnelConfig:remove") + @Log(title = "人员配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除人员配置") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotPersonnelConfigService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/domain/HotPersonnelConfig.java b/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/domain/HotPersonnelConfig.java new file mode 100644 index 0000000..75c57c7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/domain/HotPersonnelConfig.java @@ -0,0 +1,79 @@ +package com.hotwj.platform.resourceManagement.personnelConfig.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 人员配置对象 hot_personnel_config + * + * @author shihongwei + * @date 2026-01-12 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_personnel_config") +public class HotPersonnelConfig extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 单位ID + */ + private Long unitId; + + /** + * 人员名称 + */ + private String staffName; + + /** + * 联系电话(默认为登录账号) + */ + private String mobile; + + /** + * 职位身份 + */ + private String positionName; + + /** + * 权限类型: 1=超级管理员 + */ + private Long permissionType; + + /** + * 登录账号 + */ + private String loginAccount; + + /** + * 登录密码 + */ + private String loginPassword; + + /** + * 是否启用: 1=启用, 0=禁用 + */ + private Long isEnabled; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/domain/bo/HotPersonnelConfigBo.java b/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/domain/bo/HotPersonnelConfigBo.java new file mode 100644 index 0000000..ac6ece7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/domain/bo/HotPersonnelConfigBo.java @@ -0,0 +1,83 @@ +package com.hotwj.platform.resourceManagement.personnelConfig.domain.bo; + +import com.hotwj.platform.resourceManagement.personnelConfig.domain.HotPersonnelConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 人员配置业务对象 hot_personnel_config + * + * @author shihongwei + * @date 2026-01-12 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotPersonnelConfig.class, reverseConvertGenerate = false) +public class HotPersonnelConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 单位ID + */ + @NotNull(message = "单位ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long unitId; + + /** + * 人员名称 + */ + @NotBlank(message = "人员名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String staffName; + + /** + * 联系电话(默认为登录账号) + */ + @NotBlank(message = "联系电话(默认为登录账号)不能为空", groups = {AddGroup.class, EditGroup.class}) + private String mobile; + + /** + * 职位身份 + */ + private String positionName; + + /** + * 权限类型: 1=超级管理员 + */ + @NotNull(message = "权限类型: 1=超级管理员不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long permissionType; + + /** + * 登录账号 + */ + @NotBlank(message = "登录账号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String loginAccount; + + /** + * 登录密码 + */ + @NotBlank(message = "登录密码不能为空", groups = {AddGroup.class, EditGroup.class}) + private String loginPassword; + + /** + * 是否启用: 1=启用, 0=禁用 + */ + @NotNull(message = "是否启用: 1=启用, 0=禁用不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isEnabled; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/domain/vo/HotPersonnelConfigVo.java b/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/domain/vo/HotPersonnelConfigVo.java new file mode 100644 index 0000000..57fa128 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/domain/vo/HotPersonnelConfigVo.java @@ -0,0 +1,105 @@ +package com.hotwj.platform.resourceManagement.personnelConfig.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.personnelConfig.domain.HotPersonnelConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 人员配置视图对象 hot_personnel_config + * + * @author shihongwei + * @date 2026-01-12 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotPersonnelConfig.class) +public class HotPersonnelConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 单位ID + */ + @ExcelProperty(value = "单位ID") + private Long unitId; + + /** + * 人员名称 + */ + @ExcelProperty(value = "人员名称") + private String staffName; + + /** + * 联系电话(默认为登录账号) + */ + @ExcelProperty(value = "联系电话(默认为登录账号)") + private String mobile; + + /** + * 职位身份 + */ + @ExcelProperty(value = "职位身份") + private String positionName; + + /** + * 权限类型: 1=超级管理员 + */ + @ExcelProperty(value = "权限类型: 1=超级管理员") + private Long permissionType; + + /** + * 登录账号 + */ + @ExcelProperty(value = "登录账号") + private String loginAccount; + + /** + * 登录密码 + */ + @ExcelProperty(value = "登录密码") + private String loginPassword; + + /** + * 是否启用: 1=启用, 0=禁用 + */ + @ExcelProperty(value = "是否启用: 1=启用, 0=禁用") + private Long isEnabled; + + /** + * 创建者 + */ + @ExcelProperty(value = "创建者") + private Long createBy; + + private String createByName; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/mapper/HotPersonnelConfigMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/mapper/HotPersonnelConfigMapper.java new file mode 100644 index 0000000..3943524 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/mapper/HotPersonnelConfigMapper.java @@ -0,0 +1,27 @@ +package com.hotwj.platform.resourceManagement.personnelConfig.mapper; + +import com.hotwj.platform.resourceManagement.personnelConfig.domain.HotPersonnelConfig; +import com.hotwj.platform.resourceManagement.personnelConfig.domain.vo.HotPersonnelConfigVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 人员配置Mapper接口 + * + * @author shihongwei + * @date 2026-01-12 + */ +@Mapper +public interface HotPersonnelConfigMapper extends BaseMapperPlus { + + /** + * 检查当前端口下是否存在相同的手机号(通过联查单位表判断端口类型) + * + * @param mobile 手机号 + * @param unitType 单位类型(1=政企 2=代理商),即区分端口 + * @param excludeId 排除的主键ID(修改时使用) + * @return 是否存在 + */ + Long checkPhoneExist(@Param("mobile") String mobile, @Param("unitType") Long unitType, @Param("excludeId") Long excludeId); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/service/IHotPersonnelConfigService.java b/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/service/IHotPersonnelConfigService.java new file mode 100644 index 0000000..cfd6f28 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/service/IHotPersonnelConfigService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.resourceManagement.personnelConfig.service; + +import com.hotwj.platform.resourceManagement.personnelConfig.domain.bo.HotPersonnelConfigBo; +import com.hotwj.platform.resourceManagement.personnelConfig.domain.vo.HotPersonnelConfigVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 人员配置Service接口 + * + * @author shihongwei + * @date 2026-01-12 + */ +public interface IHotPersonnelConfigService { + + /** + * 查询人员配置 + * + * @param id 主键 + * @return 人员配置 + */ + HotPersonnelConfigVo queryById(Long id); + + /** + * 分页查询人员配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 人员配置分页列表 + */ + TableDataInfo queryPageList(HotPersonnelConfigBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的人员配置列表 + * + * @param bo 查询条件 + * @return 人员配置列表 + */ + List queryList(HotPersonnelConfigBo bo); + + /** + * 新增人员配置 + * + * @param bo 人员配置 + * @return 是否新增成功 + */ + Boolean insertByBo(HotPersonnelConfigBo bo); + + /** + * 修改人员配置 + * + * @param bo 人员配置 + * @return 是否修改成功 + */ + Boolean updateByBo(HotPersonnelConfigBo bo); + + /** + * 校验并批量删除人员配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/service/impl/HotPersonnelConfigServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/service/impl/HotPersonnelConfigServiceImpl.java new file mode 100644 index 0000000..d335c81 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/personnelConfig/service/impl/HotPersonnelConfigServiceImpl.java @@ -0,0 +1,367 @@ +package com.hotwj.platform.resourceManagement.personnelConfig.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.crypto.digest.BCrypt; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.common.utils.PasswordUtils; +import com.hotwj.platform.resourceManagement.company.domain.SysUserLoginPort; +import com.hotwj.platform.resourceManagement.company.mapper.SysUserLoginPortMapper; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.HotGovEnterpriseUnit; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.mapper.HotGovEnterpriseUnitMapper; +import com.hotwj.platform.resourceManagement.personnelConfig.domain.HotPersonnelConfig; +import com.hotwj.platform.resourceManagement.personnelConfig.domain.bo.HotPersonnelConfigBo; +import com.hotwj.platform.resourceManagement.personnelConfig.domain.vo.HotPersonnelConfigVo; +import com.hotwj.platform.resourceManagement.personnelConfig.mapper.HotPersonnelConfigMapper; +import com.hotwj.platform.resourceManagement.personnelConfig.service.IHotPersonnelConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.SysUserRole; +import org.dromara.system.mapper.SysUserMapper; +import org.dromara.system.mapper.SysUserRoleMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService.AGENT_ROLE_ID; +import static com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService.GOVERNMENT_ROLE_ID; + +/** + * 人员配置Service业务层处理 + * + * @author shihongwei + * @date 2026-01-12 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotPersonnelConfigServiceImpl implements IHotPersonnelConfigService { + + private final HotPersonnelConfigMapper baseMapper; + private final SysUserMapper sysUserMapper; + private final SysUserRoleMapper sysUserRoleMapper; + private final SysUserLoginPortMapper sysUserLoginPortMapper; + private final HotGovEnterpriseUnitMapper enterpriseUnitMapper; + + /** + * 查询人员配置 + * + * @param id 主键 + * @return 人员配置 + */ + @Override + public HotPersonnelConfigVo queryById(Long id) { + HotPersonnelConfigVo vo = baseMapper.selectVoById(id); + if (vo != null) { + vo.setLoginPassword(null); + } + return vo; + } + + /** + * 分页查询人员配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 人员配置分页列表 + */ + @Override + public TableDataInfo queryPageList(HotPersonnelConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的人员配置列表 + * + * @param bo 查询条件 + * @return 人员配置列表 + */ + @Override + public List queryList(HotPersonnelConfigBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotPersonnelConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotPersonnelConfig::getId); + lqw.eq(bo.getUnitId() != null, HotPersonnelConfig::getUnitId, bo.getUnitId()); + lqw.like(StringUtils.isNotBlank(bo.getStaffName()), HotPersonnelConfig::getStaffName, bo.getStaffName()); + lqw.like(StringUtils.isNotBlank(bo.getMobile()), HotPersonnelConfig::getMobile, bo.getMobile()); + lqw.like(StringUtils.isNotBlank(bo.getPositionName()), HotPersonnelConfig::getPositionName, bo.getPositionName()); + lqw.eq(bo.getPermissionType() != null, HotPersonnelConfig::getPermissionType, bo.getPermissionType()); + lqw.eq(StringUtils.isNotBlank(bo.getLoginAccount()), HotPersonnelConfig::getLoginAccount, bo.getLoginAccount()); + lqw.eq(StringUtils.isNotBlank(bo.getLoginPassword()), HotPersonnelConfig::getLoginPassword, bo.getLoginPassword()); + lqw.eq(bo.getIsEnabled() != null, HotPersonnelConfig::getIsEnabled, bo.getIsEnabled()); + lqw.eq(bo.getCreateBy() != null, HotPersonnelConfig::getCreateBy, bo.getCreateBy()); + lqw.eq(bo.getCreateTime() != null, HotPersonnelConfig::getCreateTime, bo.getCreateTime()); + lqw.eq(bo.getIsDeleted() != null, HotPersonnelConfig::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增人员配置 + * + * @param bo 人员配置 + * @return 是否新增成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(HotPersonnelConfigBo bo) { + HotPersonnelConfig add = MapstructUtils.convert(bo, HotPersonnelConfig.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + syncUser(bo); + } + return flag; + } + + /** + * 修改人员配置 + * + * @param bo 人员配置 + * @return 是否修改成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(HotPersonnelConfigBo bo) { + HotPersonnelConfig update = MapstructUtils.convert(bo, HotPersonnelConfig.class); + validEntityBeforeSave(update); + boolean flag = baseMapper.updateById(update) > 0; + if (flag) { + syncUser(bo); + } + return flag; + } + + /** + * 同步用户信息 + */ + private void syncUser(HotPersonnelConfigBo bo) { + Long unitId = bo.getUnitId(); + String staffName = bo.getStaffName(); + String mobile = bo.getMobile(); + String loginPassword = bo.getLoginPassword(); + Long isEnabled = bo.getIsEnabled(); + Long isDeleted = bo.getIsDeleted(); + + HotGovEnterpriseUnit enterpriseUnit = enterpriseUnitMapper.selectById(unitId); + + if (enterpriseUnit == null) { + throw new IllegalArgumentException("当前单位不存在"); + } + + String loginPort = ISysUserLoginPortService.GOVERNMENT_PORT; + Long roleId = GOVERNMENT_ROLE_ID; + if (enterpriseUnit.getUnitType() == 2) { + loginPort = ISysUserLoginPortService.AGENT_PORT; + roleId = AGENT_ROLE_ID; + } + + // 1. 查询用户是否存在 + SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper().eq(SysUser::getPhonenumber, mobile)); + if (sysUser == null) { + // 不存在则创建 + if (isDeleted != null && isDeleted == 1) { + // 如果是删除状态,且用户不存在,则无需创建 + return; + } + sysUser = new SysUser(); + sysUser.setPhonenumber(bo.getMobile()); + sysUser.setPassword(BCrypt.hashpw(loginPassword)); + sysUser.setUserType("sys_user"); + sysUserMapper.insert(sysUser); + } else { + // 存在则更新 + // 校验:手机号在当前端口不能重复 + assertPhoneNotDuplicateInPort(mobile, loginPort, unitId, sysUser.getUserId()); + sysUser.setPhonenumber(bo.getMobile()); + if (StringUtils.isNotBlank(loginPassword)) { + sysUser.setPassword(BCrypt.hashpw(loginPassword)); + } + sysUserMapper.updateById(sysUser); + } + + Long userId = sysUser.getUserId(); + + // 2. 绑定政府/代理端登录端口(通过端口状态控制启用/禁用) + List portList = sysUserLoginPortMapper.selectList(new LambdaQueryWrapper() + .eq(SysUserLoginPort::getUserId, userId) + .eq(SysUserLoginPort::getCompanyId, unitId) + .eq(SysUserLoginPort::getLoginPort, loginPort)); + + if (isDeleted != null && isDeleted == 1) { + // 删除:禁用当前端口 + for (SysUserLoginPort port : portList) { + port.setStatus(0); + sysUserLoginPortMapper.updateById(port); + } + return; + } + + boolean enablePort = isEnabled == null || isEnabled == 1; + + if (CollUtil.isEmpty(portList)) { + // 没有端口记录则创建一条 + SysUserLoginPort port = new SysUserLoginPort(); + port.setUserId(userId); + port.setUsername(staffName); + port.setCompanyId(unitId); + port.setLoginPort(loginPort); + port.setIsDefault(0); + port.setStatus(enablePort ? 1 : 0); + port.setSubPassword(PasswordUtils.createPassword(loginPassword)); + sysUserLoginPortMapper.insert(port); + } else { + // 修改当前端口的用户名及启用状态 + for (SysUserLoginPort port : portList) { + port.setUsername(staffName); + port.setStatus(enablePort ? 1 : 0); + port.setSubPassword(PasswordUtils.createPassword(loginPassword)); + sysUserLoginPortMapper.updateById(port); + } + } + + // 3. 绑定业务角色 (GOVERNMENT_ROLE_ID / AGENT_ROLE_ID) + + // 先清除该用户在该单位下的其他角色(根据业务需求,这里参考安全管理员逻辑:先解除其他角色,再关联当前角色) + sysUserRoleMapper.delete(new LambdaQueryWrapper() + .eq(SysUserRole::getUserId, userId) + .eq(SysUserRole::getRoleId, roleId) + .eq(SysUserRole::getCompanyId, unitId) + .eq(SysUserRole::getLoginPort, loginPort)); + + if (enablePort) { + SysUserRole userRole = new SysUserRole(); + userRole.setUserId(userId); + userRole.setRoleId(roleId); + userRole.setCompanyId(unitId); + userRole.setLoginPort(loginPort); + sysUserRoleMapper.insert(userRole); + } + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotPersonnelConfig entity) { + if (StringUtils.isNotBlank(entity.getMobile())) { + // 1. 推断当前单位对应的单位类型(即登录端口) + HotGovEnterpriseUnit unit = enterpriseUnitMapper.selectById(entity.getUnitId()); + if (unit == null) { + throw new ServiceException("所属单位不存在"); + } + Long unitType = unit.getUnitType(); // 1=政企(政府端), 2=代理商(代理商端) + + // 2. 通过 SQL 联查判断该端口下手机号是否重复 + Long count = baseMapper.checkPhoneExist(entity.getMobile(), unitType, entity.getId()); + + if (count != null && count > 0) { + String portName = (unitType == 2) ? "代理商端" : "政府监管端"; + throw new ServiceException("该手机号已在" + portName + "注册(可能在其他单位),请勿重复添加"); + } + } + } + + /** + * 校验并批量删除人员配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + // 1. 获取要删除的人员配置信息 + List list = baseMapper.selectBatchIds(ids); + if (CollUtil.isNotEmpty(list)) { + for (HotPersonnelConfig config : list) { + removeUserByConfig(config); + } + } + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 校验手机号在当前端口是否重复(排除当前用户) + */ + private void assertPhoneNotDuplicateInPort(String mobile, String loginPort, Long unitId, Long excludeUserId) { + if (StringUtils.isBlank(mobile) || unitId == null || StringUtils.isBlank(loginPort)) { + return; + } + List users = sysUserMapper.selectList(new LambdaQueryWrapper() + .eq(SysUser::getPhonenumber, mobile)); + if (CollUtil.isEmpty(users)) { + return; + } + for (SysUser u : users) { + if (u.getUserId() != null && !u.getUserId().equals(excludeUserId)) { + Long count = sysUserLoginPortMapper.selectCount(new LambdaQueryWrapper() + .eq(SysUserLoginPort::getUserId, u.getUserId()) + .eq(SysUserLoginPort::getCompanyId, unitId) + .eq(SysUserLoginPort::getLoginPort, loginPort) + .eq(SysUserLoginPort::getStatus, 1)); + if (count != null && count > 0) { + throw new org.dromara.common.core.exception.ServiceException("该手机号已在当前端口被使用"); + } + } + } + } + + private void removeUserByConfig(HotPersonnelConfig config) { + String loginAccount = config.getLoginAccount(); + Long unitId = config.getUnitId(); + if (StringUtils.isBlank(loginAccount) || unitId == null) { + return; + } + + // 1. 查询对应的 SysUser + SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper().eq(SysUser::getUserName, loginAccount)); + if (sysUser == null) { + return; + } + Long userId = sysUser.getUserId(); + + // 2. 删除该单位下的角色绑定 (SysUserRole) + sysUserRoleMapper.delete(new LambdaQueryWrapper() + .eq(SysUserRole::getUserId, userId) + .eq(SysUserRole::getCompanyId, unitId)); + + // 3. 删除该单位下的端口绑定 (SysUserCompanyRole) + sysUserLoginPortMapper.delete(new LambdaQueryWrapper() + .eq(SysUserLoginPort::getUserId, userId) + .eq(SysUserLoginPort::getCompanyId, unitId)); + + // 4. 检查该用户是否还有其他绑定,如果没有则删除用户 + // 检查是否还有其他端口 + Long portCount = sysUserLoginPortMapper.selectCount(new LambdaQueryWrapper() + .eq(SysUserLoginPort::getUserId, userId)); + + // 检查是否还有其他角色 (通常端口和角色是成对的,但为了保险起见检查两者,或者只检查端口即可) + // 这里只检查端口即可,因为没有端口就无法登录 + if (portCount == 0) { + sysUserMapper.deleteById(userId); + log.info("用户 {} 已无关联企业端口,执行删除", loginAccount); + } + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/safeOrganStructure/OrganStructureController.java b/src/main/java/com/hotwj/platform/resourceManagement/safeOrganStructure/OrganStructureController.java new file mode 100644 index 0000000..9775371 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/safeOrganStructure/OrganStructureController.java @@ -0,0 +1,37 @@ +package com.hotwj.platform.resourceManagement.safeOrganStructure; + +import cn.hutool.core.lang.tree.Tree; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.bo.HotCompanySafetyManagerBo; +import com.hotwj.platform.resourceManagement.companySafetyManager.service.IHotCompanySafetyManagerService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 安全组织架构 + * + * @author shihongwei + * @date 2025-12-15 + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/safeOrganStrucure") +@Tag(name = "安全组织架构", description = "安全组织架构模块") +public class OrganStructureController { + + private final IHotCompanySafetyManagerService hotCompanySafetyManagerService; + + /** + * 系统管理员结构图(树形) + */ + //@SaCheckPermission("resourceManagement:safeOrganStrucure:systemAdminTree") + @GetMapping("/systemAdminTree") + @Operation(summary = "查询系统管理员组织树(支持按部门筛选)") + public R> systemAdminTree(HotCompanySafetyManagerBo bo) { + return R.ok(hotCompanySafetyManagerService.buildSystemAdminTree(bo)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/temporary/controller/HotTemporaryController.java b/src/main/java/com/hotwj/platform/resourceManagement/temporary/controller/HotTemporaryController.java new file mode 100644 index 0000000..e2557d8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/temporary/controller/HotTemporaryController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.resourceManagement.temporary.controller; + +import com.hotwj.platform.resourceManagement.temporary.domain.bo.HotTemporaryBo; +import com.hotwj.platform.resourceManagement.temporary.domain.vo.HotTemporaryVo; +import com.hotwj.platform.resourceManagement.temporary.service.IHotTemporaryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 前端缓存信息 + * + * @author shihongwei + * @date 2026-03-02 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/temporary") +@Tag(name = "前端缓存信息", description = "前端缓存信息管理") +public class HotTemporaryController extends BaseController { + + private final IHotTemporaryService hotTemporaryService; + + /** + * 查询前端缓存信息列表 + */ + //@SaCheckPermission("resourceManagement:temporary:list") + @GetMapping("/list") + @Operation(summary = "分页查询前端缓存信息列表") + public TableDataInfo list(HotTemporaryBo bo, PageQuery pageQuery) { + return hotTemporaryService.queryPageList(bo, pageQuery); + } + + /** + * 导出前端缓存信息列表 + */ + //@SaCheckPermission("resourceManagement:temporary:export") + @Log(title = "前端缓存信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出前端缓存信息列表") + public void export(HotTemporaryBo bo, HttpServletResponse response) { + List list = hotTemporaryService.queryList(bo); + ExcelUtil.exportExcel(list, "前端缓存信息", HotTemporaryVo.class, response); + } + + /** + * 获取前端缓存信息详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:temporary:query") + @GetMapping("/{id}") + @Operation(summary = "获取前端缓存信息详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable String id) { + return R.ok(hotTemporaryService.queryById(id)); + } + + /** + * 新增前端缓存信息 + */ + //@SaCheckPermission("resourceManagement:temporary:add") + @Log(title = "前端缓存信息", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增前端缓存信息") + public R add(@Validated(AddGroup.class) @RequestBody HotTemporaryBo bo) { + return toAjax(hotTemporaryService.insertByBo(bo)); + } + + /** + * 修改前端缓存信息 + */ + //@SaCheckPermission("resourceManagement:temporary:edit") + @Log(title = "前端缓存信息", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改前端缓存信息") + public R edit(@Validated(EditGroup.class) @RequestBody HotTemporaryBo bo) { + return toAjax(hotTemporaryService.updateByBo(bo)); + } + + /** + * 删除前端缓存信息 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:temporary:remove") + @Log(title = "前端缓存信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除前端缓存信息") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable String[] ids) { + return toAjax(hotTemporaryService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/temporary/domain/HotTemporary.java b/src/main/java/com/hotwj/platform/resourceManagement/temporary/domain/HotTemporary.java new file mode 100644 index 0000000..05484da --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/temporary/domain/HotTemporary.java @@ -0,0 +1,54 @@ +package com.hotwj.platform.resourceManagement.temporary.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 前端缓存信息对象 hot_temporary + * + * @author shihongwei + * @date 2026-03-02 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_temporary") +public class HotTemporary extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private String id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 类型 + */ + private String temporaryType; + + /** + * json字符串 + */ + private String jsonStr; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/temporary/domain/bo/HotTemporaryBo.java b/src/main/java/com/hotwj/platform/resourceManagement/temporary/domain/bo/HotTemporaryBo.java new file mode 100644 index 0000000..88d0822 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/temporary/domain/bo/HotTemporaryBo.java @@ -0,0 +1,49 @@ +package com.hotwj.platform.resourceManagement.temporary.domain.bo; + +import com.hotwj.platform.resourceManagement.temporary.domain.HotTemporary; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 前端缓存信息业务对象 hot_temporary + * + * @author shihongwei + * @date 2026-03-02 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotTemporary.class, reverseConvertGenerate = false) +public class HotTemporaryBo extends BaseEntity { + + /** + * 主键 + */ + @NotBlank(message = "主键不能为空", groups = {EditGroup.class}) + private String id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 类型 + */ + private String temporaryType; + + /** + * json字符串 + */ + private String jsonStr; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/temporary/domain/vo/HotTemporaryVo.java b/src/main/java/com/hotwj/platform/resourceManagement/temporary/domain/vo/HotTemporaryVo.java new file mode 100644 index 0000000..4163868 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/temporary/domain/vo/HotTemporaryVo.java @@ -0,0 +1,70 @@ +package com.hotwj.platform.resourceManagement.temporary.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.temporary.domain.HotTemporary; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 前端缓存信息视图对象 hot_temporary + * + * @author shihongwei + * @date 2026-03-02 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotTemporary.class) +public class HotTemporaryVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private String id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 类型 + */ + @ExcelProperty(value = "类型") + private String temporaryType; + + /** + * json字符串 + */ + @ExcelProperty(value = "json字符串") + private String jsonStr; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 创建人显示名称 + */ + @ExcelProperty(value = "创建人显示名称") + private String createByName; + + /** + * 最后修改人显示名称 + */ + @ExcelProperty(value = "最后修改人显示名称") + private String updateByName; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/temporary/mapper/HotTemporaryMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/temporary/mapper/HotTemporaryMapper.java new file mode 100644 index 0000000..c7302af --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/temporary/mapper/HotTemporaryMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.temporary.mapper; + +import com.hotwj.platform.resourceManagement.temporary.domain.HotTemporary; +import com.hotwj.platform.resourceManagement.temporary.domain.vo.HotTemporaryVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 前端缓存信息Mapper接口 + * + * @author shihongwei + * @date 2026-03-02 + */ +@Mapper +public interface HotTemporaryMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/temporary/service/IHotTemporaryService.java b/src/main/java/com/hotwj/platform/resourceManagement/temporary/service/IHotTemporaryService.java new file mode 100644 index 0000000..eb7ea26 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/temporary/service/IHotTemporaryService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.resourceManagement.temporary.service; + +import com.hotwj.platform.resourceManagement.temporary.domain.bo.HotTemporaryBo; +import com.hotwj.platform.resourceManagement.temporary.domain.vo.HotTemporaryVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 前端缓存信息Service接口 + * + * @author shihongwei + * @date 2026-03-02 + */ +public interface IHotTemporaryService { + + /** + * 查询前端缓存信息 + * + * @param id 主键 + * @return 前端缓存信息 + */ + HotTemporaryVo queryById(String id); + + /** + * 分页查询前端缓存信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 前端缓存信息分页列表 + */ + TableDataInfo queryPageList(HotTemporaryBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的前端缓存信息列表 + * + * @param bo 查询条件 + * @return 前端缓存信息列表 + */ + List queryList(HotTemporaryBo bo); + + /** + * 新增前端缓存信息 + * + * @param bo 前端缓存信息 + * @return 是否新增成功 + */ + Boolean insertByBo(HotTemporaryBo bo); + + /** + * 修改前端缓存信息 + * + * @param bo 前端缓存信息 + * @return 是否修改成功 + */ + Boolean updateByBo(HotTemporaryBo bo); + + /** + * 校验并批量删除前端缓存信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/temporary/service/impl/HotTemporaryServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/temporary/service/impl/HotTemporaryServiceImpl.java new file mode 100644 index 0000000..cf4ea74 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/temporary/service/impl/HotTemporaryServiceImpl.java @@ -0,0 +1,137 @@ +package com.hotwj.platform.resourceManagement.temporary.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.temporary.domain.HotTemporary; +import com.hotwj.platform.resourceManagement.temporary.domain.bo.HotTemporaryBo; +import com.hotwj.platform.resourceManagement.temporary.domain.vo.HotTemporaryVo; +import com.hotwj.platform.resourceManagement.temporary.mapper.HotTemporaryMapper; +import com.hotwj.platform.resourceManagement.temporary.service.IHotTemporaryService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 前端缓存信息Service业务层处理 + * + * @author shihongwei + * @date 2026-03-02 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotTemporaryServiceImpl implements IHotTemporaryService { + + private final HotTemporaryMapper baseMapper; + + /** + * 查询前端缓存信息 + * + * @param id 主键 + * @return 前端缓存信息 + */ + @Override + public HotTemporaryVo queryById(String id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询前端缓存信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 前端缓存信息分页列表 + */ + @Override + public TableDataInfo queryPageList(HotTemporaryBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的前端缓存信息列表 + * + * @param bo 查询条件 + * @return 前端缓存信息列表 + */ + @Override + public List queryList(HotTemporaryBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotTemporaryBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotTemporary::getId); + lqw.eq(bo.getCompanyId() != null, HotTemporary::getCompanyId, bo.getCompanyId()); + lqw.eq(StringUtils.isNotBlank(bo.getTemporaryType()), HotTemporary::getTemporaryType, bo.getTemporaryType()); + lqw.eq(StringUtils.isNotBlank(bo.getJsonStr()), HotTemporary::getJsonStr, bo.getJsonStr()); + lqw.eq(bo.getIsDeleted() != null, HotTemporary::getIsDeleted, bo.getIsDeleted()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotTemporary::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotTemporary::getUpdateByName, bo.getUpdateByName()); + return lqw; + } + + /** + * 新增前端缓存信息 + * + * @param bo 前端缓存信息 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotTemporaryBo bo) { + HotTemporary add = MapstructUtils.convert(bo, HotTemporary.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改前端缓存信息 + * + * @param bo 前端缓存信息 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotTemporaryBo bo) { + HotTemporary update = MapstructUtils.convert(bo, HotTemporary.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotTemporary entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除前端缓存信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/controller/HotVehicleAnnualReviewController.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/controller/HotVehicleAnnualReviewController.java new file mode 100644 index 0000000..3a9ebd1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/controller/HotVehicleAnnualReviewController.java @@ -0,0 +1,390 @@ +package com.hotwj.platform.resourceManagement.vehicleAnnualReview.controller; + +import cn.idev.excel.annotation.ExcelIgnore; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.config.vehicleInspectionConfig.domain.bo.HotVehicleInspectionConfigBo; +import com.hotwj.platform.config.vehicleInspectionConfig.domain.vo.HotVehicleInspectionConfigVo; +import com.hotwj.platform.config.vehicleInspectionConfig.service.IHotVehicleInspectionConfigService; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.bo.HotVehicleAnnualReviewBo; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.vo.HotVehicleAnnualReviewVo; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.service.IHotVehicleAnnualReviewService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleStatusBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import com.hotwj.platform.resourceManagement.vehicleManagement.utils.VehicleUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.utils.DateUtils; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +/** + * 车辆年审明细 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/vehicleAnnualReview") +@Tag(name = "车辆年审明细", description = "车辆年审明细管理") +public class HotVehicleAnnualReviewController extends BaseController { + + private final IHotVehicleAnnualReviewService hotVehicleAnnualReviewService; + private final IHotVehicleService hotVehicleService; + private final IHotVehicleInspectionConfigService hotVehicleInspectionConfigService; + + private static LocalDate toLocalDate(Date date) { + if (date == null) { + return null; + } + return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } + + + /** + * 查询车辆年审明细列表 + */ + //@SaCheckPermission("resourceManagement:vehicleAnnualReview:list") + @GetMapping("/list") + @Operation(summary = "查询车辆年审明细列表") + public TableDataInfo list(HotVehicleAnnualReviewBo bo, PageQuery pageQuery) { + return hotVehicleAnnualReviewService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆年审状态列表 + */ + //@SaCheckPermission("resourceManagement:vehicleAnnualReview:export") + @Log(title = "车辆年审状态", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆年审状态列表") + public void export(HotVehicleStatusBo bo, HttpServletResponse response) { + Long companyId = bo != null ? bo.getCompanyId() : null; + if (companyId == null) { + ExcelUtil.exportExcel(new ArrayList<>(), "车辆年审", VehicleAnnualInspectionStatusVo.class, response); + return; + } + + HotVehicleInspectionConfigBo configBo = new HotVehicleInspectionConfigBo(); + configBo.setCompanyId(companyId); + configBo.setIsEnabled(1L); + configBo.setIsDeleted(0L); + List configs = hotVehicleInspectionConfigService.queryList(configBo); + configs = configs == null ? new ArrayList<>() : configs.stream() + .filter(c -> c != null && (c.getIsEnabled() == null || c.getIsEnabled() == 1L)) + .sorted(Comparator.comparing(c -> c.getIntervalYears() == null ? Long.MAX_VALUE : c.getIntervalYears())) + .toList(); + + List rules = new ArrayList<>(); + for (HotVehicleInspectionConfigVo c : configs) { + if (c.getIntervalYears() == null || c.getInspectionTimes() == null) { + continue; + } + rules.add(new VehicleUtils.AnnualInspectionRule(c.getIntervalYears().intValue(), c.getInspectionTimes().intValue())); + } + + if (bo == null) { + bo = new HotVehicleStatusBo(); + } + bo.setCompanyId(companyId); + PageQuery pageQuery = new PageQuery(Integer.MAX_VALUE, PageQuery.DEFAULT_PAGE_NUM); + TableDataInfo vehiclePage = hotVehicleService.queryPageListByStatus(bo, pageQuery); + List vehicles = vehiclePage.getRows(); + if (vehicles == null || vehicles.isEmpty()) { + ExcelUtil.exportExcel(new ArrayList<>(), "车辆年审", VehicleAnnualInspectionStatusVo.class, response); + return; + } + + List rows = buildAnnualInspectionRows(vehicles, rules); + ExcelUtil.exportExcel(rows, "车辆年审", VehicleAnnualInspectionStatusVo.class, response); + } + + /** + * 获取车辆年审明细详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:vehicleAnnualReview:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆年审明细详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotVehicleAnnualReviewService.queryById(id)); + } + + /** + * 新增车辆年审明细 + */ + //@SaCheckPermission("resourceManagement:vehicleAnnualReview:add") + @Log(title = "车辆年审明细", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆年审明细") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleAnnualReviewBo bo) { + return toAjax(hotVehicleAnnualReviewService.insertByBo(bo)); + } + + /** + * 修改车辆年审明细 + */ + //@SaCheckPermission("resourceManagement:vehicleAnnualReview:edit") + @Log(title = "车辆年审明细", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆年审明细") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleAnnualReviewBo bo) { + return toAjax(hotVehicleAnnualReviewService.updateByBo(bo)); + } + + /** + * 删除车辆年审明细 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:vehicleAnnualReview:remove") + @Log(title = "车辆年审明细", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆年审明细") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotVehicleAnnualReviewService.deleteWithValidByIds(List.of(ids), true)); + } + + private static Date toDate(LocalDate date) { + if (date == null) { + return null; + } + return Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant()); + } + + @GetMapping("/inspectionStatus/list") + @Operation(summary = "车辆年审状态列表") + public TableDataInfo inspectionStatusList(HotVehicleStatusBo bo, PageQuery pageQuery) { + Long companyId = bo != null ? bo.getCompanyId() : null; + + + HotVehicleInspectionConfigBo configBo = new HotVehicleInspectionConfigBo(); + configBo.setCompanyId(companyId); + configBo.setIsEnabled(1L); + configBo.setIsDeleted(0L); + List configs = hotVehicleInspectionConfigService.queryList(configBo); + configs = configs == null ? new ArrayList<>() : configs.stream() + .filter(c -> c != null && (c.getIsEnabled() == null || c.getIsEnabled() == 1L)) + .sorted(Comparator.comparing(c -> c.getIntervalYears() == null ? Long.MAX_VALUE : c.getIntervalYears())) + .toList(); + + List rules = new ArrayList<>(); + for (HotVehicleInspectionConfigVo c : configs) { + if (c.getIntervalYears() == null || c.getInspectionTimes() == null) { + continue; + } + rules.add(new VehicleUtils.AnnualInspectionRule(c.getIntervalYears().intValue(), c.getInspectionTimes().intValue())); + } + + if (bo == null) { + bo = new HotVehicleStatusBo(); + } + bo.setCompanyId(companyId); + TableDataInfo vehiclePage = hotVehicleService.queryPageListByStatus(bo, pageQuery); + List vehicles = vehiclePage.getRows(); + if (vehicles == null || vehicles.isEmpty()) { + return new TableDataInfo<>(new ArrayList<>(), vehiclePage.getTotal()); + } + + LocalDate today = LocalDate.now(); + List rows = new ArrayList<>(); + for (HotVehicleVo v : vehicles) { + if (v == null) { + continue; + } + VehicleAnnualInspectionStatusVo row = new VehicleAnnualInspectionStatusVo(); + row.setVehicleId(v.getId()); + row.setPlateNumber(v.getPlateNumber()); + row.setCompanyId(v.getCompanyId()); + row.setVehicleStatus(v.getVehicleStatus()); + row.setOperationStatus(v.getOperationStatus()); + + Date issueDate = v.getCertificateIssueDate(); + row.setIssueDate(issueDate); + + HotVehicleAnnualReviewVo lastReview = v.getId() != null ? hotVehicleAnnualReviewService.queryLatestByVehicleId(v.getId()) : null; + Date lastReviewDate = lastReview != null ? lastReview.getReviewDate() : null; + row.setLastInspectionTime(lastReviewDate); + + LocalDate registrationDate = toLocalDate(v.getCertificateIssueDate()); + LocalDate lastInspectionDate = toLocalDate(lastReviewDate); + + VehicleUtils.InspectionResult result = VehicleUtils.calculateInspectionStatus(rules, registrationDate, lastInspectionDate); + + Integer times = result != null ? result.getInspectionTimes() : null; + if (times != null) { + if (times > 0) { + row.setInspectionTimesText("每年检测(" + times + "次)"); + } else { + row.setInspectionTimesText("无需年审"); + } + } + + LocalDate dueLocalDate = result != null ? result.getNextInspectionDate() : null; + + row.setDueDate(toDate(dueLocalDate)); + + if (result != null) { + row.setWarningLevel(result.getWarningLevel()); + row.setWarningLevelText(result.getWarningLevelDesc()); + if (result.getWarningLevel() == 6) { + row.setInspectionTip("无需年审"); + } else if (result.getOverdueDays() > 0) { + row.setInspectionTip("已到期(" + result.getOverdueDays() + "天)"); + } else if (result.getRemainingDays() > 0) { + row.setInspectionTip("剩余(" + result.getRemainingDays() + "天)到期"); + } + } else if (dueLocalDate != null) { + long diff = ChronoUnit.DAYS.between(today, dueLocalDate); + if (diff < 0) { + row.setInspectionTip("已到期(" + Math.abs(diff) + "天)"); + row.setWarningLevel(1); + row.setWarningLevelText("一级预警"); + } else { + row.setInspectionTip("剩余(" + diff + "天)到期"); + } + } + + rows.add(row); + } + + return new TableDataInfo<>(rows, vehiclePage.getTotal()); + } + + private List buildAnnualInspectionRows(List vehicles, List rules) { + LocalDate today = LocalDate.now(); + List rows = new ArrayList<>(); + for (HotVehicleVo v : vehicles) { + if (v == null) { + continue; + } + VehicleAnnualInspectionStatusVo row = new VehicleAnnualInspectionStatusVo(); + row.setVehicleId(v.getId()); + row.setPlateNumber(v.getPlateNumber()); + row.setCompanyId(v.getCompanyId()); + row.setVehicleStatus(v.getVehicleStatus()); + row.setOperationStatus(v.getOperationStatus()); + + Date issueDate = v.getCertificateIssueDate(); + row.setIssueDate(issueDate); + + HotVehicleAnnualReviewVo lastReview = v.getId() != null ? hotVehicleAnnualReviewService.queryLatestByVehicleId(v.getId()) : null; + Date lastReviewDate = lastReview != null ? lastReview.getReviewDate() : null; + row.setLastInspectionTimeStr(lastReviewDate != null ? DateUtils.formatDate(lastReviewDate) : "未上传信息"); + row.setLastInspectionTime(lastReviewDate); + + LocalDate registrationDate = toLocalDate(v.getCertificateIssueDate()); + LocalDate lastInspectionDate = toLocalDate(lastReviewDate); + VehicleUtils.InspectionResult result = VehicleUtils.calculateInspectionStatus(rules, registrationDate, lastInspectionDate); + + Integer times = result != null ? result.getInspectionTimes() : null; + if (times != null) { + if (times > 0) { + row.setInspectionTimesText("每年检测(" + times + "次)"); + } else { + row.setInspectionTimesText("无需年审"); + } + } + + LocalDate dueLocalDate = result != null ? result.getNextInspectionDate() : null; + row.setDueDate(toDate(dueLocalDate)); + + if (result != null) { + row.setWarningLevel(result.getWarningLevel()); + row.setWarningLevelText(result.getWarningLevelDesc()); + if (result.getWarningLevel() == 6) { + row.setInspectionTip("无需年审"); + } else if (result.getOverdueDays() > 0) { + row.setInspectionTip("已到期(" + result.getOverdueDays() + "天)"); + } else if (result.getRemainingDays() > 0) { + row.setInspectionTip("剩余(" + result.getRemainingDays() + "天)到期"); + } + } else if (dueLocalDate != null) { + long diff = ChronoUnit.DAYS.between(today, dueLocalDate); + if (diff < 0) { + row.setInspectionTip("已到期(" + Math.abs(diff) + "天)"); + row.setWarningLevel(1); + row.setWarningLevelText("一级预警"); + } else { + row.setInspectionTip("剩余(" + diff + "天)到期"); + } + } + rows.add(row); + } + return rows; + } + + @Data + @ExcelIgnoreUnannotated + public static class VehicleAnnualInspectionStatusVo { + @ExcelIgnore + private Long companyId; + + @ExcelIgnore + private String vehicleId; + + @ExcelProperty(value = "车牌号", index = 0) + private String plateNumber; + + @ExcelProperty(value = "每年年检次数", index = 1) + private String inspectionTimesText; + + @ExcelProperty(value = "年检提示", index = 2) + private String inspectionTip; + + @ExcelIgnore + private Date lastInspectionTime; + + @ExcelProperty(value = "最近年检时间", index = 3) + private String lastInspectionTimeStr; + + @ExcelProperty(value = "行驶证发证日期", index = 4) + private Date issueDate; + + @ExcelProperty(value = "到期日期", index = 5) + private Date dueDate; + + @ExcelIgnore + private Integer warningLevel; + + @ExcelProperty(value = "提示等级", index = 6) + private String warningLevelText; + + @ExcelIgnore + private Long vehicleStatus; + + @ExcelIgnore + private Long operationStatus; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/domain/HotVehicleAnnualReview.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/domain/HotVehicleAnnualReview.java new file mode 100644 index 0000000..4888064 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/domain/HotVehicleAnnualReview.java @@ -0,0 +1,75 @@ +package com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 车辆年审明细对象 hot_vehicle_annual_review + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_annual_review") +public class HotVehicleAnnualReview extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private String id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 年审时间 + */ + private Date reviewDate; + + /** + * 评定等级:1=一级,2=二级 + */ + private Long evaluationLevel; + + /** + * 年审附件图片URL(多个逗号分隔) + */ + private String imageUrls; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/domain/bo/HotVehicleAnnualReviewBo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/domain/bo/HotVehicleAnnualReviewBo.java new file mode 100644 index 0000000..88a9365 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/domain/bo/HotVehicleAnnualReviewBo.java @@ -0,0 +1,80 @@ +package com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.bo; + +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.HotVehicleAnnualReview; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 车辆年审明细业务对象 hot_vehicle_annual_review + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleAnnualReview.class, reverseConvertGenerate = false) +public class HotVehicleAnnualReviewBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private String id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 年审时间 + */ + @NotNull(message = "年审时间不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date reviewDate; + + /** + * 到期日期(用于更新车辆检验有效期) + */ + private String inspectionValidMonth; + + /** + * 评定等级:1=一级,2=二级 + */ + @NotNull(message = "评定等级:1=一级,2=二级不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long evaluationLevel; + + /** + * 年审附件图片URL(多个逗号分隔) + */ + private String imageUrls; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/domain/vo/HotVehicleAnnualReviewVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/domain/vo/HotVehicleAnnualReviewVo.java new file mode 100644 index 0000000..e11f0a6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/domain/vo/HotVehicleAnnualReviewVo.java @@ -0,0 +1,83 @@ +package com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.HotVehicleAnnualReview; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 车辆年审明细视图对象 hot_vehicle_annual_review + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleAnnualReview.class) +public class HotVehicleAnnualReviewVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private String id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 车辆ID + */ + @ExcelProperty(value = "车辆ID") + private String vehicleId; + + /** + * 车牌号 + */ + @ExcelProperty(value = "车牌号") + private String plateNumber; + + /** + * 年审时间 + */ + @ExcelProperty(value = "年审时间") + private Date reviewDate; + + /** + * 评定等级:1=一级,2=二级 + */ + @ExcelProperty(value = "评定等级:1=一级,2=二级") + private Long evaluationLevel; + + /** + * 年审附件图片URL(多个逗号分隔) + */ + @ExcelProperty(value = "年审附件图片URL(多个逗号分隔)") + private String imageUrls; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + private Date createTime; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/mapper/HotVehicleAnnualReviewMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/mapper/HotVehicleAnnualReviewMapper.java new file mode 100644 index 0000000..2b466e2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/mapper/HotVehicleAnnualReviewMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.vehicleAnnualReview.mapper; + +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.HotVehicleAnnualReview; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.vo.HotVehicleAnnualReviewVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆年审明细Mapper接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Mapper +public interface HotVehicleAnnualReviewMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/service/IHotVehicleAnnualReviewService.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/service/IHotVehicleAnnualReviewService.java new file mode 100644 index 0000000..0cc7860 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/service/IHotVehicleAnnualReviewService.java @@ -0,0 +1,79 @@ +package com.hotwj.platform.resourceManagement.vehicleAnnualReview.service; + +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.bo.HotVehicleAnnualReviewBo; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.vo.HotVehicleAnnualReviewVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.Date; +import java.util.List; + +/** + * 车辆年审明细Service接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +public interface IHotVehicleAnnualReviewService { + + /** + * 查询车辆年审明细 + * + * @param id 主键 + * @return 车辆年审明细 + */ + HotVehicleAnnualReviewVo queryById(Long id); + + /** + * 分页查询车辆年审明细列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆年审明细分页列表 + */ + TableDataInfo queryPageList(HotVehicleAnnualReviewBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆年审明细列表 + * + * @param bo 查询条件 + * @return 车辆年审明细列表 + */ + List queryList(HotVehicleAnnualReviewBo bo); + + /** + * 根据车辆ID查询最新的一条年审记录 + * + * @param vehicleId 车辆ID + * @return 最新年审记录 + */ + HotVehicleAnnualReviewVo queryLatestByVehicleId(String vehicleId); + + HotVehicleAnnualReviewVo queryLatestByVehicleIdAndReviewDateRange(String vehicleId, Date startDate, Date endDate); + + /** + * 新增车辆年审明细 + * + * @param bo 车辆年审明细 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleAnnualReviewBo bo); + + /** + * 修改车辆年审明细 + * + * @param bo 车辆年审明细 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleAnnualReviewBo bo); + + /** + * 校验并批量删除车辆年审明细信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/service/impl/HotVehicleAnnualReviewServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/service/impl/HotVehicleAnnualReviewServiceImpl.java new file mode 100644 index 0000000..0a87aec --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleAnnualReview/service/impl/HotVehicleAnnualReviewServiceImpl.java @@ -0,0 +1,188 @@ +package com.hotwj.platform.resourceManagement.vehicleAnnualReview.service.impl; + +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.HotVehicleAnnualReview; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.bo.HotVehicleAnnualReviewBo; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.vo.HotVehicleAnnualReviewVo; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.mapper.HotVehicleAnnualReviewMapper; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.service.IHotVehicleAnnualReviewService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle; +import com.hotwj.platform.resourceManagement.vehicleManagement.mapper.HotVehicleMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 车辆年审明细Service业务层处理 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleAnnualReviewServiceImpl implements IHotVehicleAnnualReviewService { + + private final HotVehicleAnnualReviewMapper baseMapper; + private final HotVehicleMapper vehicleMapper; + + /** + * 查询车辆年审明细 + * + * @param id 主键 + * @return 车辆年审明细 + */ + @Override + public HotVehicleAnnualReviewVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆年审明细列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆年审明细分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleAnnualReviewBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆年审明细列表 + * + * @param bo 查询条件 + * @return 车辆年审明细列表 + */ + @Override + public List queryList(HotVehicleAnnualReviewBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + /** + * 根据车辆ID查询最新的一条年审记录 + * + * @param vehicleId 车辆ID + * @return 最新年审记录 + */ + @Override + public HotVehicleAnnualReviewVo queryLatestByVehicleId(String vehicleId) { + return baseMapper.selectVoOne(new LambdaQueryWrapper() + .eq(HotVehicleAnnualReview::getVehicleId, vehicleId) + .eq(HotVehicleAnnualReview::getIsDeleted, 0L) + .orderByDesc(HotVehicleAnnualReview::getReviewDate) + .last("LIMIT 1")); + } + + @Override + public HotVehicleAnnualReviewVo queryLatestByVehicleIdAndReviewDateRange(String vehicleId, Date startDate, Date endDate) { + LambdaQueryWrapper lqw = new LambdaQueryWrapper() + .eq(HotVehicleAnnualReview::getVehicleId, vehicleId) + .eq(HotVehicleAnnualReview::getIsDeleted, 0L) + .orderByDesc(HotVehicleAnnualReview::getReviewDate) + .last("LIMIT 1"); + if (startDate != null) { + lqw.ge(HotVehicleAnnualReview::getReviewDate, startDate); + } + if (endDate != null) { + lqw.le(HotVehicleAnnualReview::getReviewDate, endDate); + } + return baseMapper.selectVoOne(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleAnnualReviewBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotVehicleAnnualReview::getId); + lqw.eq(bo.getCompanyId() != null, HotVehicleAnnualReview::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getVehicleId() != null, HotVehicleAnnualReview::getVehicleId, bo.getVehicleId()); + lqw.eq(StringUtils.isNotBlank(bo.getPlateNumber()), HotVehicleAnnualReview::getPlateNumber, bo.getPlateNumber()); + lqw.eq(bo.getReviewDate() != null, HotVehicleAnnualReview::getReviewDate, bo.getReviewDate()); + lqw.eq(bo.getEvaluationLevel() != null, HotVehicleAnnualReview::getEvaluationLevel, bo.getEvaluationLevel()); + lqw.eq(StringUtils.isNotBlank(bo.getImageUrls()), HotVehicleAnnualReview::getImageUrls, bo.getImageUrls()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleAnnualReview::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增车辆年审明细 + * + * @param bo 车辆年审明细 + * @return 是否新增成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(HotVehicleAnnualReviewBo bo) { + HotVehicleAnnualReview add = MapstructUtils.convert(bo, HotVehicleAnnualReview.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + if (StringUtils.isNotBlank(bo.getInspectionValidMonth()) && StringUtils.isNotBlank(bo.getVehicleId())) { + String validMonth = bo.getInspectionValidMonth(); + try { + validMonth = DateUtil.format(DateUtil.parse(validMonth), "yyyy-MM"); + } catch (Exception e) { + log.warn("Invalid date format: {}", validMonth); + } + vehicleMapper.update(null, new LambdaUpdateWrapper() + .set(HotVehicle::getInspectionValidMonth, validMonth) + .eq(HotVehicle::getId, bo.getVehicleId())); + } + } + return flag; + } + + /** + * 修改车辆年审明细 + * + * @param bo 车辆年审明细 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleAnnualReviewBo bo) { + HotVehicleAnnualReview update = MapstructUtils.convert(bo, HotVehicleAnnualReview.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleAnnualReview entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除车辆年审明细信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/controller/HotVehicleCertReissueController.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/controller/HotVehicleCertReissueController.java new file mode 100644 index 0000000..11e50bb --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/controller/HotVehicleCertReissueController.java @@ -0,0 +1,123 @@ +package com.hotwj.platform.resourceManagement.vehicleCertReissue.controller; + +import com.hotwj.platform.resourceManagement.vehicleCertReissue.domain.bo.HotVehicleCertReissueBo; +import com.hotwj.platform.resourceManagement.vehicleCertReissue.domain.vo.HotVehicleCertReissueVo; +import com.hotwj.platform.resourceManagement.vehicleCertReissue.service.IHotVehicleCertReissueService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.service.DictService; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 证件补领登记 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/vehicleCertReissue") +@Tag(name = "证件补领登记", description = "证件补领登记管理") +public class HotVehicleCertReissueController extends BaseController { + + private final IHotVehicleCertReissueService hotVehicleCertReissueService; + private final DictService dictService; + + /** + * 查询证件补领登记列表 + */ + //@SaCheckPermission("resourceManagement:vehicleCertReissue:list") + @GetMapping("/list") + @Operation(summary = "查询证件补领登记列表") + public TableDataInfo list(HotVehicleCertReissueBo bo, PageQuery pageQuery) { + return hotVehicleCertReissueService.queryPageList(bo, pageQuery); + } + + /** + * 导出证件补领登记列表 + */ + //@SaCheckPermission("resourceManagement:vehicleCertReissue:export") + @Log(title = "证件补领登记", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出证件补领登记列表") + public void export(HotVehicleCertReissueBo bo, HttpServletResponse response) { + List list = hotVehicleCertReissueService.queryList(bo); + for (HotVehicleCertReissueVo vo : list) { + String dictLabel = dictService.getDictLabel("hot_paper_type", vo.getReissueType()); + if (StringUtils.isNotBlank(dictLabel)) { + vo.setReissueType(dictLabel); + } + } + ExcelUtil.exportExcel(list, "证件补领登记", HotVehicleCertReissueVo.class, response); + } + + /** + * 获取证件补领登记详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:vehicleCertReissue:query") + @GetMapping("/{id}") + @Operation(summary = "获取证件补领登记详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotVehicleCertReissueService.queryById(id)); + } + + /** + * 新增证件补领登记 + */ + //@SaCheckPermission("resourceManagement:vehicleCertReissue:add") + @Log(title = "证件补领登记", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增证件补领登记") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleCertReissueBo bo) { + return toAjax(hotVehicleCertReissueService.insertByBo(bo)); + } + + /** + * 修改证件补领登记 + */ + //@SaCheckPermission("resourceManagement:vehicleCertReissue:edit") + @Log(title = "证件补领登记", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改证件补领登记") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleCertReissueBo bo) { + return toAjax(hotVehicleCertReissueService.updateByBo(bo)); + } + + /** + * 删除证件补领登记 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:vehicleCertReissue:remove") + @Log(title = "证件补领登记", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除证件补领登记") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotVehicleCertReissueService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/domain/HotVehicleCertReissue.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/domain/HotVehicleCertReissue.java new file mode 100644 index 0000000..7cdb977 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/domain/HotVehicleCertReissue.java @@ -0,0 +1,80 @@ +package com.hotwj.platform.resourceManagement.vehicleCertReissue.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 证件补领登记对象 hot_vehicle_cert_reissue + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_cert_reissue") +public class HotVehicleCertReissue extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private String id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 运营状态:1=正常,2=暂停,3=过户,4=报废 + */ + private Long operationStatus; + + /** + * 补领类型(如: 行驶证) + */ + private String reissueType; + + /** + * 登记时间 + */ + private Date registerTime; + + /** + * 领取时间 + */ + private Date pickupTime; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/domain/bo/HotVehicleCertReissueBo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/domain/bo/HotVehicleCertReissueBo.java new file mode 100644 index 0000000..a033680 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/domain/bo/HotVehicleCertReissueBo.java @@ -0,0 +1,88 @@ +package com.hotwj.platform.resourceManagement.vehicleCertReissue.domain.bo; + +import com.hotwj.platform.resourceManagement.vehicleCertReissue.domain.HotVehicleCertReissue; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; +import java.util.List; + +/** + * 证件补领登记业务对象 hot_vehicle_cert_reissue + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleCertReissue.class, reverseConvertGenerate = false) +public class HotVehicleCertReissueBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private String id; + + /** + * 主键集合(用于批量导出等场景) + */ + private List ids; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 运营状态:1=正常,2=暂停,3=过户,4=报废 + */ + private Long operationStatus; + + /** + * 补领类型(如: 行驶证) + */ + @NotBlank(message = "补领类型(如: 行驶证)不能为空", groups = {AddGroup.class, EditGroup.class}) + private String reissueType; + + /** + * 登记时间 + */ + @NotNull(message = "登记时间不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date registerTime; + + /** + * 领取时间 + */ + @NotNull(message = "领取时间不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date pickupTime; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/domain/vo/HotVehicleCertReissueVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/domain/vo/HotVehicleCertReissueVo.java new file mode 100644 index 0000000..cd33c83 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/domain/vo/HotVehicleCertReissueVo.java @@ -0,0 +1,85 @@ +package com.hotwj.platform.resourceManagement.vehicleCertReissue.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.vehicleCertReissue.domain.HotVehicleCertReissue; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 证件补领登记视图对象 hot_vehicle_cert_reissue + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleCertReissue.class) +public class HotVehicleCertReissueVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private String id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 车牌号 + */ + @ExcelProperty(value = "车牌号") + private String plateNumber; + + /** + * 运营状态:1=正常,2=暂停,3=过户,4=报废 + */ + private Long operationStatus; + + /** + * 补领类型(如: 行驶证) + */ + @ExcelProperty(value = "补领类型(如: 行驶证)") + private String reissueType; + + /** + * 登记时间 + */ + @ExcelProperty(value = "登记时间") + private Date registerTime; + + /** + * 领取时间 + */ + @ExcelProperty(value = "领取时间") + private Date pickupTime; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/mapper/HotVehicleCertReissueMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/mapper/HotVehicleCertReissueMapper.java new file mode 100644 index 0000000..11b5801 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/mapper/HotVehicleCertReissueMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.vehicleCertReissue.mapper; + +import com.hotwj.platform.resourceManagement.vehicleCertReissue.domain.HotVehicleCertReissue; +import com.hotwj.platform.resourceManagement.vehicleCertReissue.domain.vo.HotVehicleCertReissueVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 证件补领登记Mapper接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Mapper +public interface HotVehicleCertReissueMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/service/IHotVehicleCertReissueService.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/service/IHotVehicleCertReissueService.java new file mode 100644 index 0000000..d76c4c5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/service/IHotVehicleCertReissueService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.resourceManagement.vehicleCertReissue.service; + +import com.hotwj.platform.resourceManagement.vehicleCertReissue.domain.bo.HotVehicleCertReissueBo; +import com.hotwj.platform.resourceManagement.vehicleCertReissue.domain.vo.HotVehicleCertReissueVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 证件补领登记Service接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +public interface IHotVehicleCertReissueService { + + /** + * 查询证件补领登记 + * + * @param id 主键 + * @return 证件补领登记 + */ + HotVehicleCertReissueVo queryById(Long id); + + /** + * 分页查询证件补领登记列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 证件补领登记分页列表 + */ + TableDataInfo queryPageList(HotVehicleCertReissueBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的证件补领登记列表 + * + * @param bo 查询条件 + * @return 证件补领登记列表 + */ + List queryList(HotVehicleCertReissueBo bo); + + /** + * 新增证件补领登记 + * + * @param bo 证件补领登记 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleCertReissueBo bo); + + /** + * 修改证件补领登记 + * + * @param bo 证件补领登记 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleCertReissueBo bo); + + /** + * 校验并批量删除证件补领登记信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/service/impl/HotVehicleCertReissueServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/service/impl/HotVehicleCertReissueServiceImpl.java new file mode 100644 index 0000000..950a566 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleCertReissue/service/impl/HotVehicleCertReissueServiceImpl.java @@ -0,0 +1,146 @@ +package com.hotwj.platform.resourceManagement.vehicleCertReissue.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.vehicleCertReissue.domain.HotVehicleCertReissue; +import com.hotwj.platform.resourceManagement.vehicleCertReissue.domain.bo.HotVehicleCertReissueBo; +import com.hotwj.platform.resourceManagement.vehicleCertReissue.domain.vo.HotVehicleCertReissueVo; +import com.hotwj.platform.resourceManagement.vehicleCertReissue.mapper.HotVehicleCertReissueMapper; +import com.hotwj.platform.resourceManagement.vehicleCertReissue.service.IHotVehicleCertReissueService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 证件补领登记Service业务层处理 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleCertReissueServiceImpl implements IHotVehicleCertReissueService { + + private final HotVehicleCertReissueMapper baseMapper; + + /** + * 查询证件补领登记 + * + * @param id 主键 + * @return 证件补领登记 + */ + @Override + public HotVehicleCertReissueVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询证件补领登记列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 证件补领登记分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleCertReissueBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的证件补领登记列表 + * + * @param bo 查询条件 + * @return 证件补领登记列表 + */ + @Override + public List queryList(HotVehicleCertReissueBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleCertReissueBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotVehicleCertReissue::getId); + lqw.eq(bo.getCompanyId() != null, HotVehicleCertReissue::getCompanyId, bo.getCompanyId()); + lqw.in(CollUtil.isNotEmpty(bo.getIds()), HotVehicleCertReissue::getId, bo.getIds()); + lqw.eq(bo.getVehicleId() != null, HotVehicleCertReissue::getVehicleId, bo.getVehicleId()); + lqw.like(StringUtils.isNotBlank(bo.getPlateNumber()), HotVehicleCertReissue::getPlateNumber, bo.getPlateNumber()); + lqw.eq(StringUtils.isNotBlank(bo.getReissueType()), HotVehicleCertReissue::getReissueType, bo.getReissueType()); + lqw.eq(bo.getRegisterTime() != null, HotVehicleCertReissue::getRegisterTime, bo.getRegisterTime()); + lqw.eq(bo.getPickupTime() != null, HotVehicleCertReissue::getPickupTime, bo.getPickupTime()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleCertReissue::getIsDeleted, bo.getIsDeleted()); + lqw.eq(bo.getOperationStatus() != null, HotVehicleCertReissue::getOperationStatus, bo.getOperationStatus()); + return lqw; + } + + /** + * 新增证件补领登记 + * + * @param bo 证件补领登记 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleCertReissueBo bo) { + HotVehicleCertReissue add = MapstructUtils.convert(bo, HotVehicleCertReissue.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改证件补领登记 + * + * @param bo 证件补领登记 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleCertReissueBo bo) { + HotVehicleCertReissue update = MapstructUtils.convert(bo, HotVehicleCertReissue.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleCertReissue entity) { + if (entity.getRegisterTime() != null + && entity.getPickupTime() != null + && !entity.getPickupTime().after(entity.getRegisterTime())) { + throw new ServiceException("领取时间必须大于登记时间"); + } + } + + /** + * 校验并批量删除证件补领登记信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/controller/HotVehicleChangeController.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/controller/HotVehicleChangeController.java new file mode 100644 index 0000000..c7d6b7e --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/controller/HotVehicleChangeController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.resourceManagement.vehicleChange.controller; + +import com.hotwj.platform.resourceManagement.vehicleChange.domain.bo.HotVehicleChangeBo; +import com.hotwj.platform.resourceManagement.vehicleChange.domain.vo.HotVehicleChangeVo; +import com.hotwj.platform.resourceManagement.vehicleChange.service.IHotVehicleChangeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 车辆变更记录 + * + * @author shihongwei + * @date 2025-12-19 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/vehicleChange") +@Tag(name = "车辆变更记录", description = "车辆变更记录增删改查与导出") +public class HotVehicleChangeController extends BaseController { + + private final IHotVehicleChangeService hotVehicleChangeService; + + /** + * 查询车辆变更记录列表 + */ + //@SaCheckPermission("resourceManagement:vehicleChange:list") + @GetMapping("/list") + @Operation(summary = "查询车辆变更记录列表") + public TableDataInfo list(HotVehicleChangeBo bo, PageQuery pageQuery) { + return hotVehicleChangeService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆变更记录列表 + */ + //@SaCheckPermission("resourceManagement:vehicleChange:export") + @Log(title = "车辆变更记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆变更记录列表") + public void export(HotVehicleChangeBo bo, HttpServletResponse response) { + List list = hotVehicleChangeService.queryList(bo); + ExcelUtil.exportExcel(list, "车辆变更记录", HotVehicleChangeVo.class, response); + } + + /** + * 获取车辆变更记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:vehicleChange:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆变更记录详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotVehicleChangeService.queryById(id)); + } + + /** + * 新增车辆变更记录 + */ + //@SaCheckPermission("resourceManagement:vehicleChange:add") + @Log(title = "车辆变更记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆变更记录") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleChangeBo bo) { + return toAjax(hotVehicleChangeService.insertByBo(bo)); + } + + /** + * 修改车辆变更记录 + */ + //@SaCheckPermission("resourceManagement:vehicleChange:edit") + @Log(title = "车辆变更记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆变更记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleChangeBo bo) { + return toAjax(hotVehicleChangeService.updateByBo(bo)); + } + + /** + * 删除车辆变更记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:vehicleChange:remove") + @Log(title = "车辆变更记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆变更记录") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotVehicleChangeService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/domain/HotVehicleChange.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/domain/HotVehicleChange.java new file mode 100644 index 0000000..b97e98f --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/domain/HotVehicleChange.java @@ -0,0 +1,85 @@ +package com.hotwj.platform.resourceManagement.vehicleChange.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 车辆变更记录对象 hot_vehicle_change + * + * @author shihongwei + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_change") +public class HotVehicleChange extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆ID + */ + private Long vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 变更日期 + */ + private Date changeDate; + + /** + * 变更事项 + */ + private String changeItem; + + /** + * 变更原因 + */ + private String changeReason; + + /** + * 记录人ID + */ + private Long recorderId; + + /** + * 记录人姓名 + */ + private String recorderName; + + /** + * 变更凭据图片URL(多个逗号分隔) + */ + private String changeCertImageUrls; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/domain/bo/HotVehicleChangeBo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/domain/bo/HotVehicleChangeBo.java new file mode 100644 index 0000000..ccee0c8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/domain/bo/HotVehicleChangeBo.java @@ -0,0 +1,91 @@ +package com.hotwj.platform.resourceManagement.vehicleChange.domain.bo; + +import com.hotwj.platform.resourceManagement.vehicleChange.domain.HotVehicleChange; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 车辆变更记录业务对象 hot_vehicle_change + * + * @author shihongwei + * @date 2025-12-19 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleChange.class, reverseConvertGenerate = false) +public class HotVehicleChangeBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 车辆ID + */ + @NotNull(message = "车辆ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 变更日期 + */ + @NotNull(message = "变更日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date changeDate; + + /** + * 变更事项 + */ + @NotBlank(message = "变更事项不能为空", groups = {AddGroup.class, EditGroup.class}) + private String changeItem; + + /** + * 变更原因 + */ + @NotBlank(message = "变更原因不能为空", groups = {AddGroup.class, EditGroup.class}) + private String changeReason; + + /** + * 记录人ID + */ + @NotNull(message = "记录人ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long recorderId; + + /** + * 记录人姓名 + */ + private String recorderName; + + /** + * 变更凭据图片URL(多个逗号分隔) + */ + @NotBlank(message = "变更凭据图片URL(多个逗号分隔)不能为空", groups = {AddGroup.class, EditGroup.class}) + private String changeCertImageUrls; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/domain/vo/HotVehicleChangeVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/domain/vo/HotVehicleChangeVo.java new file mode 100644 index 0000000..6082b3e --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/domain/vo/HotVehicleChangeVo.java @@ -0,0 +1,105 @@ +package com.hotwj.platform.resourceManagement.vehicleChange.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.vehicleChange.domain.HotVehicleChange; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 车辆变更记录视图对象 hot_vehicle_change + * + * @author shihongwei + * @date 2025-12-19 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleChange.class) +public class HotVehicleChangeVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 车辆ID + */ + @ExcelProperty(value = "车辆ID") + private Long vehicleId; + + /** + * 车牌号 + */ + @ExcelProperty(value = "车牌号") + private String plateNumber; + + /** + * 变更日期 + */ + @ExcelProperty(value = "变更日期") + private Date changeDate; + + /** + * 变更事项 + */ + @ExcelProperty(value = "变更事项", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_vehicle_status") + private String changeItem; + + /** + * 变更原因 + */ + @ExcelProperty(value = "变更原因") + private String changeReason; + + /** + * 记录人ID + */ + @ExcelProperty(value = "记录人ID") + private Long recorderId; + + /** + * 记录人姓名 + */ + @ExcelProperty(value = "记录人姓名") + private String recorderName; + + /** + * 变更凭据图片URL(多个逗号分隔) + */ + @ExcelProperty(value = "变更凭据图片URL(多个逗号分隔)") + private String changeCertImageUrls; + + /** + * 变更凭据图片URL(多个逗号分隔)Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "changeCertImageUrls") + private String changeCertImageUrlsUrl; + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/mapper/HotVehicleChangeMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/mapper/HotVehicleChangeMapper.java new file mode 100644 index 0000000..f4c4231 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/mapper/HotVehicleChangeMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.vehicleChange.mapper; + +import com.hotwj.platform.resourceManagement.vehicleChange.domain.HotVehicleChange; +import com.hotwj.platform.resourceManagement.vehicleChange.domain.vo.HotVehicleChangeVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆变更记录Mapper接口 + * + * @author shihongwei + * @date 2025-12-19 + */ +@Mapper +public interface HotVehicleChangeMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/service/IHotVehicleChangeService.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/service/IHotVehicleChangeService.java new file mode 100644 index 0000000..55fddda --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/service/IHotVehicleChangeService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.resourceManagement.vehicleChange.service; + +import com.hotwj.platform.resourceManagement.vehicleChange.domain.bo.HotVehicleChangeBo; +import com.hotwj.platform.resourceManagement.vehicleChange.domain.vo.HotVehicleChangeVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆变更记录Service接口 + * + * @author shihongwei + * @date 2025-12-19 + */ +public interface IHotVehicleChangeService { + + /** + * 查询车辆变更记录 + * + * @param id 主键 + * @return 车辆变更记录 + */ + HotVehicleChangeVo queryById(Long id); + + /** + * 分页查询车辆变更记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆变更记录分页列表 + */ + TableDataInfo queryPageList(HotVehicleChangeBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆变更记录列表 + * + * @param bo 查询条件 + * @return 车辆变更记录列表 + */ + List queryList(HotVehicleChangeBo bo); + + /** + * 新增车辆变更记录 + * + * @param bo 车辆变更记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleChangeBo bo); + + /** + * 修改车辆变更记录 + * + * @param bo 车辆变更记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleChangeBo bo); + + /** + * 校验并批量删除车辆变更记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/service/impl/HotVehicleChangeServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/service/impl/HotVehicleChangeServiceImpl.java new file mode 100644 index 0000000..6885488 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleChange/service/impl/HotVehicleChangeServiceImpl.java @@ -0,0 +1,191 @@ +package com.hotwj.platform.resourceManagement.vehicleChange.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.resourceManagement.vehicleChange.domain.HotVehicleChange; +import com.hotwj.platform.resourceManagement.vehicleChange.domain.bo.HotVehicleChangeBo; +import com.hotwj.platform.resourceManagement.vehicleChange.domain.vo.HotVehicleChangeVo; +import com.hotwj.platform.resourceManagement.vehicleChange.mapper.HotVehicleChangeMapper; +import com.hotwj.platform.resourceManagement.vehicleChange.service.IHotVehicleChangeService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle; +import com.hotwj.platform.resourceManagement.vehicleManagement.mapper.HotVehicleMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 车辆变更记录Service业务层处理 + * + * @author shihongwei + * @date 2025-12-19 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleChangeServiceImpl implements IHotVehicleChangeService { + + private final HotVehicleChangeMapper baseMapper; + private static final Set CLEAR_DRIVER_OPERATION_STATUS = new HashSet<>(Arrays.asList("3", "4", "5")); + private final HotVehicleMapper vehicleMapper; + private final HotDriverMapper driverMapper; + + /** + * 查询车辆变更记录 + * + * @param id 主键 + * @return 车辆变更记录 + */ + @Override + public HotVehicleChangeVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆变更记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆变更记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleChangeBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆变更记录列表 + * + * @param bo 查询条件 + * @return 车辆变更记录列表 + */ + @Override + public List queryList(HotVehicleChangeBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleChangeBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotVehicleChange::getCreateTime); + lqw.eq(bo.getCompanyId() != null, HotVehicleChange::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getVehicleId() != null, HotVehicleChange::getVehicleId, bo.getVehicleId()); + lqw.like(StringUtils.isNotBlank(bo.getPlateNumber()), HotVehicleChange::getPlateNumber, bo.getPlateNumber()); + lqw.eq(bo.getChangeDate() != null, HotVehicleChange::getChangeDate, bo.getChangeDate()); + lqw.eq(StringUtils.isNotBlank(bo.getChangeItem()), HotVehicleChange::getChangeItem, bo.getChangeItem()); + lqw.eq(StringUtils.isNotBlank(bo.getChangeReason()), HotVehicleChange::getChangeReason, bo.getChangeReason()); + lqw.eq(bo.getRecorderId() != null, HotVehicleChange::getRecorderId, bo.getRecorderId()); + lqw.like(StringUtils.isNotBlank(bo.getRecorderName()), HotVehicleChange::getRecorderName, bo.getRecorderName()); + lqw.eq(StringUtils.isNotBlank(bo.getChangeCertImageUrls()), HotVehicleChange::getChangeCertImageUrls, bo.getChangeCertImageUrls()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleChange::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增车辆变更记录 + * + * @param bo 车辆变更记录 + * @return 是否新增成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(HotVehicleChangeBo bo) { + HotVehicleChange add = MapstructUtils.convert(bo, HotVehicleChange.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + syncVehicleStatusAndDriverBinding(bo); + } + return flag; + } + + /** + * 修改车辆变更记录 + * + * @param bo 车辆变更记录 + * @return 是否修改成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(HotVehicleChangeBo bo) { + HotVehicleChange update = MapstructUtils.convert(bo, HotVehicleChange.class); + validEntityBeforeSave(update); + boolean flag = baseMapper.updateById(update) > 0; + if (flag) { + syncVehicleStatusAndDriverBinding(bo); + } + return flag; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleChange entity) { + //TODO 做一些数据校验,如唯一约束 + } + + private void syncVehicleStatusAndDriverBinding(HotVehicleChangeBo bo) { + String vehicleId = String.valueOf(bo.getVehicleId()); + HotVehicle vehicle = vehicleMapper.selectById(vehicleId); + if (vehicle == null) { + throw new ServiceException("车辆不存在,无法同步运营状态"); + } + Long operationStatus = Long.valueOf(bo.getChangeItem()); + Long vehicleStatus = "1".equals(bo.getChangeItem()) ? 1L : 2L; + boolean needUnbindDriver = CLEAR_DRIVER_OPERATION_STATUS.contains(bo.getChangeItem()); + + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper() + .set(HotVehicle::getOperationStatus, operationStatus) + .set(HotVehicle::getVehicleStatus, vehicleStatus) + .eq(HotVehicle::getId, vehicleId) + .eq(HotVehicle::getCompanyId, bo.getCompanyId()); + if (needUnbindDriver) { + updateWrapper.set(HotVehicle::getCurrentDriver, null); + } + vehicleMapper.update(null, updateWrapper); + + if (needUnbindDriver && StringUtils.isNotBlank(vehicle.getCurrentDriver())) { + List driverIds = Arrays.stream(vehicle.getCurrentDriver().split(",")) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toList()); + if (!driverIds.isEmpty()) { + driverMapper.update(null, new LambdaUpdateWrapper() + .set(HotDriver::getVehicleId, null) + .set(HotDriver::getPlateNumber, null) + .in(HotDriver::getId, driverIds) + .eq(HotDriver::getCompanyId, bo.getCompanyId())); + } + } + } + + /** + * 校验并批量删除车辆变更记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/controller/HotVehicleClaimController.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/controller/HotVehicleClaimController.java new file mode 100644 index 0000000..d2212ff --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/controller/HotVehicleClaimController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.resourceManagement.vehicleClaim.controller; + +import com.hotwj.platform.resourceManagement.vehicleClaim.domain.bo.HotVehicleClaimBo; +import com.hotwj.platform.resourceManagement.vehicleClaim.domain.vo.HotVehicleClaimVo; +import com.hotwj.platform.resourceManagement.vehicleClaim.service.IHotVehicleClaimService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 车辆出险记录 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/vehicleClaim") +@Tag(name = "车辆出险记录", description = "车辆出险记录管理") +public class HotVehicleClaimController extends BaseController { + + private final IHotVehicleClaimService hotVehicleClaimService; + + /** + * 查询车辆出险记录列表 + */ + //@SaCheckPermission("resourceManagement:vehicleClaim:list") + @GetMapping("/list") + @Operation(summary = "查询车辆出险记录列表") + public TableDataInfo list(HotVehicleClaimBo bo, PageQuery pageQuery) { + return hotVehicleClaimService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆出险记录列表 + */ + //@SaCheckPermission("resourceManagement:vehicleClaim:export") + @Log(title = "车辆出险记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆出险记录列表") + public void export(HotVehicleClaimBo bo, HttpServletResponse response) { + List list = hotVehicleClaimService.queryList(bo); + ExcelUtil.exportExcel(list, "车辆出险记录", HotVehicleClaimVo.class, response); + } + + /** + * 获取车辆出险记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:vehicleClaim:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆出险记录详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotVehicleClaimService.queryById(id)); + } + + /** + * 新增车辆出险记录 + */ + //@SaCheckPermission("resourceManagement:vehicleClaim:add") + @Log(title = "车辆出险记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆出险记录") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleClaimBo bo) { + return toAjax(hotVehicleClaimService.insertByBo(bo)); + } + + /** + * 修改车辆出险记录 + */ + //@SaCheckPermission("resourceManagement:vehicleClaim:edit") + @Log(title = "车辆出险记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆出险记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleClaimBo bo) { + return toAjax(hotVehicleClaimService.updateByBo(bo)); + } + + /** + * 删除车辆出险记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:vehicleClaim:remove") + @Log(title = "车辆出险记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆出险记录") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotVehicleClaimService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/domain/HotVehicleClaim.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/domain/HotVehicleClaim.java new file mode 100644 index 0000000..018f461 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/domain/HotVehicleClaim.java @@ -0,0 +1,105 @@ +package com.hotwj.platform.resourceManagement.vehicleClaim.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 车辆出险记录对象 hot_vehicle_claim + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_claim") +public class HotVehicleClaim extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private String id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 车辆保险ID + */ + private String vehicleInsuranceId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 出险地点 + */ + private String accidentLocation; + + /** + * 承保险种 + */ + private String insuranceType; + + /** + * 保险公司 + */ + private String insurerName; + + /** + * 出险登记日期 + */ + private Date incidentTime; + + /** + * 起保日期 + */ + private Date assessDate; + + /** + * 保险到期日期 + */ + private Date insuranceExpireDate; + + /** + * 出险事项 + */ + private String claimItems; + + /** + * 备注 + */ + private String remark; + + /** + * 出险附件图片URL(多个逗号分隔) + */ + private String imageUrls; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/domain/bo/HotVehicleClaimBo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/domain/bo/HotVehicleClaimBo.java new file mode 100644 index 0000000..844b490 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/domain/bo/HotVehicleClaimBo.java @@ -0,0 +1,108 @@ +package com.hotwj.platform.resourceManagement.vehicleClaim.domain.bo; + +import com.hotwj.platform.resourceManagement.vehicleClaim.domain.HotVehicleClaim; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 车辆出险记录业务对象 hot_vehicle_claim + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleClaim.class, reverseConvertGenerate = false) +public class HotVehicleClaimBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private String id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 车辆保险ID + */ + private String vehicleInsuranceId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 出险地点 + */ +// @NotBlank(message = "出险地点不能为空", groups = {AddGroup.class, EditGroup.class}) + private String accidentLocation; + + /** + * 承保险种 + */ + @NotBlank(message = "承保险种不能为空", groups = {AddGroup.class, EditGroup.class}) + private String insuranceType; + + /** + * 保险公司 + */ + @NotBlank(message = "保险公司不能为空", groups = {AddGroup.class, EditGroup.class}) + private String insurerName; + + /** + * 出险登记日期 + */ + private Date incidentTime; + + /** + * 起保日期 + */ + private Date assessDate; + + /** + * 保险到期日期 + */ + private Date insuranceExpireDate; + + /** + * 出险事项 + */ + private String claimItems; + + /** + * 备注 + */ + private String remark; + + /** + * 出险附件图片URL(多个逗号分隔) + */ + private String imageUrls; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/domain/vo/HotVehicleClaimVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/domain/vo/HotVehicleClaimVo.java new file mode 100644 index 0000000..195f3d9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/domain/vo/HotVehicleClaimVo.java @@ -0,0 +1,124 @@ +package com.hotwj.platform.resourceManagement.vehicleClaim.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.vehicleClaim.domain.HotVehicleClaim; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 车辆出险记录视图对象 hot_vehicle_claim + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleClaim.class) +public class HotVehicleClaimVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private String id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 车辆ID + */ + @ExcelProperty(value = "车辆ID") + private String vehicleId; + + /** + * 车辆保险ID + */ + @ExcelProperty(value = "车辆保险ID") + private String vehicleInsuranceId; + + /** + * 车牌号 + */ + @ExcelProperty(value = "车牌号") + private String plateNumber; + + /** + * 出险地点 + */ + @ExcelProperty(value = "出险地点") + private String accidentLocation; + + /** + * 承保险种 + */ + @ExcelProperty(value = "承保险种") + private String insuranceType; + + /** + * 保险公司 + */ + @ExcelProperty(value = "保险公司") + private String insurerName; + + /** + * 出险登记日期 + */ + @ExcelProperty(value = "出险登记日期") + private Date incidentTime; + + /** + * 起保日期 + */ + @ExcelProperty(value = "起保日期") + private Date assessDate; + + /** + * 保险到期日期 + */ + @ExcelProperty(value = "保险到期日期") + private Date insuranceExpireDate; + + /** + * 出险事项 + */ + @ExcelProperty(value = "出险事项") + private String claimItems; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 出险附件图片URL(多个逗号分隔) + */ + @ExcelProperty(value = "出险附件图片URL(多个逗号分隔)") + private String imageUrls; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + + /** + * 图片附件列表 + */ + private java.util.List images; + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/mapper/HotVehicleClaimMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/mapper/HotVehicleClaimMapper.java new file mode 100644 index 0000000..73a7ba7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/mapper/HotVehicleClaimMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.vehicleClaim.mapper; + +import com.hotwj.platform.resourceManagement.vehicleClaim.domain.HotVehicleClaim; +import com.hotwj.platform.resourceManagement.vehicleClaim.domain.vo.HotVehicleClaimVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆出险记录Mapper接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Mapper +public interface HotVehicleClaimMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/service/IHotVehicleClaimService.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/service/IHotVehicleClaimService.java new file mode 100644 index 0000000..9134743 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/service/IHotVehicleClaimService.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.resourceManagement.vehicleClaim.service; + +import com.hotwj.platform.resourceManagement.vehicleClaim.domain.bo.HotVehicleClaimBo; +import com.hotwj.platform.resourceManagement.vehicleClaim.domain.vo.HotVehicleClaimVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆出险记录Service接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +public interface IHotVehicleClaimService { + + /** + * 查询车辆出险记录 + * + * @param id 主键 + * @return 车辆出险记录 + */ + HotVehicleClaimVo queryById(Long id); + + /** + * 分页查询车辆出险记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆出险记录分页列表 + */ + TableDataInfo queryPageList(HotVehicleClaimBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆出险记录列表 + * + * @param bo 查询条件 + * @return 车辆出险记录列表 + */ + List queryList(HotVehicleClaimBo bo); + + /** + * 新增车辆出险记录 + * + * @param bo 车辆出险记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleClaimBo bo); + + /** + * 修改车辆出险记录 + * + * @param bo 车辆出险记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleClaimBo bo); + + /** + * 校验并批量删除车辆出险记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 根据车辆ID查询最新的一条出险记录 + * + * @param vehicleId 车辆ID + * @return 最新出险记录 + */ + HotVehicleClaimVo queryLatestByVehicleId(String vehicleId); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/service/impl/HotVehicleClaimServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/service/impl/HotVehicleClaimServiceImpl.java new file mode 100644 index 0000000..02a2b68 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleClaim/service/impl/HotVehicleClaimServiceImpl.java @@ -0,0 +1,232 @@ +package com.hotwj.platform.resourceManagement.vehicleClaim.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import com.hotwj.platform.resourceManagement.vehicleClaim.domain.HotVehicleClaim; +import com.hotwj.platform.resourceManagement.vehicleClaim.domain.bo.HotVehicleClaimBo; +import com.hotwj.platform.resourceManagement.vehicleClaim.domain.vo.HotVehicleClaimVo; +import com.hotwj.platform.resourceManagement.vehicleClaim.mapper.HotVehicleClaimMapper; +import com.hotwj.platform.resourceManagement.vehicleClaim.service.IHotVehicleClaimService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.DateUtils; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 车辆出险记录Service业务层处理 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleClaimServiceImpl implements IHotVehicleClaimService { + + private final HotVehicleClaimMapper baseMapper; + private final HotCompanySafetyManagerMapper managerMapper; + private final IHotSystemNotificationService notificationService; + + /** + * 查询车辆出险记录 + * + * @param id 主键 + * @return 车辆出险记录 + */ + @Override + public HotVehicleClaimVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆出险记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆出险记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleClaimBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆出险记录列表 + * + * @param bo 查询条件 + * @return 车辆出险记录列表 + */ + @Override + public List queryList(HotVehicleClaimBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleClaimBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotVehicleClaim::getId); + lqw.eq(bo.getCompanyId() != null, HotVehicleClaim::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getVehicleId() != null, HotVehicleClaim::getVehicleId, bo.getVehicleId()); + lqw.eq(bo.getVehicleInsuranceId() != null, HotVehicleClaim::getVehicleInsuranceId, bo.getVehicleInsuranceId()); + lqw.like(StringUtils.isNotBlank(bo.getPlateNumber()), HotVehicleClaim::getPlateNumber, bo.getPlateNumber()); + lqw.like(StringUtils.isNotBlank(bo.getAccidentLocation()), HotVehicleClaim::getAccidentLocation, bo.getAccidentLocation()); + lqw.like(StringUtils.isNotBlank(bo.getInsuranceType()), HotVehicleClaim::getInsuranceType, bo.getInsuranceType()); + lqw.like(StringUtils.isNotBlank(bo.getInsurerName()), HotVehicleClaim::getInsurerName, bo.getInsurerName()); + lqw.eq(bo.getIncidentTime() != null, HotVehicleClaim::getIncidentTime, bo.getIncidentTime()); + lqw.eq(bo.getAssessDate() != null, HotVehicleClaim::getAssessDate, bo.getAssessDate()); + lqw.eq(bo.getInsuranceExpireDate() != null, HotVehicleClaim::getInsuranceExpireDate, bo.getInsuranceExpireDate()); + lqw.eq(StringUtils.isNotBlank(bo.getClaimItems()), HotVehicleClaim::getClaimItems, bo.getClaimItems()); + lqw.eq(StringUtils.isNotBlank(bo.getImageUrls()), HotVehicleClaim::getImageUrls, bo.getImageUrls()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleClaim::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增车辆出险记录 + * + * @param bo 车辆出险记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleClaimBo bo) { + HotVehicleClaim add = MapstructUtils.convert(bo, HotVehicleClaim.class); + add.setIsDeleted(0L); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + if (add.getCompanyId() != null) { + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, add.getCompanyId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers != null && !managers.isEmpty()) { + List receiverIds = managers.stream().map(m -> String.valueOf(m.getId())).toList(); + String plate = StringUtils.blankToDefault(add.getPlateNumber(), "未知车牌"); + String insuranceLabel = StringUtils.blankToDefault(add.getInsuranceType(), "车辆保险"); + String dateStr = add.getIncidentTime() != null ? DateUtils.formatDate(add.getIncidentTime()) : "未知日期"; + String content = "车辆【" + plate + "】的【" + insuranceLabel + "】由【" + dateStr + "】进行了一次出险。"; + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("车辆管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送车辆出险通知失败 companyId={} vehicleId={}", add.getCompanyId(), add.getVehicleId(), e); + } + } + } + } + return flag; + } + + /** + * 修改车辆出险记录 + * + * @param bo 车辆出险记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleClaimBo bo) { + HotVehicleClaim before = null; + if (bo.getId() != null) { + before = baseMapper.selectById(bo.getId()); + } + HotVehicleClaim update = MapstructUtils.convert(bo, HotVehicleClaim.class); + validEntityBeforeSave(update); + boolean ok = baseMapper.updateById(update) > 0; + if (ok) { + Long companyId = update.getCompanyId() != null ? update.getCompanyId() : (before != null ? before.getCompanyId() : null); + if (companyId != null) { + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers != null && !managers.isEmpty()) { + List receiverIds = managers.stream().map(m -> String.valueOf(m.getId())).toList(); + String plate = org.dromara.common.core.utils.StringUtils.blankToDefault(update.getPlateNumber() != null ? update.getPlateNumber() : (before != null ? before.getPlateNumber() : null), "未知车牌"); + String insuranceLabel = org.dromara.common.core.utils.StringUtils.blankToDefault(update.getInsuranceType() != null ? update.getInsuranceType() : (before != null ? before.getInsuranceType() : null), "车辆保险"); + String dateStr = update.getIncidentTime() != null ? org.dromara.common.core.utils.DateUtils.formatDate(update.getIncidentTime()) : (before != null && before.getIncidentTime() != null ? org.dromara.common.core.utils.DateUtils.formatDate(before.getIncidentTime()) : "未知日期"); + String content = "车辆【" + plate + "】的【" + insuranceLabel + "】在【" + dateStr + "】的出险记录已变更。"; + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("车辆管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("修改车辆出险记录通知失败 companyId={} vehicleId={}", companyId, update.getVehicleId(), e); + } + } + } + } + return ok; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleClaim entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除车辆出险记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteBatchIds(ids) > 0; + } + + /** + * 根据车辆ID查询最新的一条出险记录 + * + * @param vehicleId 车辆ID + * @return 最新出险记录 + */ + @Override + public HotVehicleClaimVo queryLatestByVehicleId(String vehicleId) { + return baseMapper.selectVoOne(new LambdaQueryWrapper() + .eq(HotVehicleClaim::getVehicleId, vehicleId) + .orderByDesc(HotVehicleClaim::getIncidentTime) + .last("LIMIT 1")); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/controller/HotVehicleContractController.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/controller/HotVehicleContractController.java new file mode 100644 index 0000000..9474be0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/controller/HotVehicleContractController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.resourceManagement.vehicleContract.controller; + +import com.hotwj.platform.resourceManagement.vehicleContract.domain.bo.HotVehicleContractBo; +import com.hotwj.platform.resourceManagement.vehicleContract.domain.vo.HotVehicleContractVo; +import com.hotwj.platform.resourceManagement.vehicleContract.service.IHotVehicleContractService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 车辆合同 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/vehicleContract") +@Tag(name = "车辆合同", description = "车辆合同管理") +public class HotVehicleContractController extends BaseController { + + private final IHotVehicleContractService hotVehicleContractService; + + /** + * 查询车辆合同列表 + */ + //@SaCheckPermission("resourceManagement:vehicleContract:list") + @GetMapping("/list") + @Operation(summary = "查询车辆合同列表") + public TableDataInfo list(HotVehicleContractBo bo, PageQuery pageQuery) { + return hotVehicleContractService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆合同列表 + */ + //@SaCheckPermission("resourceManagement:vehicleContract:export") + @Log(title = "车辆合同", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆合同列表") + public void export(HotVehicleContractBo bo, HttpServletResponse response) { + List list = hotVehicleContractService.queryList(bo); + ExcelUtil.exportExcel(list, "车辆合同", HotVehicleContractVo.class, response); + } + + /** + * 获取车辆合同详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:vehicleContract:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆合同详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotVehicleContractService.queryById(id)); + } + + /** + * 新增车辆合同 + */ + //@SaCheckPermission("resourceManagement:vehicleContract:add") + @Log(title = "车辆合同", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆合同") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleContractBo bo) { + return toAjax(hotVehicleContractService.insertByBo(bo)); + } + + /** + * 修改车辆合同 + */ + //@SaCheckPermission("resourceManagement:vehicleContract:edit") + @Log(title = "车辆合同", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆合同") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleContractBo bo) { + return toAjax(hotVehicleContractService.updateByBo(bo)); + } + + /** + * 删除车辆合同 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:vehicleContract:remove") + @Log(title = "车辆合同", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆合同") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotVehicleContractService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/domain/HotVehicleContract.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/domain/HotVehicleContract.java new file mode 100644 index 0000000..8c7e1ad --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/domain/HotVehicleContract.java @@ -0,0 +1,85 @@ +package com.hotwj.platform.resourceManagement.vehicleContract.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 车辆合同对象 hot_vehicle_contract + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_contract") +public class HotVehicleContract extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private String id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 合同名称 + */ + private String contractTitle; + + /** + * 车主姓名 + */ + private String ownerName; + + /** + * 联系方式 + */ + private String contactPhone; + + /** + * 签约时间 + */ + private Date signDate; + + /** + * 到期时间 + */ + private Date expireDate; + + /** + * 合同图片URL(多个逗号分隔) + */ + private String contractImageUrls; + + /** + * 文档附件URL(docx) + */ + private String docAttachmentUrl; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/domain/bo/HotVehicleContractBo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/domain/bo/HotVehicleContractBo.java new file mode 100644 index 0000000..4e3a111 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/domain/bo/HotVehicleContractBo.java @@ -0,0 +1,88 @@ +package com.hotwj.platform.resourceManagement.vehicleContract.domain.bo; + +import com.hotwj.platform.resourceManagement.vehicleContract.domain.HotVehicleContract; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 车辆合同业务对象 hot_vehicle_contract + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleContract.class, reverseConvertGenerate = false) +public class HotVehicleContractBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private String id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 合同名称 + */ + @NotBlank(message = "合同名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String contractTitle; + + /** + * 车主姓名 + */ + private String ownerName; + + /** + * 联系方式 + */ + private String contactPhone; + + /** + * 签约时间 + */ + @NotNull(message = "签约时间不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date signDate; + + /** + * 到期时间 + */ + @NotNull(message = "到期时间不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date expireDate; + + /** + * 合同图片URL(多个逗号分隔) + */ + private String contractImageUrls; + + /** + * 文档附件URL(docx) + */ + private String docAttachmentUrl; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/domain/vo/HotVehicleContractVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/domain/vo/HotVehicleContractVo.java new file mode 100644 index 0000000..e655940 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/domain/vo/HotVehicleContractVo.java @@ -0,0 +1,105 @@ +package com.hotwj.platform.resourceManagement.vehicleContract.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.hotwj.platform.resourceManagement.vehicleContract.domain.HotVehicleContract; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 车辆合同视图对象 hot_vehicle_contract + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleContract.class) +public class HotVehicleContractVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private String id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 车辆ID + */ + @ExcelProperty(value = "车辆ID") + private String vehicleId; + + /** + * 合同名称 + */ + @ExcelProperty(value = "合同名称") + private String contractTitle; + + /** + * 车主姓名 + */ + @ExcelProperty(value = "车主姓名") + private String ownerName; + + /** + * 联系方式 + */ + @ExcelProperty(value = "联系方式") + private String contactPhone; + + /** + * 签约时间 + */ + @ExcelProperty(value = "签约时间") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date signDate; + + /** + * 到期时间 + */ + @ExcelProperty(value = "到期时间") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date expireDate; + + /** + * 合同图片URL(多个逗号分隔) + */ + @ExcelProperty(value = "合同图片URL(多个逗号分隔)") + private String contractImageUrls; + + /** + * 合同图片URL(多个逗号分隔)Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "contractImageUrls") + private String contractImageUrlsUrl; + /** + * 文档附件URL(docx) + */ + @ExcelProperty(value = "文档附件URL(docx)") + private String docAttachmentUrl; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/mapper/HotVehicleContractMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/mapper/HotVehicleContractMapper.java new file mode 100644 index 0000000..af5822e --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/mapper/HotVehicleContractMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.vehicleContract.mapper; + +import com.hotwj.platform.resourceManagement.vehicleContract.domain.HotVehicleContract; +import com.hotwj.platform.resourceManagement.vehicleContract.domain.vo.HotVehicleContractVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆合同Mapper接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Mapper +public interface HotVehicleContractMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/service/IHotVehicleContractService.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/service/IHotVehicleContractService.java new file mode 100644 index 0000000..2f83019 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/service/IHotVehicleContractService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.resourceManagement.vehicleContract.service; + +import com.hotwj.platform.resourceManagement.vehicleContract.domain.bo.HotVehicleContractBo; +import com.hotwj.platform.resourceManagement.vehicleContract.domain.vo.HotVehicleContractVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆合同Service接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +public interface IHotVehicleContractService { + + /** + * 查询车辆合同 + * + * @param id 主键 + * @return 车辆合同 + */ + HotVehicleContractVo queryById(Long id); + + /** + * 分页查询车辆合同列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆合同分页列表 + */ + TableDataInfo queryPageList(HotVehicleContractBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆合同列表 + * + * @param bo 查询条件 + * @return 车辆合同列表 + */ + List queryList(HotVehicleContractBo bo); + + /** + * 新增车辆合同 + * + * @param bo 车辆合同 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleContractBo bo); + + /** + * 修改车辆合同 + * + * @param bo 车辆合同 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleContractBo bo); + + /** + * 校验并批量删除车辆合同信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/service/impl/HotVehicleContractServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/service/impl/HotVehicleContractServiceImpl.java new file mode 100644 index 0000000..1594b5c --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleContract/service/impl/HotVehicleContractServiceImpl.java @@ -0,0 +1,141 @@ +package com.hotwj.platform.resourceManagement.vehicleContract.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.vehicleContract.domain.HotVehicleContract; +import com.hotwj.platform.resourceManagement.vehicleContract.domain.bo.HotVehicleContractBo; +import com.hotwj.platform.resourceManagement.vehicleContract.domain.vo.HotVehicleContractVo; +import com.hotwj.platform.resourceManagement.vehicleContract.mapper.HotVehicleContractMapper; +import com.hotwj.platform.resourceManagement.vehicleContract.service.IHotVehicleContractService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 车辆合同Service业务层处理 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleContractServiceImpl implements IHotVehicleContractService { + + private final HotVehicleContractMapper baseMapper; + + /** + * 查询车辆合同 + * + * @param id 主键 + * @return 车辆合同 + */ + @Override + public HotVehicleContractVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆合同列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆合同分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleContractBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆合同列表 + * + * @param bo 查询条件 + * @return 车辆合同列表 + */ + @Override + public List queryList(HotVehicleContractBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleContractBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotVehicleContract::getId); + lqw.eq(bo.getCompanyId() != null, HotVehicleContract::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getVehicleId() != null, HotVehicleContract::getVehicleId, bo.getVehicleId()); + lqw.eq(StringUtils.isNotBlank(bo.getContractTitle()), HotVehicleContract::getContractTitle, bo.getContractTitle()); + lqw.eq(StringUtils.isNotBlank(bo.getOwnerName()), HotVehicleContract::getOwnerName, bo.getOwnerName()); + lqw.eq(StringUtils.isNotBlank(bo.getContactPhone()), HotVehicleContract::getContactPhone, bo.getContactPhone()); + lqw.eq(bo.getSignDate() != null, HotVehicleContract::getSignDate, bo.getSignDate()); + lqw.eq(bo.getExpireDate() != null, HotVehicleContract::getExpireDate, bo.getExpireDate()); + lqw.eq(StringUtils.isNotBlank(bo.getContractImageUrls()), HotVehicleContract::getContractImageUrls, bo.getContractImageUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getDocAttachmentUrl()), HotVehicleContract::getDocAttachmentUrl, bo.getDocAttachmentUrl()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleContract::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增车辆合同 + * + * @param bo 车辆合同 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleContractBo bo) { + HotVehicleContract add = MapstructUtils.convert(bo, HotVehicleContract.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改车辆合同 + * + * @param bo 车辆合同 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleContractBo bo) { + HotVehicleContract update = MapstructUtils.convert(bo, HotVehicleContract.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleContract entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除车辆合同信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/controller/HotVehicleDeviceController.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/controller/HotVehicleDeviceController.java new file mode 100644 index 0000000..d29be4f --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/controller/HotVehicleDeviceController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.resourceManagement.vehicleDevice.controller; + +import com.hotwj.platform.resourceManagement.vehicleDevice.domain.bo.HotVehicleDeviceBo; +import com.hotwj.platform.resourceManagement.vehicleDevice.domain.vo.HotVehicleDeviceVo; +import com.hotwj.platform.resourceManagement.vehicleDevice.service.IHotVehicleDeviceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 车辆安全设备 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/vehicleDevice") +@Tag(name = "车辆安全设备", description = "车辆安全设备管理") +public class HotVehicleDeviceController extends BaseController { + + private final IHotVehicleDeviceService hotVehicleDeviceService; + + /** + * 查询车辆安全设备列表 + */ + //@SaCheckPermission("resourceManagement:vehicleDevice:list") + @GetMapping("/list") + @Operation(summary = "查询车辆安全设备列表") + public TableDataInfo list(HotVehicleDeviceBo bo, PageQuery pageQuery) { + return hotVehicleDeviceService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆安全设备列表 + */ + //@SaCheckPermission("resourceManagement:vehicleDevice:export") + @Log(title = "车辆安全设备", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆安全设备列表") + public void export(HotVehicleDeviceBo bo, HttpServletResponse response) { + List list = hotVehicleDeviceService.queryList(bo); + ExcelUtil.exportExcel(list, "车辆安全设备", HotVehicleDeviceVo.class, response); + } + + /** + * 获取车辆安全设备详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:vehicleDevice:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆安全设备详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotVehicleDeviceService.queryById(id)); + } + + /** + * 新增车辆安全设备 + */ + //@SaCheckPermission("resourceManagement:vehicleDevice:add") + @Log(title = "车辆安全设备", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆安全设备") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleDeviceBo bo) { + return toAjax(hotVehicleDeviceService.insertByBo(bo)); + } + + /** + * 修改车辆安全设备 + */ + //@SaCheckPermission("resourceManagement:vehicleDevice:edit") + @Log(title = "车辆安全设备", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆安全设备") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleDeviceBo bo) { + return toAjax(hotVehicleDeviceService.updateByBo(bo)); + } + + /** + * 删除车辆安全设备 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:vehicleDevice:remove") + @Log(title = "车辆安全设备", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆安全设备") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotVehicleDeviceService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/domain/HotVehicleDevice.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/domain/HotVehicleDevice.java new file mode 100644 index 0000000..a727208 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/domain/HotVehicleDevice.java @@ -0,0 +1,54 @@ +package com.hotwj.platform.resourceManagement.vehicleDevice.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 车辆安全设备对象 hot_vehicle_device + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_device") +public class HotVehicleDevice extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private String id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 设备名称 + */ + private String deviceName; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/domain/bo/HotVehicleDeviceBo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/domain/bo/HotVehicleDeviceBo.java new file mode 100644 index 0000000..8894f3d --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/domain/bo/HotVehicleDeviceBo.java @@ -0,0 +1,54 @@ +package com.hotwj.platform.resourceManagement.vehicleDevice.domain.bo; + +import com.hotwj.platform.resourceManagement.vehicleDevice.domain.HotVehicleDevice; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 车辆安全设备业务对象 hot_vehicle_device + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleDevice.class, reverseConvertGenerate = false) +public class HotVehicleDeviceBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private String id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 设备名称 + */ + @NotBlank(message = "设备名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String deviceName; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/domain/vo/HotVehicleDeviceVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/domain/vo/HotVehicleDeviceVo.java new file mode 100644 index 0000000..4fa90b8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/domain/vo/HotVehicleDeviceVo.java @@ -0,0 +1,58 @@ +package com.hotwj.platform.resourceManagement.vehicleDevice.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.vehicleDevice.domain.HotVehicleDevice; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 车辆安全设备视图对象 hot_vehicle_device + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleDevice.class) +public class HotVehicleDeviceVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private String id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 车辆ID + */ + @ExcelProperty(value = "车辆ID") + private String vehicleId; + + /** + * 设备名称 + */ + @ExcelProperty(value = "设备名称") + private String deviceName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/mapper/HotVehicleDeviceMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/mapper/HotVehicleDeviceMapper.java new file mode 100644 index 0000000..440c586 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/mapper/HotVehicleDeviceMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.vehicleDevice.mapper; + +import com.hotwj.platform.resourceManagement.vehicleDevice.domain.HotVehicleDevice; +import com.hotwj.platform.resourceManagement.vehicleDevice.domain.vo.HotVehicleDeviceVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆安全设备Mapper接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Mapper +public interface HotVehicleDeviceMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/service/IHotVehicleDeviceService.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/service/IHotVehicleDeviceService.java new file mode 100644 index 0000000..21a1572 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/service/IHotVehicleDeviceService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.resourceManagement.vehicleDevice.service; + +import com.hotwj.platform.resourceManagement.vehicleDevice.domain.bo.HotVehicleDeviceBo; +import com.hotwj.platform.resourceManagement.vehicleDevice.domain.vo.HotVehicleDeviceVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆安全设备Service接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +public interface IHotVehicleDeviceService { + + /** + * 查询车辆安全设备 + * + * @param id 主键 + * @return 车辆安全设备 + */ + HotVehicleDeviceVo queryById(Long id); + + /** + * 分页查询车辆安全设备列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆安全设备分页列表 + */ + TableDataInfo queryPageList(HotVehicleDeviceBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆安全设备列表 + * + * @param bo 查询条件 + * @return 车辆安全设备列表 + */ + List queryList(HotVehicleDeviceBo bo); + + /** + * 新增车辆安全设备 + * + * @param bo 车辆安全设备 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleDeviceBo bo); + + /** + * 修改车辆安全设备 + * + * @param bo 车辆安全设备 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleDeviceBo bo); + + /** + * 校验并批量删除车辆安全设备信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/service/impl/HotVehicleDeviceServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/service/impl/HotVehicleDeviceServiceImpl.java new file mode 100644 index 0000000..f3d5e2c --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDevice/service/impl/HotVehicleDeviceServiceImpl.java @@ -0,0 +1,135 @@ +package com.hotwj.platform.resourceManagement.vehicleDevice.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.vehicleDevice.domain.HotVehicleDevice; +import com.hotwj.platform.resourceManagement.vehicleDevice.domain.bo.HotVehicleDeviceBo; +import com.hotwj.platform.resourceManagement.vehicleDevice.domain.vo.HotVehicleDeviceVo; +import com.hotwj.platform.resourceManagement.vehicleDevice.mapper.HotVehicleDeviceMapper; +import com.hotwj.platform.resourceManagement.vehicleDevice.service.IHotVehicleDeviceService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 车辆安全设备Service业务层处理 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleDeviceServiceImpl implements IHotVehicleDeviceService { + + private final HotVehicleDeviceMapper baseMapper; + + /** + * 查询车辆安全设备 + * + * @param id 主键 + * @return 车辆安全设备 + */ + @Override + public HotVehicleDeviceVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆安全设备列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆安全设备分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleDeviceBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆安全设备列表 + * + * @param bo 查询条件 + * @return 车辆安全设备列表 + */ + @Override + public List queryList(HotVehicleDeviceBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleDeviceBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotVehicleDevice::getId); + lqw.eq(bo.getCompanyId() != null, HotVehicleDevice::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getVehicleId() != null, HotVehicleDevice::getVehicleId, bo.getVehicleId()); + lqw.like(StringUtils.isNotBlank(bo.getDeviceName()), HotVehicleDevice::getDeviceName, bo.getDeviceName()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleDevice::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增车辆安全设备 + * + * @param bo 车辆安全设备 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleDeviceBo bo) { + HotVehicleDevice add = MapstructUtils.convert(bo, HotVehicleDevice.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改车辆安全设备 + * + * @param bo 车辆安全设备 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleDeviceBo bo) { + HotVehicleDevice update = MapstructUtils.convert(bo, HotVehicleDevice.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleDevice entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除车辆安全设备信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/controller/HotVehicleDriveLogController.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/controller/HotVehicleDriveLogController.java new file mode 100644 index 0000000..3cb98b8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/controller/HotVehicleDriveLogController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.resourceManagement.vehicleDriveLog.controller; + +import com.hotwj.platform.resourceManagement.vehicleDriveLog.domain.bo.HotVehicleDriveLogBo; +import com.hotwj.platform.resourceManagement.vehicleDriveLog.domain.vo.HotVehicleDriveLogVo; +import com.hotwj.platform.resourceManagement.vehicleDriveLog.service.IHotVehicleDriveLogService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 车辆行驶日志 + * + * @author shihongwei + * @date 2026-02-03 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/vehicleDriveLog") +@Tag(name = "车辆行驶日志", description = "车辆行驶日志管理") +public class HotVehicleDriveLogController extends BaseController { + + private final IHotVehicleDriveLogService hotVehicleDriveLogService; + + /** + * 查询车辆行驶日志列表 + */ + //@SaCheckPermission("resourceManagement:vehicleDriveLog:list") + @GetMapping("/list") + @Operation(summary = "分页查询车辆行驶日志列表") + public TableDataInfo list(HotVehicleDriveLogBo bo, PageQuery pageQuery) { + return hotVehicleDriveLogService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆行驶日志列表 + */ + //@SaCheckPermission("resourceManagement:vehicleDriveLog:export") + @Log(title = "车辆行驶日志", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆行驶日志列表") + public void export(HotVehicleDriveLogBo bo, HttpServletResponse response) { + List list = hotVehicleDriveLogService.queryList(bo); + ExcelUtil.exportExcel(list, "车辆行驶日志", HotVehicleDriveLogVo.class, response); + } + + /** + * 获取车辆行驶日志详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:vehicleDriveLog:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆行驶日志详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotVehicleDriveLogService.queryById(id)); + } + + /** + * 新增车辆行驶日志 + */ + //@SaCheckPermission("resourceManagement:vehicleDriveLog:add") + @Log(title = "车辆行驶日志", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆行驶日志") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleDriveLogBo bo) { + return toAjax(hotVehicleDriveLogService.insertByBo(bo)); + } + + /** + * 修改车辆行驶日志 + */ + //@SaCheckPermission("resourceManagement:vehicleDriveLog:edit") + @Log(title = "车辆行驶日志", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆行驶日志") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleDriveLogBo bo) { + return toAjax(hotVehicleDriveLogService.updateByBo(bo)); + } + + /** + * 删除车辆行驶日志 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:vehicleDriveLog:remove") + @Log(title = "车辆行驶日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆行驶日志") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotVehicleDriveLogService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/domain/HotVehicleDriveLog.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/domain/HotVehicleDriveLog.java new file mode 100644 index 0000000..0b6afa4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/domain/HotVehicleDriveLog.java @@ -0,0 +1,116 @@ +package com.hotwj.platform.resourceManagement.vehicleDriveLog.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 车辆行驶日志对象 hot_vehicle_drive_log + * + * @author shihongwei + * @date 2026-02-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_drive_log") +public class HotVehicleDriveLog extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + private Long companyId; + + /** + * 车辆ID + */ + private Long vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 驾驶员姓名 + */ + private String driverName; + + /** + * 驾驶员电话 + */ + private String driverPhone; + + /** + * 出发地 + */ + private String startPlace; + + /** + * 目的地 + */ + private String endPlace; + + /** + * 出发时间 + */ + private Date startTime; + + /** + * 到达时间 + */ + private Date endTime; + + /** + * 出发时里程(公里) + */ + private BigDecimal startMileageKm; + + /** + * 结束时里程(公里) + */ + private BigDecimal endMileageKm; + + /** + * 行驶里程(公里) + */ + private BigDecimal driveDistanceKm; + + /** + * 用车事由 + */ + private String usePurpose; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/domain/bo/HotVehicleDriveLogBo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/domain/bo/HotVehicleDriveLogBo.java new file mode 100644 index 0000000..33ff7e1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/domain/bo/HotVehicleDriveLogBo.java @@ -0,0 +1,112 @@ +package com.hotwj.platform.resourceManagement.vehicleDriveLog.domain.bo; + +import com.hotwj.platform.resourceManagement.vehicleDriveLog.domain.HotVehicleDriveLog; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 车辆行驶日志业务对象 hot_vehicle_drive_log + * + * @author shihongwei + * @date 2026-02-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleDriveLog.class, reverseConvertGenerate = false) +public class HotVehicleDriveLogBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + private Long companyId; + + /** + * 车辆ID + */ + private Long vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 驾驶员姓名 + */ + private String driverName; + + /** + * 驾驶员电话 + */ + private String driverPhone; + + /** + * 出发地 + */ + private String startPlace; + + /** + * 目的地 + */ + private String endPlace; + + /** + * 出发时间 + */ + private Date startTime; + + /** + * 到达时间 + */ + private Date endTime; + + /** + * 出发时里程(公里) + */ + private BigDecimal startMileageKm; + + /** + * 结束时里程(公里) + */ + private BigDecimal endMileageKm; + + /** + * 行驶里程(公里) + */ + private BigDecimal driveDistanceKm; + + /** + * 用车事由 + */ + private String usePurpose; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/domain/vo/HotVehicleDriveLogVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/domain/vo/HotVehicleDriveLogVo.java new file mode 100644 index 0000000..4ebe8ea --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/domain/vo/HotVehicleDriveLogVo.java @@ -0,0 +1,147 @@ +package com.hotwj.platform.resourceManagement.vehicleDriveLog.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.vehicleDriveLog.domain.HotVehicleDriveLog; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 车辆行驶日志视图对象 hot_vehicle_drive_log + * + * @author shihongwei + * @date 2026-02-03 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleDriveLog.class) +public class HotVehicleDriveLogVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "由=应用层保证必填") + private Long companyId; + + /** + * 车辆ID + */ + @ExcelProperty(value = "车辆ID") + private Long vehicleId; + + /** + * 车牌号 + */ + @ExcelProperty(value = "车牌号") + private String plateNumber; + + /** + * 驾驶员ID + */ + @ExcelProperty(value = "驾驶员ID") + private String driverId; + + /** + * 驾驶员姓名 + */ + @ExcelProperty(value = "驾驶员姓名") + private String driverName; + + /** + * 驾驶员电话 + */ + @ExcelProperty(value = "驾驶员电话") + private String driverPhone; + + /** + * 出发地 + */ + @ExcelProperty(value = "出发地") + private String startPlace; + + /** + * 目的地 + */ + @ExcelProperty(value = "目的地") + private String endPlace; + + /** + * 出发时间 + */ + @ExcelProperty(value = "出发时间") + private Date startTime; + + /** + * 到达时间 + */ + @ExcelProperty(value = "到达时间") + private Date endTime; + + /** + * 出发时里程(公里) + */ + @ExcelProperty(value = "出发时里程(公里)") + private BigDecimal startMileageKm; + + /** + * 结束时里程(公里) + */ + @ExcelProperty(value = "结束时里程(公里)") + private BigDecimal endMileageKm; + + /** + * 行驶里程(公里) + */ + @ExcelProperty(value = "行驶里程(公里)") + private BigDecimal driveDistanceKm; + + /** + * 用车事由 + */ + @ExcelProperty(value = "用车事由") + private String usePurpose; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/mapper/HotVehicleDriveLogMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/mapper/HotVehicleDriveLogMapper.java new file mode 100644 index 0000000..2a7467c --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/mapper/HotVehicleDriveLogMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.vehicleDriveLog.mapper; + +import com.hotwj.platform.resourceManagement.vehicleDriveLog.domain.HotVehicleDriveLog; +import com.hotwj.platform.resourceManagement.vehicleDriveLog.domain.vo.HotVehicleDriveLogVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆行驶日志Mapper接口 + * + * @author shihongwei + * @date 2026-02-03 + */ +@Mapper +public interface HotVehicleDriveLogMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/service/IHotVehicleDriveLogService.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/service/IHotVehicleDriveLogService.java new file mode 100644 index 0000000..1cb7787 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/service/IHotVehicleDriveLogService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.resourceManagement.vehicleDriveLog.service; + +import com.hotwj.platform.resourceManagement.vehicleDriveLog.domain.bo.HotVehicleDriveLogBo; +import com.hotwj.platform.resourceManagement.vehicleDriveLog.domain.vo.HotVehicleDriveLogVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆行驶日志Service接口 + * + * @author shihongwei + * @date 2026-02-03 + */ +public interface IHotVehicleDriveLogService { + + /** + * 查询车辆行驶日志 + * + * @param id 主键 + * @return 车辆行驶日志 + */ + HotVehicleDriveLogVo queryById(Long id); + + /** + * 分页查询车辆行驶日志列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆行驶日志分页列表 + */ + TableDataInfo queryPageList(HotVehicleDriveLogBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆行驶日志列表 + * + * @param bo 查询条件 + * @return 车辆行驶日志列表 + */ + List queryList(HotVehicleDriveLogBo bo); + + /** + * 新增车辆行驶日志 + * + * @param bo 车辆行驶日志 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleDriveLogBo bo); + + /** + * 修改车辆行驶日志 + * + * @param bo 车辆行驶日志 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleDriveLogBo bo); + + /** + * 校验并批量删除车辆行驶日志信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/service/impl/HotVehicleDriveLogServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/service/impl/HotVehicleDriveLogServiceImpl.java new file mode 100644 index 0000000..093ba04 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleDriveLog/service/impl/HotVehicleDriveLogServiceImpl.java @@ -0,0 +1,195 @@ +package com.hotwj.platform.resourceManagement.vehicleDriveLog.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.vehicleDriveLog.domain.HotVehicleDriveLog; +import com.hotwj.platform.resourceManagement.vehicleDriveLog.domain.bo.HotVehicleDriveLogBo; +import com.hotwj.platform.resourceManagement.vehicleDriveLog.domain.vo.HotVehicleDriveLogVo; +import com.hotwj.platform.resourceManagement.vehicleDriveLog.mapper.HotVehicleDriveLogMapper; +import com.hotwj.platform.resourceManagement.vehicleDriveLog.service.IHotVehicleDriveLogService; +import com.hotwj.platform.resourceManagement.vehicleMileage.domain.bo.HotVehicleMileageBo; +import com.hotwj.platform.resourceManagement.vehicleMileage.service.IHotVehicleMileageService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 车辆行驶日志Service业务层处理 + * + * @author shihongwei + * @date 2026-02-03 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleDriveLogServiceImpl implements IHotVehicleDriveLogService { + + private final HotVehicleDriveLogMapper baseMapper; + private final IHotVehicleMileageService hotVehicleMileageService; + + /** + * 查询车辆行驶日志 + * + * @param id 主键 + * @return 车辆行驶日志 + */ + @Override + public HotVehicleDriveLogVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆行驶日志列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆行驶日志分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleDriveLogBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆行驶日志列表 + * + * @param bo 查询条件 + * @return 车辆行驶日志列表 + */ + @Override + public List queryList(HotVehicleDriveLogBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleDriveLogBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotVehicleDriveLog::getId); + lqw.eq(bo.getCompanyId() != null, HotVehicleDriveLog::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getVehicleId() != null, HotVehicleDriveLog::getVehicleId, bo.getVehicleId()); + lqw.eq(StringUtils.isNotBlank(bo.getPlateNumber()), HotVehicleDriveLog::getPlateNumber, bo.getPlateNumber()); + lqw.eq(StringUtils.isNotBlank(bo.getDriverId()), HotVehicleDriveLog::getDriverId, bo.getDriverId()); + lqw.like(StringUtils.isNotBlank(bo.getDriverName()), HotVehicleDriveLog::getDriverName, bo.getDriverName()); + lqw.eq(StringUtils.isNotBlank(bo.getDriverPhone()), HotVehicleDriveLog::getDriverPhone, bo.getDriverPhone()); + lqw.eq(StringUtils.isNotBlank(bo.getStartPlace()), HotVehicleDriveLog::getStartPlace, bo.getStartPlace()); + lqw.eq(StringUtils.isNotBlank(bo.getEndPlace()), HotVehicleDriveLog::getEndPlace, bo.getEndPlace()); + lqw.eq(bo.getStartTime() != null, HotVehicleDriveLog::getStartTime, bo.getStartTime()); + lqw.eq(bo.getEndTime() != null, HotVehicleDriveLog::getEndTime, bo.getEndTime()); + lqw.eq(bo.getStartMileageKm() != null, HotVehicleDriveLog::getStartMileageKm, bo.getStartMileageKm()); + lqw.eq(bo.getEndMileageKm() != null, HotVehicleDriveLog::getEndMileageKm, bo.getEndMileageKm()); + lqw.eq(bo.getDriveDistanceKm() != null, HotVehicleDriveLog::getDriveDistanceKm, bo.getDriveDistanceKm()); + lqw.eq(StringUtils.isNotBlank(bo.getUsePurpose()), HotVehicleDriveLog::getUsePurpose, bo.getUsePurpose()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotVehicleDriveLog::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotVehicleDriveLog::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleDriveLog::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增车辆行驶日志 + * + * @param bo 车辆行驶日志 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleDriveLogBo bo) { + HotVehicleDriveLog add = MapstructUtils.convert(bo, HotVehicleDriveLog.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + generateMileageRecord(add); + } + return flag; + } + + /** + * 修改车辆行驶日志 + * + * @param bo 车辆行驶日志 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleDriveLogBo bo) { + HotVehicleDriveLog update = MapstructUtils.convert(bo, HotVehicleDriveLog.class); + validEntityBeforeSave(update); + boolean flag = baseMapper.updateById(update) > 0; + if (flag && bo.getDriveDistanceKm() != null) { + HotVehicleDriveLog fullLog = baseMapper.selectById(bo.getId()); + generateMileageRecord(fullLog); + } + return flag; + } + + /** + * 生成车辆行驶里程记录 + * + * @param entity 车辆行驶日志 + */ + private void generateMileageRecord(HotVehicleDriveLog entity) { + if (entity.getDriveDistanceKm() != null) { + HotVehicleMileageBo mileageBo = new HotVehicleMileageBo(); + mileageBo.setCompanyId(entity.getCompanyId()); + mileageBo.setVehicleId(entity.getVehicleId()); + mileageBo.setPlateNumber(entity.getPlateNumber()); + mileageBo.setStartDate(entity.getStartTime()); + mileageBo.setEndDate(entity.getEndTime()); + mileageBo.setMileageKm(entity.getDriveDistanceKm()); + mileageBo.setRemark("由车辆行驶日志自动生成"); + hotVehicleMileageService.insertByBo(mileageBo); + } + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleDriveLog entity) { + //TODO 做一些数据校验,如唯一约束 + if (entity.getStartTime() != null && entity.getEndTime() != null) { + long timeDiff = entity.getEndTime().getTime() - entity.getStartTime().getTime(); + if (timeDiff < 0) { + throw new org.dromara.common.core.exception.ServiceException("到达时间不能早于出发时间"); + } + if (timeDiff > 0) { + java.math.BigDecimal distance = entity.getDriveDistanceKm(); + if (distance == null && entity.getStartMileageKm() != null && entity.getEndMileageKm() != null) { + distance = entity.getEndMileageKm().subtract(entity.getStartMileageKm()); + } + if (distance != null) { + double hours = timeDiff / (1000.0 * 60 * 60); + double speed = distance.doubleValue() / hours; + if (speed > 150.0) { + throw new org.dromara.common.core.exception.ServiceException("平均速度超过150km/h,请检查行驶里程和时间是否填写正确"); + } + } + } + } + } + + /** + * 校验并批量删除车辆行驶日志信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/controller/VehicleInfoController.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/controller/VehicleInfoController.java new file mode 100644 index 0000000..393a078 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/controller/VehicleInfoController.java @@ -0,0 +1,34 @@ +package com.hotwj.platform.resourceManagement.vehicleInfo.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.hotwj.platform.resourceManagement.vehicleInfo.domain.bo.VehicleInfoBo; +import com.hotwj.platform.resourceManagement.vehicleInfo.domain.vo.VehicleInfoVo; +import com.hotwj.platform.resourceManagement.vehicleInfo.service.IVehicleInfoService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/vehicle-info") +@Tag(name = "车辆信息", description = "政府端车辆信息分页查询") +public class VehicleInfoController extends BaseController { + + private final IVehicleInfoService vehicleInfoService; + + //@SaCheckPermission("resourceManagement:vehicleInfo:list") + @GetMapping("/page") + @Operation(summary = "车辆信息分页查询(政府端)") + public TableDataInfo page(PageQuery pageQuery, VehicleInfoBo bo) { + return vehicleInfoService.queryPageByGovDistrict(pageQuery, bo); + } +} + diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/domain/bo/VehicleInfoBo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/domain/bo/VehicleInfoBo.java new file mode 100644 index 0000000..bf7110a --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/domain/bo/VehicleInfoBo.java @@ -0,0 +1,29 @@ +package com.hotwj.platform.resourceManagement.vehicleInfo.domain.bo; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class VehicleInfoBo implements Serializable { + + /** + * 企业名称 + */ + private String companyName; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 燃油类型 + */ + private String fuelType; + + /** + * 运营状态 + */ + private Long operationStatus; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/domain/vo/VehicleInfoVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/domain/vo/VehicleInfoVo.java new file mode 100644 index 0000000..8c909b0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/domain/vo/VehicleInfoVo.java @@ -0,0 +1,51 @@ +package com.hotwj.platform.resourceManagement.vehicleInfo.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +@Data +public class VehicleInfoVo implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + + private String companyName; + + private String plateNumber; + + private String brandModel; + + private String vehicleType; + + private String vehicleModel; + + private String plateColor; + + @JsonFormat(pattern = "yyyy-MM-dd") + private Date vehicleRegisterDate; + + private Long ratedLoadKg; + + private String driveMotorModel; + + private String frameNumber; + + private String transportLicenseNo; + + private String fuelType; + + private String technicalLevel; + + private String transportValidMonth; + + @JsonFormat(pattern = "yyyy-MM-dd") + private Date inspectionDate; + + private Long operationStatus; +} + diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/mapper/VehicleInfoMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/mapper/VehicleInfoMapper.java new file mode 100644 index 0000000..3aae5f9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/mapper/VehicleInfoMapper.java @@ -0,0 +1,16 @@ +package com.hotwj.platform.resourceManagement.vehicleInfo.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.vehicleInfo.domain.bo.VehicleInfoBo; +import com.hotwj.platform.resourceManagement.vehicleInfo.domain.vo.VehicleInfoVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface VehicleInfoMapper { + List selectVehicleInfoPage(Page page, + @Param("districtCode") String districtCode, + @Param("bo") VehicleInfoBo bo); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/service/IVehicleInfoService.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/service/IVehicleInfoService.java new file mode 100644 index 0000000..8fdeb49 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/service/IVehicleInfoService.java @@ -0,0 +1,11 @@ +package com.hotwj.platform.resourceManagement.vehicleInfo.service; + +import com.hotwj.platform.resourceManagement.vehicleInfo.domain.bo.VehicleInfoBo; +import com.hotwj.platform.resourceManagement.vehicleInfo.domain.vo.VehicleInfoVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +public interface IVehicleInfoService { + TableDataInfo queryPageByGovDistrict(PageQuery pageQuery, VehicleInfoBo bo); +} + diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/service/impl/VehicleInfoServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/service/impl/VehicleInfoServiceImpl.java new file mode 100644 index 0000000..b3186fc --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInfo/service/impl/VehicleInfoServiceImpl.java @@ -0,0 +1,47 @@ +package com.hotwj.platform.resourceManagement.vehicleInfo.service.impl; + +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.vo.HotGovEnterpriseUnitVo; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.service.IHotGovEnterpriseUnitService; +import com.hotwj.platform.resourceManagement.vehicleInfo.domain.bo.VehicleInfoBo; +import com.hotwj.platform.resourceManagement.vehicleInfo.domain.vo.VehicleInfoVo; +import com.hotwj.platform.resourceManagement.vehicleInfo.mapper.VehicleInfoMapper; +import com.hotwj.platform.resourceManagement.vehicleInfo.service.IVehicleInfoService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class VehicleInfoServiceImpl implements IVehicleInfoService { + + private final VehicleInfoMapper vehicleInfoMapper; + private final IHotGovEnterpriseUnitService hotGovEnterpriseUnitService; + + @Override + public TableDataInfo queryPageByGovDistrict(PageQuery pageQuery, VehicleInfoBo bo) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return TableDataInfo.build(); + } + HotGovEnterpriseUnitVo unitVo = hotGovEnterpriseUnitService.queryById(loginUser.getCompanyId()); + if (unitVo == null || unitVo.getDistrictCode() == null) { + return TableDataInfo.build(); + } + Page page = pageQuery.build(); + if (page.orders() != null) { + for (OrderItem order : page.orders()) { + if ("create_time".equals(order.getColumn())) { + order.setColumn("v.create_time"); + } + } + } + page.setRecords(vehicleInfoMapper.selectVehicleInfoPage(page, unitVo.getDistrictCode(), bo)); + return TableDataInfo.build(page); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/controller/HotVehicleInsuranceController.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/controller/HotVehicleInsuranceController.java new file mode 100644 index 0000000..1c7999b --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/controller/HotVehicleInsuranceController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.resourceManagement.vehicleInsurance.controller; + +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.bo.HotVehicleInsuranceBo; +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.vo.HotVehicleInsuranceVo; +import com.hotwj.platform.resourceManagement.vehicleInsurance.service.IHotVehicleInsuranceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 车辆保险信息 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/vehicleInsurance") +@Tag(name = "车辆保险信息", description = "车辆保险信息管理") +public class HotVehicleInsuranceController extends BaseController { + + private final IHotVehicleInsuranceService hotVehicleInsuranceService; + + /** + * 查询车辆保险信息列表 + */ + //@SaCheckPermission("resourceManagement:vehicleInsurance:list") + @GetMapping("/list") + @Operation(summary = "查询车辆保险信息列表") + public TableDataInfo list(HotVehicleInsuranceBo bo, PageQuery pageQuery) { + return hotVehicleInsuranceService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆保险信息列表 + */ + //@SaCheckPermission("resourceManagement:vehicleInsurance:export") + @Log(title = "车辆保险信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆保险信息列表") + public void export(HotVehicleInsuranceBo bo, HttpServletResponse response) { + List list = hotVehicleInsuranceService.queryList(bo); + ExcelUtil.exportExcel(list, "车辆保险信息", HotVehicleInsuranceVo.class, response); + } + + /** + * 获取车辆保险信息详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:vehicleInsurance:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆保险信息详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotVehicleInsuranceService.queryById(id)); + } + + /** + * 新增车辆保险信息 + */ + //@SaCheckPermission("resourceManagement:vehicleInsurance:add") + @Log(title = "车辆保险信息", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆保险信息") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleInsuranceBo bo) { + return toAjax(hotVehicleInsuranceService.insertByBo(bo)); + } + + /** + * 修改车辆保险信息 + */ + //@SaCheckPermission("resourceManagement:vehicleInsurance:edit") + @Log(title = "车辆保险信息", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆保险信息") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleInsuranceBo bo) { + return toAjax(hotVehicleInsuranceService.updateByBo(bo)); + } + + /** + * 删除车辆保险信息 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:vehicleInsurance:remove") + @Log(title = "车辆保险信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆保险信息") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotVehicleInsuranceService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/domain/HotVehicleInsurance.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/domain/HotVehicleInsurance.java new file mode 100644 index 0000000..2e2e3b6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/domain/HotVehicleInsurance.java @@ -0,0 +1,96 @@ +package com.hotwj.platform.resourceManagement.vehicleInsurance.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 车辆保险信息对象 hot_vehicle_insurance + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_insurance") +public class HotVehicleInsurance extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private String id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 险种 + */ + private String insuranceType; + + /** + * 保险公司 + */ + private String insurerName; + + /** + * 保险费用 + */ + private BigDecimal premiumAmount; + + /** + * 渠道人 + */ + private String channelPerson; + + /** + * 渠道人电话 + */ + private String channelPhone; + + /** + * 起保日期 + */ + private Date startDate; + + /** + * 终保日期 + */ + private Date endDate; + + /** + * 保险附件图片URL(多个逗号分隔) + */ + private String imageUrls; + + /** + * 是否归档(0=否,1=是) + */ + private Integer isArchived; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/domain/bo/HotVehicleInsuranceBo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/domain/bo/HotVehicleInsuranceBo.java new file mode 100644 index 0000000..a353f3a --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/domain/bo/HotVehicleInsuranceBo.java @@ -0,0 +1,101 @@ +package com.hotwj.platform.resourceManagement.vehicleInsurance.domain.bo; + +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.HotVehicleInsurance; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 车辆保险信息业务对象 hot_vehicle_insurance + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleInsurance.class, reverseConvertGenerate = false) +public class HotVehicleInsuranceBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private String id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 险种 + */ + @NotBlank(message = "险种不能为空", groups = {AddGroup.class, EditGroup.class}) + private String insuranceType; + + /** + * 保险公司 + */ + @NotBlank(message = "保险公司不能为空", groups = {AddGroup.class, EditGroup.class}) + private String insurerName; + + /** + * 保险费用 + */ + @NotNull(message = "保险费用不能为空", groups = {AddGroup.class, EditGroup.class}) + private BigDecimal premiumAmount; + + /** + * 渠道人 + */ + private String channelPerson; + + /** + * 渠道人电话 + */ + private String channelPhone; + + /** + * 起保日期 + */ + @NotNull(message = "起保日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date startDate; + + /** + * 终保日期 + */ + @NotNull(message = "终保日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date endDate; + + /** + * 保险附件图片URL(多个逗号分隔) + */ + private String imageUrls; + + /** + * 是否归档(0=否,1=是) + */ + private Integer isArchived; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/domain/vo/HotVehicleInsuranceVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/domain/vo/HotVehicleInsuranceVo.java new file mode 100644 index 0000000..87b3694 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/domain/vo/HotVehicleInsuranceVo.java @@ -0,0 +1,119 @@ +package com.hotwj.platform.resourceManagement.vehicleInsurance.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.HotVehicleInsurance; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 车辆保险信息视图对象 hot_vehicle_insurance + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleInsurance.class) +public class HotVehicleInsuranceVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private String id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 车辆ID + */ + @ExcelProperty(value = "车辆ID") + private String vehicleId; + + /** + * 险种 + */ + @ExcelProperty(value = "险种") + private String insuranceType; + + /** + * 保险公司 + */ + @ExcelProperty(value = "保险公司") + private String insurerName; + + /** + * 保险费用 + */ + @ExcelProperty(value = "保险费用") + private BigDecimal premiumAmount; + + /** + * 渠道人 + */ + @ExcelProperty(value = "渠道人") + private String channelPerson; + + /** + * 渠道人电话 + */ + @ExcelProperty(value = "渠道人电话") + private String channelPhone; + + /** + * 起保日期 + */ + @ExcelProperty(value = "起保日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date startDate; + + /** + * 终保日期 + */ + @ExcelProperty(value = "终保日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date endDate; + + /** + * 保险附件图片URL(多个逗号分隔) + */ + @ExcelProperty(value = "保险附件图片URL(多个逗号分隔)") + private String imageUrls; + + /** + * 保险附件图片URL(多个逗号分隔)Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "imageUrls") + private String imageUrlsUrl; + + /** + * 是否归档(0=否,1=是) + */ + @ExcelProperty(value = "是否归档(0=否,1=是)") + private Integer isArchived; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/mapper/HotVehicleInsuranceMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/mapper/HotVehicleInsuranceMapper.java new file mode 100644 index 0000000..d87db8c --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/mapper/HotVehicleInsuranceMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.vehicleInsurance.mapper; + +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.HotVehicleInsurance; +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.vo.HotVehicleInsuranceVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆保险信息Mapper接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Mapper +public interface HotVehicleInsuranceMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/service/IHotVehicleInsuranceService.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/service/IHotVehicleInsuranceService.java new file mode 100644 index 0000000..9b48a4a --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/service/IHotVehicleInsuranceService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.resourceManagement.vehicleInsurance.service; + +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.bo.HotVehicleInsuranceBo; +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.vo.HotVehicleInsuranceVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆保险信息Service接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +public interface IHotVehicleInsuranceService { + + /** + * 查询车辆保险信息 + * + * @param id 主键 + * @return 车辆保险信息 + */ + HotVehicleInsuranceVo queryById(Long id); + + /** + * 分页查询车辆保险信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆保险信息分页列表 + */ + TableDataInfo queryPageList(HotVehicleInsuranceBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆保险信息列表 + * + * @param bo 查询条件 + * @return 车辆保险信息列表 + */ + List queryList(HotVehicleInsuranceBo bo); + + /** + * 新增车辆保险信息 + * + * @param bo 车辆保险信息 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleInsuranceBo bo); + + /** + * 修改车辆保险信息 + * + * @param bo 车辆保险信息 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleInsuranceBo bo); + + /** + * 校验并批量删除车辆保险信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/service/impl/HotVehicleInsuranceServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/service/impl/HotVehicleInsuranceServiceImpl.java new file mode 100644 index 0000000..198a8c1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleInsurance/service/impl/HotVehicleInsuranceServiceImpl.java @@ -0,0 +1,163 @@ +package com.hotwj.platform.resourceManagement.vehicleInsurance.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.HotVehicleInsurance; +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.bo.HotVehicleInsuranceBo; +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.vo.HotVehicleInsuranceVo; +import com.hotwj.platform.resourceManagement.vehicleInsurance.mapper.HotVehicleInsuranceMapper; +import com.hotwj.platform.resourceManagement.vehicleInsurance.service.IHotVehicleInsuranceService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 车辆保险信息Service业务层处理 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleInsuranceServiceImpl implements IHotVehicleInsuranceService { + + private final HotVehicleInsuranceMapper baseMapper; + + /** + * 查询车辆保险信息 + * + * @param id 主键 + * @return 车辆保险信息 + */ + @Override + public HotVehicleInsuranceVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆保险信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆保险信息分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleInsuranceBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆保险信息列表 + * + * @param bo 查询条件 + * @return 车辆保险信息列表 + */ + @Override + public List queryList(HotVehicleInsuranceBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleInsuranceBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotVehicleInsurance::getId); + lqw.eq(bo.getCompanyId() != null, HotVehicleInsurance::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getVehicleId() != null, HotVehicleInsurance::getVehicleId, bo.getVehicleId()); + lqw.eq(StringUtils.isNotBlank(bo.getInsuranceType()), HotVehicleInsurance::getInsuranceType, bo.getInsuranceType()); + lqw.eq(StringUtils.isNotBlank(bo.getInsurerName()), HotVehicleInsurance::getInsurerName, bo.getInsurerName()); + lqw.eq(bo.getPremiumAmount() != null, HotVehicleInsurance::getPremiumAmount, bo.getPremiumAmount()); + lqw.eq(StringUtils.isNotBlank(bo.getChannelPerson()), HotVehicleInsurance::getChannelPerson, bo.getChannelPerson()); + lqw.eq(StringUtils.isNotBlank(bo.getChannelPhone()), HotVehicleInsurance::getChannelPhone, bo.getChannelPhone()); + lqw.eq(bo.getStartDate() != null, HotVehicleInsurance::getStartDate, bo.getStartDate()); + lqw.eq(bo.getEndDate() != null, HotVehicleInsurance::getEndDate, bo.getEndDate()); + lqw.eq(bo.getIsArchived() != null, HotVehicleInsurance::getIsArchived, bo.getIsArchived()); + lqw.eq(StringUtils.isNotBlank(bo.getImageUrls()), HotVehicleInsurance::getImageUrls, bo.getImageUrls()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleInsurance::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增车辆保险信息 + * + * @param bo 车辆保险信息 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleInsuranceBo bo) { + HotVehicleInsurance add = MapstructUtils.convert(bo, HotVehicleInsurance.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改车辆保险信息 + * + * @param bo 车辆保险信息 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleInsuranceBo bo) { + // 检查是否已归档 + HotVehicleInsurance old = baseMapper.selectById(bo.getId()); + if (old != null && Integer.valueOf(1).equals(old.getIsArchived())) { + throw new ServiceException("该保险记录已归档,无法修改"); + } + + HotVehicleInsurance update = MapstructUtils.convert(bo, HotVehicleInsurance.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleInsurance entity) { + // 校验日期:起保日期不能晚于终保日期 + if (entity.getStartDate() != null && entity.getEndDate() != null) { + if (entity.getStartDate().after(entity.getEndDate())) { + throw new IllegalArgumentException("起保日期不能晚于终保日期"); + } + } + } + + /** + * 校验并批量删除车辆保险信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // 检查是否已归档 + if (ids != null && !ids.isEmpty()) { + List list = baseMapper.selectBatchIds(ids); + for (HotVehicleInsurance insurance : list) { + if (Integer.valueOf(1).equals(insurance.getIsArchived())) { + throw new ServiceException("存在已归档的保险记录,无法删除"); + } + } + } + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/controller/HotVehicleMaintenanceController.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/controller/HotVehicleMaintenanceController.java new file mode 100644 index 0000000..3431e5d --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/controller/HotVehicleMaintenanceController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.resourceManagement.vehicleMaintenance.controller; + +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.bo.HotVehicleMaintenanceBo; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.vo.HotVehicleMaintenanceVo; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.service.IHotVehicleMaintenanceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 车辆维护记录 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/vehicleMaintenance") +@Tag(name = "车辆维护记录", description = "车辆维护记录管理") +public class HotVehicleMaintenanceController extends BaseController { + + private final IHotVehicleMaintenanceService hotVehicleMaintenanceService; + + /** + * 查询车辆维护记录列表 + */ + //@SaCheckPermission("resourceManagement:vehicleMaintenance:list") + @GetMapping("/list") + @Operation(summary = "查询车辆维护记录列表") + public TableDataInfo list(HotVehicleMaintenanceBo bo, PageQuery pageQuery) { + return hotVehicleMaintenanceService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆维护记录列表 + */ + //@SaCheckPermission("resourceManagement:vehicleMaintenance:export") + @Log(title = "车辆维护记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆维护记录列表") + public void export(HotVehicleMaintenanceBo bo, HttpServletResponse response) { + List list = hotVehicleMaintenanceService.queryList(bo); + ExcelUtil.exportExcel(list, "车辆维护记录", HotVehicleMaintenanceVo.class, response); + } + + /** + * 获取车辆维护记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:vehicleMaintenance:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆维护记录详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotVehicleMaintenanceService.queryById(id)); + } + + /** + * 新增车辆维护记录 + */ + //@SaCheckPermission("resourceManagement:vehicleMaintenance:add") + @Log(title = "车辆维护记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆维护记录") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleMaintenanceBo bo) { + return toAjax(hotVehicleMaintenanceService.insertByBo(bo)); + } + + /** + * 修改车辆维护记录 + */ + //@SaCheckPermission("resourceManagement:vehicleMaintenance:edit") + @Log(title = "车辆维护记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆维护记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleMaintenanceBo bo) { + return toAjax(hotVehicleMaintenanceService.updateByBo(bo)); + } + + /** + * 删除车辆维护记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:vehicleMaintenance:remove") + @Log(title = "车辆维护记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆维护记录") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotVehicleMaintenanceService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/domain/HotVehicleMaintenance.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/domain/HotVehicleMaintenance.java new file mode 100644 index 0000000..009e7cc --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/domain/HotVehicleMaintenance.java @@ -0,0 +1,89 @@ +package com.hotwj.platform.resourceManagement.vehicleMaintenance.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serial; + +/** + * 车辆维护记录对象 hot_vehicle_maintenance + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_maintenance") +public class HotVehicleMaintenance extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private String id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 维护日期 + */ + private Date maintainDate; + + /** + * 维护完成时间 + */ + private Date finishTime; + + /** + * 拖修方 + */ + private String transferor; + + /** + * 进厂检验单图片URL(多个逗号分隔) + */ + private String entryInspectImageUrls; + + /** + * 过程检验单图片URL(多个逗号分隔) + */ + private String processInspectImageUrls; + + /** + * 竣工检验单图片URL(多个逗号分隔) + */ + private String finalInspectImageUrls; + + /** + * 竣工出厂合格证图片URL(多个逗号分隔) + */ + private String factoryCertImageUrls; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/domain/bo/HotVehicleMaintenanceBo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/domain/bo/HotVehicleMaintenanceBo.java new file mode 100644 index 0000000..1c172f7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/domain/bo/HotVehicleMaintenanceBo.java @@ -0,0 +1,90 @@ +package com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.bo; + +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.HotVehicleMaintenance; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * 车辆维护记录业务对象 hot_vehicle_maintenance + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleMaintenance.class, reverseConvertGenerate = false) +public class HotVehicleMaintenanceBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { EditGroup.class }) + private String id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 维护日期 + */ + @NotNull(message = "维护日期不能为空", groups = { AddGroup.class, EditGroup.class }) + private Date maintainDate; + + /** + * 维护完成时间 + */ + private Date finishTime; + + /** + * 拖修方 + */ + private String transferor; + + /** + * 进厂检验单图片URL(多个逗号分隔) + */ + private String entryInspectImageUrls; + + /** + * 过程检验单图片URL(多个逗号分隔) + */ + private String processInspectImageUrls; + + /** + * 竣工检验单图片URL(多个逗号分隔) + */ + private String finalInspectImageUrls; + + /** + * 竣工出厂合格证图片URL(多个逗号分隔) + */ + private String factoryCertImageUrls; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/domain/vo/HotVehicleMaintenanceVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/domain/vo/HotVehicleMaintenanceVo.java new file mode 100644 index 0000000..46ebffa --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/domain/vo/HotVehicleMaintenanceVo.java @@ -0,0 +1,106 @@ +package com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.vo; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.HotVehicleMaintenance; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 车辆维护记录视图对象 hot_vehicle_maintenance + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleMaintenance.class) +public class HotVehicleMaintenanceVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private String id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 车辆ID + */ + @ExcelProperty(value = "车辆ID") + private String vehicleId; + + /** + * 车牌号 + */ + @ExcelProperty(value = "车牌号") + private String plateNumber; + + /** + * 维护日期 + */ + @ExcelProperty(value = "维护日期") + private Date maintainDate; + + /** + * 维护完成时间 + */ + @ExcelProperty(value = "维护完成时间") + private Date finishTime; + + /** + * 拖修方 + */ + @ExcelProperty(value = "拖修方") + private String transferor; + + /** + * 进厂检验单图片URL(多个逗号分隔) + */ + @ExcelProperty(value = "进厂检验单图片URL(多个逗号分隔)") + private String entryInspectImageUrls; + + /** + * 过程检验单图片URL(多个逗号分隔) + */ + @ExcelProperty(value = "过程检验单图片URL(多个逗号分隔)") + private String processInspectImageUrls; + + /** + * 竣工检验单图片URL(多个逗号分隔) + */ + @ExcelProperty(value = "竣工检验单图片URL(多个逗号分隔)") + private String finalInspectImageUrls; + + /** + * 竣工出厂合格证图片URL(多个逗号分隔) + */ + @ExcelProperty(value = "竣工出厂合格证图片URL(多个逗号分隔)") + private String factoryCertImageUrls; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/mapper/HotVehicleMaintenanceMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/mapper/HotVehicleMaintenanceMapper.java new file mode 100644 index 0000000..9a6cb6b --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/mapper/HotVehicleMaintenanceMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.vehicleMaintenance.mapper; + +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.HotVehicleMaintenance; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.vo.HotVehicleMaintenanceVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆维护记录Mapper接口 + * + * @author shihongwei + * @date 2025-12-15 + */ + @Mapper +public interface HotVehicleMaintenanceMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/service/IHotVehicleMaintenanceService.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/service/IHotVehicleMaintenanceService.java new file mode 100644 index 0000000..c4fe52c --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/service/IHotVehicleMaintenanceService.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.resourceManagement.vehicleMaintenance.service; + +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.vo.HotVehicleMaintenanceVo; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.bo.HotVehicleMaintenanceBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆维护记录Service接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +public interface IHotVehicleMaintenanceService { + + /** + * 查询车辆维护记录 + * + * @param id 主键 + * @return 车辆维护记录 + */ + HotVehicleMaintenanceVo queryById(Long id); + + /** + * 分页查询车辆维护记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆维护记录分页列表 + */ + TableDataInfo queryPageList(HotVehicleMaintenanceBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆维护记录列表 + * + * @param bo 查询条件 + * @return 车辆维护记录列表 + */ + List queryList(HotVehicleMaintenanceBo bo); + + /** + * 根据车辆ID查询最新的一条维护记录 + * + * @param vehicleId 车辆ID + * @return 最新维护记录 + */ + HotVehicleMaintenanceVo queryLatestByVehicleId(String vehicleId); + + /** + * 新增车辆维护记录 + * + * @param bo 车辆维护记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleMaintenanceBo bo); + + /** + * 修改车辆维护记录 + * + * @param bo 车辆维护记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleMaintenanceBo bo); + + /** + * 校验并批量删除车辆维护记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/service/impl/HotVehicleMaintenanceServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/service/impl/HotVehicleMaintenanceServiceImpl.java new file mode 100644 index 0000000..307a156 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMaintenance/service/impl/HotVehicleMaintenanceServiceImpl.java @@ -0,0 +1,161 @@ +package com.hotwj.platform.resourceManagement.vehicleMaintenance.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.HotVehicleMaintenance; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.bo.HotVehicleMaintenanceBo; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.vo.HotVehicleMaintenanceVo; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.mapper.HotVehicleMaintenanceMapper; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.service.IHotVehicleMaintenanceService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 车辆维护记录Service业务层处理 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleMaintenanceServiceImpl implements IHotVehicleMaintenanceService { + + private final HotVehicleMaintenanceMapper baseMapper; + + /** + * 查询车辆维护记录 + * + * @param id 主键 + * @return 车辆维护记录 + */ + @Override + public HotVehicleMaintenanceVo queryById(Long id){ + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆维护记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆维护记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleMaintenanceBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆维护记录列表 + * + * @param bo 查询条件 + * @return 车辆维护记录列表 + */ + @Override + public List queryList(HotVehicleMaintenanceBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + /** + * 根据车辆ID查询最新的一条维护记录 + * + * @param vehicleId 车辆ID + * @return 最新维护记录 + */ + @Override + public HotVehicleMaintenanceVo queryLatestByVehicleId(String vehicleId) { + return baseMapper.selectVoOne(new LambdaQueryWrapper() + .eq(HotVehicleMaintenance::getVehicleId, vehicleId) + .eq(HotVehicleMaintenance::getIsDeleted, 0L) + .orderByDesc(HotVehicleMaintenance::getFinishTime) + .last("LIMIT 1")); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleMaintenanceBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotVehicleMaintenance::getId); + lqw.eq(bo.getCompanyId() != null, HotVehicleMaintenance::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getVehicleId() != null, HotVehicleMaintenance::getVehicleId, bo.getVehicleId()); + lqw.eq(StringUtils.isNotBlank(bo.getPlateNumber()), HotVehicleMaintenance::getPlateNumber, bo.getPlateNumber()); + lqw.eq(bo.getMaintainDate() != null, HotVehicleMaintenance::getMaintainDate, bo.getMaintainDate()); + lqw.eq(bo.getFinishTime() != null, HotVehicleMaintenance::getFinishTime, bo.getFinishTime()); + lqw.eq(StringUtils.isNotBlank(bo.getTransferor()), HotVehicleMaintenance::getTransferor, bo.getTransferor()); + lqw.eq(StringUtils.isNotBlank(bo.getEntryInspectImageUrls()), HotVehicleMaintenance::getEntryInspectImageUrls, bo.getEntryInspectImageUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getProcessInspectImageUrls()), HotVehicleMaintenance::getProcessInspectImageUrls, bo.getProcessInspectImageUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getFinalInspectImageUrls()), HotVehicleMaintenance::getFinalInspectImageUrls, bo.getFinalInspectImageUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getFactoryCertImageUrls()), HotVehicleMaintenance::getFactoryCertImageUrls, bo.getFactoryCertImageUrls()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleMaintenance::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增车辆维护记录 + * + * @param bo 车辆维护记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleMaintenanceBo bo) { + HotVehicleMaintenance add = MapstructUtils.convert(bo, HotVehicleMaintenance.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改车辆维护记录 + * + * @param bo 车辆维护记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleMaintenanceBo bo) { + HotVehicleMaintenance update = MapstructUtils.convert(bo, HotVehicleMaintenance.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleMaintenance entity){ + if (entity.getMaintainDate() != null && entity.getFinishTime() != null) { + if (!entity.getFinishTime().after(entity.getMaintainDate())) { + throw new IllegalArgumentException("维护完成时间必须大于维护日期"); + } + } + } + + /** + * 校验并批量删除车辆维护记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/controller/HotVehicleController.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/controller/HotVehicleController.java new file mode 100644 index 0000000..df18dd6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/controller/HotVehicleController.java @@ -0,0 +1,245 @@ +package com.hotwj.platform.resourceManagement.vehicleManagement.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import cn.hutool.core.bean.BeanUtil; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.vo.HotVehicleAnnualReviewVo; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.service.IHotVehicleAnnualReviewService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleStatusBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleCategoryStatVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleExportVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleImportVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.DateUtils; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 公司车辆信息 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/vehicle") +@Tag(name = "公司车辆信息", description = "公司车辆信息管理") +public class HotVehicleController extends BaseController { + + private final IHotVehicleService hotVehicleService; + private final IHotVehicleAnnualReviewService hotVehicleAnnualReviewService; + + /** + * 统计公司车辆分类数量 + */ + //@SaCheckPermission("resourceManagement:vehicle:list") + @GetMapping("/stat") + @Operation(summary = "统计公司车辆分类数量") + public R> stat(Long companyId) { + return R.ok(hotVehicleService.queryVehicleCategoryStat(companyId)); + } + + /** + * 查询公司车辆信息列表 + */ + //@SaCheckPermission("resourceManagement:vehicle:list") + @GetMapping("/list") + @Operation(summary = "查询公司车辆信息列表") + public TableDataInfo list(HotVehicleBo bo, PageQuery pageQuery) { + return hotVehicleService.queryPageList(bo, pageQuery); + } + + /** + * 查询公司车辆信息列表(状态为数组) + */ + //@SaCheckPermission("resourceManagement:vehicle:list") + @GetMapping("/listByStatus") + @Operation(summary = "查询公司车辆信息列表(状态为数组)") + public TableDataInfo listByStatus(HotVehicleStatusBo bo, PageQuery pageQuery) { + return hotVehicleService.queryPageListByStatus(bo, pageQuery); + } + /** + * 导入车辆 + */ + //@SaCheckPermission("resourceManagement:vehicle:import") + @Log(title = "公司车辆信息", businessType = BusinessType.IMPORT) + @RepeatSubmit() + @PostMapping("/importData") + @Operation(summary = "导入公司车辆信息") + public R importData(@RequestPart("file") MultipartFile file, @NotNull(message = "公司ID不能为空") Long companyId) throws Exception { + List list = ExcelUtil.importExcel(file.getInputStream(), HotVehicleImportVo.class); + return R.ok(hotVehicleService.importData(list, companyId)); + } + + /** + * 下载导入模板 + */ + @SaIgnore + @GetMapping("/importTemplate") + @Operation(summary = "下载导入模板") + public void importTemplate(HttpServletResponse response) { + ExcelUtil.exportExcel(new ArrayList<>(), "车辆导入模板", HotVehicleImportVo.class, response); + } + + /** + * 导出公司车辆信息列表 + */ + //@SaCheckPermission("resourceManagement:vehicle:export") + @Log(title = "公司车辆信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "车辆台账导出") + public void export(@NotEmpty(message = "车辆ID不能为空") String ids, HttpServletResponse response) { + List vehicleIds = Arrays.stream(ids.split(",")) + .map(String::trim) + .filter(id -> !id.isEmpty()) + .collect(Collectors.toList()); + if (vehicleIds.isEmpty()) { + throw new ServiceException("车辆ID不能为空"); + } + + HotVehicleBo bo = new HotVehicleBo(); + bo.setIds(vehicleIds); + List list = hotVehicleService.queryList(bo); + List exportList = new ArrayList<>(); + for (HotVehicleVo vehicle : list) { + HotVehicleExportVo vo = new HotVehicleExportVo(); + vo.setArchiveNo(vehicle.getArchiveNo()); + vo.setPlateNumber(vehicle.getPlateNumber()); + vo.setBrandModel(vehicle.getBrandModel()); + vo.setVehicleType(vehicle.getVehicleType()); + vo.setVehicleModel(vehicle.getVehicleModel()); + vo.setPlateColor(vehicle.getPlateColor()); + vo.setVehicleRegisterDate(vehicle.getVehicleRegisterDate()); + vo.setRatedLoadKg(vehicle.getRatedLoadKg()); + vo.setEngineNumber(vehicle.getDriveMotorModel()); + vo.setFrameNumber(vehicle.getFrameNumber()); + vo.setTransportLicenseNo(vehicle.getTransportLicenseNo()); + vo.setFuelType(vehicle.getFuelType()); + vo.setTransportValidMonth(vehicle.getTransportValidMonth()); + + HotVehicleAnnualReviewVo review = hotVehicleAnnualReviewService.queryLatestByVehicleId(vehicle.getId()); + if (review != null) { + Long level = review.getEvaluationLevel(); + if (level != null) { + if (level == 1L) { + vo.setTechnicalLevel("一级"); + } else if (level == 2L) { + vo.setTechnicalLevel("二级"); + } else { + vo.setTechnicalLevel(String.valueOf(level)); + } + } + vo.setInspectionDate(review.getReviewDate()); + } + exportList.add(vo); + } + ExcelUtil.exportExcel(exportList, "车辆台账信息", HotVehicleExportVo.class, response); + } + + /** + * 导出车辆全部信息 + */ + //@SaCheckPermission("resourceManagement:vehicle:export") + @Log(title = "公司车辆信息", businessType = BusinessType.EXPORT) + @PostMapping("/exportTemplateData") + @Operation(summary = "导出车辆全部信息") + public void exportTemplateData(@NotEmpty(message = "车辆ID不能为空") String ids, HttpServletResponse response) { + List vehicleIds = Arrays.stream(ids.split(",")) + .map(String::trim) + .filter(id -> !id.isEmpty()) + .collect(Collectors.toList()); + if (vehicleIds.isEmpty()) { + throw new ServiceException("车辆ID不能为空"); + } + + HotVehicleBo bo = new HotVehicleBo(); + bo.setIds(vehicleIds); + List list = hotVehicleService.queryList(bo); + List exportList = new ArrayList<>(); + for (HotVehicleVo vehicle : list) { + HotVehicleImportVo vo = new HotVehicleImportVo(); + BeanUtil.copyProperties(vehicle, vo, "transportValidMonth", "inspectionValidMonth"); + vo.setEngineNo(vehicle.getDriveMotorModel()); + vo.setTransportValidMonth(DateUtils.parseDate(vehicle.getTransportValidMonth())); + vo.setInspectionValidMonth(DateUtils.parseDate(vehicle.getInspectionValidMonth())); + exportList.add(vo); + } + ExcelUtil.exportExcel(exportList, "车辆全部信息", HotVehicleImportVo.class, response); + } + + /** + * 获取公司车辆信息详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:vehicle:query") + @GetMapping("/{id}") + @Operation(summary = "获取公司车辆信息详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotVehicleService.queryById(id)); + } + + /** + * 新增公司车辆信息 + */ + //@SaCheckPermission("resourceManagement:vehicle:add") + @Log(title = "公司车辆信息", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增公司车辆信息") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleBo bo) { + return toAjax(hotVehicleService.insertByBo(bo)); + } + + /** + * 修改公司车辆信息 + */ + //@SaCheckPermission("resourceManagement:vehicle:edit") + @Log(title = "公司车辆信息", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改公司车辆信息") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleBo bo) { + return toAjax(hotVehicleService.updateByBo(bo)); + } + + /** + * 删除公司车辆信息 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:vehicle:remove") + @Log(title = "公司车辆信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除公司车辆信息") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotVehicleService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/HotVehicle.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/HotVehicle.java new file mode 100644 index 0000000..5bcb751 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/HotVehicle.java @@ -0,0 +1,436 @@ +package com.hotwj.platform.resourceManagement.vehicleManagement.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 公司车辆信息对象 hot_vehicle + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle") +public class HotVehicle extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private String id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * GPS参数 + */ + private String gpsParams; + + /** + * 是否挂车0=否, 1=是 + */ + private Long isTrailer; + + /** + * 车辆状态 1=正常, 0=停用 + */ + private Long vehicleStatus; + + /** + * 运营状态:1=正常,2=暂停,3=过户,4=报废 + */ + private Long operationStatus; + + /** + * 备注 + */ + private String remark; + + /** + * 当前驾驶员id(多个逗号分隔) + */ + private String currentDriver; + + /** + * 车身照片URL(多个逗号分隔) + */ + private String bodyImageUrls; + + /** + * 其它证附件URL(多个逗号分隔) + */ + private String otherAttachmentUrls; + + /** + * 车身颜色 + */ + private String color; + + /** + * 出厂日期 + */ + private Date manufactureDate; + + /** + * 燃料种类 + */ + private String fuelType; + + /** + * 制造厂名称 + */ + private String manufacturerName; + + /** + * 车牌颜色 + */ + private String plateColor; + + /** + * 车辆登记日期 + */ + private Date vehicleRegisterDate; + + /** + * 发动机排量(ml) + */ + private Long engineDisplacementMl; + + /** + * 轮胎数量 + */ + private Long tireCount; + + /** + * 底盘型号 + */ + private String chassisModel; + + /** + * 卫星定位装置 + */ + private String satellitePositioningDevice; + + /** + * 变速器 + */ + private String transmission; + + /** + * 缓速器 + */ + private String retarder; + + /** + * 货箱内部容积(立方米) + */ + private BigDecimal cargoInnerVolumeM3; + + /** + * 排放标准 + */ + private String emissionStandard; + + /** + * 电池类型 + */ + private String batteryType; + + /** + * 承保座数 + */ + private Long insuredSeatCount; + + /** + * 行车制动方式 + */ + private String serviceBrakeType; + + /** + * 前轮制动器形式 + */ + private String frontBrakeType; + + /** + * 后轮制动器形式 + */ + private String rearBrakeType; + + /** + * 制动防抱死系统 + */ + private String hasAbs; + + /** + * 空调系统 + */ + private String hasAirConditioning; + + /** + * 三角木标记 + */ + private String hasTriangleBlock; + + /** + * 灭火器 + */ + private String hasFireExtinguisher; + + /** + * 故障警示牌 + */ + private String hasBreakdownWarningSign; + + /** + * 国产标记 + */ + private String domesticMark; + + /** + * 车辆分类 + */ + private String vehicleCategory; + + /** + * 强制报废期止 + */ + private Date mandatoryScrapEndDate; + + /** + * 使用性质 + */ + private String useNature; + + /** + * 车辆型号 + */ + private String vehicleModel; + + /** + * 车架号 + */ + private String frameNumber; + + /** + * 货箱内部长(mm) + */ + private Long cargoInnerLengthMm; + + /** + * 货箱内部宽(mm) + */ + private Long cargoInnerWidthMm; + + /** + * 货箱内部高(mm) + */ + private Long cargoInnerHeightMm; + + /** + * 是否按揭:0=否,1=是 + */ + private Long isMortgaged; + + /** + * 按揭公司 + */ + private String mortgageCompany; + + /** + * 按揭联系人 + */ + private String mortgageContact; + + /** + * 按揭联系人电话 + */ + private String mortgageContactPhone; + + /** + * 发动机净功率(kW) + */ + private Long enginePowerKw; + + /** + * 电机功率(kW) + */ + private Long motorPowerKw; + + /** + * 驱动电机型号 + */ + private String engineNumber; + + /** + * 轮胎规格 + */ + private String tireSpec; + + /** + * 轴距(mm) + */ + private Long wheelbaseMm; + + /** + * 车轴数 + */ + private Long axleCount; + + /** + * 登记证书图片URL(多个逗号分隔) + */ + private String registerCertImageUrls; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 档案号 + */ + private String archiveNo; + + /** + * 车辆类型 + */ + private String vehicleType; + + /** + * 注册日期 + */ + private Date firstRegisterDate; + + /** + * 发证日期 + */ + private Date certificateIssueDate; + + /** + * 检验有效期(YYYY-MM) + */ + private String inspectionValidMonth; + + /** + * 机动车所有人 + */ + private String owner; + + /** + * 地址 + */ + private String address; + + /** + * 品牌型号 + */ + private String brandModel; + + /** + * 核定载人数 + */ + private Long ratedPassengerCount; + + /** + * 总质量(kg) + */ + private Long grossWeightKg; + + /** + * 整备质量(kg) + */ + private Long curbWeightKg; + + /** + * 核定载质量(kg) + */ + private Long ratedLoadKg; + + /** + * 准牵引总质量(kg) + */ + private Long towingMassKg; + + /** + * 车辆外廓长度(mm) + */ + private Long vehicleLengthMm; + + /** + * 车辆外廓宽度(mm) + */ + private Long vehicleWidthMm; + + /** + * 车辆外廓高度(mm) + */ + private Long vehicleHeightMm; + + /** + * 行驶证主页图片URL + */ + private String drivingLicenseMainUrls; + + /** + * 行驶证副页图片URL + */ + private String drivingLicenseAuxUrls; + + /** + * 行驶证其他URL + */ + private String drivingLicenseOtherUrl; + + /** + * 运输证号 + */ + private String transportLicenseNo; + + /** + * 运输证注册日期 + */ + private Date transportRegisterDate; + + /** + * 运输证发证日期 + */ + private Date transportIssueDate; + + /** + * 运输证有效期(YYYY-MM) + */ + private String transportValidMonth; + + /** + * 运输证图片URL(多个逗号分隔) + */ + private String transportLicenseUrls; + + /** + * 发动机号 + */ + private String driveMotorModel; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/bo/HotVehicleBo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/bo/HotVehicleBo.java new file mode 100644 index 0000000..398070f --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/bo/HotVehicleBo.java @@ -0,0 +1,460 @@ +package com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo; + +import com.hotwj.platform.resourceManagement.vehicleContract.domain.bo.HotVehicleContractBo; +import com.hotwj.platform.resourceManagement.vehicleDevice.domain.bo.HotVehicleDeviceBo; +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.bo.HotVehicleInsuranceBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * 公司车辆信息业务对象 hot_vehicle + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicle.class, reverseConvertGenerate = false) +public class HotVehicleBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private String id; + + /** + * 主键集合(用于批量场景) + */ + private List ids; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * GPS参数 + */ + private String gpsParams; + + /** + * 是否挂车0=否, 1=是 + */ + private Long isTrailer; + + /** + * 车辆状态 1=正常, 2=停用 + */ + private Long vehicleStatus; + + /** + * 运营状态:1=正常,2=暂停,3=过户,4=报废 + */ + private Long operationStatus; + + /** + * 备注 + */ + private String remark; + + /** + * 当前驾驶员(多个逗号分隔) + */ + private String currentDriver; + + /** + * 车身照片URL(多个逗号分隔) + */ + private String bodyImageUrls; + + /** + * 其它证附件URL(多个逗号分隔) + */ + private String otherAttachmentUrls; + + /** + * 车身颜色 + */ + private String color; + + /** + * 出厂日期 + */ + private Date manufactureDate; + + /** + * 燃料种类 + */ + private String fuelType; + + /** + * 制造厂名称 + */ + private String manufacturerName; + + /** + * 车牌颜色 + */ + private String plateColor; + + /** + * 车辆登记日期 + */ + private Date vehicleRegisterDate; + + /** + * 发动机排量(ml) + */ + private Long engineDisplacementMl; + + /** + * 轮胎数量 + */ + private Long tireCount; + + /** + * 底盘型号 + */ + private String chassisModel; + + /** + * 卫星定位装置 + */ + private String satellitePositioningDevice; + + /** + * 变速器 + */ + private String transmission; + + /** + * 缓速器 + */ + private String retarder; + + /** + * 货箱内部容积(立方米) + */ + private BigDecimal cargoInnerVolumeM3; + + /** + * 排放标准 + */ + private String emissionStandard; + + /** + * 电池类型 + */ + private String batteryType; + + /** + * 承保座数 + */ + private Long insuredSeatCount; + + /** + * 行车制动方式 + */ + private String serviceBrakeType; + + /** + * 前轮制动器形式 + */ + private String frontBrakeType; + + /** + * 后轮制动器形式 + */ + private String rearBrakeType; + + /** + * 制动防抱死系统 + */ + private String hasAbs; + + /** + * 空调系统 + */ + private String hasAirConditioning; + + /** + * 三角木标记 + */ + private String hasTriangleBlock; + + /** + * 灭火器 + */ + private String hasFireExtinguisher; + + /** + * 故障警示牌 + */ + private String hasBreakdownWarningSign; + + /** + * 国产标记 + */ + private String domesticMark; + + /** + * 车辆分类 + */ + private String vehicleCategory; + + /** + * 强制报废期止 + */ + @NotNull(message = "强制报废期止不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date mandatoryScrapEndDate; + + /** + * 使用性质 + */ + private String useNature; + + /** + * 车辆型号 + */ + private String vehicleModel; + + /** + * 车架号 + */ + private String frameNumber; + + /** + * 货箱内部长(mm) + */ + private Long cargoInnerLengthMm; + + /** + * 货箱内部宽(mm) + */ + private Long cargoInnerWidthMm; + + /** + * 货箱内部高(mm) + */ + private Long cargoInnerHeightMm; + + /** + * 是否按揭:0=否,1=是 + */ + private Long isMortgaged; + + /** + * 按揭公司 + */ + private String mortgageCompany; + + /** + * 按揭联系人 + */ + private String mortgageContact; + + /** + * 按揭联系人电话 + */ + private String mortgageContactPhone; + + /** + * 发动机净功率(kW) + */ + private Long enginePowerKw; + + /** + * 电机功率(kW) + */ + private Long motorPowerKw; + + /** + * 驱动电机型号 + */ + private String engineNumber; + + /** + * 轮胎规格 + */ + private String tireSpec; + + /** + * 轴距(mm) + */ + private Long wheelbaseMm; + + /** + * 车轴数 + */ + private Long axleCount; + + /** + * 登记证书图片URL(多个逗号分隔) + */ + private String registerCertImageUrls; + + /** + * 车牌号 + */ + @NotBlank(message = "车牌号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String plateNumber; + + /** + * 档案号 + */ + private String archiveNo; + + /** + * 车辆类型 + */ + @NotBlank(message = "车辆类型不能为空", groups = {AddGroup.class, EditGroup.class}) + private String vehicleType; + + /** + * 注册日期 + */ + private Date firstRegisterDate; + + /** + * 发证日期 + */ + @NotNull(message = "发证日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date certificateIssueDate; + + /** + * 检验有效期(YYYY-MM) + */ + @NotBlank(message = "检验有效期(YYYY-MM)不能为空", groups = {AddGroup.class, EditGroup.class}) + private String inspectionValidMonth; + + /** + * 机动车所有人 + */ + private String owner; + + /** + * 地址 + */ + private String address; + + /** + * 品牌型号 + */ + private String brandModel; + + /** + * 核定载人数 + */ + private Long ratedPassengerCount; + + /** + * 总质量(kg) + */ + private Long grossWeightKg; + + /** + * 整备质量(kg) + */ + private Long curbWeightKg; + + /** + * 核定载质量(kg) + */ + private Long ratedLoadKg; + + /** + * 准牵引总质量(kg) + */ + private Long towingMassKg; + + /** + * 车辆外廓长度(mm) + */ + private Long vehicleLengthMm; + + /** + * 车辆外廓宽度(mm) + */ + private Long vehicleWidthMm; + + /** + * 车辆外廓高度(mm) + */ + private Long vehicleHeightMm; + + /** + * 行驶证主页图片URL + */ + private String drivingLicenseMainUrls; + + /** + * 行驶证副页图片URL + */ + private String drivingLicenseAuxUrls; + + /** + * 行驶证其他URL + */ + private String drivingLicenseOtherUrl; + + /** + * 运输证号 + */ +// @NotBlank(message = "运输证号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String transportLicenseNo; + + /** + * 运输证注册日期 + */ + private Date transportRegisterDate; + + /** + * 运输证发证日期 + */ + private Date transportIssueDate; + + /** + * 运输证有效期(YYYY-MM) + */ +// @NotBlank(message = "运输证有效期(YYYY-MM)不能为空", groups = {AddGroup.class, EditGroup.class}) + private String transportValidMonth; + + /** + * 运输证图片URL(多个逗号分隔) + */ + private String transportLicenseUrls; + + // -- 子表数据 + /** + * 车辆合同列表 + */ + private List vehicleContracts; + + /** + * 车辆保险列表 + */ + private List vehicleInsurances; + + /** + * 车辆安全设备列表 + */ + private List vehicleDevices; + + /** + * 发动机号 + */ + private String driveMotorModel; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/bo/HotVehicleStatusBo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/bo/HotVehicleStatusBo.java new file mode 100644 index 0000000..b6629a8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/bo/HotVehicleStatusBo.java @@ -0,0 +1,450 @@ +package com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo; + +import com.hotwj.platform.resourceManagement.vehicleContract.domain.bo.HotVehicleContractBo; +import com.hotwj.platform.resourceManagement.vehicleDevice.domain.bo.HotVehicleDeviceBo; +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.bo.HotVehicleInsuranceBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; +import java.util.List; + +/** + * 公司车辆信息业务对象 hot_vehicle + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicle.class, reverseConvertGenerate = false) +public class HotVehicleStatusBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private String id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * GPS参数 + */ + private String gpsParams; + + /** + * 是否挂车0=否, 1=是 + */ + private Long isTrailer; + + /** + * 车辆状态 1=正常, 0=停用 + */ + private Long vehicleStatus; + + /** + * 运营状态:1=正常,2=暂停,3=过户,4=报废 + * 逗号拼接 + */ + private String operationStatus; + + /** + * 备注 + */ + private String remark; + + /** + * 当前驾驶员(多个逗号分隔) + */ + private String currentDriver; + + /** + * 车身照片URL(多个逗号分隔) + */ + private String bodyImageUrls; + + /** + * 其它证附件URL(多个逗号分隔) + */ + private String otherAttachmentUrls; + + /** + * 车身颜色 + */ + private String color; + + /** + * 出厂日期 + */ + private Date manufactureDate; + + /** + * 燃料种类 + */ + private String fuelType; + + /** + * 制造厂名称 + */ + private String manufacturerName; + + /** + * 车牌颜色 + */ + private String plateColor; + + /** + * 车辆登记日期 + */ + private Date vehicleRegisterDate; + + /** + * 发动机排量(ml) + */ + private Long engineDisplacementMl; + + /** + * 轮胎数量 + */ + private Long tireCount; + + /** + * 底盘型号 + */ + private String chassisModel; + + /** + * 卫星定位装置 + */ + private String satellitePositioningDevice; + + /** + * 变速器 + */ + private String transmission; + + /** + * 缓速器 + */ + private String retarder; + + /** + * 货箱内部容积(立方米) + */ + private Long cargoInnerVolumeM3; + + /** + * 排放标准 + */ + private String emissionStandard; + + /** + * 电池类型 + */ + private String batteryType; + + /** + * 承保座数 + */ + private Long insuredSeatCount; + + /** + * 行车制动方式 + */ + private String serviceBrakeType; + + /** + * 前轮制动器形式 + */ + private String frontBrakeType; + + /** + * 后轮制动器形式 + */ + private String rearBrakeType; + + /** + * 制动防抱死系统:0=无,1=有 + */ + private Long hasAbs; + + /** + * 空调系统:0=无,1=有 + */ + private Long hasAirConditioning; + + /** + * 三角木标记:0=无,1=有 + */ + private Long hasTriangleBlock; + + /** + * 灭火器:0=无,1=有 + */ + private Long hasFireExtinguisher; + + /** + * 故障警示牌:0=无,1=有 + */ + private Long hasBreakdownWarningSign; + + /** + * 国产标记:0=否,1=是 + */ + private Long domesticMark; + + /** + * 车辆分类 + */ + private String vehicleCategory; + + /** + * 强制报废期止 + */ + @NotNull(message = "强制报废期止不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date mandatoryScrapEndDate; + + /** + * 使用性质 + */ + private String useNature; + + /** + * 车辆型号 + */ + private String vehicleModel; + + /** + * 车架号 + */ + private String frameNumber; + + /** + * 货箱内部长(mm) + */ + private Long cargoInnerLengthMm; + + /** + * 货箱内部宽(mm) + */ + private Long cargoInnerWidthMm; + + /** + * 货箱内部高(mm) + */ + private Long cargoInnerHeightMm; + + /** + * 是否按揭:0=否,1=是 + */ + private Long isMortgaged; + + /** + * 按揭公司 + */ + private String mortgageCompany; + + /** + * 按揭联系人 + */ + private String mortgageContact; + + /** + * 按揭联系人电话 + */ + private String mortgageContactPhone; + + /** + * 发动机净功率(kW) + */ + private Long enginePowerKw; + + /** + * 电机功率(kW) + */ + private Long motorPowerKw; + + /** + * 驱动电机型号 + */ + private String engineNumber; + + /** + * 轮胎规格 + */ + private String tireSpec; + + /** + * 轴距(mm) + */ + private Long wheelbaseMm; + + /** + * 车轴数 + */ + private Long axleCount; + + /** + * 登记证书图片URL(多个逗号分隔) + */ + private String registerCertImageUrls; + + /** + * 车牌号 + */ + @NotBlank(message = "车牌号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String plateNumber; + + /** + * 档案号 + */ + private String archiveNo; + + /** + * 车辆类型 + */ + @NotBlank(message = "车辆类型不能为空", groups = {AddGroup.class, EditGroup.class}) + private String vehicleType; + + /** + * 注册日期 + */ + private Date firstRegisterDate; + + /** + * 发证日期 + */ + @NotNull(message = "发证日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date certificateIssueDate; + + /** + * 检验有效期(YYYY-MM) + */ + @NotBlank(message = "检验有效期(YYYY-MM)不能为空", groups = {AddGroup.class, EditGroup.class}) + private String inspectionValidMonth; + + /** + * 机动车所有人 + */ + private String owner; + + /** + * 地址 + */ + private String address; + + /** + * 品牌型号 + */ + private String brandModel; + + /** + * 核定载人数 + */ + private Long ratedPassengerCount; + + /** + * 总质量(kg) + */ + private Long grossWeightKg; + + /** + * 整备质量(kg) + */ + private Long curbWeightKg; + + /** + * 核定载质量(kg) + */ + private Long ratedLoadKg; + + /** + * 准牵引总质量(kg) + */ + private Long towingMassKg; + + /** + * 车辆外廓长度(mm) + */ + private Long vehicleLengthMm; + + /** + * 车辆外廓宽度(mm) + */ + private Long vehicleWidthMm; + + /** + * 车辆外廓高度(mm) + */ + private Long vehicleHeightMm; + + /** + * 行驶证主页图片URL + */ + private String drivingLicenseMainUrls; + + /** + * 行驶证副页图片URL + */ + private String drivingLicenseAuxUrls; + + /** + * 行驶证其他URL + */ + private String drivingLicenseOtherUrl; + + /** + * 运输证号 + */ + @NotBlank(message = "运输证号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String transportLicenseNo; + + /** + * 运输证注册日期 + */ + private Date transportRegisterDate; + + /** + * 运输证发证日期 + */ + private Date transportIssueDate; + + /** + * 运输证有效期(YYYY-MM) + */ + @NotBlank(message = "运输证有效期(YYYY-MM)不能为空", groups = {AddGroup.class, EditGroup.class}) + private String transportValidMonth; + + /** + * 运输证图片URL(多个逗号分隔) + */ + private String transportLicenseUrls; + + // -- 子表数据 + /** + * 车辆合同列表 + */ + private List vehicleContracts; + + /** + * 车辆保险列表 + */ + private List vehicleInsurances; + + /** + * 车辆安全设备列表 + */ + private List vehicleDevices; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/HotVehicleCategoryStatVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/HotVehicleCategoryStatVo.java new file mode 100644 index 0000000..874b18d --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/HotVehicleCategoryStatVo.java @@ -0,0 +1,26 @@ +package com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 车辆分类统计 VO + */ +@Data +public class HotVehicleCategoryStatVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 车辆分类 + */ + private String vehicleCategory; + + /** + * 数量 + */ + private Long count; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/HotVehicleExportVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/HotVehicleExportVo.java new file mode 100644 index 0000000..7c73a09 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/HotVehicleExportVo.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 公司车辆台账导出字段 + * + * @author shihongwei + * @date 2025-12-18 + */ +@Data +@ExcelIgnoreUnannotated +public class HotVehicleExportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty(value = "车辆档案号", index = 0) + private String archiveNo; + + @ExcelProperty(value = "车牌号", index = 1) + private String plateNumber; + + @ExcelProperty(value = "厂牌型号", index = 2) + private String brandModel; + + @ExcelProperty(value = "车型", index = 3) + private String vehicleType; + + @ExcelProperty(value = "车辆型号", index = 4) + private String vehicleModel; + + @ExcelProperty(value = "号牌颜色", index = 5) + private String plateColor; + + @ExcelProperty(value = "登记日期", index = 6) + private Date vehicleRegisterDate; + + @ExcelProperty(value = "核载(KG)", index = 7) + private Long ratedLoadKg; + + @ExcelProperty(value = "发动机号", index = 8) + private String engineNumber; + + @ExcelProperty(value = "车架号", index = 9) + private String frameNumber; + + @ExcelProperty(value = "运输证号", index = 10) + private String transportLicenseNo; + + @ExcelProperty(value = "燃油类型", index = 11) + private String fuelType; + + @ExcelProperty(value = "技术等级", index = 12) + private String technicalLevel; + + @ExcelProperty(value = "运输证有效期", index = 13) + private String transportValidMonth; + + @ExcelProperty(value = "审车日期", index = 14) + private Date inspectionDate; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/HotVehicleImportVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/HotVehicleImportVo.java new file mode 100644 index 0000000..2296e50 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/HotVehicleImportVo.java @@ -0,0 +1,173 @@ +package com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.Min; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 车辆导入VO + */ +@Data +@AutoMapper(target = HotVehicle.class) +public class HotVehicleImportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty("档案号") + private String archiveNo; + + @ExcelProperty("车牌号") + private String plateNumber; + + @ExcelProperty("车辆类型") + private String vehicleType; + + @ExcelProperty("运输证号") + private String transportLicenseNo; + + @ExcelProperty("有效期") + private Date transportValidMonth; + + @ExcelProperty("机动车所有人") + private String owner; + + @ExcelProperty("地址") + private String address; + + @ExcelProperty("使用性质") + private String useNature; + + @ExcelProperty("品牌型号") + private String brandModel; + + @ExcelProperty("车架号") + private String frameNumber; + + @ExcelProperty("发动机号") + private String engineNo; + + @ExcelProperty("行驶证注册日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date firstRegisterDate; + + @ExcelProperty("行驶证发证日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date certificateIssueDate; + + @ExcelProperty("行驶证检验有效期") + private Date inspectionValidMonth; + + @ExcelProperty("出厂日期") + private Date manufactureDate; + + @ExcelProperty("核定载人数(人)") + @Min(value = 0, message = "核定载人数必须大于等于0") + private Long ratedPassengerCount; + + @ExcelProperty("总质量(KG)") + @Min(value = 0, message = "总质量必须大于等于0") + private Long grossWeightKg; + + @ExcelProperty("整备质量(KG)") + @Min(value = 0, message = "整备质量必须大于等于0") + private Long curbWeightKg; + + @ExcelProperty("核定载质量(KG)") + @Min(value = 0, message = "核定载质量必须大于等于0") + private Long ratedLoadKg; + + @ExcelProperty("车辆外廓长(mm)") + @Min(value = 0, message = "车辆外廓长必须大于等于0") + private Long vehicleLengthMm; + + @ExcelProperty("车辆外廓宽(mm)") + @Min(value = 0, message = "车辆外廓宽必须大于等于0") + private Long vehicleWidthMm; + + @ExcelProperty("车辆外廓高(mm)") + @Min(value = 0, message = "车辆外廓高必须大于等于0") + private Long vehicleHeightMm; + + @ExcelProperty("准牵引总质量(KG)") + @Min(value = 0, message = "准牵引总质量必须大于等于0") + private Long towingMassKg; + + @ExcelProperty("强制报废期止") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date mandatoryScrapEndDate; + + @ExcelProperty("车身颜色") + private String color; + + @ExcelProperty("燃料种类") + private String fuelType; + + @ExcelProperty("发动机排量(ml)") + @Min(value = 0, message = "发动机排量必须大于等于0") + private Long engineDisplacementMl; + + @ExcelProperty("发动机净功率(KW)") + @Min(value = 0, message = "发动机净功率必须大于等于0") + private Long enginePowerKw; + + @ExcelProperty("轮胎数量") + @Min(value = 0, message = "轮胎数量必须大于等于0") + private Long tireCount; + + @ExcelProperty("轴距(mm)") + @Min(value = 0, message = "轴距必须大于等于0") + private Long wheelbaseMm; + + @ExcelProperty("货箱内部长(mm)") + @Min(value = 0, message = "货箱内部长必须大于等于0") + private Long cargoInnerLengthMm; + + @ExcelProperty("货箱内部宽(mm)") + @Min(value = 0, message = "货箱内部宽必须大于等于0") + private Long cargoInnerWidthMm; + + @ExcelProperty("货箱内部高(mm)") + @Min(value = 0, message = "货箱内部高必须大于等于0") + private Long cargoInnerHeightMm; + + @ExcelProperty("车牌颜色") + private String plateColor; + + @ExcelProperty("登记日期") + private Date vehicleRegisterDate; + + @ExcelProperty("制造厂名称") + private String manufacturerName; + + @ExcelProperty("电机功率(KW)") + @Min(value = 0, message = "电机功率必须大于等于0") + private Long motorPowerKw; + + @ExcelProperty("驱动电机型号") + private String engineNumber; + + @ExcelProperty("轮胎规格") + private String tireSpec; + + @ExcelProperty("车轴数量") + @Min(value = 0, message = "车轴数量必须大于等于0") + private Long axleCount; + + @ExcelProperty(value = "车辆分类", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_vehicle_vehicle") + private String vehicleCategory; + + @ExcelProperty(value = "是否为挂车", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_yes_no") + private Long isTrailer; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/HotVehicleVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/HotVehicleVo.java new file mode 100644 index 0000000..991bec9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/domain/vo/HotVehicleVo.java @@ -0,0 +1,572 @@ +package com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 公司车辆信息视图对象 hot_vehicle + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicle.class) +public class HotVehicleVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private String id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * GPS参数 + */ + @ExcelProperty(value = "GPS参数") + private String gpsParams; + + /** + * 是否挂车0=否, 1=是 + */ + @ExcelProperty(value = "是否挂车0=否, 1=是", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_yes_no") + private Long isTrailer; + + /** + * 车辆状态 1=正常, 0=停用 + */ + @ExcelProperty(value = "车辆状态 1=正常, 0=停用", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_yes_no") + private Long vehicleStatus; + + /** + * 运营状态:1=正常,2=暂停,3=过户,4=报废 + */ + @ExcelProperty(value = "运营状态:1=正常,2=暂停,3=过户,4=报废", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_vehicle_status") + private Long operationStatus; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 当前驾驶员 + */ + @ExcelProperty(value = "当前驾驶员") + private String currentDriver; + + @Translation(type = TransConstant.DRIVER_ID_TO_NAME, mapper = "currentDriver") + private String currentDriverName; + + @Translation(type = TransConstant.DRIVER_ID_TO_NAME_WITH_TYPE, mapper = "currentDriver") + private String currentBindingPersonName; + + /** + * 车身照片URL(多个逗号分隔) + */ + @ExcelProperty(value = "车身照片URL(多个逗号分隔)") + private String bodyImageUrls; + + /** + * 车身照片URL(多个逗号分隔)Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "bodyImageUrls") + private String bodyImageUrlsUrl; + /** + * 其它证附件URL(多个逗号分隔) + */ + @ExcelProperty(value = "其它证附件URL(多个逗号分隔)") + private String otherAttachmentUrls; + + /** + * 其它证附件URL(多个逗号分隔)Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "otherAttachmentUrls") + private String otherAttachmentUrlsUrl; + /** + * 车身颜色 + */ + @ExcelProperty(value = "车身颜色") + private String color; + + /** + * 出厂日期 + */ + @ExcelProperty(value = "出厂日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date manufactureDate; + + /** + * 燃料种类 + */ + @ExcelProperty(value = "燃料种类") + private String fuelType; + + /** + * 制造厂名称 + */ + @ExcelProperty(value = "制造厂名称") + private String manufacturerName; + + /** + * 车牌颜色 + */ + @ExcelProperty(value = "车牌颜色") + private String plateColor; + + /** + * 车辆登记日期 + */ + @ExcelProperty(value = "车辆登记日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date vehicleRegisterDate; + + /** + * 发动机排量(ml) + */ + @ExcelProperty(value = "发动机排量(ml)") + private Long engineDisplacementMl; + + /** + * 轮胎数量 + */ + @ExcelProperty(value = "轮胎数量") + private Long tireCount; + + /** + * 底盘型号 + */ + @ExcelProperty(value = "底盘型号") + private String chassisModel; + + /** + * 卫星定位装置 + */ + @ExcelProperty(value = "卫星定位装置") + private String satellitePositioningDevice; + + /** + * 变速器 + */ + @ExcelProperty(value = "变速器") + private String transmission; + + /** + * 缓速器 + */ + @ExcelProperty(value = "缓速器") + private String retarder; + + /** + * 货箱内部容积(立方米) + */ + @ExcelProperty(value = "货箱内部容积(立方米)") + private BigDecimal cargoInnerVolumeM3; + + /** + * 排放标准 + */ + @ExcelProperty(value = "排放标准") + private String emissionStandard; + + /** + * 电池类型 + */ + @ExcelProperty(value = "电池类型") + private String batteryType; + + /** + * 承保座数 + */ + @ExcelProperty(value = "承保座数") + private Long insuredSeatCount; + + /** + * 行车制动方式 + */ + @ExcelProperty(value = "行车制动方式") + private String serviceBrakeType; + + /** + * 前轮制动器形式 + */ + @ExcelProperty(value = "前轮制动器形式") + private String frontBrakeType; + + /** + * 后轮制动器形式 + */ + @ExcelProperty(value = "后轮制动器形式") + private String rearBrakeType; + + /** + * 制动防抱死系统 + */ + @ExcelProperty(value = "制动防抱死系统") + private String hasAbs; + + /** + * 空调系统 + */ + @ExcelProperty(value = "空调系统") + private String hasAirConditioning; + + /** + * 三角木标记 + */ + @ExcelProperty(value = "三角木标记") + private String hasTriangleBlock; + + /** + * 灭火器 + */ + @ExcelProperty(value = "灭火器") + private String hasFireExtinguisher; + + /** + * 故障警示牌 + */ + @ExcelProperty(value = "故障警示牌") + private String hasBreakdownWarningSign; + + /** + * 国产标记 + */ + @ExcelProperty(value = "国产标记") + private String domesticMark; + + /** + * 车辆分类 + */ + @ExcelProperty(value = "车辆分类") + private String vehicleCategory; + + /** + * 强制报废期止 + */ + @ExcelProperty(value = "强制报废期止") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date mandatoryScrapEndDate; + + /** + * 使用性质 + */ + @ExcelProperty(value = "使用性质") + private String useNature; + + /** + * 车辆型号 + */ + @ExcelProperty(value = "车辆型号") + private String vehicleModel; + + /** + * 车架号 + */ + @ExcelProperty(value = "车架号") + private String frameNumber; + + /** + * 货箱内部长(mm) + */ + @ExcelProperty(value = "货箱内部长(mm)") + private Long cargoInnerLengthMm; + + /** + * 货箱内部宽(mm) + */ + @ExcelProperty(value = "货箱内部宽(mm)") + private Long cargoInnerWidthMm; + + /** + * 货箱内部高(mm) + */ + @ExcelProperty(value = "货箱内部高(mm)") + private Long cargoInnerHeightMm; + + /** + * 是否按揭:0=否,1=是 + */ + @ExcelProperty(value = "是否按揭:0=否,1=是", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "sys_yes_no") + private Long isMortgaged; + + /** + * 按揭公司 + */ + @ExcelProperty(value = "按揭公司") + private String mortgageCompany; + + /** + * 按揭联系人 + */ + @ExcelProperty(value = "按揭联系人") + private String mortgageContact; + + /** + * 按揭联系人电话 + */ + @ExcelProperty(value = "按揭联系人电话") + private String mortgageContactPhone; + + /** + * 发动机净功率(kW) + */ + @ExcelProperty(value = "发动机净功率(kW)") + private Long enginePowerKw; + + /** + * 电机功率(kW) + */ + @ExcelProperty(value = "电机功率(kW)") + private Long motorPowerKw; + + /** + * 驱动电机型号 + */ + @ExcelProperty(value = "驱动电机型号") + private String engineNumber; + + /** + * 轮胎规格 + */ + @ExcelProperty(value = "轮胎规格") + private String tireSpec; + + /** + * 轴距(mm) + */ + @ExcelProperty(value = "轴距(mm)") + private Long wheelbaseMm; + + /** + * 车轴数 + */ + @ExcelProperty(value = "车轴数") + private Long axleCount; + + /** + * 登记证书图片URL(多个逗号分隔) + */ + @ExcelProperty(value = "登记证书图片URL(多个逗号分隔)") + private String registerCertImageUrls; + + /** + * 登记证书图片URL(多个逗号分隔)Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "registerCertImageUrls") + private String registerCertImageUrlsUrl; + /** + * 车牌号 + */ + @ExcelProperty(value = "车牌号") + private String plateNumber; + + /** + * 档案号 + */ + @ExcelProperty(value = "档案号") + private String archiveNo; + + /** + * 车辆类型 + */ + @ExcelProperty(value = "车辆类型") + private String vehicleType; + + /** + * 注册日期 + */ + @ExcelProperty(value = "注册日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date firstRegisterDate; + + /** + * 发证日期 + */ + @ExcelProperty(value = "发证日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date certificateIssueDate; + + /** + * 检验有效期(YYYY-MM) + */ + @ExcelProperty(value = "检验有效期(YYYY-MM)") + private String inspectionValidMonth; + + /** + * 机动车所有人 + */ + @ExcelProperty(value = "机动车所有人") + private String owner; + + /** + * 地址 + */ + @ExcelProperty(value = "地址") + private String address; + + /** + * 品牌型号 + */ + @ExcelProperty(value = "品牌型号") + private String brandModel; + + /** + * 核定载人数 + */ + @ExcelProperty(value = "核定载人数") + private Long ratedPassengerCount; + + /** + * 总质量(kg) + */ + @ExcelProperty(value = "总质量(kg)") + private Long grossWeightKg; + + /** + * 整备质量(kg) + */ + @ExcelProperty(value = "整备质量(kg)") + private Long curbWeightKg; + + /** + * 核定载质量(kg) + */ + @ExcelProperty(value = "核定载质量(kg)") + private Long ratedLoadKg; + + /** + * 准牵引总质量(kg) + */ + @ExcelProperty(value = "准牵引总质量(kg)") + private Long towingMassKg; + + /** + * 车辆外廓长度(mm) + */ + @ExcelProperty(value = "车辆外廓长度(mm)") + private Long vehicleLengthMm; + + /** + * 车辆外廓宽度(mm) + */ + @ExcelProperty(value = "车辆外廓宽度(mm)") + private Long vehicleWidthMm; + + /** + * 车辆外廓高度(mm) + */ + @ExcelProperty(value = "车辆外廓高度(mm)") + private Long vehicleHeightMm; + + /** + * 行驶证主页图片URL + */ + @ExcelProperty(value = "行驶证主页图片URL") + private String drivingLicenseMainUrls; + + /** + * 行驶证主页图片URLUrl + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "drivingLicenseMainUrls") + private String drivingLicenseMainUrlsUrl; + /** + * 行驶证副页图片URL + */ + @ExcelProperty(value = "行驶证副页图片URL") + private String drivingLicenseAuxUrls; + + /** + * 行驶证副页图片URLUrl + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "drivingLicenseAuxUrls") + private String drivingLicenseAuxUrlsUrl; + /** + * 行驶证其他URL + */ + @ExcelProperty(value = "行驶证其他URL") + private String drivingLicenseOtherUrl; + + /** + * 行驶证其他URLUrl + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "drivingLicenseOtherUrl") + private String drivingLicenseOtherUrlUrl; + /** + * 运输证号 + */ + @ExcelProperty(value = "运输证号") + private String transportLicenseNo; + + /** + * 运输证注册日期 + */ + @ExcelProperty(value = "运输证注册日期") + private Date transportRegisterDate; + + /** + * 运输证发证日期 + */ + @ExcelProperty(value = "运输证发证日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date transportIssueDate; + + /** + * 运输证有效期(YYYY-MM) + */ + @ExcelProperty(value = "运输证有效期(YYYY-MM)") + private String transportValidMonth; + + /** + * 运输证图片URL(多个逗号分隔) + */ + @ExcelProperty(value = "运输证图片URL(多个逗号分隔)") + private String transportLicenseUrls; + + /** + * 运输证图片URL(多个逗号分隔)Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "transportLicenseUrls") + private String transportLicenseUrlsUrl; + + /** + * 发动机号 + */ + private String driveMotorModel; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/mapper/HotVehicleMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/mapper/HotVehicleMapper.java new file mode 100644 index 0000000..eb3d5f9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/mapper/HotVehicleMapper.java @@ -0,0 +1,36 @@ +package com.hotwj.platform.resourceManagement.vehicleManagement.mapper; + +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleCategoryStatVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +import java.util.List; + +/** + * 公司车辆信息Mapper接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Mapper +public interface HotVehicleMapper extends BaseMapperPlus { + + /** + * 统计公司车辆分类数量 + * + * @param companyId 公司ID + * @return 统计结果 + */ + List selectVehicleCategoryStat(@Param("companyId") Long companyId); + + /** + * 查询可进行三检的车辆列表(无历史记录或上一轮已完结) + * + * @param companyId 公司ID + * @return 车辆列表 + */ + List selectAvailableForThreeInspect(@Param("companyId") Long companyId); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/service/IHotVehicleService.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/service/IHotVehicleService.java new file mode 100644 index 0000000..04a9458 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/service/IHotVehicleService.java @@ -0,0 +1,97 @@ +package com.hotwj.platform.resourceManagement.vehicleManagement.service; + +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleStatusBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleCategoryStatVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleImportVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 公司车辆信息Service接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +public interface IHotVehicleService { + + /** + * 统计公司车辆分类数量 + * + * @param companyId 公司ID + * @return 统计结果 + */ + List queryVehicleCategoryStat(Long companyId); + + /** + * 导入车辆数据 + * + * @param vehicleList 车辆数据列表 + * @param companyId 公司ID + * @return 导入结果 + */ + String importData(List vehicleList, Long companyId); + + /** + * 查询公司车辆信息 + * + * @param id 主键 + * @return 公司车辆信息 + */ + HotVehicleVo queryById(Long id); + + /** + * 分页查询公司车辆信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 公司车辆信息分页列表 + */ + TableDataInfo queryPageList(HotVehicleBo bo, PageQuery pageQuery); + + /** + * 分页查询公司车辆信息列表 + * + * @param bo 查询条件 车辆状态为字符串逗号拼接 + * @param pageQuery 分页参数 + * @return 公司车辆信息分页列表 + */ + TableDataInfo queryPageListByStatus(HotVehicleStatusBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的公司车辆信息列表 + * + * @param bo 查询条件 + * @return 公司车辆信息列表 + */ + List queryList(HotVehicleBo bo); + + /** + * 新增公司车辆信息 + * + * @param bo 公司车辆信息 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleBo bo); + + /** + * 修改公司车辆信息 + * + * @param bo 公司车辆信息 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleBo bo); + + /** + * 校验并批量删除公司车辆信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/service/impl/HotVehicleServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/service/impl/HotVehicleServiceImpl.java new file mode 100644 index 0000000..edba3d3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/service/impl/HotVehicleServiceImpl.java @@ -0,0 +1,748 @@ +package com.hotwj.platform.resourceManagement.vehicleManagement.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import com.hotwj.platform.resourceManagement.vehicleCertReissue.domain.HotVehicleCertReissue; +import com.hotwj.platform.resourceManagement.vehicleCertReissue.mapper.HotVehicleCertReissueMapper; +import com.hotwj.platform.resourceManagement.vehicleContract.domain.HotVehicleContract; +import com.hotwj.platform.resourceManagement.vehicleContract.domain.bo.HotVehicleContractBo; +import com.hotwj.platform.resourceManagement.vehicleContract.mapper.HotVehicleContractMapper; +import com.hotwj.platform.resourceManagement.vehicleDevice.domain.HotVehicleDevice; +import com.hotwj.platform.resourceManagement.vehicleDevice.domain.bo.HotVehicleDeviceBo; +import com.hotwj.platform.resourceManagement.vehicleDevice.mapper.HotVehicleDeviceMapper; +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.HotVehicleInsurance; +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.bo.HotVehicleInsuranceBo; +import com.hotwj.platform.resourceManagement.vehicleInsurance.mapper.HotVehicleInsuranceMapper; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleStatusBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleCategoryStatVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleImportVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.mapper.HotVehicleMapper; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import com.hotwj.platform.resourceManagement.vehicleRepair.domain.HotVehicleRepair; +import com.hotwj.platform.resourceManagement.vehicleRepair.mapper.HotVehicleRepairMapper; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.service.DictService; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.ValidatorUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleServiceImpl implements IHotVehicleService { + + private final HotVehicleMapper baseMapper; + private final HotVehicleContractMapper vehicleContractMapper; + private final HotVehicleInsuranceMapper vehicleInsuranceMapper; + private final HotVehicleDeviceMapper vehicleDeviceMapper; + private final HotDriverMapper driverMapper; + private final HotCompanySafetyManagerMapper managerMapper; + private final IHotSystemNotificationService notificationService; + private final HotVehicleRepairMapper repairMapper; + private final HotVehicleCertReissueMapper certReissueMapper; + private final DictService dictService; + private static final Set CLEAR_DRIVER_OPERATION_STATUS = new HashSet<>(Arrays.asList(3L, 4L, 5L)); + + /** + * 统计公司车辆分类数量 + * + * @param companyId 公司ID + * @return 统计结果 + */ + @Override + public List queryVehicleCategoryStat(Long companyId) { + return baseMapper.selectVehicleCategoryStat(companyId); + } + + /** + * 导入车辆数据 + * + * @param vehicleList 车辆数据列表 + * @param companyId 公司ID + * @return 导入结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public String importData(List vehicleList, Long companyId) { + if (CollUtil.isEmpty(vehicleList)) { + throw new ServiceException("导入数据不能为空!"); + } + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + + for (HotVehicleImportVo vo : vehicleList) { + try { + // 验证数据格式 + ValidatorUtils.validate(vo); + + // 验证是否存在这个车牌号 + boolean exists = baseMapper.selectCount(new LambdaQueryWrapper() + .eq(HotVehicle::getPlateNumber, vo.getPlateNumber()) + .eq(HotVehicle::getCompanyId, companyId)) > 0; + + if (exists) { + failureNum++; + failureMsg.append("
" + failureNum + "、车牌号 " + vo.getPlateNumber() + " 已存在"); + continue; + } + + HotVehicle vehicle = MapstructUtils.convert(vo, HotVehicle.class); + vehicle.setCompanyId(companyId); + + // 处理日期格式转换 + if (vo.getTransportValidMonth() != null) { + vehicle.setTransportValidMonth(DateUtil.format(vo.getTransportValidMonth(), "yyyy-MM")); + } + if (vo.getInspectionValidMonth() != null) { + vehicle.setInspectionValidMonth(DateUtil.format(vo.getInspectionValidMonth(), "yyyy-MM")); + } + + // 备注 + vehicle.setRemark("Excel导入"); + + validEntityBeforeSave(vehicle); + + baseMapper.insert(vehicle); + successNum++; + successMsg.append("
" + successNum + "、车牌号 " + vo.getPlateNumber() + " 导入成功"); + } catch (ConstraintViolationException e) { + failureNum++; + String msg = e.getConstraintViolations().stream() + .map(ConstraintViolation::getMessage) + .collect(Collectors.joining(", ")); + failureMsg.append("
" + failureNum + "、车牌号 " + vo.getPlateNumber() + " 导入失败:" + msg); + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、车牌号 " + vo.getPlateNumber() + " 导入失败:"; + failureMsg.append(msg + e.getMessage()); + log.error(msg, e); + } + } + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + + /** + * 查询公司车辆信息 + * + * @param id 主键 + * @return 公司车辆信息 + */ + @Override + public HotVehicleVo queryById(Long id) { + HotVehicleVo vo = baseMapper.selectVoById(id); + fillCurrentBindingPersonName(vo); + return vo; + } + + /** + * 分页查询公司车辆信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 公司车辆信息分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + fillCurrentBindingPersonName(result.getRecords()); + return TableDataInfo.build(result); + } + + /** + * 分页查询公司车辆信息列表 + * + * @param bo 查询条件 车辆状态为字符串逗号拼接 + * @param pageQuery 分页参数 + * @return 公司车辆信息分页列表 + */ + @Override + public TableDataInfo queryPageListByStatus(HotVehicleStatusBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapperByStatus(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + fillCurrentBindingPersonName(result.getRecords()); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的公司车辆信息列表 + * + * @param bo 查询条件 + * @return 公司车辆信息列表 + */ + @Override + public List queryList(HotVehicleBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + List list = baseMapper.selectVoList(lqw); + fillCurrentBindingPersonName(list); + return list; + } + + private void fillCurrentBindingPersonName(List list) { + if (CollUtil.isEmpty(list)) { + return; + } + for (HotVehicleVo vo : list) { + fillCurrentBindingPersonName(vo); + } + } + + private void fillCurrentBindingPersonName(HotVehicleVo vo) { + if (vo == null || StringUtils.isBlank(vo.getCurrentDriver())) { + return; + } + List displayParts = new ArrayList<>(); + String[] ids = vo.getCurrentDriver().split(","); + for (String idRaw : ids) { + String id = idRaw == null ? null : idRaw.trim(); + if (StringUtils.isBlank(id)) { + continue; + } + HotDriver driver = driverMapper.selectById(id); + if (driver == null || StringUtils.isBlank(driver.getName())) { + continue; + } + String personTypeLabel = "未知"; + if (StringUtils.isNotBlank(driver.getPersonType())) { + String label = dictService.getDictLabel("hot_person_type", driver.getPersonType()); + personTypeLabel = StringUtils.isNotBlank(label) ? label : driver.getPersonType(); + } + displayParts.add(driver.getName() + "(" + personTypeLabel + ")"); + } + if (CollUtil.isNotEmpty(displayParts)) { + vo.setCurrentBindingPersonName(String.join(",", displayParts)); + } + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotVehicle::getOperationStatus) + .orderByAsc(HotVehicle::getArchiveNo); + lqw.eq(bo.getCompanyId() != null, HotVehicle::getCompanyId, bo.getCompanyId()); + lqw.in(CollUtil.isNotEmpty(bo.getIds()), HotVehicle::getId, bo.getIds()); + lqw.eq(StringUtils.isNotBlank(bo.getGpsParams()), HotVehicle::getGpsParams, bo.getGpsParams()); + lqw.eq(bo.getIsTrailer() != null, HotVehicle::getIsTrailer, bo.getIsTrailer()); + lqw.eq(bo.getVehicleStatus() != null, HotVehicle::getVehicleStatus, bo.getVehicleStatus()); + lqw.eq(bo.getOperationStatus() != null, HotVehicle::getOperationStatus, bo.getOperationStatus()); + lqw.eq(StringUtils.isNotBlank(bo.getCurrentDriver()), HotVehicle::getCurrentDriver, bo.getCurrentDriver()); + lqw.eq(StringUtils.isNotBlank(bo.getBodyImageUrls()), HotVehicle::getBodyImageUrls, bo.getBodyImageUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getOtherAttachmentUrls()), HotVehicle::getOtherAttachmentUrls, bo.getOtherAttachmentUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getColor()), HotVehicle::getColor, bo.getColor()); + lqw.eq(bo.getManufactureDate() != null, HotVehicle::getManufactureDate, bo.getManufactureDate()); + lqw.eq(StringUtils.isNotBlank(bo.getFuelType()), HotVehicle::getFuelType, bo.getFuelType()); + lqw.like(StringUtils.isNotBlank(bo.getManufacturerName()), HotVehicle::getManufacturerName, bo.getManufacturerName()); + lqw.eq(StringUtils.isNotBlank(bo.getPlateColor()), HotVehicle::getPlateColor, bo.getPlateColor()); + lqw.eq(bo.getVehicleRegisterDate() != null, HotVehicle::getVehicleRegisterDate, bo.getVehicleRegisterDate()); + lqw.eq(bo.getEngineDisplacementMl() != null, HotVehicle::getEngineDisplacementMl, bo.getEngineDisplacementMl()); + lqw.eq(bo.getTireCount() != null, HotVehicle::getTireCount, bo.getTireCount()); + lqw.eq(StringUtils.isNotBlank(bo.getChassisModel()), HotVehicle::getChassisModel, bo.getChassisModel()); + lqw.eq(StringUtils.isNotBlank(bo.getSatellitePositioningDevice()), HotVehicle::getSatellitePositioningDevice, bo.getSatellitePositioningDevice()); + lqw.eq(StringUtils.isNotBlank(bo.getTransmission()), HotVehicle::getTransmission, bo.getTransmission()); + lqw.eq(StringUtils.isNotBlank(bo.getRetarder()), HotVehicle::getRetarder, bo.getRetarder()); + lqw.eq(bo.getCargoInnerVolumeM3() != null, HotVehicle::getCargoInnerVolumeM3, bo.getCargoInnerVolumeM3()); + lqw.eq(StringUtils.isNotBlank(bo.getEmissionStandard()), HotVehicle::getEmissionStandard, bo.getEmissionStandard()); + lqw.eq(StringUtils.isNotBlank(bo.getBatteryType()), HotVehicle::getBatteryType, bo.getBatteryType()); + lqw.eq(bo.getInsuredSeatCount() != null, HotVehicle::getInsuredSeatCount, bo.getInsuredSeatCount()); + lqw.eq(StringUtils.isNotBlank(bo.getServiceBrakeType()), HotVehicle::getServiceBrakeType, bo.getServiceBrakeType()); + lqw.eq(StringUtils.isNotBlank(bo.getFrontBrakeType()), HotVehicle::getFrontBrakeType, bo.getFrontBrakeType()); + lqw.eq(StringUtils.isNotBlank(bo.getRearBrakeType()), HotVehicle::getRearBrakeType, bo.getRearBrakeType()); + lqw.eq(bo.getHasAbs() != null, HotVehicle::getHasAbs, bo.getHasAbs()); + lqw.eq(bo.getHasAirConditioning() != null, HotVehicle::getHasAirConditioning, bo.getHasAirConditioning()); + lqw.eq(bo.getHasTriangleBlock() != null, HotVehicle::getHasTriangleBlock, bo.getHasTriangleBlock()); + lqw.eq(bo.getHasFireExtinguisher() != null, HotVehicle::getHasFireExtinguisher, bo.getHasFireExtinguisher()); + lqw.eq(bo.getHasBreakdownWarningSign() != null, HotVehicle::getHasBreakdownWarningSign, bo.getHasBreakdownWarningSign()); + lqw.eq(bo.getDomesticMark() != null, HotVehicle::getDomesticMark, bo.getDomesticMark()); + lqw.eq(StringUtils.isNotBlank(bo.getVehicleCategory()), HotVehicle::getVehicleCategory, bo.getVehicleCategory()); + lqw.eq(bo.getMandatoryScrapEndDate() != null, HotVehicle::getMandatoryScrapEndDate, bo.getMandatoryScrapEndDate()); + lqw.eq(StringUtils.isNotBlank(bo.getUseNature()), HotVehicle::getUseNature, bo.getUseNature()); + lqw.eq(StringUtils.isNotBlank(bo.getVehicleModel()), HotVehicle::getVehicleModel, bo.getVehicleModel()); + lqw.eq(StringUtils.isNotBlank(bo.getFrameNumber()), HotVehicle::getFrameNumber, bo.getFrameNumber()); + lqw.eq(bo.getCargoInnerLengthMm() != null, HotVehicle::getCargoInnerLengthMm, bo.getCargoInnerLengthMm()); + lqw.eq(bo.getCargoInnerWidthMm() != null, HotVehicle::getCargoInnerWidthMm, bo.getCargoInnerWidthMm()); + lqw.eq(bo.getCargoInnerHeightMm() != null, HotVehicle::getCargoInnerHeightMm, bo.getCargoInnerHeightMm()); + lqw.eq(bo.getIsMortgaged() != null, HotVehicle::getIsMortgaged, bo.getIsMortgaged()); + lqw.eq(StringUtils.isNotBlank(bo.getMortgageCompany()), HotVehicle::getMortgageCompany, bo.getMortgageCompany()); + lqw.eq(StringUtils.isNotBlank(bo.getMortgageContact()), HotVehicle::getMortgageContact, bo.getMortgageContact()); + lqw.eq(StringUtils.isNotBlank(bo.getMortgageContactPhone()), HotVehicle::getMortgageContactPhone, bo.getMortgageContactPhone()); + lqw.eq(bo.getEnginePowerKw() != null, HotVehicle::getEnginePowerKw, bo.getEnginePowerKw()); + lqw.eq(bo.getMotorPowerKw() != null, HotVehicle::getMotorPowerKw, bo.getMotorPowerKw()); + lqw.eq(StringUtils.isNotBlank(bo.getEngineNumber()), HotVehicle::getEngineNumber, bo.getEngineNumber()); + lqw.eq(StringUtils.isNotBlank(bo.getTireSpec()), HotVehicle::getTireSpec, bo.getTireSpec()); + lqw.eq(bo.getWheelbaseMm() != null, HotVehicle::getWheelbaseMm, bo.getWheelbaseMm()); + lqw.eq(bo.getAxleCount() != null, HotVehicle::getAxleCount, bo.getAxleCount()); + lqw.eq(StringUtils.isNotBlank(bo.getRegisterCertImageUrls()), HotVehicle::getRegisterCertImageUrls, bo.getRegisterCertImageUrls()); + lqw.like(StringUtils.isNotBlank(bo.getPlateNumber()), HotVehicle::getPlateNumber, bo.getPlateNumber()); + lqw.like(StringUtils.isNotBlank(bo.getArchiveNo()), HotVehicle::getArchiveNo, bo.getArchiveNo()); + lqw.eq(StringUtils.isNotBlank(bo.getVehicleType()), HotVehicle::getVehicleType, bo.getVehicleType()); + lqw.eq(bo.getFirstRegisterDate() != null, HotVehicle::getFirstRegisterDate, bo.getFirstRegisterDate()); + lqw.eq(bo.getCertificateIssueDate() != null, HotVehicle::getCertificateIssueDate, bo.getCertificateIssueDate()); + lqw.eq(StringUtils.isNotBlank(bo.getInspectionValidMonth()), HotVehicle::getInspectionValidMonth, bo.getInspectionValidMonth()); + lqw.eq(StringUtils.isNotBlank(bo.getOwner()), HotVehicle::getOwner, bo.getOwner()); + lqw.eq(StringUtils.isNotBlank(bo.getAddress()), HotVehicle::getAddress, bo.getAddress()); + lqw.eq(StringUtils.isNotBlank(bo.getBrandModel()), HotVehicle::getBrandModel, bo.getBrandModel()); + lqw.eq(bo.getRatedPassengerCount() != null, HotVehicle::getRatedPassengerCount, bo.getRatedPassengerCount()); + lqw.eq(bo.getGrossWeightKg() != null, HotVehicle::getGrossWeightKg, bo.getGrossWeightKg()); + lqw.eq(bo.getCurbWeightKg() != null, HotVehicle::getCurbWeightKg, bo.getCurbWeightKg()); + lqw.eq(bo.getRatedLoadKg() != null, HotVehicle::getRatedLoadKg, bo.getRatedLoadKg()); + lqw.eq(bo.getTowingMassKg() != null, HotVehicle::getTowingMassKg, bo.getTowingMassKg()); + lqw.eq(bo.getVehicleLengthMm() != null, HotVehicle::getVehicleLengthMm, bo.getVehicleLengthMm()); + lqw.eq(bo.getVehicleWidthMm() != null, HotVehicle::getVehicleWidthMm, bo.getVehicleWidthMm()); + lqw.eq(bo.getVehicleHeightMm() != null, HotVehicle::getVehicleHeightMm, bo.getVehicleHeightMm()); + lqw.eq(StringUtils.isNotBlank(bo.getDrivingLicenseMainUrls()), HotVehicle::getDrivingLicenseMainUrls, bo.getDrivingLicenseMainUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getDrivingLicenseAuxUrls()), HotVehicle::getDrivingLicenseAuxUrls, bo.getDrivingLicenseAuxUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getDrivingLicenseOtherUrl()), HotVehicle::getDrivingLicenseOtherUrl, bo.getDrivingLicenseOtherUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getTransportLicenseNo()), HotVehicle::getTransportLicenseNo, bo.getTransportLicenseNo()); + lqw.eq(bo.getTransportRegisterDate() != null, HotVehicle::getTransportRegisterDate, bo.getTransportRegisterDate()); + lqw.eq(bo.getTransportIssueDate() != null, HotVehicle::getTransportIssueDate, bo.getTransportIssueDate()); + lqw.eq(StringUtils.isNotBlank(bo.getTransportValidMonth()), HotVehicle::getTransportValidMonth, bo.getTransportValidMonth()); + lqw.eq(StringUtils.isNotBlank(bo.getTransportLicenseUrls()), HotVehicle::getTransportLicenseUrls, bo.getTransportLicenseUrls()); + lqw.like(StringUtils.isNotBlank(bo.getDriveMotorModel()), HotVehicle::getDriveMotorModel, bo.getDriveMotorModel()); + return lqw; + } + + private LambdaQueryWrapper buildQueryWrapperByStatus(HotVehicleStatusBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotVehicle::getCreateTime); + lqw.eq(bo.getCompanyId() != null, HotVehicle::getCompanyId, bo.getCompanyId()); + lqw.eq(StringUtils.isNotBlank(bo.getGpsParams()), HotVehicle::getGpsParams, bo.getGpsParams()); + lqw.eq(bo.getIsTrailer() != null, HotVehicle::getIsTrailer, bo.getIsTrailer()); + lqw.eq(bo.getVehicleStatus() != null, HotVehicle::getVehicleStatus, bo.getVehicleStatus()); + lqw.in(HotVehicle::getOperationStatus, Arrays.asList(bo.getOperationStatus().split(","))); + lqw.eq(StringUtils.isNotBlank(bo.getCurrentDriver()), HotVehicle::getCurrentDriver, bo.getCurrentDriver()); + lqw.eq(StringUtils.isNotBlank(bo.getBodyImageUrls()), HotVehicle::getBodyImageUrls, bo.getBodyImageUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getOtherAttachmentUrls()), HotVehicle::getOtherAttachmentUrls, bo.getOtherAttachmentUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getColor()), HotVehicle::getColor, bo.getColor()); + lqw.eq(bo.getManufactureDate() != null, HotVehicle::getManufactureDate, bo.getManufactureDate()); + lqw.eq(StringUtils.isNotBlank(bo.getFuelType()), HotVehicle::getFuelType, bo.getFuelType()); + lqw.like(StringUtils.isNotBlank(bo.getManufacturerName()), HotVehicle::getManufacturerName, bo.getManufacturerName()); + lqw.eq(StringUtils.isNotBlank(bo.getPlateColor()), HotVehicle::getPlateColor, bo.getPlateColor()); + lqw.eq(bo.getVehicleRegisterDate() != null, HotVehicle::getVehicleRegisterDate, bo.getVehicleRegisterDate()); + lqw.eq(bo.getEngineDisplacementMl() != null, HotVehicle::getEngineDisplacementMl, bo.getEngineDisplacementMl()); + lqw.eq(bo.getTireCount() != null, HotVehicle::getTireCount, bo.getTireCount()); + lqw.eq(StringUtils.isNotBlank(bo.getChassisModel()), HotVehicle::getChassisModel, bo.getChassisModel()); + lqw.eq(StringUtils.isNotBlank(bo.getSatellitePositioningDevice()), HotVehicle::getSatellitePositioningDevice, bo.getSatellitePositioningDevice()); + lqw.eq(StringUtils.isNotBlank(bo.getTransmission()), HotVehicle::getTransmission, bo.getTransmission()); + lqw.eq(StringUtils.isNotBlank(bo.getRetarder()), HotVehicle::getRetarder, bo.getRetarder()); + lqw.eq(bo.getCargoInnerVolumeM3() != null, HotVehicle::getCargoInnerVolumeM3, bo.getCargoInnerVolumeM3()); + lqw.eq(StringUtils.isNotBlank(bo.getEmissionStandard()), HotVehicle::getEmissionStandard, bo.getEmissionStandard()); + lqw.eq(StringUtils.isNotBlank(bo.getBatteryType()), HotVehicle::getBatteryType, bo.getBatteryType()); + lqw.eq(bo.getInsuredSeatCount() != null, HotVehicle::getInsuredSeatCount, bo.getInsuredSeatCount()); + lqw.eq(StringUtils.isNotBlank(bo.getServiceBrakeType()), HotVehicle::getServiceBrakeType, bo.getServiceBrakeType()); + lqw.eq(StringUtils.isNotBlank(bo.getFrontBrakeType()), HotVehicle::getFrontBrakeType, bo.getFrontBrakeType()); + lqw.eq(StringUtils.isNotBlank(bo.getRearBrakeType()), HotVehicle::getRearBrakeType, bo.getRearBrakeType()); + lqw.eq(bo.getHasAbs() != null, HotVehicle::getHasAbs, bo.getHasAbs()); + lqw.eq(bo.getHasAirConditioning() != null, HotVehicle::getHasAirConditioning, bo.getHasAirConditioning()); + lqw.eq(bo.getHasTriangleBlock() != null, HotVehicle::getHasTriangleBlock, bo.getHasTriangleBlock()); + lqw.eq(bo.getHasFireExtinguisher() != null, HotVehicle::getHasFireExtinguisher, bo.getHasFireExtinguisher()); + lqw.eq(bo.getHasBreakdownWarningSign() != null, HotVehicle::getHasBreakdownWarningSign, bo.getHasBreakdownWarningSign()); + lqw.eq(bo.getDomesticMark() != null, HotVehicle::getDomesticMark, bo.getDomesticMark()); + lqw.eq(StringUtils.isNotBlank(bo.getVehicleCategory()), HotVehicle::getVehicleCategory, bo.getVehicleCategory()); + lqw.eq(bo.getMandatoryScrapEndDate() != null, HotVehicle::getMandatoryScrapEndDate, bo.getMandatoryScrapEndDate()); + lqw.eq(StringUtils.isNotBlank(bo.getUseNature()), HotVehicle::getUseNature, bo.getUseNature()); + lqw.eq(StringUtils.isNotBlank(bo.getVehicleModel()), HotVehicle::getVehicleModel, bo.getVehicleModel()); + lqw.eq(StringUtils.isNotBlank(bo.getFrameNumber()), HotVehicle::getFrameNumber, bo.getFrameNumber()); + lqw.eq(bo.getCargoInnerLengthMm() != null, HotVehicle::getCargoInnerLengthMm, bo.getCargoInnerLengthMm()); + lqw.eq(bo.getCargoInnerWidthMm() != null, HotVehicle::getCargoInnerWidthMm, bo.getCargoInnerWidthMm()); + lqw.eq(bo.getCargoInnerHeightMm() != null, HotVehicle::getCargoInnerHeightMm, bo.getCargoInnerHeightMm()); + lqw.eq(bo.getIsMortgaged() != null, HotVehicle::getIsMortgaged, bo.getIsMortgaged()); + lqw.eq(StringUtils.isNotBlank(bo.getMortgageCompany()), HotVehicle::getMortgageCompany, bo.getMortgageCompany()); + lqw.eq(StringUtils.isNotBlank(bo.getMortgageContact()), HotVehicle::getMortgageContact, bo.getMortgageContact()); + lqw.eq(StringUtils.isNotBlank(bo.getMortgageContactPhone()), HotVehicle::getMortgageContactPhone, bo.getMortgageContactPhone()); + lqw.eq(bo.getEnginePowerKw() != null, HotVehicle::getEnginePowerKw, bo.getEnginePowerKw()); + lqw.eq(bo.getMotorPowerKw() != null, HotVehicle::getMotorPowerKw, bo.getMotorPowerKw()); + lqw.eq(StringUtils.isNotBlank(bo.getEngineNumber()), HotVehicle::getEngineNumber, bo.getEngineNumber()); + lqw.eq(StringUtils.isNotBlank(bo.getTireSpec()), HotVehicle::getTireSpec, bo.getTireSpec()); + lqw.eq(bo.getWheelbaseMm() != null, HotVehicle::getWheelbaseMm, bo.getWheelbaseMm()); + lqw.eq(bo.getAxleCount() != null, HotVehicle::getAxleCount, bo.getAxleCount()); + lqw.eq(StringUtils.isNotBlank(bo.getRegisterCertImageUrls()), HotVehicle::getRegisterCertImageUrls, bo.getRegisterCertImageUrls()); + lqw.like(StringUtils.isNotBlank(bo.getPlateNumber()), HotVehicle::getPlateNumber, bo.getPlateNumber()); + lqw.eq(StringUtils.isNotBlank(bo.getArchiveNo()), HotVehicle::getArchiveNo, bo.getArchiveNo()); + lqw.eq(StringUtils.isNotBlank(bo.getVehicleType()), HotVehicle::getVehicleType, bo.getVehicleType()); + lqw.eq(bo.getFirstRegisterDate() != null, HotVehicle::getFirstRegisterDate, bo.getFirstRegisterDate()); + lqw.eq(bo.getCertificateIssueDate() != null, HotVehicle::getCertificateIssueDate, bo.getCertificateIssueDate()); + lqw.eq(StringUtils.isNotBlank(bo.getInspectionValidMonth()), HotVehicle::getInspectionValidMonth, bo.getInspectionValidMonth()); + lqw.eq(StringUtils.isNotBlank(bo.getOwner()), HotVehicle::getOwner, bo.getOwner()); + lqw.eq(StringUtils.isNotBlank(bo.getAddress()), HotVehicle::getAddress, bo.getAddress()); + lqw.eq(StringUtils.isNotBlank(bo.getBrandModel()), HotVehicle::getBrandModel, bo.getBrandModel()); + lqw.eq(bo.getRatedPassengerCount() != null, HotVehicle::getRatedPassengerCount, bo.getRatedPassengerCount()); + lqw.eq(bo.getGrossWeightKg() != null, HotVehicle::getGrossWeightKg, bo.getGrossWeightKg()); + lqw.eq(bo.getCurbWeightKg() != null, HotVehicle::getCurbWeightKg, bo.getCurbWeightKg()); + lqw.eq(bo.getRatedLoadKg() != null, HotVehicle::getRatedLoadKg, bo.getRatedLoadKg()); + lqw.eq(bo.getTowingMassKg() != null, HotVehicle::getTowingMassKg, bo.getTowingMassKg()); + lqw.eq(bo.getVehicleLengthMm() != null, HotVehicle::getVehicleLengthMm, bo.getVehicleLengthMm()); + lqw.eq(bo.getVehicleWidthMm() != null, HotVehicle::getVehicleWidthMm, bo.getVehicleWidthMm()); + lqw.eq(bo.getVehicleHeightMm() != null, HotVehicle::getVehicleHeightMm, bo.getVehicleHeightMm()); + lqw.eq(StringUtils.isNotBlank(bo.getDrivingLicenseMainUrls()), HotVehicle::getDrivingLicenseMainUrls, bo.getDrivingLicenseMainUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getDrivingLicenseAuxUrls()), HotVehicle::getDrivingLicenseAuxUrls, bo.getDrivingLicenseAuxUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getDrivingLicenseOtherUrl()), HotVehicle::getDrivingLicenseOtherUrl, bo.getDrivingLicenseOtherUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getTransportLicenseNo()), HotVehicle::getTransportLicenseNo, bo.getTransportLicenseNo()); + lqw.eq(bo.getTransportRegisterDate() != null, HotVehicle::getTransportRegisterDate, bo.getTransportRegisterDate()); + lqw.eq(bo.getTransportIssueDate() != null, HotVehicle::getTransportIssueDate, bo.getTransportIssueDate()); + lqw.eq(StringUtils.isNotBlank(bo.getTransportValidMonth()), HotVehicle::getTransportValidMonth, bo.getTransportValidMonth()); + lqw.eq(StringUtils.isNotBlank(bo.getTransportLicenseUrls()), HotVehicle::getTransportLicenseUrls, bo.getTransportLicenseUrls()); + return lqw; + } + /** + * 新增公司车辆信息 + * + * @param bo 公司车辆信息 + * @return 是否新增成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(HotVehicleBo bo) { + // 新增车辆时不允许绑定驾驶员,驾驶员需在车辆创建后通过编辑流程再绑定 + bo.setCurrentDriver(null); + HotVehicle add = MapstructUtils.convert(bo, HotVehicle.class); + if (add.getOperationStatus() != null && CLEAR_DRIVER_OPERATION_STATUS.contains(add.getOperationStatus())) { + add.setCurrentDriver(null); + bo.setCurrentDriver(null); + } + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (!flag) { + return false; + } + String vehicleId = add.getId(); + if (StringUtils.isBlank(vehicleId)) { + throw new ServiceException("新增车辆后未获取到车辆ID"); + } + bo.setId(vehicleId); + insertContracts(bo, vehicleId); + insertVehicleInsurances(bo, vehicleId); + insertVehicleDevices(bo, vehicleId); + + return true; + } + + private void insertContracts(HotVehicleBo bo, String id) { + if (CollUtil.isNotEmpty(bo.getVehicleContracts())) { + List contracts = MapstructUtils.convert(bo.getVehicleContracts(), HotVehicleContract.class); + for (HotVehicleContract contract : contracts) { + contract.setId(IdWorker.get32UUID()); + contract.setVehicleId(id); + contract.setCompanyId(bo.getCompanyId()); + contract.setIsDeleted(0L); + + vehicleContractMapper.insert(contract); + } + } + } + + private void insertVehicleInsurances(HotVehicleBo bo, String id) { + if (CollUtil.isNotEmpty(bo.getVehicleInsurances())) { + List insurances = MapstructUtils.convert(bo.getVehicleInsurances(), HotVehicleInsurance.class); + for (HotVehicleInsurance insurance : insurances) { + insurance.setId(IdWorker.get32UUID()); + insurance.setVehicleId(id); + insurance.setCompanyId(bo.getCompanyId()); + insurance.setIsDeleted(0L); + + vehicleInsuranceMapper.insert(insurance); + } + } + } + + private void insertVehicleDevices(HotVehicleBo bo, String id) { + if (CollUtil.isNotEmpty(bo.getVehicleDevices())) { + List devices = MapstructUtils.convert(bo.getVehicleDevices(), HotVehicleDevice.class); + for (HotVehicleDevice device : devices) { + device.setId(IdWorker.get32UUID()); + device.setVehicleId(id); + device.setCompanyId(bo.getCompanyId()); + device.setIsDeleted(0L); + + vehicleDeviceMapper.insert(device); + } + } + } + + /** + * 修改公司车辆信息 + * + * @param bo 公司车辆信息 + * @return 是否修改成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(HotVehicleBo bo) { + // 获取旧数据用于比对 + HotVehicle oldVehicle = baseMapper.selectById(bo.getId()); + + HotVehicle update = MapstructUtils.convert(bo, HotVehicle.class); + if (update.getOperationStatus() != null && CLEAR_DRIVER_OPERATION_STATUS.contains(update.getOperationStatus())) { + update.setCurrentDriver(null); + bo.setCurrentDriver(null); + } + validEntityBeforeSave(update); + boolean flag = baseMapper.updateById(update) > 0; + + String vehicleId = update.getId(); + Long companyId = update.getCompanyId(); + + // 处理驾驶员变更:同步驾驶员信息的车牌号 + String oldDriverId = oldVehicle != null ? oldVehicle.getCurrentDriver() : null; + String newDriverId = update.getCurrentDriver(); + String oldPlate = oldVehicle != null ? oldVehicle.getPlateNumber() : null; + String newPlate = update.getPlateNumber(); + + // 如果驾驶员发生变化 或 车牌号发生变化 + if (!StringUtils.equals(oldDriverId, newDriverId) || !StringUtils.equals(oldPlate, newPlate)) { + List oldDrivers = StringUtils.isBlank(oldDriverId) ? new ArrayList<>() : Arrays.asList(oldDriverId.split(",")); + List newDrivers = StringUtils.isBlank(newDriverId) ? new ArrayList<>() : Arrays.asList(newDriverId.split(",")); + + // 1. 处理移除的驾驶员 (在旧列表中但不在新列表中 -> 清除车牌关联) + List removedDrivers = oldDrivers.stream() + .filter(d -> !newDrivers.contains(d)) + .collect(Collectors.toList()); + + if (CollUtil.isNotEmpty(removedDrivers)) { + driverMapper.update(null, + new LambdaUpdateWrapper() + .set(HotDriver::getPlateNumber, null) + .set(HotDriver::getVehicleId, null) + .in(HotDriver::getId, removedDrivers) + .eq(HotDriver::getCompanyId, companyId)); + } + + // 2. 处理当前的驾驶员 (无论是新增的还是保留的,都统一更新为当前车辆信息) + if (CollUtil.isNotEmpty(newDrivers)) { + driverMapper.update(null, + new LambdaUpdateWrapper() + .set(HotDriver::getPlateNumber, newPlate) + .set(HotDriver::getVehicleId, vehicleId) + .in(HotDriver::getId, newDrivers) + .eq(HotDriver::getCompanyId, companyId)); + // 2.1 对新增绑定的驾驶员发送系统消息 + List addedDrivers = newDrivers.stream() + .filter(d -> !oldDrivers.contains(d)) + .collect(Collectors.toList()); + if (CollUtil.isNotEmpty(addedDrivers) && StringUtils.isNotBlank(newPlate)) { + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent("您已经绑定车辆【" + newPlate + "】。"); + bos.setSourceType("驾驶员管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("驾驶员"); + bos.setReceiverIds(addedDrivers); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送驾驶员绑定车辆通知失败 companyId={} vehicleId={} plate={}", companyId, vehicleId, newPlate, e); + } + } + } + } + + // 更新合同信息 + if (CollUtil.isNotEmpty(bo.getVehicleContracts())) { + for (HotVehicleContractBo contractBo : bo.getVehicleContracts()) { + if (StringUtils.isBlank(contractBo.getId())) { + // 新增 + HotVehicleContract contract = MapstructUtils.convert(contractBo, HotVehicleContract.class); + contract.setId(IdWorker.get32UUID()); + contract.setVehicleId(vehicleId); + contract.setCompanyId(companyId); + contract.setIsDeleted(0L); + vehicleContractMapper.insert(contract); + } else if (contractBo.getIsDeleted() != null && contractBo.getIsDeleted() == 1L) { + // 删除 + vehicleContractMapper.deleteById(contractBo.getId()); + } else { + // 更新 + HotVehicleContract contract = MapstructUtils.convert(contractBo, HotVehicleContract.class); + contract.setVehicleId(vehicleId); + contract.setCompanyId(companyId); + vehicleContractMapper.updateById(contract); + } + } + } + + // 更新保险信息 + if (CollUtil.isNotEmpty(bo.getVehicleInsurances())) { + for (HotVehicleInsuranceBo insuranceBo : bo.getVehicleInsurances()) { + if (StringUtils.isBlank(insuranceBo.getId())) { + // 新增 + HotVehicleInsurance insurance = MapstructUtils.convert(insuranceBo, HotVehicleInsurance.class); + insurance.setId(IdWorker.get32UUID()); + insurance.setVehicleId(vehicleId); + insurance.setCompanyId(companyId); + insurance.setIsDeleted(0L); + vehicleInsuranceMapper.insert(insurance); + } else if (insuranceBo.getIsDeleted() != null && insuranceBo.getIsDeleted() == 1L) { + // 删除 + vehicleInsuranceMapper.deleteById(insuranceBo.getId()); + } else { + // 更新 + HotVehicleInsurance insurance = MapstructUtils.convert(insuranceBo, HotVehicleInsurance.class); + insurance.setVehicleId(vehicleId); + insurance.setCompanyId(companyId); + vehicleInsuranceMapper.updateById(insurance); + } + } + } + + // 更新设备信息 + if (CollUtil.isNotEmpty(bo.getVehicleDevices())) { + for (HotVehicleDeviceBo deviceBo : bo.getVehicleDevices()) { + if (StringUtils.isBlank(deviceBo.getId())) { + // 新增 + HotVehicleDevice device = MapstructUtils.convert(deviceBo, HotVehicleDevice.class); + device.setId(IdWorker.get32UUID()); + device.setVehicleId(vehicleId); + device.setCompanyId(companyId); + device.setIsDeleted(0L); + vehicleDeviceMapper.insert(device); + } else if (deviceBo.getIsDeleted() != null && deviceBo.getIsDeleted() == 1L) { + // 删除 + vehicleDeviceMapper.deleteById(deviceBo.getId()); + } else { + // 更新 + HotVehicleDevice device = MapstructUtils.convert(deviceBo, HotVehicleDevice.class); + device.setVehicleId(vehicleId); + device.setCompanyId(companyId); + vehicleDeviceMapper.updateById(device); + } + } + } + + if (flag) { + Long oldVehicleStatus = oldVehicle != null ? oldVehicle.getVehicleStatus() : null; + Long newVehicleStatus = update.getVehicleStatus(); + Long oldOperationStatus = oldVehicle != null ? oldVehicle.getOperationStatus() : null; + Long newOperationStatus = update.getOperationStatus(); + boolean vsChanged = oldVehicleStatus != null && newVehicleStatus != null && !oldVehicleStatus.equals(newVehicleStatus); + boolean osChanged = oldOperationStatus != null && newOperationStatus != null && !oldOperationStatus.equals(newOperationStatus); + if (vsChanged || osChanged) { + // 如果运营状态发生变更,同步更新 维修记录 和 证件补办记录 中的车辆运营状态 + if (osChanged && newOperationStatus != null) { + // 同步维修记录 + repairMapper.update(null, new LambdaUpdateWrapper() + .set(HotVehicleRepair::getOperationStatus, newOperationStatus) + .eq(HotVehicleRepair::getVehicleId, vehicleId) + .eq(HotVehicleRepair::getCompanyId, companyId)); + + // 同步证件补办记录 + certReissueMapper.update(null, new LambdaUpdateWrapper() + .set(HotVehicleCertReissue::getOperationStatus, newOperationStatus) + .eq(HotVehicleCertReissue::getVehicleId, vehicleId) + .eq(HotVehicleCertReissue::getCompanyId, companyId)); + } + + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (CollUtil.isNotEmpty(managers)) { + List receiverIds = managers.stream().map(m -> String.valueOf(m.getId())).collect(Collectors.toList()); + String vehicleStatusLabelOld = oldVehicleStatus == null ? "-" : (oldVehicleStatus == 1L ? "正常" : "停用"); + String vehicleStatusLabelNew = newVehicleStatus == null ? "-" : (newVehicleStatus == 1L ? "正常" : "停用"); + String operationStatusLabelOld = oldOperationStatus == null ? "-" : switch (oldOperationStatus.intValue()) { case 1 -> "正常"; case 2 -> "暂停"; case 3 -> "过户"; case 4 -> "报废"; default -> String.valueOf(oldOperationStatus); }; + String operationStatusLabelNew = newOperationStatus == null ? "-" : switch (newOperationStatus.intValue()) { case 1 -> "正常"; case 2 -> "暂停"; case 3 -> "过户"; case 4 -> "报废"; default -> String.valueOf(newOperationStatus); }; + String content; + if (vsChanged && osChanged) { + content = "车辆【" + update.getPlateNumber() + "】状态变更:车辆状态【" + vehicleStatusLabelOld + "】→【" + vehicleStatusLabelNew + "】,运营状态【" + operationStatusLabelOld + "】→【" + operationStatusLabelNew + "】。"; + } else if (vsChanged) { + content = "车辆【" + update.getPlateNumber() + "】状态变更:车辆状态【" + vehicleStatusLabelOld + "】→【" + vehicleStatusLabelNew + "】。"; + } else { + content = "车辆【" + update.getPlateNumber() + "】状态变更:运营状态【" + operationStatusLabelOld + "】→【" + operationStatusLabelNew + "】。"; + } + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("车辆管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + notificationService.insertByBo(bos); + } + } + } + + return flag; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicle entity) { + if (org.dromara.common.core.utils.StringUtils.isNotBlank(entity.getPlateNumber()) && entity.getCompanyId() != null) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(HotVehicle::getPlateNumber, entity.getPlateNumber()); + lqw.eq(HotVehicle::getIsDeleted, 0L); + lqw.ne(entity.getId() != null, HotVehicle::getId, entity.getId()); + lqw.last("limit 1"); + HotVehicle existsVehicle = baseMapper.selectOne(lqw); + if (existsVehicle != null) { + if (existsVehicle.getCompanyId() != null + && !Objects.equals(existsVehicle.getCompanyId(), entity.getCompanyId())) { + throw new ServiceException("同一车牌号的车辆不能存在于不同企业"); + } + throw new ServiceException("车牌号已被录入"); + } + } + } + + /** + * 校验并批量删除公司车辆信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // 校验车辆是否绑定驾驶员 + List vehicles = baseMapper.selectBatchIds(ids); + if (CollUtil.isNotEmpty(vehicles)) { + for (HotVehicle vehicle : vehicles) { + if (StringUtils.isNotBlank(vehicle.getCurrentDriver())) { + throw new ServiceException("车辆【" + vehicle.getPlateNumber() + "】已绑定驾驶员,不可删除!"); + } + } + } + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/utils/VehicleUtils.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/utils/VehicleUtils.java new file mode 100644 index 0000000..37c2612 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleManagement/utils/VehicleUtils.java @@ -0,0 +1,173 @@ +package com.hotwj.platform.resourceManagement.vehicleManagement.utils; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 车辆业务计算工具类 + * 支持:年审检测 (Annual Inspection) & 二级维护 (Secondary Maintenance) + */ +public class VehicleUtils { + + /** + * 计算车辆【年审】状态 + * + * @param rules qynjpz表规则 + * @param registrationDate 行驶证注册日期 + * @param lastInspectionDate 最近一次年审日期 (clxx_nstim) + */ + public static InspectionResult calculateInspectionStatus( + List rules, + LocalDate registrationDate, + LocalDate lastInspectionDate) { + return coreCalculate(rules, registrationDate, lastInspectionDate); + } + + /** + * 计算车辆【二级维护】状态 + * + * @param rules qywhpz表规则 + * @param registrationDate 行驶证注册日期 + * @param lastMaintenanceDate 最近一次维护日期 (clxx_whsj) + */ + public static InspectionResult calculateMaintenanceStatus( + List rules, + LocalDate registrationDate, + LocalDate lastMaintenanceDate) { + return coreCalculate(rules, registrationDate, lastMaintenanceDate); + } + + /** + * 核心计算逻辑 (由原 PHP 函数 clxx_ns 和 clxx_wh 抽象而来) + */ + private static InspectionResult coreCalculate( + List rules, + LocalDate registrationDate, + LocalDate lastBusinessDate) { + + // 1. 基础校验:静默返回 null,防止列表查询因单条数据缺失崩溃 + if (registrationDate == null || rules == null || rules.isEmpty()) { + return null; + } + + LocalDate today = LocalDate.now(); + + // 2. 自动解析免检期:匹配次数为 0 的规则阈值 (PHP 中的 $wl_0_jg) + int initialGracePeriodYears = rules.stream() + .filter(r -> r.getInspectionTimes() == 0) + .map(AnnualInspectionRule::getIntervalYearThreshold) + .findFirst() + .orElse(0); + + // 3. 确定计算锚点:若无办理记录,则以“注册日期 + 免检年数”为起点 + LocalDate effectiveLastDate = (lastBusinessDate != null) + ? lastBusinessDate + : registrationDate.plusYears(initialGracePeriodYears); + + // 4. 排序规则:确保按年限从小到大匹配 (PHP 中的 order by qywhpz_jgtim+0 asc) + List sortedRules = rules.stream() + .sorted(Comparator.comparingInt(AnnualInspectionRule::getIntervalYearThreshold)) + .collect(Collectors.toList()); + + // 5. 计算车龄:向下取整 (不到生日不算一岁) + long vehicleAgeYears = ChronoUnit.YEARS.between(registrationDate, today); + + // 6. 匹配区间并计算 + for (AnnualInspectionRule rule : sortedRules) { + if (vehicleAgeYears < rule.getIntervalYearThreshold()) { + + int timesPerYear = rule.getInspectionTimes(); + InspectionResult result = new InspectionResult(); + result.setInspectionTimes(timesPerYear); + + // 次数为 0 代表该区间免检/无需维护 (PHP 中的 yj_dj=6) + if (timesPerYear <= 0) { + result.setWarningLevel(6); + return result; + } + + // 计算周期:12个月 / 每年次数 (PHP 中的 $one_tear / $qynjpz_cs) + int cycleMonths = 12 / timesPerYear; + LocalDate nextDeadline = effectiveLastDate.plusMonths(cycleMonths); + result.setNextInspectionDate(nextDeadline); + + // 7. 判定状态 + if (today.isBefore(nextDeadline)) { + // 未到期 + long remainingDays = ChronoUnit.DAYS.between(today, nextDeadline); + result.setRemainingDays((int) remainingDays); + result.setOverdueDays(0); + result.setWarningLevel(determineWarningLevel((int) remainingDays)); + } else { + // 已到期/已逾期 + long overdueDays = ChronoUnit.DAYS.between(nextDeadline, today); + result.setRemainingDays(0); + result.setOverdueDays((int) overdueDays); + result.setWarningLevel(1); // 红色预警 + } + return result; + } + } + return null; + } + + private static int determineWarningLevel(int remainingDays) { + if (remainingDays <= 5) return 1; + if (remainingDays <= 10) return 2; + if (remainingDays <= 20) return 3; + if (remainingDays <= 30) return 4; + return 5; + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class AnnualInspectionRule { + private int intervalYearThreshold; + private int inspectionTimes; + } + + @Data + public static class InspectionResult { + private int warningLevel; + private int remainingDays; + /** + * 逾期天数 + */ + private int overdueDays; + private int inspectionTimes; + private LocalDate nextInspectionDate; + + public String getWarningLevelDesc() { + switch (this.warningLevel) { + case 1: + return "一级预警"; + case 2: + return "二级预警"; + case 3: + return "三级预警"; + case 4: + return "四级预警"; + case 5: + return ""; + case 6: + return ""; + default: + return "未知状态"; + } + } + + @Override + public String toString() { + return String.format("等级:%d, 剩余:%d天, 逾期:%d天, 下次日期:%s", + warningLevel, remainingDays, overdueDays, nextInspectionDate); + } + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/controller/HotVehicleMileageController.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/controller/HotVehicleMileageController.java new file mode 100644 index 0000000..ef935fd --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/controller/HotVehicleMileageController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.resourceManagement.vehicleMileage.controller; + +import com.hotwj.platform.resourceManagement.vehicleMileage.domain.bo.HotVehicleMileageBo; +import com.hotwj.platform.resourceManagement.vehicleMileage.domain.vo.HotVehicleMileageVo; +import com.hotwj.platform.resourceManagement.vehicleMileage.service.IHotVehicleMileageService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 车辆行驶里程 + * + * @author shihongwei + * @date 2026-01-04 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/vehicleMileage") +@Tag(name = "车辆行驶里程", description = "车辆行驶里程管理") +public class HotVehicleMileageController extends BaseController { + + private final IHotVehicleMileageService hotVehicleMileageService; + + /** + * 查询车辆行驶里程列表 + */ + //@SaCheckPermission("resourceManagement:vehicleMileage:list") + @GetMapping("/list") + @Operation(summary = "分页查询车辆行驶里程列表") + public TableDataInfo list(HotVehicleMileageBo bo, PageQuery pageQuery) { + return hotVehicleMileageService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆行驶里程列表 + */ + //@SaCheckPermission("resourceManagement:vehicleMileage:export") + @Log(title = "车辆行驶里程", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆行驶里程列表") + public void export(HotVehicleMileageBo bo, HttpServletResponse response) { + List list = hotVehicleMileageService.queryList(bo); + ExcelUtil.exportExcel(list, "车辆行驶里程", HotVehicleMileageVo.class, response); + } + + /** + * 获取车辆行驶里程详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:vehicleMileage:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆行驶里程详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotVehicleMileageService.queryById(id)); + } + + /** + * 新增车辆行驶里程 + */ + //@SaCheckPermission("resourceManagement:vehicleMileage:add") + @Log(title = "车辆行驶里程", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆行驶里程") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleMileageBo bo) { + return toAjax(hotVehicleMileageService.insertByBo(bo)); + } + + /** + * 修改车辆行驶里程 + */ + //@SaCheckPermission("resourceManagement:vehicleMileage:edit") + @Log(title = "车辆行驶里程", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆行驶里程") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleMileageBo bo) { + return toAjax(hotVehicleMileageService.updateByBo(bo)); + } + + /** + * 删除车辆行驶里程 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:vehicleMileage:remove") + @Log(title = "车辆行驶里程", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆行驶里程") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotVehicleMileageService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/domain/HotVehicleMileage.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/domain/HotVehicleMileage.java new file mode 100644 index 0000000..b590ef2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/domain/HotVehicleMileage.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.resourceManagement.vehicleMileage.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 车辆行驶里程对象 hot_vehicle_mileage + * + * @author shihongwei + * @date 2026-01-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_mileage") +public class HotVehicleMileage extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆ID + */ + private Long vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 行驶开始日期 + */ + private Date startDate; + + /** + * 行驶结束日期 + */ + private Date endDate; + + /** + * 行驶里程(公里) + */ + private BigDecimal mileageKm; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/domain/bo/HotVehicleMileageBo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/domain/bo/HotVehicleMileageBo.java new file mode 100644 index 0000000..5875950 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/domain/bo/HotVehicleMileageBo.java @@ -0,0 +1,81 @@ +package com.hotwj.platform.resourceManagement.vehicleMileage.domain.bo; + +import com.hotwj.platform.resourceManagement.vehicleMileage.domain.HotVehicleMileage; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 车辆行驶里程业务对象 hot_vehicle_mileage + * + * @author shihongwei + * @date 2026-01-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleMileage.class, reverseConvertGenerate = false) +public class HotVehicleMileageBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 车辆ID + */ + @NotNull(message = "车辆ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long vehicleId; + + /** + * 车牌号 + */ + @NotBlank(message = "车牌号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String plateNumber; + + /** + * 行驶开始日期 + */ + @NotNull(message = "行驶开始日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date startDate; + + /** + * 行驶结束日期 + */ + @NotNull(message = "行驶结束日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date endDate; + + /** + * 行驶里程(公里) + */ + @NotNull(message = "行驶里程(公里)不能为空", groups = {AddGroup.class, EditGroup.class}) + private BigDecimal mileageKm; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/domain/vo/HotVehicleMileageVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/domain/vo/HotVehicleMileageVo.java new file mode 100644 index 0000000..90efad1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/domain/vo/HotVehicleMileageVo.java @@ -0,0 +1,84 @@ +package com.hotwj.platform.resourceManagement.vehicleMileage.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.vehicleMileage.domain.HotVehicleMileage; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 车辆行驶里程视图对象 hot_vehicle_mileage + * + * @author shihongwei + * @date 2026-01-04 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleMileage.class) +public class HotVehicleMileageVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 车辆ID + */ + @ExcelProperty(value = "车辆ID") + private Long vehicleId; + + /** + * 车牌号 + */ + @ExcelProperty(value = "车牌号") + private String plateNumber; + + /** + * 行驶开始日期 + */ + @ExcelProperty(value = "行驶开始日期") + private Date startDate; + + /** + * 行驶结束日期 + */ + @ExcelProperty(value = "行驶结束日期") + private Date endDate; + + /** + * 行驶里程(公里) + */ + @ExcelProperty(value = "行驶里程(公里)") + private BigDecimal mileageKm; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/mapper/HotVehicleMileageMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/mapper/HotVehicleMileageMapper.java new file mode 100644 index 0000000..b2e925a --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/mapper/HotVehicleMileageMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.vehicleMileage.mapper; + +import com.hotwj.platform.resourceManagement.vehicleMileage.domain.HotVehicleMileage; +import com.hotwj.platform.resourceManagement.vehicleMileage.domain.vo.HotVehicleMileageVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆行驶里程Mapper接口 + * + * @author shihongwei + * @date 2026-01-04 + */ +@Mapper +public interface HotVehicleMileageMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/service/IHotVehicleMileageService.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/service/IHotVehicleMileageService.java new file mode 100644 index 0000000..ae0b896 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/service/IHotVehicleMileageService.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.resourceManagement.vehicleMileage.service; + +import com.hotwj.platform.resourceManagement.vehicleMileage.domain.bo.HotVehicleMileageBo; +import com.hotwj.platform.resourceManagement.vehicleMileage.domain.vo.HotVehicleMileageVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆行驶里程Service接口 + * + * @author shihongwei + * @date 2026-01-04 + */ +public interface IHotVehicleMileageService { + + /** + * 查询车辆行驶里程 + * + * @param id 主键 + * @return 车辆行驶里程 + */ + HotVehicleMileageVo queryById(Long id); + + /** + * 分页查询车辆行驶里程列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆行驶里程分页列表 + */ + TableDataInfo queryPageList(HotVehicleMileageBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆行驶里程列表 + * + * @param bo 查询条件 + * @return 车辆行驶里程列表 + */ + List queryList(HotVehicleMileageBo bo); + + /** + * 新增车辆行驶里程 + * + * @param bo 车辆行驶里程 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleMileageBo bo); + + /** + * 修改车辆行驶里程 + * + * @param bo 车辆行驶里程 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleMileageBo bo); + + /** + * 根据车辆ID查询行驶里程记录列表(按开始时间倒序) + * + * @param vehicleId 车辆ID + * @return 行驶里程记录列表 + */ + List queryListByVehicleId(String vehicleId); + + /** + * 校验并批量删除车辆行驶里程信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/service/impl/HotVehicleMileageServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/service/impl/HotVehicleMileageServiceImpl.java new file mode 100644 index 0000000..9e1584a --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleMileage/service/impl/HotVehicleMileageServiceImpl.java @@ -0,0 +1,150 @@ +package com.hotwj.platform.resourceManagement.vehicleMileage.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.vehicleMileage.domain.HotVehicleMileage; +import com.hotwj.platform.resourceManagement.vehicleMileage.domain.bo.HotVehicleMileageBo; +import com.hotwj.platform.resourceManagement.vehicleMileage.domain.vo.HotVehicleMileageVo; +import com.hotwj.platform.resourceManagement.vehicleMileage.mapper.HotVehicleMileageMapper; +import com.hotwj.platform.resourceManagement.vehicleMileage.service.IHotVehicleMileageService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 车辆行驶里程Service业务层处理 + * + * @author shihongwei + * @date 2026-01-04 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleMileageServiceImpl implements IHotVehicleMileageService { + + private final HotVehicleMileageMapper baseMapper; + + /** + * 查询车辆行驶里程 + * + * @param id 主键 + * @return 车辆行驶里程 + */ + @Override + public HotVehicleMileageVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆行驶里程列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆行驶里程分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleMileageBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆行驶里程列表 + * + * @param bo 查询条件 + * @return 车辆行驶里程列表 + */ + @Override + public List queryList(HotVehicleMileageBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleMileageBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotVehicleMileage::getId); + lqw.eq(bo.getCompanyId() != null, HotVehicleMileage::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getVehicleId() != null, HotVehicleMileage::getVehicleId, bo.getVehicleId()); + lqw.like(bo.getStartDate() != null, HotVehicleMileage::getStartDate, bo.getStartDate()); + lqw.like(bo.getEndDate() != null, HotVehicleMileage::getEndDate, bo.getEndDate()); + lqw.eq(bo.getMileageKm() != null, HotVehicleMileage::getMileageKm, bo.getMileageKm()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleMileage::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增车辆行驶里程 + * + * @param bo 车辆行驶里程 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleMileageBo bo) { + HotVehicleMileage add = MapstructUtils.convert(bo, HotVehicleMileage.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改车辆行驶里程 + * + * @param bo 车辆行驶里程 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleMileageBo bo) { + HotVehicleMileage update = MapstructUtils.convert(bo, HotVehicleMileage.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleMileage entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 根据车辆ID查询行驶里程记录列表(按开始时间倒序) + * + * @param vehicleId 车辆ID + * @return 行驶里程记录列表 + */ + @Override + public List queryListByVehicleId(String vehicleId) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(HotVehicleMileage::getVehicleId, vehicleId); + lqw.orderByDesc(HotVehicleMileage::getStartDate); + return baseMapper.selectVoList(lqw); + } + + /** + * 校验并批量删除车辆行驶里程信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/controller/HotVehicleRepairController.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/controller/HotVehicleRepairController.java new file mode 100644 index 0000000..9ed5ada --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/controller/HotVehicleRepairController.java @@ -0,0 +1,154 @@ +package com.hotwj.platform.resourceManagement.vehicleRepair.controller; + +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.vo.HotCompanySafetyManagerVo; +import com.hotwj.platform.resourceManagement.companySafetyManager.service.IHotCompanySafetyManagerService; +import com.hotwj.platform.resourceManagement.vehicleRepair.domain.bo.HotVehicleRepairBo; +import com.hotwj.platform.resourceManagement.vehicleRepair.domain.vo.HotVehicleRepairVo; +import com.hotwj.platform.resourceManagement.vehicleRepair.service.IHotVehicleRepairService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 车辆维修记录 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/vehicleRepair") +@Tag(name = "车辆维修记录", description = "车辆维修记录管理") +public class HotVehicleRepairController extends BaseController { + + private final IHotVehicleRepairService hotVehicleRepairService; + private final IHotCompanySafetyManagerService hotCompanySafetyManagerService; + private final IHotDriverService hotDriverService; + + /** + * 查询车辆维修记录列表 + */ + //@SaCheckPermission("resourceManagement:vehicleRepair:list") + @GetMapping("/list") + @Operation(summary = "查询车辆维修记录列表") + public TableDataInfo list(HotVehicleRepairBo bo, PageQuery pageQuery) { + return hotVehicleRepairService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆维修记录列表 + */ + //@SaCheckPermission("resourceManagement:vehicleRepair:export") + @Log(title = "车辆维修记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆维修记录列表") + public void export(HotVehicleRepairBo bo, HttpServletResponse response) { + List list = hotVehicleRepairService.queryList(bo); + Map recorderNameCache = new HashMap<>(); + for (HotVehicleRepairVo item : list) { + item.setRecorder(resolveRecorderName(item.getRecorder(), recorderNameCache)); + } + ExcelUtil.exportExcel(list, "车辆维修记录", HotVehicleRepairVo.class, response); + } + + private String resolveRecorderName(String recorder, Map cache) { + if (recorder == null || recorder.isBlank()) { + return recorder; + } + if (cache.containsKey(recorder)) { + return cache.get(recorder); + } + Long recorderId; + try { + recorderId = Long.valueOf(recorder); + } catch (NumberFormatException e) { + HotDriverVo driver = hotDriverService.queryById(recorder); + if (driver != null && driver.getName() != null && !driver.getName().isBlank()) { + String recorderName = driver.getName(); + cache.put(recorder, recorderName); + return recorderName; + } + return recorder; + } + HotCompanySafetyManagerVo manager = hotCompanySafetyManagerService.queryById(recorderId); + String recorderName = manager != null ? manager.getName() : null; + if (recorderName == null || recorderName.isBlank()) { + recorderName = recorder; + } + cache.put(recorder, recorderName); + return recorderName; + } + + /** + * 获取车辆维修记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:vehicleRepair:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆维修记录详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotVehicleRepairService.queryById(id)); + } + + /** + * 新增车辆维修记录 + */ + //@SaCheckPermission("resourceManagement:vehicleRepair:add") + @Log(title = "车辆维修记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆维修记录") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleRepairBo bo) { + return toAjax(hotVehicleRepairService.insertByBo(bo)); + } + + /** + * 修改车辆维修记录 + */ + //@SaCheckPermission("resourceManagement:vehicleRepair:edit") + @Log(title = "车辆维修记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆维修记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleRepairBo bo) { + return toAjax(hotVehicleRepairService.updateByBo(bo)); + } + + /** + * 删除车辆维修记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:vehicleRepair:remove") + @Log(title = "车辆维修记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆维修记录") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotVehicleRepairService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/domain/HotVehicleRepair.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/domain/HotVehicleRepair.java new file mode 100644 index 0000000..11166eb --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/domain/HotVehicleRepair.java @@ -0,0 +1,126 @@ +package com.hotwj.platform.resourceManagement.vehicleRepair.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 车辆维修记录对象 hot_vehicle_repair + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_repair") +public class HotVehicleRepair extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private String id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 运营状态:1=正常,2=暂停,3=过户,4=报废 + */ + private Long operationStatus; + + /** + * 维修类别 + */ + private String repairCategory; + + /** + * 维修缘由 + */ + private String repairReason; + + /** + * 维修单位 + */ + private String repairUnit; + + /** + * 维修单位电话 + */ + private String repairUnitPhone; + + /** + * 维修金额 + */ + private BigDecimal repairAmount; + + /** + * 是否主要部件更换:0=否,1=是 + */ + private Long isMajorPartReplaced; + + /** + * 记录人 + */ + private String recorder; + + /** + * 维修日期 + */ + private Date repairDate; + + /** + * 维修完成日期 + */ + private Date finishDate; + + /** + * 更换部件 + */ + private String replacedParts; + + /** + * 部件规格 + */ + private String partSpec; + + /** + * 维修内容 + */ + private String repairContent; + + /** + * 维修凭据图片URL(多个逗号分隔) + */ + private String proofImageUrls; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/domain/bo/HotVehicleRepairBo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/domain/bo/HotVehicleRepairBo.java new file mode 100644 index 0000000..41965e8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/domain/bo/HotVehicleRepairBo.java @@ -0,0 +1,138 @@ +package com.hotwj.platform.resourceManagement.vehicleRepair.domain.bo; + +import com.hotwj.platform.resourceManagement.vehicleRepair.domain.HotVehicleRepair; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * 车辆维修记录业务对象 hot_vehicle_repair + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleRepair.class, reverseConvertGenerate = false) +public class HotVehicleRepairBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private String id; + + /** + * 主键集合(用于批量导出等场景) + */ + private List ids; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 运营状态:1=正常,2=暂停,3=过户,4=报废 + */ + private Long operationStatus; + + /** + * 维修类别 + */ + @NotBlank(message = "维修类别不能为空", groups = {AddGroup.class, EditGroup.class}) + private String repairCategory; + + /** + * 维修缘由 + */ + @NotBlank(message = "维修缘由不能为空", groups = {AddGroup.class, EditGroup.class}) + private String repairReason; + + /** + * 维修单位 + */ + private String repairUnit; + + /** + * 维修单位电话 + */ + private String repairUnitPhone; + + /** + * 维修金额 + */ + private BigDecimal repairAmount; + + /** + * 是否主要部件更换:0=否,1=是 + */ + @NotNull(message = "是否主要部件更换:0=否,1=是不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isMajorPartReplaced; + + /** + * 记录人 + */ + @NotBlank(message = "记录人不能为空", groups = {AddGroup.class, EditGroup.class}) + private String recorder; + + /** + * 维修日期 + */ + @NotNull(message = "维修日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date repairDate; + + /** + * 维修完成日期 + */ + @NotNull(message = "维修完成日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date finishDate; + + /** + * 更换部件 + */ + private String replacedParts; + + /** + * 部件规格 + */ + private String partSpec; + + /** + * 维修内容 + */ + private String repairContent; + + /** + * 维修凭据图片URL(多个逗号分隔) + */ + private String proofImageUrls; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/domain/vo/HotVehicleRepairVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/domain/vo/HotVehicleRepairVo.java new file mode 100644 index 0000000..6495377 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/domain/vo/HotVehicleRepairVo.java @@ -0,0 +1,153 @@ +package com.hotwj.platform.resourceManagement.vehicleRepair.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.vehicleRepair.domain.HotVehicleRepair; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 车辆维修记录视图对象 hot_vehicle_repair + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleRepair.class) +public class HotVehicleRepairVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private String id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 车牌号 + */ + @ExcelProperty(value = "车牌号") + private String plateNumber; + + /** + * 运营状态:1=正常,2=暂停,3=过户,4=报废 + */ + private Long operationStatus; + + /** + * 维修类别 + */ + @ExcelProperty(value = "维修类别", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_vehicle_repair") + private String repairCategory; + + /** + * 维修缘由 + */ + @ExcelProperty(value = "维修缘由", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "hot_vehicle_reason") + private String repairReason; + + /** + * 维修单位 + */ + @ExcelProperty(value = "维修单位") + private String repairUnit; + + /** + * 维修单位电话 + */ + @ExcelProperty(value = "维修单位电话") + private String repairUnitPhone; + + /** + * 维修金额 + */ + @ExcelProperty(value = "维修金额") + private BigDecimal repairAmount; + + /** + * 是否主要部件更换:0=否,1=是 + */ + @ExcelProperty(value = "是否主要部件更换:0=否,1=是") + private Long isMajorPartReplaced; + + /** + * 记录人 + */ + @ExcelProperty(value = "记录人") + private String recorder; + + /** + * 维修日期 + */ + @ExcelProperty(value = "维修日期") + private Date repairDate; + + /** + * 维修完成日期 + */ + @ExcelProperty(value = "维修完成日期") + private Date finishDate; + + /** + * 更换部件 + */ + @ExcelProperty(value = "更换部件") + private String replacedParts; + + /** + * 部件规格 + */ + @ExcelProperty(value = "部件规格") + private String partSpec; + + /** + * 维修内容 + */ + @ExcelProperty(value = "维修内容") + private String repairContent; + + /** + * 维修凭据图片URL(多个逗号分隔) + */ + private String proofImageUrls; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + + /** + * 图片列表 + */ + private java.util.List images; + + /** + * 车辆信息 + */ + private com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo vehicle; + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/mapper/HotVehicleRepairMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/mapper/HotVehicleRepairMapper.java new file mode 100644 index 0000000..866a982 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/mapper/HotVehicleRepairMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.vehicleRepair.mapper; + +import com.hotwj.platform.resourceManagement.vehicleRepair.domain.HotVehicleRepair; +import com.hotwj.platform.resourceManagement.vehicleRepair.domain.vo.HotVehicleRepairVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆维修记录Mapper接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Mapper +public interface HotVehicleRepairMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/service/IHotVehicleRepairService.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/service/IHotVehicleRepairService.java new file mode 100644 index 0000000..bcfc7fd --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/service/IHotVehicleRepairService.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.resourceManagement.vehicleRepair.service; + +import com.hotwj.platform.resourceManagement.vehicleRepair.domain.bo.HotVehicleRepairBo; +import com.hotwj.platform.resourceManagement.vehicleRepair.domain.vo.HotVehicleRepairVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆维修记录Service接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +public interface IHotVehicleRepairService { + + /** + * 查询车辆维修记录 + * + * @param id 主键 + * @return 车辆维修记录 + */ + HotVehicleRepairVo queryById(Long id); + + /** + * 分页查询车辆维修记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆维修记录分页列表 + */ + TableDataInfo queryPageList(HotVehicleRepairBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆维修记录列表 + * + * @param bo 查询条件 + * @return 车辆维修记录列表 + */ + List queryList(HotVehicleRepairBo bo); + + /** + * 根据车辆ID查询最新的一条维修记录 + * + * @param vehicleId 车辆ID + * @return 最新维修记录 + */ + HotVehicleRepairVo queryLatestByVehicleId(String vehicleId); + + /** + * 新增车辆维修记录 + * + * @param bo 车辆维修记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleRepairBo bo); + + /** + * 修改车辆维修记录 + * + * @param bo 车辆维修记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleRepairBo bo); + + /** + * 校验并批量删除车辆维修记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/service/impl/HotVehicleRepairServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/service/impl/HotVehicleRepairServiceImpl.java new file mode 100644 index 0000000..566f618 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleRepair/service/impl/HotVehicleRepairServiceImpl.java @@ -0,0 +1,239 @@ +package com.hotwj.platform.resourceManagement.vehicleRepair.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import com.hotwj.platform.resourceManagement.vehicleRepair.domain.HotVehicleRepair; +import com.hotwj.platform.resourceManagement.vehicleRepair.domain.bo.HotVehicleRepairBo; +import com.hotwj.platform.resourceManagement.vehicleRepair.domain.vo.HotVehicleRepairVo; +import com.hotwj.platform.resourceManagement.vehicleRepair.mapper.HotVehicleRepairMapper; +import com.hotwj.platform.resourceManagement.vehicleRepair.service.IHotVehicleRepairService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 车辆维修记录Service业务层处理 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleRepairServiceImpl implements IHotVehicleRepairService { + + private final HotVehicleRepairMapper baseMapper; + private final HotCompanySafetyManagerMapper managerMapper; + private final IHotSystemNotificationService notificationService; + + /** + * 查询车辆维修记录 + * + * @param id 主键 + * @return 车辆维修记录 + */ + @Override + public HotVehicleRepairVo queryById(Long id){ + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆维修记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆维修记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleRepairBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆维修记录列表 + * + * @param bo 查询条件 + * @return 车辆维修记录列表 + */ + @Override + public List queryList(HotVehicleRepairBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + /** + * 根据车辆ID查询最新的一条维修记录 + * + * @param vehicleId 车辆ID + * @return 最新维修记录 + */ + @Override + public HotVehicleRepairVo queryLatestByVehicleId(String vehicleId) { + return baseMapper.selectVoOne(new LambdaQueryWrapper() + .eq(HotVehicleRepair::getVehicleId, vehicleId) + .orderByDesc(HotVehicleRepair::getFinishDate) + .last("LIMIT 1")); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleRepairBo bo) { + Map params = bo.getParams(); + Object beginTime = params.get("beginTime"); + Object endTime = params.get("endTime"); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotVehicleRepair::getId); + lqw.eq(bo.getCompanyId() != null, HotVehicleRepair::getCompanyId, bo.getCompanyId()); + lqw.in(CollUtil.isNotEmpty(bo.getIds()), HotVehicleRepair::getId, bo.getIds()); + lqw.eq(bo.getVehicleId() != null, HotVehicleRepair::getVehicleId, bo.getVehicleId()); + lqw.like(StringUtils.isNotBlank(bo.getPlateNumber()), HotVehicleRepair::getPlateNumber, bo.getPlateNumber()); + lqw.eq(StringUtils.isNotBlank(bo.getRepairCategory()), HotVehicleRepair::getRepairCategory, bo.getRepairCategory()); + lqw.eq(StringUtils.isNotBlank(bo.getRepairReason()), HotVehicleRepair::getRepairReason, bo.getRepairReason()); + lqw.eq(StringUtils.isNotBlank(bo.getRepairUnit()), HotVehicleRepair::getRepairUnit, bo.getRepairUnit()); + lqw.eq(StringUtils.isNotBlank(bo.getRepairUnitPhone()), HotVehicleRepair::getRepairUnitPhone, bo.getRepairUnitPhone()); + lqw.eq(bo.getRepairAmount() != null, HotVehicleRepair::getRepairAmount, bo.getRepairAmount()); + lqw.eq(bo.getIsMajorPartReplaced() != null, HotVehicleRepair::getIsMajorPartReplaced, bo.getIsMajorPartReplaced()); + lqw.eq(StringUtils.isNotBlank(bo.getRecorder()), HotVehicleRepair::getRecorder, bo.getRecorder()); + lqw.eq(bo.getRepairDate() != null, HotVehicleRepair::getRepairDate, bo.getRepairDate()); + lqw.eq(bo.getFinishDate() != null, HotVehicleRepair::getFinishDate, bo.getFinishDate()); + lqw.eq(StringUtils.isNotBlank(bo.getReplacedParts()), HotVehicleRepair::getReplacedParts, bo.getReplacedParts()); + lqw.eq(StringUtils.isNotBlank(bo.getPartSpec()), HotVehicleRepair::getPartSpec, bo.getPartSpec()); + lqw.eq(StringUtils.isNotBlank(bo.getRepairContent()), HotVehicleRepair::getRepairContent, bo.getRepairContent()); + lqw.eq(StringUtils.isNotBlank(bo.getProofImageUrls()), HotVehicleRepair::getProofImageUrls, bo.getProofImageUrls()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleRepair::getIsDeleted, bo.getIsDeleted()); + lqw.eq(bo.getOperationStatus() != null, HotVehicleRepair::getOperationStatus, bo.getOperationStatus()); + lqw.gt(beginTime != null, HotVehicleRepair::getRepairDate, beginTime); + lqw.lt(endTime != null, HotVehicleRepair::getFinishDate, endTime); + return lqw; + } + + /** + * 新增车辆维修记录 + * + * @param bo 车辆维修记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleRepairBo bo) { + HotVehicleRepair add = MapstructUtils.convert(bo, HotVehicleRepair.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + if (add.getCompanyId() != null + && add.getIsMajorPartReplaced() != null && Long.valueOf(1L).equals(add.getIsMajorPartReplaced()) + && StringUtils.isNotBlank(add.getReplacedParts())) { + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, add.getCompanyId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers != null && !managers.isEmpty()) { + List receiverIds = managers.stream().map(m -> String.valueOf(m.getId())).toList(); + String plate = StringUtils.blankToDefault(add.getPlateNumber(), "未知车牌"); + String content = "车辆【" + plate + "】更换了主要部件(" + add.getReplacedParts() + ")。"; + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("车辆管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送主要部件更换通知失败 companyId={} vehicleId={}", add.getCompanyId(), add.getVehicleId(), e); + } + } + } + } + return flag; + } + + /** + * 修改车辆维修记录 + * + * @param bo 车辆维修记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleRepairBo bo) { + HotVehicleRepair old = baseMapper.selectById(bo.getId()); + HotVehicleRepair update = MapstructUtils.convert(bo, HotVehicleRepair.class); + validEntityBeforeSave(update); + boolean flag = baseMapper.updateById(update) > 0; + if (flag) { + boolean changedToMajor = update.getCompanyId() != null + && update.getIsMajorPartReplaced() != null && Long.valueOf(1L).equals(update.getIsMajorPartReplaced()) + && (old == null || !Long.valueOf(1L).equals(old.getIsMajorPartReplaced())) + && StringUtils.isNotBlank(update.getReplacedParts()); + if (changedToMajor) { + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, update.getCompanyId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers != null && !managers.isEmpty()) { + List receiverIds = managers.stream().map(m -> String.valueOf(m.getId())).toList(); + String plate = StringUtils.blankToDefault(update.getPlateNumber(), "未知车牌"); + String content = "车辆【" + plate + "】更换了主要部件(" + update.getReplacedParts() + ")。"; + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("车辆管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送主要部件更换通知失败 companyId={} vehicleId={}", update.getCompanyId(), update.getVehicleId(), e); + } + } + } + } + return flag; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleRepair entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除车辆维修记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/controller/HotVehicleTerminalInstallController.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/controller/HotVehicleTerminalInstallController.java new file mode 100644 index 0000000..282885f --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/controller/HotVehicleTerminalInstallController.java @@ -0,0 +1,194 @@ +package com.hotwj.platform.resourceManagement.vehicleTerminalInstall.controller; + +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.StrUtil; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.vo.HotCompanySafetyManagerVo; +import com.hotwj.platform.resourceManagement.companySafetyManager.service.IHotCompanySafetyManagerService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.bo.HotVehicleTerminalInstallBo; +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.vo.HotVehicleTerminalInstallExportVo; +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.vo.HotVehicleTerminalInstallVo; +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.service.IHotVehicleTerminalInstallService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 定位终端安装记录 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/resourceManagement/vehicleTerminalInstall") +@Tag(name = "定位终端安装记录", description = "定位终端安装记录管理") +public class HotVehicleTerminalInstallController extends BaseController { + + private final IHotVehicleTerminalInstallService hotVehicleTerminalInstallService; + private final IHotCompanySafetyManagerService hotCompanySafetyManagerService; + private final IHotVehicleService hotVehicleService; + + /** + * 查询定位终端安装记录列表 + */ + //@SaCheckPermission("resourceManagement:vehicleTerminalInstall:list") + @GetMapping("/list") + @Operation(summary = "查询定位终端安装记录列表") + public TableDataInfo list(HotVehicleTerminalInstallBo bo, PageQuery pageQuery) { + return hotVehicleTerminalInstallService.queryPageList(bo, pageQuery); + } + + /** + * 查询定位终端安装记录列表(车辆安装页面专用) + */ + @GetMapping("/listForVehiclePage") + @Operation(summary = "查询定位终端安装记录列表(车辆安装页面专用)") + public TableDataInfo listForVehiclePage(HotVehicleTerminalInstallBo bo, PageQuery pageQuery) { + return hotVehicleTerminalInstallService.queryPageListForVehiclePage(bo, pageQuery); + } + + /** + * 导出定位终端安装记录列表 + */ + //@SaCheckPermission("resourceManagement:vehicleTerminalInstall:export") + @Log(title = "定位终端安装记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出定位终端安装记录列表") + public void export(HotVehicleTerminalInstallBo bo, HttpServletResponse response) { + List list = hotVehicleTerminalInstallService.queryList(bo); + List exportList = new ArrayList<>(list.size()); + Map recorderNameCache = new HashMap<>(); + Map plateNumberCache = new HashMap<>(); + for (HotVehicleTerminalInstallVo item : list) { + HotVehicleTerminalInstallExportVo exportVo = new HotVehicleTerminalInstallExportVo(); + exportVo.setPlateNumber(resolvePlateNumber(item.getPlateNumber(), item.getVehicleId(), plateNumberCache)); + exportVo.setServiceProvider(item.getServiceProvider()); + exportVo.setDocumentNo(item.getDocumentNo()); + exportVo.setInstallDate(item.getInstallDate()); + exportVo.setServiceEndDate(item.getServiceEndDate()); + exportVo.setRecorderName(resolveRecorderName(item.getRecorderName(), item.getRecorder(), recorderNameCache)); + exportList.add(exportVo); + } + ExcelUtil.exportExcel(exportList, "定位终端安装记录", HotVehicleTerminalInstallExportVo.class, response); + } + + private String resolveRecorderName(String recorderName, String recorder, Map cache) { + if (StrUtil.isNotBlank(recorderName)) { + return recorderName; + } + if (StrUtil.isBlank(recorder)) { + return recorder; + } + String cached = cache.get(recorder); + if (StrUtil.isNotBlank(cached)) { + return cached; + } + if (!NumberUtil.isLong(recorder)) { + cache.put(recorder, recorder); + return recorder; + } + HotCompanySafetyManagerVo manager = hotCompanySafetyManagerService.queryById(Long.valueOf(recorder)); + if (manager != null && StrUtil.isNotBlank(manager.getName())) { + cache.put(recorder, manager.getName()); + return manager.getName(); + } + cache.put(recorder, recorder); + return recorder; + } + + private String resolvePlateNumber(String plateNumber, String vehicleId, Map cache) { + if (StrUtil.isNotBlank(plateNumber)) { + return plateNumber; + } + if (StrUtil.isBlank(vehicleId)) { + return plateNumber; + } + String cached = cache.get(vehicleId); + if (StrUtil.isNotBlank(cached)) { + return cached; + } + if (!NumberUtil.isLong(vehicleId)) { + return plateNumber; + } + HotVehicleVo vehicle = hotVehicleService.queryById(Long.valueOf(vehicleId)); + if (vehicle != null && StrUtil.isNotBlank(vehicle.getPlateNumber())) { + cache.put(vehicleId, vehicle.getPlateNumber()); + return vehicle.getPlateNumber(); + } + return plateNumber; + } + + /** + * 获取定位终端安装记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("resourceManagement:vehicleTerminalInstall:query") + @GetMapping("/{id}") + @Operation(summary = "获取定位终端安装记录详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotVehicleTerminalInstallService.queryById(id)); + } + + /** + * 新增定位终端安装记录 + */ + //@SaCheckPermission("resourceManagement:vehicleTerminalInstall:add") + @Log(title = "定位终端安装记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增定位终端安装记录") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleTerminalInstallBo bo) { + return toAjax(hotVehicleTerminalInstallService.insertByBo(bo)); + } + + /** + * 修改定位终端安装记录 + */ + //@SaCheckPermission("resourceManagement:vehicleTerminalInstall:edit") + @Log(title = "定位终端安装记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改定位终端安装记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleTerminalInstallBo bo) { + return toAjax(hotVehicleTerminalInstallService.updateByBo(bo)); + } + + /** + * 删除定位终端安装记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("resourceManagement:vehicleTerminalInstall:remove") + @Log(title = "定位终端安装记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除定位终端安装记录") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotVehicleTerminalInstallService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/domain/HotVehicleTerminalInstall.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/domain/HotVehicleTerminalInstall.java new file mode 100644 index 0000000..32b975d --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/domain/HotVehicleTerminalInstall.java @@ -0,0 +1,85 @@ +package com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 定位终端安装记录对象 hot_vehicle_terminal_install + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_terminal_install") +public class HotVehicleTerminalInstall extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private String id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 终端服务商 + */ + private String serviceProvider; + + /** + * 单据编号 + */ + private String documentNo; + + /** + * 记录人 + */ + private String recorder; + + /** + * 安装日期 + */ + private Date installDate; + + /** + * 服务截止日期 + */ + private Date serviceEndDate; + + /** + * 入网登记表图片URL(多个逗号分隔) + */ + private String registerImageUrls; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/domain/bo/HotVehicleTerminalInstallBo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/domain/bo/HotVehicleTerminalInstallBo.java new file mode 100644 index 0000000..5ecf950 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/domain/bo/HotVehicleTerminalInstallBo.java @@ -0,0 +1,89 @@ +package com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.bo; + +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.HotVehicleTerminalInstall; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 定位终端安装记录业务对象 hot_vehicle_terminal_install + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleTerminalInstall.class, reverseConvertGenerate = false) +public class HotVehicleTerminalInstallBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private String id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 车辆ID + */ + private String vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 终端服务商 + */ + @NotBlank(message = "终端服务商不能为空", groups = {AddGroup.class, EditGroup.class}) + private String serviceProvider; + + /** + * 单据编号 + */ + @NotBlank(message = "单据编号不能为空", groups = {AddGroup.class, EditGroup.class}) + private String documentNo; + + /** + * 记录人 + */ + @NotBlank(message = "记录人不能为空", groups = {AddGroup.class, EditGroup.class}) + private String recorder; + + /** + * 安装日期 + */ + @NotNull(message = "安装日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date installDate; + + /** + * 服务截止日期 + */ + @NotNull(message = "服务截止日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date serviceEndDate; + + /** + * 入网登记表图片URL(多个逗号分隔) + */ + private String registerImageUrls; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/domain/vo/HotVehicleTerminalInstallExportVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/domain/vo/HotVehicleTerminalInstallExportVo.java new file mode 100644 index 0000000..a14d572 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/domain/vo/HotVehicleTerminalInstallExportVo.java @@ -0,0 +1,41 @@ +package com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 定位终端安装记录导出字段 + * + * @author shihongwei + * @date 2025-12-18 + */ +@Data +@ExcelIgnoreUnannotated +public class HotVehicleTerminalInstallExportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty(value = "车牌号", index = 0) + private String plateNumber; + + @ExcelProperty(value = "终端服务商", index = 1) + private String serviceProvider; + + @ExcelProperty(value = "单据编号", index = 2) + private String documentNo; + + @ExcelProperty(value = "安装日期", index = 3) + private Date installDate; + + @ExcelProperty(value = "服务截止日期", index = 4) + private Date serviceEndDate; + + @ExcelProperty(value = "记录人", index = 5) + private String recorderName; +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/domain/vo/HotVehicleTerminalInstallVo.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/domain/vo/HotVehicleTerminalInstallVo.java new file mode 100644 index 0000000..d054563 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/domain/vo/HotVehicleTerminalInstallVo.java @@ -0,0 +1,100 @@ +package com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.HotVehicleTerminalInstall; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 定位终端安装记录视图对象 hot_vehicle_terminal_install + * + * @author shihongwei + * @date 2025-12-15 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleTerminalInstall.class) +public class HotVehicleTerminalInstallVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private String id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 车辆ID + */ + @ExcelProperty(value = "车辆ID") + private String vehicleId; + + /** + * 车牌号 + */ + @ExcelProperty(value = "车牌号") + private String plateNumber; + + /** + * 终端服务商 + */ + @ExcelProperty(value = "终端服务商") + private String serviceProvider; + + /** + * 单据编号 + */ + @ExcelProperty(value = "单据编号") + private String documentNo; + + /** + * 记录人 + */ + @ExcelProperty(value = "记录人") + private String recorder; + + @Translation(type = TransConstant.SAFETY_MANAGER_ID_TO_NAME, mapper = "recorder") + private String recorderName; + + /** + * 安装日期 + */ + @ExcelProperty(value = "安装日期") + private Date installDate; + + /** + * 服务截止日期 + */ + @ExcelProperty(value = "服务截止日期") + private Date serviceEndDate; + + /** + * 入网登记表图片URL(多个逗号分隔) + */ + @ExcelProperty(value = "入网登记表图片URL(多个逗号分隔)") + private String registerImageUrls; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/mapper/HotVehicleTerminalInstallMapper.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/mapper/HotVehicleTerminalInstallMapper.java new file mode 100644 index 0000000..d4b05d7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/mapper/HotVehicleTerminalInstallMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.resourceManagement.vehicleTerminalInstall.mapper; + +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.HotVehicleTerminalInstall; +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.vo.HotVehicleTerminalInstallVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 定位终端安装记录Mapper接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Mapper +public interface HotVehicleTerminalInstallMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/service/IHotVehicleTerminalInstallService.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/service/IHotVehicleTerminalInstallService.java new file mode 100644 index 0000000..841059f --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/service/IHotVehicleTerminalInstallService.java @@ -0,0 +1,77 @@ +package com.hotwj.platform.resourceManagement.vehicleTerminalInstall.service; + +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.bo.HotVehicleTerminalInstallBo; +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.vo.HotVehicleTerminalInstallVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 定位终端安装记录Service接口 + * + * @author shihongwei + * @date 2025-12-15 + */ +public interface IHotVehicleTerminalInstallService { + + /** + * 查询定位终端安装记录 + * + * @param id 主键 + * @return 定位终端安装记录 + */ + HotVehicleTerminalInstallVo queryById(Long id); + + /** + * 分页查询定位终端安装记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 定位终端安装记录分页列表 + */ + TableDataInfo queryPageList(HotVehicleTerminalInstallBo bo, PageQuery pageQuery); + + /** + * 分页查询定位终端安装记录列表(车辆安装页面专用) + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 定位终端安装记录分页列表 + */ + TableDataInfo queryPageListForVehiclePage(HotVehicleTerminalInstallBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的定位终端安装记录列表 + * + * @param bo 查询条件 + * @return 定位终端安装记录列表 + */ + List queryList(HotVehicleTerminalInstallBo bo); + + /** + * 新增定位终端安装记录 + * + * @param bo 定位终端安装记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleTerminalInstallBo bo); + + /** + * 修改定位终端安装记录 + * + * @param bo 定位终端安装记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleTerminalInstallBo bo); + + /** + * 校验并批量删除定位终端安装记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/service/impl/HotVehicleTerminalInstallServiceImpl.java b/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/service/impl/HotVehicleTerminalInstallServiceImpl.java new file mode 100644 index 0000000..e2541b0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/resourceManagement/vehicleTerminalInstall/service/impl/HotVehicleTerminalInstallServiceImpl.java @@ -0,0 +1,163 @@ +package com.hotwj.platform.resourceManagement.vehicleTerminalInstall.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.HotVehicleTerminalInstall; +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.bo.HotVehicleTerminalInstallBo; +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.vo.HotVehicleTerminalInstallVo; +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.mapper.HotVehicleTerminalInstallMapper; +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.service.IHotVehicleTerminalInstallService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 定位终端安装记录Service业务层处理 + * + * @author shihongwei + * @date 2025-12-15 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleTerminalInstallServiceImpl implements IHotVehicleTerminalInstallService { + + private final HotVehicleTerminalInstallMapper baseMapper; + + /** + * 查询定位终端安装记录 + * + * @param id 主键 + * @return 定位终端安装记录 + */ + @Override + public HotVehicleTerminalInstallVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询定位终端安装记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 定位终端安装记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleTerminalInstallBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + @Override + public TableDataInfo queryPageListForVehiclePage(HotVehicleTerminalInstallBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildVehiclePageQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的定位终端安装记录列表 + * + * @param bo 查询条件 + * @return 定位终端安装记录列表 + */ + @Override + public List queryList(HotVehicleTerminalInstallBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleTerminalInstallBo bo) { + Map params = bo.getParams(); + Long isDeleted = bo.getIsDeleted() == null ? 0L : bo.getIsDeleted(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotVehicleTerminalInstall::getId); + lqw.eq(bo.getCompanyId() != null, HotVehicleTerminalInstall::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getVehicleId() != null, HotVehicleTerminalInstall::getVehicleId, bo.getVehicleId()); + lqw.eq(StringUtils.isNotBlank(bo.getPlateNumber()), HotVehicleTerminalInstall::getPlateNumber, bo.getPlateNumber()); + lqw.eq(StringUtils.isNotBlank(bo.getServiceProvider()), HotVehicleTerminalInstall::getServiceProvider, bo.getServiceProvider()); + lqw.eq(StringUtils.isNotBlank(bo.getDocumentNo()), HotVehicleTerminalInstall::getDocumentNo, bo.getDocumentNo()); + lqw.eq(StringUtils.isNotBlank(bo.getRecorder()), HotVehicleTerminalInstall::getRecorder, bo.getRecorder()); + lqw.eq(bo.getInstallDate() != null, HotVehicleTerminalInstall::getInstallDate, bo.getInstallDate()); + lqw.eq(bo.getServiceEndDate() != null, HotVehicleTerminalInstall::getServiceEndDate, bo.getServiceEndDate()); + lqw.eq(StringUtils.isNotBlank(bo.getRegisterImageUrls()), HotVehicleTerminalInstall::getRegisterImageUrls, bo.getRegisterImageUrls()); + lqw.eq(HotVehicleTerminalInstall::getIsDeleted, isDeleted); + return lqw; + } + + private LambdaQueryWrapper buildVehiclePageQueryWrapper(HotVehicleTerminalInstallBo bo) { + Long isDeleted = bo.getIsDeleted() == null ? 0L : bo.getIsDeleted(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotVehicleTerminalInstall::getId); + lqw.eq(bo.getCompanyId() != null, HotVehicleTerminalInstall::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getVehicleId() != null, HotVehicleTerminalInstall::getVehicleId, bo.getVehicleId()); + lqw.like(StringUtils.isNotBlank(bo.getServiceProvider()), HotVehicleTerminalInstall::getServiceProvider, bo.getServiceProvider()); + lqw.like(StringUtils.isNotBlank(bo.getDocumentNo()), HotVehicleTerminalInstall::getDocumentNo, bo.getDocumentNo()); + lqw.eq(bo.getInstallDate() != null, HotVehicleTerminalInstall::getInstallDate, bo.getInstallDate()); + lqw.eq(bo.getServiceEndDate() != null, HotVehicleTerminalInstall::getServiceEndDate, bo.getServiceEndDate()); + lqw.eq(HotVehicleTerminalInstall::getIsDeleted, isDeleted); + return lqw; + } + + /** + * 新增定位终端安装记录 + * + * @param bo 定位终端安装记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleTerminalInstallBo bo) { + HotVehicleTerminalInstall add = MapstructUtils.convert(bo, HotVehicleTerminalInstall.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改定位终端安装记录 + * + * @param bo 定位终端安装记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleTerminalInstallBo bo) { + HotVehicleTerminalInstall update = MapstructUtils.convert(bo, HotVehicleTerminalInstall.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleTerminalInstall entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除定位终端安装记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/controller/HotAccidentArchiveController.java b/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/controller/HotAccidentArchiveController.java new file mode 100644 index 0000000..72f8f77 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/controller/HotAccidentArchiveController.java @@ -0,0 +1,133 @@ +package com.hotwj.platform.securityManagement.accidentArchive.controller; + +import com.hotwj.platform.securityManagement.accidentArchive.domain.bo.HotAccidentArchiveBo; +import com.hotwj.platform.securityManagement.accidentArchive.domain.vo.HotAccidentArchiveVo; +import com.hotwj.platform.securityManagement.accidentArchive.service.IHotAccidentArchiveService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 事故档案 + * + * @author shihongwei + * @date 2026-01-04 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/accidentArchive") +@Tag(name = "事故档案", description = "事故档案管理") +public class HotAccidentArchiveController extends BaseController { + + private final IHotAccidentArchiveService hotAccidentArchiveService; + + /** + * 查询事故档案列表 + */ + //@SaCheckPermission("securityManagement:accidentArchive:list") + @GetMapping("/list") + @Operation(summary = "分页查询事故档案列表") + public TableDataInfo list( + HotAccidentArchiveBo bo, + PageQuery pageQuery, + @RequestParam(value = "createby", required = false) Long createby + ) { + if (bo.getCreateBy() == null && createby != null) { + bo.setCreateBy(createby); + } + return hotAccidentArchiveService.queryPageList(bo, pageQuery); + } + + /** + * 导出事故档案列表 + */ + //@SaCheckPermission("securityManagement:accidentArchive:export") + @Log(title = "事故档案", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出事故档案列表") + public void export(HotAccidentArchiveBo bo, HttpServletResponse response) { + List list = hotAccidentArchiveService.queryList(bo); + ExcelUtil.exportExcel(list, "事故档案", HotAccidentArchiveVo.class, response); + } + + /** + * 获取事故档案详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:accidentArchive:query") + @GetMapping("/{id}") + @Operation(summary = "获取事故档案详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotAccidentArchiveService.queryById(id)); + } + + /** + * 新增事故档案 + */ + //@SaCheckPermission("securityManagement:accidentArchive:add") + @Log(title = "事故档案", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增事故档案") + public R add(@Validated(AddGroup.class) @RequestBody HotAccidentArchiveBo bo) { +// if (DriverLoginContextHelper.isDriverPort()) { +// HotDriver driver = DriverLoginContextHelper.getCurrentDriver(); +// bo.setDriverId(driver.getId()); +// bo.setDriverName(driver.getName()); +// bo.setDriverPhone(driver.getPhone()); +// bo.setVehicleId(driver.getVehicleId() != null ? Long.valueOf(driver.getVehicleId()) : null); +// bo.setPlateNumber(driver.getPlateNumber()); +// bo.setCompanyId(driver.getCompanyId()); +// } + return toAjax(hotAccidentArchiveService.insertByBo(bo)); + } + + /** + * 修改事故档案 + */ + //@SaCheckPermission("securityManagement:accidentArchive:edit") + @Log(title = "事故档案", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改事故档案") + public R edit(@Validated(EditGroup.class) @RequestBody HotAccidentArchiveBo bo) { + return toAjax(hotAccidentArchiveService.updateByBo(bo)); + } + + /** + * 删除事故档案 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:accidentArchive:remove") + @Log(title = "事故档案", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除事故档案") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotAccidentArchiveService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/domain/HotAccidentArchive.java b/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/domain/HotAccidentArchive.java new file mode 100644 index 0000000..0d2243d --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/domain/HotAccidentArchive.java @@ -0,0 +1,341 @@ +package com.hotwj.platform.securityManagement.accidentArchive.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 事故档案对象 hot_accident_archive + * + * @author shihongwei + * @date 2026-01-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_accident_archive") +public class HotAccidentArchive extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(多租户) + */ + private Long companyId; + + /** + * 事故编号(如 SG-时间戳) + */ + private String accidentNo; + + /** + * 车辆ID + */ + private Long vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 驾驶员姓名 + */ + private String driverName; + + /** + * 驾驶员电话 + */ + private String driverPhone; + + /** + * 从业资格证编号 + */ + private String driverProfCertNo; + + /** + * 发生时间 + */ + private Date accidentTime; + + /** + * 营运证号 + */ + private String operationCertificateNumber; + + /** + * 事故地点 + */ + private String occurLocation; + + /** + * 事故图片 + */ + private String occurImgUrl; + + /** + * 主要情况及事故原因 + */ + private String mainCause; + + /** + * 责任划分 + */ + private String divisionResponsibilities; + + /** + * 死亡人数 + */ + private Long deathToll; + + /** + * 重伤人员 + */ + private Long numberSeriouslyInjured; + + /** + * 轻伤人数 + */ + private Long numberMinorInjuries; + + /** + * 经济损失 + */ + private BigDecimal economicLoss; + + /** + * 事故形态 + */ + private String accidentPattern; + + /** + * 天气情况 + */ + private String weather; + + /** + * 公路技术等级 + */ + private String roadTechnicalGrade; + + /** + * 路段线性状况 + */ + private String roadLinearityStatus; + + /** + * 路段路面状况 + */ + private String roadSurfaceCondition; + + /** + * 事故形态(多选,逗号分隔:碰撞/刮擦/颠压/侧翻/失火/撞固定物/撞静止车辆/其他) + */ + private String accidentForms; + + /** + * 事故直接原因(多选,逗号分隔:超载/超速/驾驶员操作不当/疲劳驾驶/机械故障/酒驾/道路及设施原因/其他) + */ + private String accidentDirectCauses; + + /** + * 线路类型 + */ + private String routeType; + + /** + * 车辆等级 + */ + private String vehicleGrade; + + /** + * 运行线路 + */ + private String routeName; + + /** + * 始发站(地) + */ + private String originStation; + + /** + * 企业资质等级 + */ + private String companyQualificationGrade; + + /** + * 车型 + */ + private String vehicleType; + + /** + * 核定人数(人) + */ + private Long approvedPersons; + + /** + * 核定吨数(吨) + */ + private BigDecimal approvedTonnage; + + /** + * 实载人数(人) + */ + private Long carriedPersons; + + /** + * 实载吨数(吨) + */ + private BigDecimal carriedTonnage; + + /** + * 失踪(人) + */ + private Long missingPersons; + + /** + * 受伤(人) + */ + private Long injuredPersons; + + /** + * 单位主管负责人 + */ + private String unitResponsible; + + /** + * 统计负责人 + */ + private String leadResponsible; + + /** + * 填表人 + */ + private String reporterName; + + /** + * 填表人联系电话 + */ + private String reporterPhone; + + /** + * 事故缘故 + */ + private String accidentSituation; + + /** + * 备注 + */ + private String remark; + + /** + * 事故内容 + */ + private String accidentContent; + + /** + * 风险等级评估(可能性) + */ + private String accidentLevelLike; + + /** + * 事故等级划分(严重程度) + */ + private String accidentLevelDegree; + + /** + * 风险等级评估(可能性)JSON详情 + */ + private String riskAssessLike; + + /** + * 事故等级划分(严重程度)JSON详情 + */ + private String riskAssessDegree; + + /** + * 事故等级划分 + */ + private String accidentLevel; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 是否整改 + */ + private Long isRectified; + + /** + * 整改人员 + */ + private String rectifierName; + + /** + * 整改联系方式 + */ + private String contactInfo; + + /** + * 整改时间 + */ + private Date rectifyTime; + + /** + * 处理结果书url + */ + private String resultDocUrl; + + /** + * 责任认定书图片 + */ + private String responsibilityCertificateUrls; + + /** + * 结案时间 + */ + private Date caseClosingTime; + + /** + * 处理结果 + */ + private String treatmentResult; + + /** + * 整改备注 + */ + private String rectifyRemark; + + /** + * 整改内容 + */ + private String rectifyContent; + + /** + * 责任人签名 + */ + private String responsiblePersonSignature; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/domain/bo/HotAccidentArchiveBo.java b/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/domain/bo/HotAccidentArchiveBo.java new file mode 100644 index 0000000..ac4b8e6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/domain/bo/HotAccidentArchiveBo.java @@ -0,0 +1,370 @@ +package com.hotwj.platform.securityManagement.accidentArchive.domain.bo; + +import com.hotwj.platform.securityManagement.accidentArchive.domain.HotAccidentArchive; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 事故档案业务对象 hot_accident_archive + * + * @author shihongwei + * @date 2026-01-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotAccidentArchive.class, reverseConvertGenerate = false) +public class HotAccidentArchiveBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 公司ID(多租户) + */ + @NotNull(message = "公司ID(多租户)不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long companyId; + + /** + * 事故编号(如 SG-时间戳) + */ + private String accidentNo; + + /** + * 车辆ID + */ +// @NotNull(message = "车辆ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 驾驶员ID + */ + @NotBlank(message = "驾驶员ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private String driverId; + + /** + * 驾驶员姓名 + */ + private String driverName; + + /** + * 驾驶员电话 + */ +// @NotBlank(message = "驾驶员电话不能为空", groups = { AddGroup.class, EditGroup.class }) + private String driverPhone; + + /** + * 从业资格证编号 + */ + private String driverProfCertNo; + + /** + * 发生时间 + */ + @NotNull(message = "发生时间不能为空", groups = { AddGroup.class, EditGroup.class }) + private Date accidentTime; + + /** + * 营运证号 + */ + private String operationCertificateNumber; + + /** + * 事故地点 + */ + @NotBlank(message = "事故地点不能为空", groups = { AddGroup.class, EditGroup.class }) + private String occurLocation; + + /** + * 事故图片 + */ + @NotBlank(message = "事故图片不能为空", groups = { AddGroup.class, EditGroup.class }) + private String occurImgUrl; + + /** + * 主要情况及事故原因 + */ +// @NotBlank(message = "主要情况及事故原因不能为空", groups = { AddGroup.class, EditGroup.class }) + private String mainCause; + + /** + * 责任划分 + */ +// @NotBlank(message = "责任划分不能为空", groups = { AddGroup.class, EditGroup.class }) + private String divisionResponsibilities; + + /** + * 死亡人数 + */ +// @NotNull(message = "死亡人数不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long deathToll; + + /** + * 重伤人员 + */ +// @NotNull(message = "重伤人员不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long numberSeriouslyInjured; + + // /** +// * 轻伤人数 +// */ +// @NotNull(message = "轻伤人数不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long numberMinorInjuries; + + /** + * 经济损失 + */ +// @NotNull(message = "经济损失不能为空", groups = { AddGroup.class, EditGroup.class }) + private BigDecimal economicLoss; + + /** + * 事故形态 + */ + private String accidentPattern; + + /** + * 天气情况 + */ + @NotBlank(message = "天气情况不能为空", groups = { AddGroup.class, EditGroup.class }) + private String weather; + + /** + * 公路技术等级 + */ +// @NotBlank(message = "公路技术等级不能为空", groups = { AddGroup.class, EditGroup.class }) + private String roadTechnicalGrade; + + /** + * 路段线性状况 + */ + @NotBlank(message = "路段线性状况不能为空", groups = { AddGroup.class, EditGroup.class }) + private String roadLinearityStatus; + + /** + * 路段路面状况 + */ + @NotBlank(message = "路段路面状况不能为空", groups = { AddGroup.class, EditGroup.class }) + private String roadSurfaceCondition; + + /** + * 事故形态(多选,逗号分隔:碰撞/刮擦/颠压/侧翻/失火/撞固定物/撞静止车辆/其他) + */ +// @NotBlank(message = "事故形态(多选,逗号分隔:碰撞/刮擦/颠压/侧翻/失火/撞固定物/撞静止车辆/其他)不能为空", groups = { AddGroup.class, EditGroup.class }) + private String accidentForms; + + /** + * 事故直接原因(多选,逗号分隔:超载/超速/驾驶员操作不当/疲劳驾驶/机械故障/酒驾/道路及设施原因/其他) + */ +// @NotBlank(message = "事故直接原因(多选,逗号分隔:超载/超速/驾驶员操作不当/疲劳驾驶/机械故障/酒驾/道路及设施原因/其他)不能为空", groups = { AddGroup.class, EditGroup.class }) + private String accidentDirectCauses; + + /** + * 线路类型 + */ + private String routeType; + + /** + * 车辆等级 + */ + private String vehicleGrade; + + /** + * 运行线路 + */ + @NotBlank(message = "运行线路不能为空", groups = { AddGroup.class, EditGroup.class }) + private String routeName; + + /** + * 始发站(地) + */ + @NotBlank(message = "始发站(地)不能为空", groups = { AddGroup.class, EditGroup.class }) + private String originStation; + + /** + * 企业资质等级 + */ +// @NotBlank(message = "企业资质等级不能为空", groups = { AddGroup.class, EditGroup.class }) + private String companyQualificationGrade; + + /** + * 车型 + */ +// @NotBlank(message = "车型不能为空", groups = { AddGroup.class, EditGroup.class }) + private String vehicleType; + + /** + * 核定人数(人) + */ + @NotNull(message = "核定人数(人)不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long approvedPersons; + + /** + * 核定吨数(吨) + */ + private BigDecimal approvedTonnage; + + /** + * 实载人数(人) + */ + @NotNull(message = "实载人数(人)不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long carriedPersons; + + /** + * 实载吨数(吨) + */ + private BigDecimal carriedTonnage; + + /** + * 失踪(人) + */ +// @NotNull(message = "失踪(人)不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long missingPersons; + + /** + * 受伤(人) + */ + private Long injuredPersons; + + /** + * 单位主管负责人 + */ +// @NotBlank(message = "单位主管负责人不能为空", groups = { AddGroup.class, EditGroup.class }) + private String unitResponsible; + + /** + * 统计负责人 + */ +// @NotBlank(message = "统计负责人不能为空", groups = { AddGroup.class, EditGroup.class }) + private String leadResponsible; + + /** + * 填表人 + */ +// @NotBlank(message = "填表人不能为空", groups = { AddGroup.class, EditGroup.class }) + private String reporterName; + + /** + * 填表人联系电话 + */ +// @NotBlank(message = "填表人联系电话不能为空", groups = { AddGroup.class, EditGroup.class }) + private String reporterPhone; + + /** + * 事故缘故 + */ + private String accidentSituation; + + /** + * 备注 + */ + private String remark; + + /** + * 事故内容 + */ + private String accidentContent; + + /** + * 风险等级评估(可能性) + */ + private String accidentLevelLike; + + /** + * 事故等级划分(严重程度) + */ + private String accidentLevelDegree; + + /** + * 风险等级评估(可能性)JSON详情 + */ + private String riskAssessLike; + + /** + * 事故等级划分(严重程度)JSON详情 + */ +// @NotBlank(message = "事故等级划分(严重程度)JSON详情不能为空", groups = { AddGroup.class, EditGroup.class }) + private String riskAssessDegree; + + /** + * 事故等级划分 + */ + private String accidentLevel; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + /** + * 是否整改 + */ + private Long isRectified; + + /** + * 整改人员 + */ + private String rectifierName; + + /** + * 整改联系方式 + */ + private String contactInfo; + + /** + * 整改时间 + */ + private Date rectifyTime; + + /** + * 处理结果书url + */ + private String resultDocUrl; + + /** + * 责任认定书图片 + */ + private String responsibilityCertificateUrls; + + /** + * 结案时间 + */ + private Date caseClosingTime; + + /** + * 处理结果 + */ + private String treatmentResult; + + /** + * 整改备注 + */ + private String rectifyRemark; + + /** + * 整改内容 + */ + private String rectifyContent; + + /** + * 责任人签名 + */ + private String responsiblePersonSignature; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/domain/vo/HotAccidentArchiveVo.java b/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/domain/vo/HotAccidentArchiveVo.java new file mode 100644 index 0000000..b7de7d3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/domain/vo/HotAccidentArchiveVo.java @@ -0,0 +1,416 @@ +package com.hotwj.platform.securityManagement.accidentArchive.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.accidentArchive.domain.HotAccidentArchive; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + + +/** + * 事故档案视图对象 hot_accident_archive + * + * @author shihongwei + * @date 2026-01-04 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotAccidentArchive.class) +public class HotAccidentArchiveVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(多租户) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "多=租户") + private Long companyId; + + /** + * 事故编号(如 SG-时间戳) + */ + @ExcelProperty(value = "事故编号", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "如=,S=G-时间戳") + private String accidentNo; + + /** + * 车辆ID + */ + @ExcelProperty(value = "车辆ID") + private Long vehicleId; + + /** + * 车牌号 + */ + @ExcelProperty(value = "车牌号") + private String plateNumber; + + /** + * 驾驶员ID + */ + @ExcelProperty(value = "驾驶员ID") + private String driverId; + + /** + * 驾驶员姓名 + */ + @ExcelProperty(value = "驾驶员姓名") + private String driverName; + + /** + * 驾驶员电话 + */ + @ExcelProperty(value = "驾驶员电话") + private String driverPhone; + + /** + * 从业资格证编号 + */ + @ExcelProperty(value = "从业资格证编号") + private String driverProfCertNo; + + /** + * 发生时间 + */ + @ExcelProperty(value = "发生时间") + private Date accidentTime; + + /** + * 营运证号 + */ + @ExcelProperty(value = "营运证号") + private String operationCertificateNumber; + + /** + * 事故地点 + */ + @ExcelProperty(value = "事故地点") + private String occurLocation; + + /** + * 事故图片 + */ + @ExcelProperty(value = "事故图片") + private String occurImgUrl; + + /** + * 主要情况及事故原因 + */ + @ExcelProperty(value = "主要情况及事故原因") + private String mainCause; + + /** + * 责任划分 + */ + @ExcelProperty(value = "责任划分") + private String divisionResponsibilities; + + /** + * 死亡人数 + */ + @ExcelProperty(value = "死亡人数") + private Long deathToll; + + /** + * 重伤人员 + */ + @ExcelProperty(value = "重伤人员") + private Long numberSeriouslyInjured; + + /** + * 轻伤人数 + */ + @ExcelProperty(value = "轻伤人数") + private Long numberMinorInjuries; + + /** + * 经济损失 + */ + @ExcelProperty(value = "经济损失") + private BigDecimal economicLoss; + + /** + * 事故形态 + */ + @ExcelProperty(value = "事故形态") + private String accidentPattern; + + /** + * 天气情况 + */ + @ExcelProperty(value = "天气情况") + private String weather; + + /** + * 公路技术等级 + */ + @ExcelProperty(value = "公路技术等级") + private String roadTechnicalGrade; + + /** + * 路段线性状况 + */ + @ExcelProperty(value = "路段线性状况") + private String roadLinearityStatus; + + /** + * 路段路面状况 + */ + @ExcelProperty(value = "路段路面状况") + private String roadSurfaceCondition; + + /** + * 事故形态(多选,逗号分隔:碰撞/刮擦/颠压/侧翻/失火/撞固定物/撞静止车辆/其他) + */ + @ExcelProperty(value = "事故形态(多选,逗号分隔:碰撞/刮擦/颠压/侧翻/失火/撞固定物/撞静止车辆/其他)") + private String accidentForms; + + /** + * 事故直接原因(多选,逗号分隔:超载/超速/驾驶员操作不当/疲劳驾驶/机械故障/酒驾/道路及设施原因/其他) + */ + @ExcelProperty(value = "事故直接原因(多选,逗号分隔:超载/超速/驾驶员操作不当/疲劳驾驶/机械故障/酒驾/道路及设施原因/其他)") + private String accidentDirectCauses; + + /** + * 线路类型 + */ + @ExcelProperty(value = "线路类型") + private String routeType; + + /** + * 车辆等级 + */ + @ExcelProperty(value = "车辆等级") + private String vehicleGrade; + + /** + * 运行线路 + */ + @ExcelProperty(value = "运行线路") + private String routeName; + + /** + * 始发站(地) + */ + @ExcelProperty(value = "始发站(地)") + private String originStation; + + /** + * 企业资质等级 + */ + @ExcelProperty(value = "企业资质等级") + private String companyQualificationGrade; + + /** + * 车型 + */ + @ExcelProperty(value = "车型") + private String vehicleType; + + /** + * 核定人数(人) + */ + @ExcelProperty(value = "核定人数(人)") + private Long approvedPersons; + + /** + * 核定吨数(吨) + */ + @ExcelProperty(value = "核定吨数(吨)") + private BigDecimal approvedTonnage; + + /** + * 实载人数(人) + */ + @ExcelProperty(value = "实载人数(人)") + private Long carriedPersons; + + /** + * 实载吨数(吨) + */ + @ExcelProperty(value = "实载吨数(吨)") + private BigDecimal carriedTonnage; + + /** + * 失踪(人) + */ + @ExcelProperty(value = "失踪(人)") + private Long missingPersons; + + /** + * 受伤(人) + */ + @ExcelProperty(value = "受伤(人)") + private Long injuredPersons; + + /** + * 单位主管负责人 + */ + @ExcelProperty(value = "单位主管负责人") + private String unitResponsible; + + /** + * 统计负责人 + */ + @ExcelProperty(value = "统计负责人") + private String leadResponsible; + + /** + * 填表人 + */ + @ExcelProperty(value = "填表人") + private String reporterName; + + /** + * 填表人联系电话 + */ + @ExcelProperty(value = "填表人联系电话") + private String reporterPhone; + + /** + * 事故缘故 + */ + @ExcelProperty(value = "事故缘故") + private String accidentSituation; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 事故内容 + */ + @ExcelProperty(value = "事故内容") + private String accidentContent; + + /** + * 风险等级评估(可能性) + */ + @ExcelProperty(value = "风险等级评估", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "可=能性") + private String accidentLevelLike; + + /** + * 事故等级划分(严重程度) + */ + @ExcelProperty(value = "事故等级划分", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "严=重程度") + private String accidentLevelDegree; + + /** + * 风险等级评估(可能性)JSON详情 + */ + @ExcelProperty(value = "风险等级评估", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "可=能性") + private String riskAssessLike; + + /** + * 事故等级划分(严重程度)JSON详情 + */ + @ExcelProperty(value = "事故等级划分", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "严=重程度") + private String riskAssessDegree; + + /** + * 事故等级划分 + */ + @ExcelProperty(value = "事故等级划分") + private String accidentLevel; + + /** + * 图片列表 + */ + private java.util.List images; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 是否整改 + */ + @ExcelProperty(value = "是否整改") + private Long isRectified; + + /** + * 整改人员 + */ + @ExcelProperty(value = "整改人员") + private String rectifierName; + + /** + * 整改联系方式 + */ + @ExcelProperty(value = "整改联系方式") + private String contactInfo; + + /** + * 整改时间 + */ + @ExcelProperty(value = "整改时间") + private Date rectifyTime; + + /** + * 处理结果书url + */ + @ExcelProperty(value = "处理结果书url") + private String resultDocUrl; + + /** + * 责任认定书图片 + */ + @ExcelProperty(value = "责任认定书图片") + private String responsibilityCertificateUrls; + + /** + * 结案时间 + */ + @ExcelProperty(value = "结案时间") + private Date caseClosingTime; + + /** + * 处理结果 + */ + @ExcelProperty(value = "处理结果") + private String treatmentResult; + + /** + * 整改备注 + */ + @ExcelProperty(value = "整改备注") + private String rectifyRemark; + + /** + * 整改内容 + */ + @ExcelProperty(value = "整改内容") + private String rectifyContent; + + /** + * 责任人签名 + */ + @ExcelProperty(value = "责任人签名") + private String responsiblePersonSignature; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/mapper/HotAccidentArchiveMapper.java b/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/mapper/HotAccidentArchiveMapper.java new file mode 100644 index 0000000..c028d2a --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/mapper/HotAccidentArchiveMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.accidentArchive.mapper; + +import com.hotwj.platform.securityManagement.accidentArchive.domain.HotAccidentArchive; +import com.hotwj.platform.securityManagement.accidentArchive.domain.vo.HotAccidentArchiveVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 事故档案Mapper接口 + * + * @author shihongwei + * @date 2026-01-04 + */ + @Mapper +public interface HotAccidentArchiveMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/service/IHotAccidentArchiveService.java b/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/service/IHotAccidentArchiveService.java new file mode 100644 index 0000000..6e20078 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/service/IHotAccidentArchiveService.java @@ -0,0 +1,84 @@ +package com.hotwj.platform.securityManagement.accidentArchive.service; + +import com.hotwj.platform.securityManagement.accidentArchive.domain.vo.HotAccidentArchiveVo; +import com.hotwj.platform.securityManagement.accidentArchive.domain.bo.HotAccidentArchiveBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 事故档案Service接口 + * + * @author shihongwei + * @date 2026-01-04 + */ +public interface IHotAccidentArchiveService { + + /** + * 查询事故档案 + * + * @param id 主键 + * @return 事故档案 + */ + HotAccidentArchiveVo queryById(Long id); + + /** + * 分页查询事故档案列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 事故档案分页列表 + */ + TableDataInfo queryPageList(HotAccidentArchiveBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的事故档案列表 + * + * @param bo 查询条件 + * @return 事故档案列表 + */ + List queryList(HotAccidentArchiveBo bo); + + /** + * 根据车辆ID查询最新的一条事故档案 + * + * @param vehicleId 车辆ID + * @return 最新事故档案 + */ + HotAccidentArchiveVo queryLatestByVehicleId(String vehicleId); + + /** + * 根据车辆ID查询事故档案列表 + * + * @param vehicleId 车辆ID + * @return 事故档案列表 + */ + List queryListByVehicleId(String vehicleId); + + /** + * 新增事故档案 + * + * @param bo 事故档案 + * @return 是否新增成功 + */ + Boolean insertByBo(HotAccidentArchiveBo bo); + + /** + * 修改事故档案 + * + * @param bo 事故档案 + * @return 是否修改成功 + */ + Boolean updateByBo(HotAccidentArchiveBo bo); + + /** + * 校验并批量删除事故档案信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/service/impl/HotAccidentArchiveServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/service/impl/HotAccidentArchiveServiceImpl.java new file mode 100644 index 0000000..46a845d --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/accidentArchive/service/impl/HotAccidentArchiveServiceImpl.java @@ -0,0 +1,219 @@ +package com.hotwj.platform.securityManagement.accidentArchive.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.securityManagement.accidentArchive.domain.HotAccidentArchive; +import com.hotwj.platform.securityManagement.accidentArchive.domain.bo.HotAccidentArchiveBo; +import com.hotwj.platform.securityManagement.accidentArchive.domain.vo.HotAccidentArchiveVo; +import com.hotwj.platform.securityManagement.accidentArchive.mapper.HotAccidentArchiveMapper; +import com.hotwj.platform.securityManagement.accidentArchive.service.IHotAccidentArchiveService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 事故档案Service业务层处理 + * + * @author shihongwei + * @date 2026-01-04 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotAccidentArchiveServiceImpl implements IHotAccidentArchiveService { + + private final HotAccidentArchiveMapper baseMapper; + + /** + * 查询事故档案 + * + * @param id 主键 + * @return 事故档案 + */ + @Override + public HotAccidentArchiveVo queryById(Long id){ + return baseMapper.selectVoById(id); + } + + /** + * 分页查询事故档案列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 事故档案分页列表 + */ + @Override + public TableDataInfo queryPageList(HotAccidentArchiveBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的事故档案列表 + * + * @param bo 查询条件 + * @return 事故档案列表 + */ + @Override + public List queryList(HotAccidentArchiveBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + /** + * 根据车辆ID查询最新的一条事故档案 + * + * @param vehicleId 车辆ID + * @return 最新事故档案 + */ + @Override + public HotAccidentArchiveVo queryLatestByVehicleId(String vehicleId) { + return baseMapper.selectVoOne(new LambdaQueryWrapper() + .eq(HotAccidentArchive::getVehicleId, vehicleId) + .orderByDesc(HotAccidentArchive::getId) + .last("LIMIT 1")); + } + + /** + * 根据车辆ID查询事故档案列表 + * + * @param vehicleId 车辆ID + * @return 事故档案列表 + */ + @Override + public List queryListByVehicleId(String vehicleId) { + return baseMapper.selectVoList(new LambdaQueryWrapper() + .eq(HotAccidentArchive::getVehicleId, vehicleId) + .orderByDesc(HotAccidentArchive::getId)); + } + + private LambdaQueryWrapper buildQueryWrapper(HotAccidentArchiveBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotAccidentArchive::getAccidentTime); + if (bo.getAccidentTime() != null) { + java.util.Calendar calendarStart = java.util.Calendar.getInstance(); + calendarStart.setTime(bo.getAccidentTime()); + calendarStart.set(java.util.Calendar.HOUR_OF_DAY, 0); + calendarStart.set(java.util.Calendar.MINUTE, 0); + calendarStart.set(java.util.Calendar.SECOND, 0); + calendarStart.set(java.util.Calendar.MILLISECOND, 0); + java.util.Calendar calendarEnd = (java.util.Calendar) calendarStart.clone(); + calendarEnd.add(java.util.Calendar.DAY_OF_MONTH, 1); + lqw.ge(HotAccidentArchive::getAccidentTime, calendarStart.getTime()); + lqw.lt(HotAccidentArchive::getAccidentTime, calendarEnd.getTime()); + } + lqw.eq(bo.getCompanyId() != null, HotAccidentArchive::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getCreateBy() != null, HotAccidentArchive::getCreateBy, bo.getCreateBy()); + lqw.eq(StringUtils.isNotBlank(bo.getAccidentNo()), HotAccidentArchive::getAccidentNo, bo.getAccidentNo()); + lqw.eq(bo.getVehicleId() != null, HotAccidentArchive::getVehicleId, bo.getVehicleId()); + lqw.like(StringUtils.isNotBlank(bo.getPlateNumber()), HotAccidentArchive::getPlateNumber, bo.getPlateNumber()); + lqw.eq(bo.getDriverId() != null, HotAccidentArchive::getDriverId, bo.getDriverId()); + lqw.like(StringUtils.isNotBlank(bo.getDriverName()), HotAccidentArchive::getDriverName, bo.getDriverName()); + lqw.eq(StringUtils.isNotBlank(bo.getDriverPhone()), HotAccidentArchive::getDriverPhone, bo.getDriverPhone()); + lqw.eq(StringUtils.isNotBlank(bo.getDriverProfCertNo()), HotAccidentArchive::getDriverProfCertNo, bo.getDriverProfCertNo()); + lqw.eq(StringUtils.isNotBlank(bo.getOperationCertificateNumber()), HotAccidentArchive::getOperationCertificateNumber, bo.getOperationCertificateNumber()); + lqw.eq(StringUtils.isNotBlank(bo.getOccurLocation()), HotAccidentArchive::getOccurLocation, bo.getOccurLocation()); + lqw.eq(StringUtils.isNotBlank(bo.getMainCause()), HotAccidentArchive::getMainCause, bo.getMainCause()); + lqw.eq(StringUtils.isNotBlank(bo.getDivisionResponsibilities()), HotAccidentArchive::getDivisionResponsibilities, bo.getDivisionResponsibilities()); + lqw.eq(bo.getDeathToll() != null, HotAccidentArchive::getDeathToll, bo.getDeathToll()); + lqw.eq(bo.getNumberSeriouslyInjured() != null, HotAccidentArchive::getNumberSeriouslyInjured, bo.getNumberSeriouslyInjured()); + lqw.eq(bo.getNumberMinorInjuries() != null, HotAccidentArchive::getNumberMinorInjuries, bo.getNumberMinorInjuries()); + lqw.eq(bo.getEconomicLoss() != null, HotAccidentArchive::getEconomicLoss, bo.getEconomicLoss()); + lqw.eq(StringUtils.isNotBlank(bo.getAccidentPattern()), HotAccidentArchive::getAccidentPattern, bo.getAccidentPattern()); + lqw.eq(StringUtils.isNotBlank(bo.getWeather()), HotAccidentArchive::getWeather, bo.getWeather()); + lqw.eq(StringUtils.isNotBlank(bo.getRoadTechnicalGrade()), HotAccidentArchive::getRoadTechnicalGrade, bo.getRoadTechnicalGrade()); + lqw.eq(StringUtils.isNotBlank(bo.getRoadLinearityStatus()), HotAccidentArchive::getRoadLinearityStatus, bo.getRoadLinearityStatus()); + lqw.eq(StringUtils.isNotBlank(bo.getRoadSurfaceCondition()), HotAccidentArchive::getRoadSurfaceCondition, bo.getRoadSurfaceCondition()); + lqw.eq(StringUtils.isNotBlank(bo.getAccidentForms()), HotAccidentArchive::getAccidentForms, bo.getAccidentForms()); + lqw.eq(StringUtils.isNotBlank(bo.getAccidentDirectCauses()), HotAccidentArchive::getAccidentDirectCauses, bo.getAccidentDirectCauses()); + lqw.eq(StringUtils.isNotBlank(bo.getRouteType()), HotAccidentArchive::getRouteType, bo.getRouteType()); + lqw.eq(StringUtils.isNotBlank(bo.getVehicleGrade()), HotAccidentArchive::getVehicleGrade, bo.getVehicleGrade()); + lqw.like(StringUtils.isNotBlank(bo.getRouteName()), HotAccidentArchive::getRouteName, bo.getRouteName()); + lqw.eq(StringUtils.isNotBlank(bo.getOriginStation()), HotAccidentArchive::getOriginStation, bo.getOriginStation()); + lqw.eq(StringUtils.isNotBlank(bo.getCompanyQualificationGrade()), HotAccidentArchive::getCompanyQualificationGrade, bo.getCompanyQualificationGrade()); + lqw.like(StringUtils.isNotBlank(bo.getAccidentLevel()), HotAccidentArchive::getAccidentLevel, bo.getAccidentLevel()); + lqw.eq(bo.getIsRectified() != null, HotAccidentArchive::getIsRectified, bo.getIsRectified()); + lqw.eq(StringUtils.isNotBlank(bo.getVehicleType()), HotAccidentArchive::getVehicleType, bo.getVehicleType()); + lqw.eq(bo.getApprovedPersons() != null, HotAccidentArchive::getApprovedPersons, bo.getApprovedPersons()); + lqw.eq(bo.getApprovedTonnage() != null, HotAccidentArchive::getApprovedTonnage, bo.getApprovedTonnage()); + lqw.eq(bo.getCarriedPersons() != null, HotAccidentArchive::getCarriedPersons, bo.getCarriedPersons()); + lqw.eq(bo.getCarriedTonnage() != null, HotAccidentArchive::getCarriedTonnage, bo.getCarriedTonnage()); + lqw.eq(bo.getMissingPersons() != null, HotAccidentArchive::getMissingPersons, bo.getMissingPersons()); + lqw.eq(bo.getInjuredPersons() != null, HotAccidentArchive::getInjuredPersons, bo.getInjuredPersons()); + lqw.eq(StringUtils.isNotBlank(bo.getUnitResponsible()), HotAccidentArchive::getUnitResponsible, bo.getUnitResponsible()); + lqw.eq(StringUtils.isNotBlank(bo.getLeadResponsible()), HotAccidentArchive::getLeadResponsible, bo.getLeadResponsible()); + lqw.like(StringUtils.isNotBlank(bo.getReporterName()), HotAccidentArchive::getReporterName, bo.getReporterName()); + lqw.like(StringUtils.isNotBlank(bo.getReporterPhone()), HotAccidentArchive::getReporterPhone, bo.getReporterPhone()); + lqw.eq(StringUtils.isNotBlank(bo.getAccidentSituation()), HotAccidentArchive::getAccidentSituation, bo.getAccidentSituation()); + lqw.eq(StringUtils.isNotBlank(bo.getAccidentContent()), HotAccidentArchive::getAccidentContent, bo.getAccidentContent()); + lqw.eq(bo.getIsDeleted() != null, HotAccidentArchive::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增事故档案 + * + * @param bo 事故档案 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotAccidentArchiveBo bo) { + HotAccidentArchive add = MapstructUtils.convert(bo, HotAccidentArchive.class); + if (StringUtils.isBlank(add.getAccidentNo())) { + add.setAccidentNo("SG-" + System.currentTimeMillis() / 1000); + } + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + bo.setAccidentNo(add.getAccidentNo()); + } + return flag; + } + + /** + * 修改事故档案 + * + * @param bo 事故档案 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotAccidentArchiveBo bo) { + HotAccidentArchive update = MapstructUtils.convert(bo, HotAccidentArchive.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotAccidentArchive entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除事故档案信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/accidentReport/controller/HotAccidentReportController.java b/src/main/java/com/hotwj/platform/securityManagement/accidentReport/controller/HotAccidentReportController.java new file mode 100644 index 0000000..412d6d5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/accidentReport/controller/HotAccidentReportController.java @@ -0,0 +1,113 @@ +package com.hotwj.platform.securityManagement.accidentReport.controller; + +import com.hotwj.platform.securityManagement.accidentReport.domain.bo.HotAccidentReportBo; +import com.hotwj.platform.securityManagement.accidentReport.domain.vo.HotAccidentReportVo; +import com.hotwj.platform.securityManagement.accidentReport.service.IHotAccidentReportService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 事故报告 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/accidentReport") +@Tag(name = "事故报告", description = "事故报告管理") +public class HotAccidentReportController extends BaseController { + + private final IHotAccidentReportService hotAccidentReportService; + + /** + * 查询事故报告列表 + */ + //@SaCheckPermission("securityManagement:accidentReport:list") + @GetMapping("/list") + @Operation(summary = "分页查询事故报告列表") + public TableDataInfo list(HotAccidentReportBo bo, PageQuery pageQuery) { + return hotAccidentReportService.queryPageList(bo, pageQuery); + } + + /** + * 导出事故报告列表 + */ + //@SaCheckPermission("securityManagement:accidentReport:export") + @Log(title = "事故报告", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出事故报告列表") + public void export(HotAccidentReportBo bo, HttpServletResponse response) { + List list = hotAccidentReportService.queryList(bo); + ExcelUtil.exportExcel(list, "事故报告", HotAccidentReportVo.class, response); + } + + /** + * 获取事故报告详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:accidentReport:query") + @GetMapping("/{id}") + @Operation(summary = "获取事故报告详细信息") + public R getInfo(@NotNull(message = "主键不能为空") @Parameter(name = "id", description = "主键", required = true, example = "1") @PathVariable Long id) { + return R.ok(hotAccidentReportService.queryById(id)); + } + + /** + * 新增事故报告 + */ + //@SaCheckPermission("securityManagement:accidentReport:add") + @Log(title = "事故报告", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增事故报告") + public R add(@Validated(AddGroup.class) @RequestBody HotAccidentReportBo bo) { + return toAjax(hotAccidentReportService.insertByBo(bo)); + } + + /** + * 修改事故报告 + */ + //@SaCheckPermission("securityManagement:accidentReport:edit") + @Log(title = "事故报告", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改事故报告") + public R edit(@Validated(EditGroup.class) @RequestBody HotAccidentReportBo bo) { + return toAjax(hotAccidentReportService.updateByBo(bo)); + } + + /** + * 删除事故报告 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:accidentReport:remove") + @Log(title = "事故报告", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除事故报告") + public R remove(@NotEmpty(message = "主键不能为空") @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") @PathVariable Long[] ids) { + return toAjax(hotAccidentReportService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/accidentReport/domain/HotAccidentReport.java b/src/main/java/com/hotwj/platform/securityManagement/accidentReport/domain/HotAccidentReport.java new file mode 100644 index 0000000..ddaa8eb --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/accidentReport/domain/HotAccidentReport.java @@ -0,0 +1,59 @@ +package com.hotwj.platform.securityManagement.accidentReport.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 事故报告对象 hot_accident_report + * + * @author shihongwei + * @date 2026-01-06 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_accident_report") +public class HotAccidentReport extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 事故ID + */ + private Long accidentId; + + /** + * 报告名称 + */ + private String reportName; + + /** + * 附件URL + */ + private String attachmentUrls; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/accidentReport/domain/bo/HotAccidentReportBo.java b/src/main/java/com/hotwj/platform/securityManagement/accidentReport/domain/bo/HotAccidentReportBo.java new file mode 100644 index 0000000..2d08385 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/accidentReport/domain/bo/HotAccidentReportBo.java @@ -0,0 +1,60 @@ +package com.hotwj.platform.securityManagement.accidentReport.domain.bo; + +import com.hotwj.platform.securityManagement.accidentReport.domain.HotAccidentReport; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 事故报告业务对象 hot_accident_report + * + * @author shihongwei + * @date 2026-01-06 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotAccidentReport.class, reverseConvertGenerate = false) +public class HotAccidentReportBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 事故ID + */ + @NotNull(message = "事故ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long accidentId; + + /** + * 报告名称 + */ + @NotBlank(message = "报告名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String reportName; + + /** + * 附件URL + */ + @NotBlank(message = "附件URL不能为空", groups = {AddGroup.class, EditGroup.class}) + private String attachmentUrls; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/accidentReport/domain/vo/HotAccidentReportVo.java b/src/main/java/com/hotwj/platform/securityManagement/accidentReport/domain/vo/HotAccidentReportVo.java new file mode 100644 index 0000000..fcedc63 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/accidentReport/domain/vo/HotAccidentReportVo.java @@ -0,0 +1,71 @@ +package com.hotwj.platform.securityManagement.accidentReport.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.accidentReport.domain.HotAccidentReport; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 事故报告视图对象 hot_accident_report + * + * @author shihongwei + * @date 2026-01-06 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotAccidentReport.class) +public class HotAccidentReportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 事故ID + */ + @ExcelProperty(value = "事故ID") + private Long accidentId; + + /** + * 报告名称 + */ + @ExcelProperty(value = "报告名称") + private String reportName; + + /** + * 附件URL + */ + @ExcelProperty(value = "附件URL") + private String attachmentUrls; + + /** + * 创建时间 + */ + @ExcelProperty(value = "创建时间") + private Date createTime; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/accidentReport/mapper/HotAccidentReportMapper.java b/src/main/java/com/hotwj/platform/securityManagement/accidentReport/mapper/HotAccidentReportMapper.java new file mode 100644 index 0000000..5e5ef05 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/accidentReport/mapper/HotAccidentReportMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.accidentReport.mapper; + +import com.hotwj.platform.securityManagement.accidentReport.domain.HotAccidentReport; +import com.hotwj.platform.securityManagement.accidentReport.domain.vo.HotAccidentReportVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 事故报告Mapper接口 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Mapper +public interface HotAccidentReportMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/accidentReport/service/IHotAccidentReportService.java b/src/main/java/com/hotwj/platform/securityManagement/accidentReport/service/IHotAccidentReportService.java new file mode 100644 index 0000000..2ab728e --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/accidentReport/service/IHotAccidentReportService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.securityManagement.accidentReport.service; + +import com.hotwj.platform.securityManagement.accidentReport.domain.bo.HotAccidentReportBo; +import com.hotwj.platform.securityManagement.accidentReport.domain.vo.HotAccidentReportVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 事故报告Service接口 + * + * @author shihongwei + * @date 2026-01-06 + */ +public interface IHotAccidentReportService { + + /** + * 查询事故报告 + * + * @param id 主键 + * @return 事故报告 + */ + HotAccidentReportVo queryById(Long id); + + /** + * 分页查询事故报告列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 事故报告分页列表 + */ + TableDataInfo queryPageList(HotAccidentReportBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的事故报告列表 + * + * @param bo 查询条件 + * @return 事故报告列表 + */ + List queryList(HotAccidentReportBo bo); + + /** + * 新增事故报告 + * + * @param bo 事故报告 + * @return 是否新增成功 + */ + Boolean insertByBo(HotAccidentReportBo bo); + + /** + * 修改事故报告 + * + * @param bo 事故报告 + * @return 是否修改成功 + */ + Boolean updateByBo(HotAccidentReportBo bo); + + /** + * 校验并批量删除事故报告信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/accidentReport/service/impl/HotAccidentReportServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/accidentReport/service/impl/HotAccidentReportServiceImpl.java new file mode 100644 index 0000000..5bdc829 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/accidentReport/service/impl/HotAccidentReportServiceImpl.java @@ -0,0 +1,137 @@ +package com.hotwj.platform.securityManagement.accidentReport.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.securityManagement.accidentReport.domain.HotAccidentReport; +import com.hotwj.platform.securityManagement.accidentReport.domain.bo.HotAccidentReportBo; +import com.hotwj.platform.securityManagement.accidentReport.domain.vo.HotAccidentReportVo; +import com.hotwj.platform.securityManagement.accidentReport.mapper.HotAccidentReportMapper; +import com.hotwj.platform.securityManagement.accidentReport.service.IHotAccidentReportService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 事故报告Service业务层处理 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotAccidentReportServiceImpl implements IHotAccidentReportService { + + private final HotAccidentReportMapper baseMapper; + + /** + * 查询事故报告 + * + * @param id 主键 + * @return 事故报告 + */ + @Override + public HotAccidentReportVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询事故报告列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 事故报告分页列表 + */ + @Override + public TableDataInfo queryPageList(HotAccidentReportBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的事故报告列表 + * + * @param bo 查询条件 + * @return 事故报告列表 + */ + @Override + public List queryList(HotAccidentReportBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotAccidentReportBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotAccidentReport::getId); + lqw.eq(bo.getCompanyId() != null, HotAccidentReport::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getAccidentId() != null, HotAccidentReport::getAccidentId, bo.getAccidentId()); + lqw.like(StringUtils.isNotBlank(bo.getReportName()), HotAccidentReport::getReportName, bo.getReportName()); + lqw.eq(StringUtils.isNotBlank(bo.getAttachmentUrls()), HotAccidentReport::getAttachmentUrls, bo.getAttachmentUrls()); + lqw.eq(bo.getCreateTime() != null, HotAccidentReport::getCreateTime, bo.getCreateTime()); + lqw.eq(bo.getIsDeleted() != null, HotAccidentReport::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增事故报告 + * + * @param bo 事故报告 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotAccidentReportBo bo) { + HotAccidentReport add = MapstructUtils.convert(bo, HotAccidentReport.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改事故报告 + * + * @param bo 事故报告 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotAccidentReportBo bo) { + HotAccidentReport update = MapstructUtils.convert(bo, HotAccidentReport.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotAccidentReport entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除事故报告信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/controller/HotHiddenDangerController.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/controller/HotHiddenDangerController.java new file mode 100644 index 0000000..3044169 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/controller/HotHiddenDangerController.java @@ -0,0 +1,130 @@ +package com.hotwj.platform.securityManagement.hiddenDanger.controller; + +import com.hotwj.platform.securityManagement.hiddenDanger.domain.bo.HotHiddenDangerBo; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.vo.HotHiddenDangerVo; +import com.hotwj.platform.securityManagement.hiddenDanger.service.IHotHiddenDangerService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 隐患治理 + * + * @author shihongwei + * @date 2026-01-03 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/hiddenDanger") +@Tag(name = "隐患治理", description = "隐患治理管理") +public class HotHiddenDangerController extends BaseController { + + private final IHotHiddenDangerService hotHiddenDangerService; + + /** + * 查询隐患治理列表 + */ + //@SaCheckPermission("securityManagement:hiddenDanger:list") + @GetMapping("/list") + @Operation(summary = "分页查询隐患治理列表") + public TableDataInfo list(HotHiddenDangerBo bo, PageQuery pageQuery) { + return hotHiddenDangerService.queryPageList(bo, pageQuery); + } + + /** + * 导出隐患治理列表 + */ + //@SaCheckPermission("securityManagement:hiddenDanger:export") + @Log(title = "隐患治理", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出隐患治理列表") + public void export(HotHiddenDangerBo bo, HttpServletResponse response) { + List list = hotHiddenDangerService.queryList(bo); + ExcelUtil.exportExcel(list, "隐患治理", HotHiddenDangerVo.class, response); + } + + /** + * 获取隐患治理详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:hiddenDanger:query") + @GetMapping("/{id}") + @Operation(summary = "获取隐患治理详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotHiddenDangerService.queryById(id)); + } + + /** + * 新增隐患治理 + */ +// //@SaCheckPermission("securityManagement:hiddenDanger:add") + @Log(title = "隐患治理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增隐患治理") + public R add(@Validated(AddGroup.class) @RequestBody HotHiddenDangerBo bo) { + return toAjax(hotHiddenDangerService.insertByBo(bo)); + } + + /** + * 修改隐患治理 + */ +// //@SaCheckPermission("securityManagement:hiddenDanger:edit") + @Log(title = "隐患治理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改隐患治理") + public R edit(@Validated(EditGroup.class) @RequestBody HotHiddenDangerBo bo) { + return toAjax(hotHiddenDangerService.updateByBo(bo)); + } + + /** + * 审核隐患治理 + */ + // //@SaCheckPermission("securityManagement:hiddenDanger:audit") + @Log(title = "隐患治理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/audit") + @Operation(summary = "审核隐患治理") + public R audit(@RequestBody HotHiddenDangerBo bo) { + hotHiddenDangerService.audit(bo); + return R.ok(); + } + + /** + * 删除隐患治理 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:hiddenDanger:remove") + @Log(title = "隐患治理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除隐患治理") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotHiddenDangerService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/controller/HotHiddenDangerReviewRecordController.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/controller/HotHiddenDangerReviewRecordController.java new file mode 100644 index 0000000..1bdd183 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/controller/HotHiddenDangerReviewRecordController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.securityManagement.hiddenDanger.controller; + +import com.hotwj.platform.securityManagement.hiddenDanger.domain.bo.HotHiddenDangerReviewRecordBo; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.vo.HotHiddenDangerReviewRecordVo; +import com.hotwj.platform.securityManagement.hiddenDanger.service.IHotHiddenDangerReviewRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 隐患复查记录 + * + * @author shihongwei + * @date 2026-01-04 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/hiddenDangerReviewRecord") +@Tag(name = "隐患复查记录", description = "隐患复查记录管理") +public class HotHiddenDangerReviewRecordController extends BaseController { + + private final IHotHiddenDangerReviewRecordService reviewRecordService; + + /** + * 查询隐患复查记录列表 + */ + //@SaCheckPermission("securityManagement:hiddenDangerReviewRecord:list") + @GetMapping("/list") + @Operation(summary = "分页查询隐患复查记录列表") + public TableDataInfo list(HotHiddenDangerReviewRecordBo bo, PageQuery pageQuery) { + return reviewRecordService.queryPageList(bo, pageQuery); + } + + /** + * 导出隐患复查记录列表 + */ + //@SaCheckPermission("securityManagement:hiddenDangerReviewRecord:export") + @Log(title = "隐患复查记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出隐患复查记录列表") + public void export(HotHiddenDangerReviewRecordBo bo, HttpServletResponse response) { + List list = reviewRecordService.queryList(bo); + ExcelUtil.exportExcel(list, "隐患复查记录", HotHiddenDangerReviewRecordVo.class, response); + } + + /** + * 获取隐患复查记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:hiddenDangerReviewRecord:query") + @GetMapping("/{id}") + @Operation(summary = "获取隐患复查记录详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(reviewRecordService.queryById(id)); + } + + /** + * 新增隐患复查记录 + */ + //@SaCheckPermission("securityManagement:hiddenDangerReviewRecord:add") + @Log(title = "隐患复查记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增隐患复查记录") + public R add(@Validated(AddGroup.class) @RequestBody HotHiddenDangerReviewRecordBo bo) { + return toAjax(reviewRecordService.insertByBo(bo)); + } + + /** + * 修改隐患复查记录 + */ + //@SaCheckPermission("securityManagement:hiddenDangerReviewRecord:edit") + @Log(title = "隐患复查记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改隐患复查记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotHiddenDangerReviewRecordBo bo) { + return toAjax(reviewRecordService.updateByBo(bo)); + } + + /** + * 删除隐患复查记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:hiddenDangerReviewRecord:remove") + @Log(title = "隐患复查记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除隐患复查记录") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(reviewRecordService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/HotHiddenDanger.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/HotHiddenDanger.java new file mode 100644 index 0000000..e3e7831 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/HotHiddenDanger.java @@ -0,0 +1,258 @@ +package com.hotwj.platform.securityManagement.hiddenDanger.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 隐患治理对象 hot_hidden_danger + * + * @author shihongwei + * @date 2026-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_hidden_danger") +public class HotHiddenDanger extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 隐患编号 + */ + private String dangerNo; + + /** + * 隐患简称 + */ + private String dangerName; + + /** + * 隐患来源类型:1=公司 2=车辆 3=人员 + */ + private Long sourceType; + + /** + * 来源对象名称(如车牌号、姓名、企业名称) + */ + private String sourceName; + + /** + * 数据来源:1.隐患排查 2.车辆三检 + */ + private Long dataSource; + + /** + * 来源对象ID(关联隐患项目id或者车辆三检id) + */ + private String sourceId; + + /** + * 隐患描述 + */ + private String description; + + /** + * 排查日期/审核时间 + */ + private Date checkTime; + + /** + * 排查人/审核员ID + */ + private String checkerId; + + /** + * 排查人/审核员姓名 + */ + private String checkerName; + + /** + * 隐患附件/二维码URL(逗号分隔) + */ + private String evidenceUrls; + + /** + * 评估人签字 + */ + private String evaluatorSign; + + /** + * 评估人id + */ + private Long evaluatorId; + + /** + * 评估人姓名 + */ + private String evaluatorName; + + /** + * 评估时间 + */ + private Date evalTime; + + /** + * 风险评估(可能性) + */ + private String riskAssessLike; + + /** + * 风险评估(严重程度) + */ + private String riskAssessDegree; + + /** + * 风险评估项目 + */ + private String riskProject; + + /** + * 风险评估等级 + */ + private String riskLevel; + + /** + * 可能导致的后果 + */ + private Long likeResult; + + /** + * 评估过程 + */ + private String evalProcess; + + /** + * 治理要求(可行性及要求) + */ + private String governRequire; + + /** + * 治理人id + */ + private Long governPersonId; + + /** + * 治理人姓名 + */ + private String governPersonName; + + /** + * 治理期限(完成时间) + */ + private Date governDeadline; + + /** + * 资金来源 + */ + private String fundSource; + + /** + * 治理责任人签字 + */ + private String governPersonSign; + + /** + * 治理方案 + */ + private String governPlan; + + /** + * 治理结果 + */ + private String governResult; + + /** + * 治理相关附件URL + */ + private String governAttachUrls; + + /** + * 治理照片URL(治理前/后) + */ + private String governPhotoUrls; + + /** + * 复查人id + */ + private Long reviewerId; + + /** + * 复查人姓名 + */ + private String reviewerName; + + /** + * 复查人签字 + */ + private String reviewSign; + + /** + * 复查日期 + */ + private Date reviewTime; + + /** + * 复查是否合格:1=合格 0=不合格 + */ + private Long isQualified; + + /** + * 复查结论 + */ + private String reviewConclusion; + + /** + * 状态:0=评估 1=治理 2=复查 3=完结 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 治理人签字时间 + */ + private String governPersonSignTime; + + /** + * 复查人签字时间 + */ + private String reviewSignTime; + + /** + * 流程实例ID + */ + private String instanceId; + + /** + * 流程状态 + */ + private String flowStatus; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/HotHiddenDangerReviewRecord.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/HotHiddenDangerReviewRecord.java new file mode 100644 index 0000000..da845de --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/HotHiddenDangerReviewRecord.java @@ -0,0 +1,74 @@ +package com.hotwj.platform.securityManagement.hiddenDanger.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 隐患复查记录对象 hot_hidden_danger_review_record + * + * @author shihongwei + * @date 2026-01-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_hidden_danger_review_record") +public class HotHiddenDangerReviewRecord extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 隐患ID + */ + private Long hiddenDangerId; + + /** + * 复查人ID + */ + private Long reviewerId; + + /** + * 复查人姓名 + */ + private String reviewerName; + + /** + * 复查时间 + */ + private Date reviewTime; + + /** + * 复查结论 + */ + private String reviewConclusion; + + /** + * 是否合格:1=合格 0=不合格 + */ + private Long isQualified; + + /** + * 复查人签字 + */ + private String reviewSign; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/bo/HotHiddenDangerBo.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/bo/HotHiddenDangerBo.java new file mode 100644 index 0000000..ef3ce28 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/bo/HotHiddenDangerBo.java @@ -0,0 +1,261 @@ +package com.hotwj.platform.securityManagement.hiddenDanger.domain.bo; + +import com.hotwj.platform.securityManagement.hiddenDanger.domain.HotHiddenDanger; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 隐患治理业务对象 hot_hidden_danger + * + * @author shihongwei + * @date 2026-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotHiddenDanger.class, reverseConvertGenerate = false) +public class HotHiddenDangerBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 隐患编号 + */ + private String dangerNo; + + /** + * 隐患简称 + */ + private String dangerName; + + /** + * 隐患来源类型:1=公司 2=车辆 3=人员 + */ + private Long sourceType; + + /** + * 来源对象名称(如车牌号、姓名、企业名称) + */ + private String sourceName; + + /** + * 数据来源:1.隐患排查 2.车辆三检 + */ + private Long dataSource; + + /** + * 来源对象ID(关联隐患项目id或者车辆三检id) + */ + private String sourceId; + + /** + * 隐患描述 + */ + private String description; + + /** + * 排查日期/审核时间 + */ + private Date checkTime; + + /** + * 排查人/审核员ID + */ + private String checkerId; + + /** + * 排查人/审核员姓名 + */ + private String checkerName; + + /** + * 隐患附件/二维码URL(逗号分隔) + */ + private String evidenceUrls; + + /** + * 评估人签字 + */ + private String evaluatorSign; + + /** + * 评估人id + */ + private Long evaluatorId; + + /** + * 评估人姓名 + */ + private String evaluatorName; + + /** + * 评估时间 + */ + private Date evalTime; + + /** + * 风险评估(可能性) + */ + private String riskAssessLike; + + /** + * 风险评估(严重程度) + */ + private String riskAssessDegree; + + /** + * 风险评估项目 + */ + private String riskProject; + + /** + * 风险评估等级 + */ + private String riskLevel; + + /** + * 可能导致的后果 + */ + private Long likeResult; + + /** + * 评估过程 + */ + private String evalProcess; + + /** + * 治理要求(可行性及要求) + */ + private String governRequire; + + /** + * 治理人id + */ + private Long governPersonId; + + /** + * 治理人姓名 + */ + private String governPersonName; + + /** + * 治理期限(完成时间) + */ + private Date governDeadline; + + /** + * 资金来源 + */ + private String fundSource; + + /** + * 治理责任人签字 + */ + private String governPersonSign; + + /** + * 治理方案 + */ + private String governPlan; + + /** + * 治理结果 + */ + private String governResult; + + /** + * 治理相关附件URL + */ + private String governAttachUrls; + + /** + * 治理照片URL(治理前/后) + */ + private String governPhotoUrls; + + /** + * 复查人id + */ + private Long reviewerId; + + /** + * 复查人姓名 + */ + private String reviewerName; + + /** + * 复查人签字 + */ + private String reviewSign; + + /** + * 复查日期 + */ + private Date reviewTime; + + /** + * 复查是否合格:1=合格 0=不合格 + */ + private Long isQualified; + + /** + * 复查结论 + */ + private String reviewConclusion; + + /** + * 状态:0=评估 1=治理 2=复查 3=完结 + */ + private Long status; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + /** + * 治理人签字时间 + */ + private String governPersonSignTime; + + /** + * 复查人签字时间 + */ + private String reviewSignTime; + + /** + * 流程任务ID + */ + private String taskId; + + /** + * 流程实例ID + */ + private String instanceId; + + /** + * 流程状态 + */ + private String flowStatus; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/bo/HotHiddenDangerReviewRecordBo.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/bo/HotHiddenDangerReviewRecordBo.java new file mode 100644 index 0000000..1adff3d --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/bo/HotHiddenDangerReviewRecordBo.java @@ -0,0 +1,67 @@ +package com.hotwj.platform.securityManagement.hiddenDanger.domain.bo; + +import com.hotwj.platform.securityManagement.hiddenDanger.domain.HotHiddenDangerReviewRecord; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 隐患复查记录业务对象 hot_hidden_danger_review_record + * + * @author shihongwei + * @date 2026-01-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotHiddenDangerReviewRecord.class, reverseConvertGenerate = false) +public class HotHiddenDangerReviewRecordBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 隐患ID + */ + @NotNull(message = "隐患ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long hiddenDangerId; + + /** + * 复查人ID + */ + private Long reviewerId; + + /** + * 复查人姓名 + */ + private String reviewerName; + + /** + * 复查时间 + */ + private Date reviewTime; + + /** + * 复查结论 + */ + private String reviewConclusion; + + /** + * 是否合格:1=合格 0=不合格 + */ + private Long isQualified; + + /** + * 复查人签字 + */ + private String reviewSign; + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/vo/HotHiddenDangerReviewRecordVo.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/vo/HotHiddenDangerReviewRecordVo.java new file mode 100644 index 0000000..55da4ee --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/vo/HotHiddenDangerReviewRecordVo.java @@ -0,0 +1,65 @@ +package com.hotwj.platform.securityManagement.hiddenDanger.domain.vo; + +import com.hotwj.platform.securityManagement.hiddenDanger.domain.HotHiddenDangerReviewRecord; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 隐患复查记录视图对象 hot_hidden_danger_review_record + * + * @author shihongwei + * @date 2026-01-04 + */ +@Data +@AutoMapper(target = HotHiddenDangerReviewRecord.class) +public class HotHiddenDangerReviewRecordVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long id; + + /** + * 隐患ID + */ + private Long hiddenDangerId; + + /** + * 复查人ID + */ + private Long reviewerId; + + /** + * 复查人姓名 + */ + private String reviewerName; + + /** + * 复查时间 + */ + private Date reviewTime; + + /** + * 复查结论 + */ + private String reviewConclusion; + + /** + * 是否合格:1=合格 0=不合格 + */ + private Long isQualified; + + /** + * 复查人签字 + */ + private String reviewSign; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/vo/HotHiddenDangerVo.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/vo/HotHiddenDangerVo.java new file mode 100644 index 0000000..8e7112f --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/domain/vo/HotHiddenDangerVo.java @@ -0,0 +1,321 @@ +package com.hotwj.platform.securityManagement.hiddenDanger.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.HotHiddenDanger; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 隐患治理视图对象 hot_hidden_danger + * + * @author shihongwei + * @date 2026-01-03 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotHiddenDanger.class) +public class HotHiddenDangerVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 隐患编号 + */ + @ExcelProperty(value = "隐患编号") + private String dangerNo; + + /** + * 隐患简称 + */ + @ExcelProperty(value = "隐患简称") + private String dangerName; + + /** + * 隐患来源类型:1=公司 2=车辆 3=人员 + */ + @ExcelProperty(value = "隐患来源类型:1=公司 2=车辆 3=人员") + private Long sourceType; + + /** + * 来源对象名称(如车牌号、姓名、企业名称) + */ + @ExcelProperty(value = "来源对象名称", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "如=车牌号、姓名、企业名称") + private String sourceName; + + /** + * 数据来源:1.隐患排查 2.车辆三检 + */ + @ExcelProperty(value = "数据来源:1.隐患排查 2.车辆三检") + private Long dataSource; + + /** + * 来源对象ID(关联隐患项目id或者车辆三检id) + */ + @ExcelProperty(value = "来源对象ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "关=联隐患项目id或者车辆三检id") + private String sourceId; + + /** + * 隐患描述 + */ + @ExcelProperty(value = "隐患描述") + private String description; + + /** + * 排查日期/审核时间 + */ + @ExcelProperty(value = "排查日期/审核时间") + private Date checkTime; + + /** + * 排查人/审核员ID + */ + private String checkerId; + + /** + * 排查人/审核员姓名 + */ + @ExcelProperty(value = "排查人/审核员姓名") + private String checkerName; + + /** + * 隐患附件/二维码URL(逗号分隔) + */ + @ExcelProperty(value = "隐患附件/二维码URL", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逗=号分隔") + private String evidenceUrls; + + /** + * 评估人签字 + */ + @ExcelProperty(value = "评估人签字 ") + private String evaluatorSign; + + /** + * 评估人id + */ + @ExcelProperty(value = "评估人id") + private Long evaluatorId; + + /** + * 评估人姓名 + */ + @ExcelProperty(value = "评估人姓名") + private String evaluatorName; + + /** + * 评估时间 + */ + @ExcelProperty(value = "评估时间") + private Date evalTime; + + /** + * 风险评估(可能性) + */ + @ExcelProperty(value = "风险评估", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "可=能性") + private String riskAssessLike; + + /** + * 风险评估(严重程度) + */ + @ExcelProperty(value = "风险评估", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "严=重程度") + private String riskAssessDegree; + + /** + * 风险评估项目 + */ + @ExcelProperty(value = "风险评估项目") + private String riskProject; + + /** + * 风险评估等级 + */ + @ExcelProperty(value = "风险评估等级") + private String riskLevel; + + /** + * 可能导致的后果 + */ + @ExcelProperty(value = "可能导致的后果") + private Long likeResult; + + /** + * 评估过程 + */ + @ExcelProperty(value = "评估过程") + private String evalProcess; + + /** + * 治理要求(可行性及要求) + */ + @ExcelProperty(value = "治理要求", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "可=行性及要求") + private String governRequire; + + /** + * 治理人id + */ + @ExcelProperty(value = "治理人id") + private Long governPersonId; + + /** + * 治理人姓名 + */ + @ExcelProperty(value = "治理人姓名") + private String governPersonName; + + /** + * 治理期限(完成时间) + */ + @ExcelProperty(value = "治理期限", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "完=成时间") + private Date governDeadline; + + /** + * 资金来源 + */ + @ExcelProperty(value = "资金来源") + private String fundSource; + + /** + * 治理责任人签字 + */ + @ExcelProperty(value = "治理责任人签字") + private String governPersonSign; + + /** + * 治理方案 + */ + @ExcelProperty(value = "治理方案") + private String governPlan; + + /** + * 治理结果 + */ + @ExcelProperty(value = "治理结果") + private String governResult; + + /** + * 治理相关附件URL + */ + @ExcelProperty(value = "治理相关附件URL") + private String governAttachUrls; + + /** + * 治理照片URL(治理前/后) + */ + @ExcelProperty(value = "治理照片URL", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "治=理前/后") + private String governPhotoUrls; + + /** + * 复查人id + */ + @ExcelProperty(value = "复查人id") + private Long reviewerId; + + /** + * 复查人姓名 + */ + @ExcelProperty(value = "复查人姓名") + private String reviewerName; + + /** + * 复查人签字 + */ + @ExcelProperty(value = "复查人签字") + private String reviewSign; + + /** + * 复查日期 + */ + @ExcelProperty(value = "复查日期") + private Date reviewTime; + + /** + * 复查是否合格:1=合格 0=不合格 + */ + @ExcelProperty(value = "复查是否合格:1=合格 0=不合格") + private Long isQualified; + + /** + * 复查结论 + */ + @ExcelProperty(value = "复查结论") + private String reviewConclusion; + + /** + * 状态:0=评估 1=治理 2=复查 3=完结 + */ + @ExcelProperty(value = "状态:0=评估 1=治理 2=复查 3=完结") + private Long status; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 治理人签字时间 + */ + @ExcelProperty(value = "治理人签字时间") + private String governPersonSignTime; + + /** + * 复查人签字时间 + */ + @ExcelProperty(value = "复查人签字时间") + private String reviewSignTime; + + /** + * 流程实例ID + */ + @ExcelProperty(value = "流程实例ID") + private String instanceId; + + /** + * 流程状态 + */ + @ExcelProperty(value = "流程状态") + private String flowStatus; + + /** + * 流程任务ID + */ + @ExcelProperty(value = "流程任务ID") + private String taskId; + + private String currentApproverId; + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/mapper/HotHiddenDangerMapper.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/mapper/HotHiddenDangerMapper.java new file mode 100644 index 0000000..d2732be --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/mapper/HotHiddenDangerMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.hiddenDanger.mapper; + +import com.hotwj.platform.securityManagement.hiddenDanger.domain.HotHiddenDanger; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.vo.HotHiddenDangerVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 隐患治理Mapper接口 + * + * @author shihongwei + * @date 2026-01-03 + */ +@Mapper +public interface HotHiddenDangerMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/mapper/HotHiddenDangerReviewRecordMapper.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/mapper/HotHiddenDangerReviewRecordMapper.java new file mode 100644 index 0000000..79e657d --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/mapper/HotHiddenDangerReviewRecordMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.hiddenDanger.mapper; + +import com.hotwj.platform.securityManagement.hiddenDanger.domain.HotHiddenDangerReviewRecord; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.vo.HotHiddenDangerReviewRecordVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 隐患复查记录Mapper接口 + * + * @author shihongwei + * @date 2026-01-04 + */ +@Mapper +public interface HotHiddenDangerReviewRecordMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/service/IHotHiddenDangerReviewRecordService.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/service/IHotHiddenDangerReviewRecordService.java new file mode 100644 index 0000000..176d024 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/service/IHotHiddenDangerReviewRecordService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.securityManagement.hiddenDanger.service; + +import com.hotwj.platform.securityManagement.hiddenDanger.domain.bo.HotHiddenDangerReviewRecordBo; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.vo.HotHiddenDangerReviewRecordVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 隐患复查记录Service接口 + * + * @author shihongwei + * @date 2026-01-04 + */ +public interface IHotHiddenDangerReviewRecordService { + + /** + * 查询隐患复查记录 + * + * @param id 主键 + * @return 隐患复查记录 + */ + HotHiddenDangerReviewRecordVo queryById(Long id); + + /** + * 分页查询隐患复查记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 隐患复查记录分页列表 + */ + TableDataInfo queryPageList(HotHiddenDangerReviewRecordBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的隐患复查记录列表 + * + * @param bo 查询条件 + * @return 隐患复查记录列表 + */ + List queryList(HotHiddenDangerReviewRecordBo bo); + + /** + * 新增隐患复查记录 + * + * @param bo 隐患复查记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotHiddenDangerReviewRecordBo bo); + + /** + * 修改隐患复查记录 + * + * @param bo 隐患复查记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotHiddenDangerReviewRecordBo bo); + + /** + * 校验并批量删除隐患复查记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/service/IHotHiddenDangerService.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/service/IHotHiddenDangerService.java new file mode 100644 index 0000000..64fea78 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/service/IHotHiddenDangerService.java @@ -0,0 +1,75 @@ +package com.hotwj.platform.securityManagement.hiddenDanger.service; + +import com.hotwj.platform.securityManagement.hiddenDanger.domain.bo.HotHiddenDangerBo; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.vo.HotHiddenDangerVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 隐患治理Service接口 + * + * @author shihongwei + * @date 2026-01-03 + */ +public interface IHotHiddenDangerService { + + /** + * 查询隐患治理 + * + * @param id 主键 + * @return 隐患治理 + */ + HotHiddenDangerVo queryById(Long id); + + /** + * 分页查询隐患治理列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 隐患治理分页列表 + */ + TableDataInfo queryPageList(HotHiddenDangerBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的隐患治理列表 + * + * @param bo 查询条件 + * @return 隐患治理列表 + */ + List queryList(HotHiddenDangerBo bo); + + /** + * 新增隐患治理 + * + * @param bo 隐患治理 + * @return 是否新增成功 + */ + Boolean insertByBo(HotHiddenDangerBo bo); + + /** + * 修改隐患治理 + * + * @param bo 隐患治理 + * @return 是否修改成功 + */ + Boolean updateByBo(HotHiddenDangerBo bo); + + /** + * 审核隐患治理 + * + * @param bo 隐患治理 + */ + void audit(HotHiddenDangerBo bo); + + /** + * 校验并批量删除隐患治理信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/service/impl/HotHiddenDangerReviewRecordServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/service/impl/HotHiddenDangerReviewRecordServiceImpl.java new file mode 100644 index 0000000..e173eea --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/service/impl/HotHiddenDangerReviewRecordServiceImpl.java @@ -0,0 +1,132 @@ +package com.hotwj.platform.securityManagement.hiddenDanger.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.HotHiddenDangerReviewRecord; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.bo.HotHiddenDangerReviewRecordBo; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.vo.HotHiddenDangerReviewRecordVo; +import com.hotwj.platform.securityManagement.hiddenDanger.mapper.HotHiddenDangerReviewRecordMapper; +import com.hotwj.platform.securityManagement.hiddenDanger.service.IHotHiddenDangerReviewRecordService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; + +/** + * 隐患复查记录Service业务层处理 + * + * @author shihongwei + * @date 2026-01-04 + */ +@RequiredArgsConstructor +@Service +public class HotHiddenDangerReviewRecordServiceImpl implements IHotHiddenDangerReviewRecordService { + + private final HotHiddenDangerReviewRecordMapper baseMapper; + + /** + * 查询隐患复查记录 + * + * @param id 主键 + * @return 隐患复查记录 + */ + @Override + public HotHiddenDangerReviewRecordVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询隐患复查记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 隐患复查记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotHiddenDangerReviewRecordBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的隐患复查记录列表 + * + * @param bo 查询条件 + * @return 隐患复查记录列表 + */ + @Override + public List queryList(HotHiddenDangerReviewRecordBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotHiddenDangerReviewRecordBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(bo.getHiddenDangerId() != null, HotHiddenDangerReviewRecord::getHiddenDangerId, bo.getHiddenDangerId()); + lqw.eq(bo.getReviewerId() != null, HotHiddenDangerReviewRecord::getReviewerId, bo.getReviewerId()); + lqw.like(StringUtils.isNotBlank(bo.getReviewerName()), HotHiddenDangerReviewRecord::getReviewerName, bo.getReviewerName()); + lqw.eq(bo.getReviewTime() != null, HotHiddenDangerReviewRecord::getReviewTime, bo.getReviewTime()); + lqw.like(StringUtils.isNotBlank(bo.getReviewConclusion()), HotHiddenDangerReviewRecord::getReviewConclusion, bo.getReviewConclusion()); + lqw.eq(bo.getIsQualified() != null, HotHiddenDangerReviewRecord::getIsQualified, bo.getIsQualified()); + return lqw; + } + + /** + * 新增隐患复查记录 + * + * @param bo 隐患复查记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotHiddenDangerReviewRecordBo bo) { + HotHiddenDangerReviewRecord add = MapstructUtils.convert(bo, HotHiddenDangerReviewRecord.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改隐患复查记录 + * + * @param bo 隐患复查记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotHiddenDangerReviewRecordBo bo) { + HotHiddenDangerReviewRecord update = MapstructUtils.convert(bo, HotHiddenDangerReviewRecord.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotHiddenDangerReviewRecord entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除隐患复查记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/service/impl/HotHiddenDangerServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/service/impl/HotHiddenDangerServiceImpl.java new file mode 100644 index 0000000..a4e03e3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDanger/service/impl/HotHiddenDangerServiceImpl.java @@ -0,0 +1,328 @@ +package com.hotwj.platform.securityManagement.hiddenDanger.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.flow.service.ISysFlowService; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.HotHiddenDanger; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.bo.HotHiddenDangerBo; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.bo.HotHiddenDangerReviewRecordBo; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.vo.HotHiddenDangerVo; +import com.hotwj.platform.securityManagement.hiddenDanger.mapper.HotHiddenDangerMapper; +import com.hotwj.platform.securityManagement.hiddenDanger.service.IHotHiddenDangerReviewRecordService; +import com.hotwj.platform.securityManagement.hiddenDanger.service.IHotHiddenDangerService; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.HotHiddenDangerInspection; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.mapper.HotHiddenDangerInspectionMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * 隐患治理Service业务层处理 + * + * @author shihongwei + * @date 2026-01-03 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotHiddenDangerServiceImpl implements IHotHiddenDangerService { + + private final HotHiddenDangerMapper baseMapper; + private final HotHiddenDangerInspectionMapper dangerInspectionMapper; + private final IHotHiddenDangerReviewRecordService reviewRecordService; + private final ISysFlowService flowService; + + /** + * 查询隐患治理 + * + * @param id 主键 + * @return 隐患治理 + */ + @Override + public HotHiddenDangerVo queryById(Long id) { + HotHiddenDangerVo vo = baseMapper.selectVoById(id); + if (vo != null && StringUtils.isNotBlank(vo.getInstanceId())) { + try { + String userId = String.valueOf(LoginHelper.getBusinessUserId()); + vo.setTaskId(flowService.getTaskId(vo.getInstanceId(), userId)); + List approverIds = flowService.getApproverIdsByInstance(vo.getInstanceId()); + if (approverIds != null && !approverIds.isEmpty()) { + vo.setCurrentApproverId(Optional.of(approverIds).orElse(new ArrayList<>()).stream().findFirst().orElse(null)); + } + } catch (Exception e) { + // ignore + } + } + return vo; + } + + /** + * 分页查询隐患治理列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 隐患治理分页列表 + */ + @Override + public TableDataInfo queryPageList(HotHiddenDangerBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + + String userId = null; + try { + userId = String.valueOf(LoginHelper.getBusinessUserId()); + } catch (Exception e) { + // ignore + } + + if (userId != null && result.getRecords() != null) { + for (HotHiddenDangerVo vo : result.getRecords()) { + if (StringUtils.isNotBlank(vo.getInstanceId())) { + vo.setTaskId(flowService.getTaskId(vo.getInstanceId(), userId)); + } + } + } + + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的隐患治理列表 + * + * @param bo 查询条件 + * @return 隐患治理列表 + */ + @Override + public List queryList(HotHiddenDangerBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotHiddenDangerBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + Object beginTime = params == null ? null : params.get("beginTime"); + Object endTime = params == null ? null : params.get("endTime"); + boolean hasBeginTime = beginTime != null && StringUtils.isNotBlank(String.valueOf(beginTime)) && !"null".equalsIgnoreCase(String.valueOf(beginTime)); + boolean hasEndTime = endTime != null && StringUtils.isNotBlank(String.valueOf(endTime)) && !"null".equalsIgnoreCase(String.valueOf(endTime)); + if (hasBeginTime && hasEndTime) { + lqw.between(HotHiddenDanger::getCheckTime, beginTime, endTime); + } else if (hasBeginTime) { + lqw.ge(HotHiddenDanger::getCheckTime, beginTime); + } else if (hasEndTime) { + lqw.le(HotHiddenDanger::getCheckTime, endTime); + } + lqw.orderByDesc(HotHiddenDanger::getCreateTime); + lqw.eq(bo.getCompanyId() != null, HotHiddenDanger::getCompanyId, bo.getCompanyId()); + lqw.eq(StringUtils.isNotBlank(bo.getDangerNo()), HotHiddenDanger::getDangerNo, bo.getDangerNo()); + lqw.like(StringUtils.isNotBlank(bo.getDangerName()), HotHiddenDanger::getDangerName, bo.getDangerName()); + lqw.eq(bo.getSourceType() != null, HotHiddenDanger::getSourceType, bo.getSourceType()); + lqw.like(StringUtils.isNotBlank(bo.getSourceName()), HotHiddenDanger::getSourceName, bo.getSourceName()); + lqw.eq(StringUtils.isNotBlank(bo.getSourceId()), HotHiddenDanger::getSourceId, bo.getSourceId()); + lqw.eq(StringUtils.isNotBlank(bo.getDescription()), HotHiddenDanger::getDescription, bo.getDescription()); + lqw.eq(!hasBeginTime && !hasEndTime && bo.getCheckTime() != null, HotHiddenDanger::getCheckTime, bo.getCheckTime()); + lqw.like(StringUtils.isNotBlank(bo.getCheckerName()), HotHiddenDanger::getCheckerName, bo.getCheckerName()); + lqw.eq(StringUtils.isNotBlank(bo.getEvidenceUrls()), HotHiddenDanger::getEvidenceUrls, bo.getEvidenceUrls()); + lqw.like(StringUtils.isNotBlank(bo.getEvaluatorName()), HotHiddenDanger::getEvaluatorName, bo.getEvaluatorName()); + lqw.eq(bo.getEvalTime() != null, HotHiddenDanger::getEvalTime, bo.getEvalTime()); + lqw.eq(StringUtils.isNotBlank(bo.getRiskProject()), HotHiddenDanger::getRiskProject, bo.getRiskProject()); + lqw.eq(StringUtils.isNotBlank(bo.getRiskLevel()), HotHiddenDanger::getRiskLevel, bo.getRiskLevel()); + lqw.eq(StringUtils.isNotBlank(bo.getEvalProcess()), HotHiddenDanger::getEvalProcess, bo.getEvalProcess()); + lqw.eq(StringUtils.isNotBlank(bo.getGovernRequire()), HotHiddenDanger::getGovernRequire, bo.getGovernRequire()); + lqw.like(StringUtils.isNotBlank(bo.getGovernPersonName()), HotHiddenDanger::getGovernPersonName, bo.getGovernPersonName()); + lqw.eq(bo.getGovernDeadline() != null, HotHiddenDanger::getGovernDeadline, bo.getGovernDeadline()); + lqw.eq(StringUtils.isNotBlank(bo.getFundSource()), HotHiddenDanger::getFundSource, bo.getFundSource()); + lqw.eq(StringUtils.isNotBlank(bo.getGovernPlan()), HotHiddenDanger::getGovernPlan, bo.getGovernPlan()); + lqw.eq(StringUtils.isNotBlank(bo.getGovernResult()), HotHiddenDanger::getGovernResult, bo.getGovernResult()); + lqw.eq(StringUtils.isNotBlank(bo.getGovernAttachUrls()), HotHiddenDanger::getGovernAttachUrls, bo.getGovernAttachUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getGovernPhotoUrls()), HotHiddenDanger::getGovernPhotoUrls, bo.getGovernPhotoUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getGovernPersonSignTime()), HotHiddenDanger::getGovernPersonSignTime, bo.getGovernPersonSignTime()); + lqw.like(StringUtils.isNotBlank(bo.getReviewerName()), HotHiddenDanger::getReviewerName, bo.getReviewerName()); + lqw.eq(bo.getReviewTime() != null, HotHiddenDanger::getReviewTime, bo.getReviewTime()); + lqw.eq(StringUtils.isNotBlank(bo.getReviewSignTime()), HotHiddenDanger::getReviewSignTime, bo.getReviewSignTime()); + lqw.eq(bo.getIsQualified() != null, HotHiddenDanger::getIsQualified, bo.getIsQualified()); + lqw.eq(StringUtils.isNotBlank(bo.getReviewConclusion()), HotHiddenDanger::getReviewConclusion, bo.getReviewConclusion()); + lqw.eq(bo.getStatus() != null, HotHiddenDanger::getStatus, bo.getStatus()); + lqw.eq(bo.getIsDeleted() != null, HotHiddenDanger::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增隐患治理 + * + * @param bo 隐患治理 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotHiddenDangerBo bo) { + HotHiddenDanger add = MapstructUtils.convert(bo, HotHiddenDanger.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改隐患治理 + * + * @param bo 隐患治理 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotHiddenDangerBo bo) { + HotHiddenDanger update = MapstructUtils.convert(bo, HotHiddenDanger.class); + validEntityBeforeSave(update); + + // 如果复查不合格,新增复查记录 + if (bo.getIsQualified() != null && bo.getIsQualified() == 0) { + HotHiddenDangerReviewRecordBo recordBo = new HotHiddenDangerReviewRecordBo(); + recordBo.setHiddenDangerId(bo.getId()); + recordBo.setReviewerId(bo.getReviewerId()); + recordBo.setReviewerName(bo.getReviewerName()); + recordBo.setReviewTime(bo.getReviewTime()); + recordBo.setReviewConclusion(bo.getReviewConclusion()); + recordBo.setIsQualified(bo.getIsQualified()); + recordBo.setReviewSign(bo.getReviewSign()); + reviewRecordService.insertByBo(recordBo); + } + + return baseMapper.updateById(update) > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void audit(HotHiddenDangerBo bo) { + // 1. 参数校验 + if (StringUtils.isBlank(bo.getTaskId())) { + throw new ServiceException("流程任务ID不能为空"); + } + if (bo.getId() == null) { + throw new ServiceException("隐患ID不能为空"); + } + HotHiddenDanger oldDanger = baseMapper.selectById(bo.getId()); + if (oldDanger == null) { + throw new ServiceException("隐患信息不存在"); + } + + // 2. 准备更新数据 + HotHiddenDanger update = MapstructUtils.convert(bo, HotHiddenDanger.class); + + // 3. 根据当前状态处理流转逻辑 + boolean pass = true; + String comment = "通过"; + Long currentStatus = oldDanger.getStatus(); + + // 隐患排查来源处理 + HotHiddenDangerInspection inspection = null; + if (oldDanger.getDataSource() != null && oldDanger.getDataSource() == 1L && StringUtils.isNotBlank(oldDanger.getSourceId())) { + try { + inspection = dangerInspectionMapper.selectById(Long.valueOf(oldDanger.getSourceId())); + } catch (NumberFormatException e) { + log.warn("Invalid sourceId: {}", oldDanger.getSourceId()); + } + } + + if (currentStatus == 0L) { + // 阶段:评估 -> 治理 + update.setStatus(1L); + comment = StringUtils.isBlank(bo.getEvalProcess()) ? "评估通过" : "评估完成"; + updateInspectState(inspection, 7L); // 治理 + } else if (currentStatus == 1L) { + // 阶段:治理 -> 复查 + update.setStatus(2L); + comment = StringUtils.isBlank(bo.getGovernResult()) ? "治理提交" : "治理完成"; + updateInspectState(inspection, 8L); // 复查 + } else if (currentStatus == 2L) { + // 阶段:复查 -> 完结 或 驳回治理 + if (bo.getIsQualified() == null) { + throw new ServiceException("请选择复查结论"); + } + + pass = bo.getIsQualified() == 1L; + comment = bo.getReviewConclusion(); + + if (pass) { + update.setStatus(3L); // 合格 -> 完结 + updateInspectState(inspection, 9L); // 完结 + } else { + update.setStatus(1L); // 不合格 -> 回退到治理 + updateInspectState(inspection, 7L); // 治理 + // 记录复查历史 + saveReviewRecord(bo); + } + } else { + // 容错处理:未知状态 + log.warn("Unknown hidden danger status: {}", currentStatus); + } + + // 4. 更新业务数据 + baseMapper.updateById(update); + + // 5. 推动流程 + flowService.audit( + bo.getTaskId(), + pass, + comment, + LoginHelper.getBusinessUserId() + ); + } + + private void updateInspectState(HotHiddenDangerInspection inspection, Long status) { + if (inspection == null || status == null) { + return; + } + + inspection.setStatus(status); + + dangerInspectionMapper.updateById(inspection); + } + + /** + * 保存复查记录 + */ + private void saveReviewRecord(HotHiddenDangerBo bo) { + HotHiddenDangerReviewRecordBo recordBo = new HotHiddenDangerReviewRecordBo(); + recordBo.setHiddenDangerId(bo.getId()); + recordBo.setReviewerId(bo.getReviewerId()); + recordBo.setReviewerName(bo.getReviewerName()); + recordBo.setReviewTime(bo.getReviewTime()); + recordBo.setReviewConclusion(bo.getReviewConclusion()); + recordBo.setIsQualified(bo.getIsQualified()); + recordBo.setReviewSign(bo.getReviewSign()); + reviewRecordService.insertByBo(recordBo); + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotHiddenDanger entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除隐患治理信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/controller/HotHiddenDangerInspectionController.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/controller/HotHiddenDangerInspectionController.java new file mode 100644 index 0000000..5348923 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/controller/HotHiddenDangerInspectionController.java @@ -0,0 +1,141 @@ +package com.hotwj.platform.securityManagement.hiddenDangerInspection.controller; + +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.bo.HotHiddenDangerInspectionBo; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.vo.HotHiddenDangerInspectionVo; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.service.IHotHiddenDangerInspectionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 隐患排查-排查项目 + * + * @author shihongwei + * @date 2025-12-31 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/hiddenDangerInspection") +@Tag(name = "隐患排查-排查项目", description = "隐患排查-排查项目管理") +public class HotHiddenDangerInspectionController extends BaseController { + + private final IHotHiddenDangerInspectionService hotHiddenDangerInspectionService; + + /** + * 查询隐患排查-排查项目列表 + */ + //@SaCheckPermission("securityManagement:hiddenDangerInspection:list") + @GetMapping("/list") + @Operation(summary = "分页查询隐患排查-排查项目列表") + public TableDataInfo list(HotHiddenDangerInspectionBo bo, PageQuery pageQuery) { + return hotHiddenDangerInspectionService.queryPageList(bo, pageQuery); + } + + /** + * 导出隐患排查-排查项目列表 + */ + //@SaCheckPermission("securityManagement:hiddenDangerInspection:export") + @Log(title = "隐患排查-排查项目", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出隐患排查-排查项目列表") + public void export(HotHiddenDangerInspectionBo bo, HttpServletResponse response) { + List list = hotHiddenDangerInspectionService.queryList(bo); + ExcelUtil.exportExcel(list, "隐患排查-排查项目", HotHiddenDangerInspectionVo.class, response); + } + + /** + * 获取隐患排查-排查项目详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:hiddenDangerInspection:query") + @GetMapping("/{id}") + @Operation(summary = "获取隐患排查-排查项目详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotHiddenDangerInspectionService.queryById(id)); + } + + /** + * 新增隐患排查-排查项目 + */ + //@SaCheckPermission("securityManagement:hiddenDangerInspection:add") + @Log(title = "隐患排查-排查项目", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增隐患排查-排查项目") + public R add(@Validated(AddGroup.class) @RequestBody HotHiddenDangerInspectionBo bo) { + return toAjax(hotHiddenDangerInspectionService.insertByBo(bo)); + } + + /** + * 修改隐患排查-排查项目 + */ + //@SaCheckPermission("securityManagement:hiddenDangerInspection:edit") + @Log(title = "隐患排查-排查项目", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改隐患排查-排查项目") + public R edit(@Validated(EditGroup.class) @RequestBody HotHiddenDangerInspectionBo bo) { + return toAjax(hotHiddenDangerInspectionService.updateByBo(bo)); + } + + /** + * 审核隐患排查-排查项目 + */ + //@SaCheckPermission("securityManagement:hiddenDangerInspection:edit") + @Log(title = "隐患排查-排查项目", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/audit") + @Operation(summary = "审核隐患排查-排查项目") + public R audit(@RequestBody HotHiddenDangerInspectionBo bo) { + hotHiddenDangerInspectionService.audit(bo); + return R.ok(); + } + + /** + * 删除隐患排查-排查项目 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:hiddenDangerInspection:remove") + @Log(title = "隐患排查-排查项目", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除隐患排查-排查项目") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotHiddenDangerInspectionService.deleteWithValidByIds(List.of(ids), true)); + } + + @GetMapping("/hidden-danger-plans/page") + @Operation( + summary = "移动端分页查询隐患排查计划列表", + description = "权限说明:驾驶员仅可查看本人及所属车辆当月全部计划和历史未做计划;管理员可查看企业当月全部计划和历史未做计划。" + ) + public TableDataInfo hiddenDangerPlanPage( + @RequestParam(value = "keyword", required = false) String keyword, + PageQuery pageQuery) { + return hotHiddenDangerInspectionService.queryMobilePlanPage(keyword, pageQuery); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/domain/HotHiddenDangerInspection.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/domain/HotHiddenDangerInspection.java new file mode 100644 index 0000000..e21f286 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/domain/HotHiddenDangerInspection.java @@ -0,0 +1,153 @@ +package com.hotwj.platform.securityManagement.hiddenDangerInspection.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 隐患排查-排查项目对象 hot_hidden_danger_inspection + * + * @author shihongwei + * @date 2025-12-31 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_hidden_danger_inspection") +public class HotHiddenDangerInspection extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 计划ID + */ + private Long planId; + + /** + * 排查项目 + */ + private String projectName; + + /** + * 排查项目对应id 类型为1无 + */ + private String projectId; + + /** + * 排查内容JSON + */ + private String checkContentJson; + + /** + * 隐患描述 + */ + private String dangerDesc; + + /** + * 附件url + */ + private String attachmentUrl; + + /** + * 排查人ID + */ + private String checkerId; + + /** + * 排查人姓名 + */ + private String checkerName; + + /** + * 排查日期 + */ + private Date checkDate; + + /** + * 评估人id + */ + private Long evaluatorId; + + /** + * 评估人姓名 + */ + private String evaluatorName; + + /** + * 审核人ID + */ + private Long approverId; + + /** + * 审核人姓名 + */ + private String approverName; + + /** + * 进度状态 无异常:1=特处理、2=审核中、3=完结 有异常:4=特处理、5=审核中、6=评估、7=治理、8=复查、9=完结 + */ + private Long status; + + /** + * 排查人签名图片url + */ + private String signImgUrl; + + /** + * 审核日期 + */ + private Date auditDate; + + /** + * 审核结论 + */ + private String auditConclusion; + + /** + * 审核人签名图片url + */ + private String approverSignImgUrl; + + /** + * 审核-是否存在隐患 0=否 1=是 + */ + private Long auditHasDanger; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 排查类型(1=公司 2=车辆 3=人员) + */ + private Long projectType; + + /** + * 流程实例ID + */ + private String instanceId; + + /** + * 流程状态 + */ + private String flowStatus; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/domain/bo/HotHiddenDangerInspectionBo.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/domain/bo/HotHiddenDangerInspectionBo.java new file mode 100644 index 0000000..6546e89 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/domain/bo/HotHiddenDangerInspectionBo.java @@ -0,0 +1,167 @@ +package com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.bo; + +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.HotHiddenDangerInspection; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 隐患排查-排查项目业务对象 hot_hidden_danger_inspection + * + * @author shihongwei + * @date 2025-12-31 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotHiddenDangerInspection.class, reverseConvertGenerate = false) +public class HotHiddenDangerInspectionBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 计划ID + */ + @NotNull(message = "计划ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long planId; + + /** + * 排查项目 + */ + private String projectName; + + /** + * 排查项目对应id 类型为1无 + */ + private String projectId; + + /** + * 排查内容JSON + */ + @NotBlank(message = "排查内容JSON不能为空", groups = {AddGroup.class, EditGroup.class}) + private String checkContentJson; + + private String keyword; + + /** + * 隐患描述 + */ + private String dangerDesc; + + /** + * 附件url + */ + private String attachmentUrl; + + /** + * 排查人ID + */ +// @NotNull(message = "排查人ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private String checkerId; + + /** + * 排查人姓名 + */ + private String checkerName; + + /** + * 排查日期 + */ +// @NotNull(message = "排查日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date checkDate; + + /** + * 评估人id + */ + private Long evaluatorId; + + /** + * 评估人姓名 + */ + private String evaluatorName; + + /** + * 审核人ID + */ +// @NotNull(message = "审核人ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long approverId; + + /** + * 审核人姓名 + */ + private String approverName; + + /** + * 进度状态 无异常:1=特处理、2=审核中、3=完结 有异常:4=特处理、5=审核中、6=评估、7=治理、8=复查、9=完结 + */ + private Long status; + + /** + * 排查人签名图片url + */ +// @NotBlank(message = "排查人签名图片url不能为空", groups = {AddGroup.class, EditGroup.class}) + private String signImgUrl; + + /** + * 审核日期 + */ + private Date auditDate; + + /** + * 审核结论 + */ + private String auditConclusion; + + /** + * 审核人签名图片url + */ + private String approverSignImgUrl; + + /** + * 审核-是否存在隐患 0=否 1=是 + */ + private Long auditHasDanger; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + /** + * 排查类型(1=公司 2=车辆 3=人员) + */ + private Long projectType; + + /** + * 流程实例ID + */ + private String instanceId; + + /** + * 流程状态 + */ + private String flowStatus; + + + /** + * 流程任务ID + */ + private String taskId; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/domain/vo/HotHiddenDangerInspectionVo.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/domain/vo/HotHiddenDangerInspectionVo.java new file mode 100644 index 0000000..8d90942 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/domain/vo/HotHiddenDangerInspectionVo.java @@ -0,0 +1,195 @@ +package com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.HotHiddenDangerInspection; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 隐患排查-排查项目视图对象 hot_hidden_danger_inspection + * + * @author shihongwei + * @date 2025-12-31 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotHiddenDangerInspection.class) +public class HotHiddenDangerInspectionVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 计划ID + */ + @ExcelProperty(value = "计划ID") + private Long planId; + + /** + * 排查项目 + */ + @ExcelProperty(value = "排查项目") + private String projectName; + + /** + * 排查项目对应id 类型为1无 + */ + @ExcelProperty(value = "排查项目对应id 类型为1无") + private String projectId; + + /** + * 排查内容JSON + */ + @ExcelProperty(value = "排查内容JSON") + private String checkContentJson; + + /** + * 隐患描述 + */ + @ExcelProperty(value = "隐患描述") + private String dangerDesc; + + /** + * 附件url + */ + @ExcelProperty(value = "附件url") + private String attachmentUrl; + + /** + * 排查人ID + */ + @ExcelProperty(value = "排查人ID") + private String checkerId; + + /** + * 排查人姓名 + */ + @ExcelProperty(value = "排查人姓名") + private String checkerName; + + /** + * 排查日期 + */ + @ExcelProperty(value = "排查日期") + private Date checkDate; + + /** + * 评估人id + */ + @ExcelProperty(value = "评估人id") + private Long evaluatorId; + + /** + * 评估人姓名 + */ + @ExcelProperty(value = "评估人姓名") + private String evaluatorName; + + /** + * 审核人ID + */ + @ExcelProperty(value = "审核人ID") + private Long approverId; + + /** + * 审核人姓名 + */ + @ExcelProperty(value = "审核人姓名") + private String approverName; + + /** + * 进度状态 无异常:1=特处理、2=审核中、3=完结 有异常:4=特处理、5=审核中、6=评估、7=治理、8=复查、9=完结 + */ + @ExcelProperty(value = "进度状态 无异常:1=特处理、2=审核中、3=完结 有异常:4=特处理、5=审核中、6=评估、7=治理、8=复查、9=完结") + private Long status; + + /** + * 排查人签名图片url + */ + @ExcelProperty(value = "排查人签名图片url") + private String signImgUrl; + + /** + * 审核日期 + */ + @ExcelProperty(value = "审核日期") + private Date auditDate; + + /** + * 审核结论 + */ + @ExcelProperty(value = "审核结论") + private String auditConclusion; + + /** + * 审核人签名图片url + */ + @ExcelProperty(value = "审核人签名图片url") + private String approverSignImgUrl; + + /** + * 审核-是否存在隐患 0=否 1=是 + */ + @ExcelProperty(value = "审核-是否存在隐患 0=否 1=是") + private Long auditHasDanger; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 排查类型(1=公司 2=车辆 3=企业) + */ + @ExcelProperty(value = "排查类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1==公司,2==车辆,3==企业") + private Long projectType; + + /** + * 流程实例ID + */ + @ExcelProperty(value = "流程实例ID") + private String instanceId; + + /** + * 流程状态 + */ + @ExcelProperty(value = "流程状态") + private String flowStatus; + + /** + * 流程任务ID + */ + private String taskId; + + private String planName; + + private Date planStartDate; + + private Date planEndDate; + + private Integer todoSortGroup; + + private String todoSortLabel; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/mapper/HotHiddenDangerInspectionMapper.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/mapper/HotHiddenDangerInspectionMapper.java new file mode 100644 index 0000000..f7836e0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/mapper/HotHiddenDangerInspectionMapper.java @@ -0,0 +1,28 @@ +package com.hotwj.platform.securityManagement.hiddenDangerInspection.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.HotHiddenDangerInspection; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.vo.HotHiddenDangerInspectionVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +import java.util.List; + +/** + * 隐患排查-排查项目Mapper接口 + * + * @author shihongwei + * @date 2025-12-31 + */ +@Mapper +public interface HotHiddenDangerInspectionMapper extends BaseMapperPlus { + + List selectMobilePlanPage(Page page, + @Param("companyId") Long companyId, + @Param("isAdmin") Boolean isAdmin, + @Param("driverId") String driverId, + @Param("vehicleId") String vehicleId, + @Param("keyword") String keyword, + @Param("currentMonth") String currentMonth); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/service/IHotHiddenDangerInspectionService.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/service/IHotHiddenDangerInspectionService.java new file mode 100644 index 0000000..b24a482 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/service/IHotHiddenDangerInspectionService.java @@ -0,0 +1,77 @@ +package com.hotwj.platform.securityManagement.hiddenDangerInspection.service; + +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.bo.HotHiddenDangerInspectionBo; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.vo.HotHiddenDangerInspectionVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 隐患排查-排查项目Service接口 + * + * @author shihongwei + * @date 2025-12-31 + */ +public interface IHotHiddenDangerInspectionService { + + /** + * 查询隐患排查-排查项目 + * + * @param id 主键 + * @return 隐患排查-排查项目 + */ + HotHiddenDangerInspectionVo queryById(Long id); + + /** + * 分页查询隐患排查-排查项目列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 隐患排查-排查项目分页列表 + */ + TableDataInfo queryPageList(HotHiddenDangerInspectionBo bo, PageQuery pageQuery); + + TableDataInfo queryMobilePlanPage(String keyword, PageQuery pageQuery); + + /** + * 查询符合条件的隐患排查-排查项目列表 + * + * @param bo 查询条件 + * @return 隐患排查-排查项目列表 + */ + List queryList(HotHiddenDangerInspectionBo bo); + + /** + * 新增隐患排查-排查项目 + * + * @param bo 隐患排查-排查项目 + * @return 是否新增成功 + */ + Boolean insertByBo(HotHiddenDangerInspectionBo bo); + + /** + * 修改隐患排查-排查项目 + * + * @param bo 隐患排查-排查项目 + * @return 是否修改成功 + */ + Boolean updateByBo(HotHiddenDangerInspectionBo bo); + + /** + * 校验并批量删除隐患排查-排查项目信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 审核隐患排查 + * + * @param bo 隐患排查 + */ + void audit(HotHiddenDangerInspectionBo bo); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/service/impl/HotHiddenDangerInspectionServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/service/impl/HotHiddenDangerInspectionServiceImpl.java new file mode 100644 index 0000000..bbb4fa6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerInspection/service/impl/HotHiddenDangerInspectionServiceImpl.java @@ -0,0 +1,300 @@ +package com.hotwj.platform.securityManagement.hiddenDangerInspection.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.common.helper.DriverLoginContextHelper; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.flow.service.ISysFlowService; +import com.hotwj.platform.integration.ocr.SignatureVerifyService; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.HotHiddenDangerInspection; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.bo.HotHiddenDangerInspectionBo; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.vo.HotHiddenDangerInspectionVo; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.mapper.HotHiddenDangerInspectionMapper; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.service.IHotHiddenDangerInspectionService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.YearMonth; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 隐患排查-排查项目Service业务层处理 + * + * @author shihongwei + * @date 2025-12-31 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotHiddenDangerInspectionServiceImpl implements IHotHiddenDangerInspectionService { + + private final HotHiddenDangerInspectionMapper baseMapper; + private final ISysFlowService flowService; + private final IHotSystemNotificationService notificationService; + private final SignatureVerifyService signatureVerifyService; + + /** + * 查询隐患排查-排查项目 + * + * @param id 主键 + * @return 隐患排查-排查项目 + */ + @Override + public HotHiddenDangerInspectionVo queryById(Long id) { + HotHiddenDangerInspectionVo vo = baseMapper.selectVoById(id); + if (vo != null && StringUtils.isNotBlank(vo.getInstanceId())) { + String userId = LoginHelper.getBusinessUserId(); + vo.setTaskId(flowService.getTaskId(vo.getInstanceId(), userId)); + } + return vo; + } + + /** + * 分页查询隐患排查-排查项目列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 隐患排查-排查项目分页列表 + */ + @Override + public TableDataInfo queryPageList(HotHiddenDangerInspectionBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + + fillFlowExtras(result.getRecords()); + + return TableDataInfo.build(result); + } + + @Override + public TableDataInfo queryMobilePlanPage(String keyword, PageQuery pageQuery) { + Long companyId = LoginHelper.getCompanyId(); + if (companyId == null) { + throw new ServiceException("当前登录用户无公司信息"); + } + boolean isDriver = DriverLoginContextHelper.isDriverPort(); + String driverId = null; + String vehicleId = null; + if (isDriver) { + HotDriver driver = DriverLoginContextHelper.getCurrentDriver(); + driverId = driver.getId(); + vehicleId = driver.getVehicleId(); + } + String currentMonth = YearMonth.now().toString(); + Page page = pageQuery.build(); + List rows = baseMapper.selectMobilePlanPage( + page, + companyId, + !isDriver, + driverId, + vehicleId, + StringUtils.trimToNull(keyword), + currentMonth + ); + page.setRecords(rows); + fillFlowExtras(rows); + return TableDataInfo.build(page); + } + + /** + * 查询符合条件的隐患排查-排查项目列表 + * + * @param bo 查询条件 + * @return 隐患排查-排查项目列表 + */ + @Override + public List queryList(HotHiddenDangerInspectionBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotHiddenDangerInspectionBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotHiddenDangerInspection::getId); + lqw.eq(bo.getCompanyId() != null, HotHiddenDangerInspection::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getPlanId() != null, HotHiddenDangerInspection::getPlanId, bo.getPlanId()); + lqw.like(StringUtils.isNotBlank(bo.getProjectName()), HotHiddenDangerInspection::getProjectName, bo.getProjectName()); + lqw.eq(StringUtils.isNotBlank(bo.getProjectId()), HotHiddenDangerInspection::getProjectId, bo.getProjectId()); + lqw.eq(StringUtils.isNotBlank(bo.getCheckContentJson()), HotHiddenDangerInspection::getCheckContentJson, bo.getCheckContentJson()); + lqw.eq(StringUtils.isNotBlank(bo.getDangerDesc()), HotHiddenDangerInspection::getDangerDesc, bo.getDangerDesc()); + lqw.eq(StringUtils.isNotBlank(bo.getAttachmentUrl()), HotHiddenDangerInspection::getAttachmentUrl, bo.getAttachmentUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getCheckerId()), HotHiddenDangerInspection::getCheckerId, bo.getCheckerId()); + lqw.like(StringUtils.isNotBlank(bo.getCheckerName()), HotHiddenDangerInspection::getCheckerName, bo.getCheckerName()); + if (StringUtils.isNotBlank(bo.getKeyword())) { + String keyword = bo.getKeyword().trim(); + lqw.like(HotHiddenDangerInspection::getProjectName, keyword); + } + lqw.eq(bo.getCheckDate() != null, HotHiddenDangerInspection::getCheckDate, bo.getCheckDate()); + lqw.eq(bo.getApproverId() != null, HotHiddenDangerInspection::getApproverId, bo.getApproverId()); + lqw.like(StringUtils.isNotBlank(bo.getApproverName()), HotHiddenDangerInspection::getApproverName, bo.getApproverName()); + lqw.eq(bo.getStatus() != null, HotHiddenDangerInspection::getStatus, bo.getStatus()); + lqw.eq(StringUtils.isNotBlank(bo.getSignImgUrl()), HotHiddenDangerInspection::getSignImgUrl, bo.getSignImgUrl()); + lqw.eq(bo.getIsDeleted() != null, HotHiddenDangerInspection::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + private void fillFlowExtras(List records) { + if (records == null || records.isEmpty()) { + return; + } + String userId = LoginHelper.getBusinessUserId(); + for (HotHiddenDangerInspectionVo vo : records) { + if (vo == null) { + continue; + } + if (StringUtils.isNotBlank(vo.getInstanceId())) { + vo.setTaskId(flowService.getTaskId(vo.getInstanceId(), userId)); + } + if (StringUtils.isBlank(vo.getFlowStatus())) { + if (vo.getStatus() == null || vo.getStatus() == 0L) { + vo.setFlowStatus("TODO"); + } else if (vo.getStatus() == 3L || vo.getStatus() == 9L) { + vo.setFlowStatus("DONE"); + } else { + vo.setFlowStatus("PROCESSING"); + } + } + } + } + + /** + * 新增隐患排查-排查项目 + * + * @param bo 隐患排查-排查项目 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotHiddenDangerInspectionBo bo) { + HotHiddenDangerInspection add = MapstructUtils.convert(bo, HotHiddenDangerInspection.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改隐患排查-排查项目 + * + * @param bo 隐患排查-排查项目 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotHiddenDangerInspectionBo bo) { + HotHiddenDangerInspection original = baseMapper.selectById(bo.getId()); + HotHiddenDangerInspection update = MapstructUtils.convert(bo, HotHiddenDangerInspection.class); + validEntityBeforeSave(update); + String signImgUrl = bo.getSignImgUrl(); + if (DriverLoginContextHelper.isDriverPort() && StringUtils.isNotBlank(signImgUrl)) { + String checkerName = bo.getCheckerName(); + + if (original == null || StringUtils.isBlank(checkerName)) { + throw new ServiceException("排查人姓名不能为空"); + } + signatureVerifyService.validateSelfSignature(parseSignatureOssId(signImgUrl), checkerName); + } + boolean flag = baseMapper.updateById(update) > 0; + if (flag && (original == null || StringUtils.isBlank(original.getInstanceId()))) { + String initiator = String.valueOf(LoginHelper.getBusinessUserId()); + String approverId = bo.getApproverId() != null ? String.valueOf(bo.getApproverId()) : null; + if (StringUtils.isBlank(approverId)) { + throw new ServiceException("请选择审核人"); + } + String instanceId = flowService.startFlow( + "HIDDEN_DANGER_CHECK", + String.valueOf(update.getId()), + update.getCompanyId(), + initiator, + approverId + ); + update.setInstanceId(instanceId); + update.setFlowStatus("SUBMITTED"); + baseMapper.updateById(update); + + try { + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent("你有一条隐患排查需要处理。"); + bos.setSourceType("隐患管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(java.util.Collections.singletonList(approverId)); + bos.setIsDeleted(0L); + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("Hidden danger inspection assign notify failed approverId={} inspectionId={}", approverId, update.getId(), e); + } + } + return flag; + } + + private Long parseSignatureOssId(String signImgUrl) { + String first = signImgUrl.split(",")[0].trim(); + if (!first.matches("^\\d+$")) { + throw new ServiceException("签名文件格式错误"); + } + return Long.parseLong(first); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void audit(HotHiddenDangerInspectionBo bo) { + // 1. 校验参数 + if (bo.getTaskId() == null) { + throw new ServiceException("流程任务ID不能为空"); + } + + // 2. 更新业务数据 + HotHiddenDangerInspection update = MapstructUtils.convert(bo, HotHiddenDangerInspection.class); + boolean flag = baseMapper.updateById(update) > 0; + + if (flag) { + // 4. 完成流程任务 + // 如果有隐患,我们认为流程是"驳回"的(需要整改) + boolean auditPass = bo.getAuditHasDanger() != null && bo.getAuditHasDanger() == 0L; + flowService.audit( + bo.getTaskId(), + auditPass, + bo.getAuditConclusion(), + LoginHelper.getBusinessUserId() + ); + } + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotHiddenDangerInspection entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除隐患排查-排查项目信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/controller/HotHiddenDangerPlanController.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/controller/HotHiddenDangerPlanController.java new file mode 100644 index 0000000..7588266 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/controller/HotHiddenDangerPlanController.java @@ -0,0 +1,147 @@ +package com.hotwj.platform.securityManagement.hiddenDangerPlan.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.bo.HotHiddenDangerPlanBo; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.vo.HotHiddenDangerPlanVo; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.service.IHotHiddenDangerPlanService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 隐患排查 + * + * @author shihongwei + * @date 2025-12-30 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/hiddenDangerPlan") +@Tag(name = "隐患排查", description = "隐患排查管理") +public class HotHiddenDangerPlanController extends BaseController { + + private final IHotHiddenDangerPlanService hotHiddenDangerPlanService; + + /** + * 查询隐患排查列表 + */ + //@SaCheckPermission("securityManagement:hiddenDangerPlan:list") + @GetMapping("/list") + @Operation(summary = "分页查询隐患排查列表") + public TableDataInfo list(HotHiddenDangerPlanBo bo, PageQuery pageQuery) { + return hotHiddenDangerPlanService.queryPageList(bo, pageQuery); + } + + /** + * 导出隐患排查列表 + */ + //@SaCheckPermission("securityManagement:hiddenDangerPlan:export") + @Log(title = "隐患排查", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出隐患排查列表") + public void export(HotHiddenDangerPlanBo bo, HttpServletResponse response) { + List list = hotHiddenDangerPlanService.queryList(bo); + ExcelUtil.exportExcel(list, "隐患排查", HotHiddenDangerPlanVo.class, response); + } + + /** + * 获取隐患排查详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:hiddenDangerPlan:query") + @GetMapping("/{id}") + @Operation(summary = "获取隐患排查详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotHiddenDangerPlanService.queryById(id)); + } + + /** + * 手动生成隐患排查计划 + */ + @SaIgnore + @Log(title = "隐患排查", businessType = BusinessType.INSERT) + @PostMapping("/generatePlans") + @Operation(summary = "手动生成隐患排查计划") + public R generatePlans() { + hotHiddenDangerPlanService.generatePlans(); + return R.ok(); + } + + /** + * 生成指定企业的隐患排查计划 + * + * @param companyId 企业ID + */ + //@SaCheckPermission("securityManagement:hiddenDangerPlan:add") + @Log(title = "隐患排查", businessType = BusinessType.INSERT) + @PostMapping("/generatePlan/{companyId}") + @Operation(summary = "生成指定企业的隐患排查计划") + public R generatePlan(@NotNull(message = "企业ID不能为空") + @Parameter(name = "companyId", description = "企业ID", required = true, example = "1") + @PathVariable Long companyId) { + hotHiddenDangerPlanService.generatePlan(companyId); + return R.ok(); + } + + /** + * 新增隐患排查 + */ + //@SaCheckPermission("securityManagement:hiddenDangerPlan:add") + @Log(title = "隐患排查", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增隐患排查") + public R add(@Validated(AddGroup.class) @RequestBody HotHiddenDangerPlanBo bo) { + Long id = hotHiddenDangerPlanService.insertByBo(bo); + return R.ok(id); + } + + /** + * 修改隐患排查 + */ + //@SaCheckPermission("securityManagement:hiddenDangerPlan:edit") + @Log(title = "隐患排查", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改隐患排查") + public R edit(@Validated(EditGroup.class) @RequestBody HotHiddenDangerPlanBo bo) { + return toAjax(hotHiddenDangerPlanService.updateByBo(bo)); + } + + /** + * 删除隐患排查 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:hiddenDangerPlan:remove") + @Log(title = "隐患排查", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除隐患排查") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotHiddenDangerPlanService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/domain/HotHiddenDangerPlan.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/domain/HotHiddenDangerPlan.java new file mode 100644 index 0000000..a7477ed --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/domain/HotHiddenDangerPlan.java @@ -0,0 +1,90 @@ +package com.hotwj.platform.securityManagement.hiddenDangerPlan.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 隐患排查对象 hot_hidden_danger_plan + * + * @author shihongwei + * @date 2025-12-30 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_hidden_danger_plan") +public class HotHiddenDangerPlan extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 检查项id + */ + private Long inspectId; + + /** + * 检查项名称 + */ + private String inspectName; + + /** + * 排查计划名称 + */ + private String planName; + + /** + * 开始日期 + */ + private Date startDate; + + /** + * 结束日期 + */ + private Date endDate; + + /** + * 进度 0=待处理 1=审核中 2=评估 3=治理 4=复查 5=完结 + */ + private Long progress; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 是否后台自动生成:0-否,1-是 + */ + private Long isAutoGeneratedByBackend; + + /** + * 计划周期Key(yyyy-MM, yyyy-Q1等) + */ + private String planDateKey; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/domain/bo/HotHiddenDangerPlanBo.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/domain/bo/HotHiddenDangerPlanBo.java new file mode 100644 index 0000000..65e88bb --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/domain/bo/HotHiddenDangerPlanBo.java @@ -0,0 +1,94 @@ +package com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.bo; + +import com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.HotHiddenDangerPlan; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 隐患排查业务对象 hot_hidden_danger_plan + * + * @author shihongwei + * @date 2025-12-30 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotHiddenDangerPlan.class, reverseConvertGenerate = false) +public class HotHiddenDangerPlanBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 检查项id + */ + private Long inspectId; + + /** + * 检查项名称 + */ + private String inspectName; + + /** + * 排查计划名称 + */ + @NotBlank(message = "排查计划名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String planName; + + /** + * 开始日期 + */ + @NotNull(message = "开始日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date startDate; + + /** + * 结束日期 + */ + @NotNull(message = "结束日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date endDate; + + /** + * 进度 0=待处理 1=审核中 2=评估 3=治理 4=复查 5=完结 + */ + @NotNull(message = "进度 0=待处理 1=审核中 2=评估 3=治理 4=复查 5=完结不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long progress; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + /** + * 是否后台自动生成:0-否,1-是 + */ + private Long isAutoGeneratedByBackend; + + /** + * 计划周期Key(yyyy-MM, yyyy-Q1等) + */ + private String planDateKey; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/domain/vo/HotHiddenDangerPlanVo.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/domain/vo/HotHiddenDangerPlanVo.java new file mode 100644 index 0000000..301c255 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/domain/vo/HotHiddenDangerPlanVo.java @@ -0,0 +1,101 @@ +package com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.HotHiddenDangerPlan; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 隐患排查视图对象 hot_hidden_danger_plan + * + * @author shihongwei + * @date 2025-12-30 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotHiddenDangerPlan.class) +public class HotHiddenDangerPlanVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 检查项id + */ + @ExcelProperty(value = "检查项id") + private Long inspectId; + + /** + * 检查项名称 + */ + @ExcelProperty(value = "检查项名称") + private String inspectName; + + /** + * 排查计划名称 + */ + @ExcelProperty(value = "排查计划名称") + private String planName; + + /** + * 开始日期 + */ + @ExcelProperty(value = "开始日期") + private Date startDate; + + /** + * 结束日期 + */ + @ExcelProperty(value = "结束日期") + private Date endDate; + + /** + * 进度 0=待处理 1=审核中 2=评估 3=治理 4=复查 5=完结 + */ + @ExcelProperty(value = "进度 0=待处理 1=审核中 2=评估 3=治理 4=复查 5=完结") + private Long progress; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 是否后台自动生成:0-否,1-是 + */ +// @ExcelProperty(value = "是否后台自动生成:0-否,1-是") + private Long isAutoGeneratedByBackend; + + /** + * 计划周期Key(yyyy-MM, yyyy-Q1等) + */ + @ExcelProperty(value = "计划周期Key") + private String planDateKey; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/mapper/HotHiddenDangerPlanMapper.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/mapper/HotHiddenDangerPlanMapper.java new file mode 100644 index 0000000..dd74e34 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/mapper/HotHiddenDangerPlanMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.hiddenDangerPlan.mapper; + +import com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.HotHiddenDangerPlan; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.vo.HotHiddenDangerPlanVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 隐患排查Mapper接口 + * + * @author shihongwei + * @date 2025-12-30 + */ +@Mapper +public interface HotHiddenDangerPlanMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/service/IHotHiddenDangerPlanService.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/service/IHotHiddenDangerPlanService.java new file mode 100644 index 0000000..e6d000d --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/service/IHotHiddenDangerPlanService.java @@ -0,0 +1,80 @@ +package com.hotwj.platform.securityManagement.hiddenDangerPlan.service; + +import com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.bo.HotHiddenDangerPlanBo; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.vo.HotHiddenDangerPlanVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 隐患排查Service接口 + * + * @author shihongwei + * @date 2025-12-30 + */ +public interface IHotHiddenDangerPlanService { + + /** + * 查询隐患排查 + * + * @param id 主键 + * @return 隐患排查 + */ + HotHiddenDangerPlanVo queryById(Long id); + + /** + * 分页查询隐患排查列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 隐患排查分页列表 + */ + TableDataInfo queryPageList(HotHiddenDangerPlanBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的隐患排查列表 + * + * @param bo 查询条件 + * @return 隐患排查列表 + */ + List queryList(HotHiddenDangerPlanBo bo); + + /** + * 新增隐患排查 + * + * @param bo 隐患排查 + * @return 是否新增成功 + */ + Long insertByBo(HotHiddenDangerPlanBo bo); + + /** + * 修改隐患排查 + * + * @param bo 隐患排查 + * @return 是否修改成功 + */ + Boolean updateByBo(HotHiddenDangerPlanBo bo); + + /** + * 生成隐患排查计划 + */ + void generatePlans(); + + /** + * 生成指定企业的隐患排查计划 + * + * @param companyId 企业ID + */ + void generatePlan(Long companyId); + + /** + * 校验并批量删除隐患排查信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/service/impl/HotHiddenDangerPlanServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/service/impl/HotHiddenDangerPlanServiceImpl.java new file mode 100644 index 0000000..85967cb --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/service/impl/HotHiddenDangerPlanServiceImpl.java @@ -0,0 +1,670 @@ +package com.hotwj.platform.securityManagement.hiddenDangerPlan.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.common.utils.TimeRangeUtil; +import com.hotwj.platform.config.companyBasicConfig.domain.bo.HotCompanyBasicConfigBo; +import com.hotwj.platform.config.companyBasicConfig.domain.vo.HotCompanyBasicConfigVo; +import com.hotwj.platform.config.companyBasicConfig.service.IHotCompanyBasicConfigService; +import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.bo.HotHiddenDangerCheckConfigBo; +import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.vo.HotHiddenDangerCheckConfigVo; +import com.hotwj.platform.config.hiddenDangerCheckConfig.service.IHotHiddenDangerCheckConfigService; +import com.hotwj.platform.driverManagement.driver.domain.bo.HotDriverBo; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.HotHiddenDangerInspection; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.bo.HotHiddenDangerInspectionBo; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.mapper.HotHiddenDangerInspectionMapper; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.service.IHotHiddenDangerInspectionService; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.HotHiddenDangerPlan; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.bo.HotHiddenDangerPlanBo; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.vo.HotHiddenDangerPlanVo; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.mapper.HotHiddenDangerPlanMapper; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.service.IHotHiddenDangerPlanService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.temporal.IsoFields; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 隐患排查Service业务层处理 + * + * @author shihongwei + * @date 2025-12-30 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotHiddenDangerPlanServiceImpl implements IHotHiddenDangerPlanService { + + private final IHotHiddenDangerCheckConfigService checkConfigService; + private final IHotCompanyBasicConfigService companyBasicConfigService; + private final IHotVehicleService vehicleService; + private final IHotDriverService driverService; + private final IHotHiddenDangerInspectionService inspectionService; + private final HotHiddenDangerInspectionMapper inspectionMapper; + private final ISysCompanyService companyService; + private final HotHiddenDangerPlanMapper baseMapper; + private final HotCompanySafetyManagerMapper managerMapper; + private final IHotSystemNotificationService notificationService; + + /** + * 查询隐患排查 + * + * @param id 主键 + * @return 隐患排查 + */ + @Override + public HotHiddenDangerPlanVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询隐患排查列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 隐患排查分页列表 + */ + @Override + public TableDataInfo queryPageList(HotHiddenDangerPlanBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的隐患排查列表 + * + * @param bo 查询条件 + * @return 隐患排查列表 + */ + @Override + public List queryList(HotHiddenDangerPlanBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + /** + * 生成隐患排查计划 + */ + @Override + public void generatePlans() { + // 1. 获取所有开启了隐患排查规则的公司配置 + HotCompanyBasicConfigBo companyConfigBo = new HotCompanyBasicConfigBo(); + companyConfigBo.setIsDeleted(0L); + companyConfigBo.setRoleIsEnable(1L); + List companyConfigs = companyBasicConfigService.queryList(companyConfigBo); + + if (companyConfigs == null || companyConfigs.isEmpty()) { + return; + } + + // 2. 获取所有启用的检查项配置,并按公司ID分组 + HotHiddenDangerCheckConfigBo configBo = new HotHiddenDangerCheckConfigBo(); + configBo.setIsDeleted(0L); + List allCheckConfigs = checkConfigService.queryList(configBo); + + Map> checkConfigsMap; + if (allCheckConfigs != null && !allCheckConfigs.isEmpty()) { + checkConfigsMap = allCheckConfigs.stream() + .filter(c -> c.getCompanyId() != null) + .collect(java.util.stream.Collectors.groupingBy(HotHiddenDangerCheckConfigVo::getCompanyId)); + } else { + checkConfigsMap = new java.util.HashMap<>(); + } + + for (HotCompanyBasicConfigVo companyConfig : companyConfigs) { + try { + processCompanyPlan(companyConfig, checkConfigsMap); + } catch (Exception e) { + log.error("创建公司: {} 隐患排查计划失败", companyConfig.getCompanyId(), e); + } + } + } + + /** + * 生成指定企业的隐患排查计划 + * + * @param companyId 企业ID + */ + @Override + public void generatePlan(Long companyId) { + if (companyId == null) { + throw new IllegalArgumentException("公司ID不能为空"); + } + + // 1. 获取企业配置 + HotCompanyBasicConfigBo companyConfigBo = new HotCompanyBasicConfigBo(); + companyConfigBo.setCompanyId(companyId); + companyConfigBo.setIsDeleted(0L); + companyConfigBo.setRoleIsEnable(1L); + List companyConfigs = companyBasicConfigService.queryList(companyConfigBo); + + if (companyConfigs == null || companyConfigs.isEmpty()) { + log.info("公司: {} 基本配置未找到或隐患排查角色已禁用,跳过计划生成", companyId); + return; + } + HotCompanyBasicConfigVo companyConfig = companyConfigs.get(0); + + // 2. 获取检查项配置 + HotHiddenDangerCheckConfigBo configBo = new HotHiddenDangerCheckConfigBo(); + configBo.setCompanyId(companyId); + configBo.setIsDeleted(0L); + List checkConfigs = checkConfigService.queryList(configBo); + + Map> checkConfigsMap = new java.util.HashMap<>(); + if (checkConfigs != null && !checkConfigs.isEmpty()) { + checkConfigsMap.put(companyId, checkConfigs); + } + + // 3. 处理 + processCompanyPlan(companyConfig, checkConfigsMap); + } + + /** + * 处理单个公司的排查计划 + */ + private void processCompanyPlan(HotCompanyBasicConfigVo companyConfig, Map> checkConfigsMap) { + if (companyConfig == null || companyConfig.getCompanyId() == null) { + return; + } + + Long role = companyConfig.getHiddenDangerRole(); + boolean enable = Long.valueOf(1L).equals(companyConfig.getRoleIsEnable()); + if (role == null || !enable) { + SysCompanyVo sysCompanyVo = companyService.queryById(companyConfig.getCompanyId()); + if (sysCompanyVo != null) { + log.info("公司: {} 隐患排查角色未启用,跳过计划生成", sysCompanyVo.getCompanyName()); + } else { + log.info("公司: {} 隐患排查角色未启用,跳过计划生成", companyConfig.getCompanyId()); + } + return; + } + + // 计算时间范围和计划名称 + PlanTimeInfo timeInfo = calculatePlanTimeInfo(role); + if (timeInfo == null) { + return; + } + + + // 获取有效的检查项配置 + HotHiddenDangerCheckConfigVo checkConfig = getEffectiveCheckConfig(companyConfig.getCompanyId(), checkConfigsMap); + + if (checkConfig == null) { + return; + } + + HotHiddenDangerPlan plan = findPlan(companyConfig.getCompanyId(), timeInfo); + boolean isNewPlan = false; + if (plan == null) { + Long inspectId = checkConfig == null ? null : checkConfig.getId(); + String inspectName = checkConfig == null ? null : checkConfig.getTypeName(); + HotHiddenDangerPlanBo newPlan = createPlan(companyConfig.getCompanyId(), inspectId, inspectName, timeInfo); + if (newPlan == null || newPlan.getId() == null) { + log.error("创建公司: {} 隐患排查计划失败", companyConfig.getCompanyId()); + return; + } + plan = baseMapper.selectById(newPlan.getId()); + isNewPlan = true; + } else if (checkConfig != null && plan.getInspectId() == null) { + baseMapper.update( + null, + Wrappers.lambdaUpdate() + .set(HotHiddenDangerPlan::getInspectId, checkConfig.getId()) + .set(HotHiddenDangerPlan::getInspectName, checkConfig.getTypeName()) + .eq(HotHiddenDangerPlan::getId, plan.getId()) + ); + } + + + String companyName = "企业"; + try { + SysCompanyVo company = companyService.queryById(companyConfig.getCompanyId()); + if (company != null && StringUtils.isNotBlank(company.getCompanyName())) { + companyName = company.getCompanyName(); + } + } catch (Exception e) { + log.warn("查询公司: {} 名称失败,异常信息: {}", companyConfig.getCompanyId(), e.getMessage()); + } + + if (isNewPlan) { + String periodText = switch (companyConfig.getHiddenDangerRole() != null ? companyConfig.getHiddenDangerRole().intValue() : -1) { + case 1 -> "本周"; + case 2 -> "本月"; + case 3 -> "本季"; + case 4 -> "本半年"; + case 5 -> "本年"; + default -> "本期"; + }; + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyConfig.getCompanyId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers != null && !managers.isEmpty()) { + List receiverIds = managers.stream().map(m -> String.valueOf(m.getId())).toList(); + String content = periodText + "隐患排查计划已创建,请及时进行排查。"; + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("隐患管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("创建公司: {} 隐患排查计划通知失败,周期: {}", companyConfig.getCompanyId(), periodText, e); + } + } + } + + syncInspectionDetails(plan.getId(), companyConfig.getCompanyId(), checkConfig, companyName); + } + + /** + * 根据公司ID和计划时间信息查询隐患排查计划 + */ + private HotHiddenDangerPlan findPlan(Long companyId, PlanTimeInfo timeInfo) { + if (companyId == null || timeInfo == null) { + return null; + } + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(HotHiddenDangerPlan::getCompanyId, companyId); + lqw.eq(HotHiddenDangerPlan::getIsDeleted, 0L); + lqw.eq(HotHiddenDangerPlan::getPlanDateKey, timeInfo.getPlanDateKey()); + lqw.orderByDesc(HotHiddenDangerPlan::getCreateTime); + lqw.last("LIMIT 1"); + return baseMapper.selectOne(lqw); + } + + /** + * 计算计划的时间范围和名称 + */ + private PlanTimeInfo calculatePlanTimeInfo(Long role) { + TimeRangeUtil.TimeRange timeRange; + String planName; + String planDateKey; + LocalDate now = LocalDate.now(); + + // 1=周 2=月 3=季度 4=半年 5=年 + if (role == 1) { + timeRange = TimeRangeUtil.getCurrentWeek(); + planName = TimeRangeUtil.formatDate(timeRange.getStartTime(), "yyyy年MM月dd日") + + " 至 " + + TimeRangeUtil.formatDate(timeRange.getEndTime(), "yyyy年MM月dd日") + + " 排查计划"; + int week = now.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); + int year = now.get(IsoFields.WEEK_BASED_YEAR); + planDateKey = String.format("%d-W%02d", year, week); + } else if (role == 2) { + timeRange = TimeRangeUtil.getCurrentMonth(); + planName = TimeRangeUtil.formatDate(timeRange.getStartTime(), "yyyy年MM月") + " 排查计划"; + planDateKey = now.format(DateTimeFormatter.ofPattern("yyyy-MM")); + } else if (role == 3) { + timeRange = TimeRangeUtil.getCurrentQuarter(); + int season = TimeRangeUtil.getCurrentQuarterNumber(); + planName = TimeRangeUtil.formatDate(timeRange.getStartTime(), "yyyy年") + season + " 季度排查计划"; + planDateKey = String.format("%d-Q%d", now.getYear(), season); + } else if (role == 4) { + timeRange = TimeRangeUtil.getCurrentHalfYear(); + String halfYearDesc = TimeRangeUtil.getCurrentHalfYearDescription(); + planName = TimeRangeUtil.formatDate(timeRange.getStartTime(), "yyyy年") + " " + halfYearDesc + "排查计划"; + int half = now.getMonthValue() <= 6 ? 1 : 2; + planDateKey = String.format("%d-H%d", now.getYear(), half); + } else if (role == 5) { + timeRange = TimeRangeUtil.getCurrentYear(); + planName = TimeRangeUtil.formatDate(timeRange.getStartTime(), "yyyy年") + " 排查计划"; + planDateKey = String.valueOf(now.getYear()); + } else { + return null; + } + return new PlanTimeInfo(timeRange, planName, planDateKey); + } + + /** + * 获取有效的检查项配置 + */ + private HotHiddenDangerCheckConfigVo getEffectiveCheckConfig(Long companyId, Map> checkConfigsMap) { + List companyCheckConfigs = checkConfigsMap.get(companyId); + if (companyCheckConfigs != null && !companyCheckConfigs.isEmpty()) { + return companyCheckConfigs.stream() + .filter(c -> c.getIsDefault() != null && c.getIsDefault() == 1L) + .findFirst() + .orElse(companyCheckConfigs.get(0)); + } + return null; + } + + /** + * 创建计划实体 + */ + private HotHiddenDangerPlanBo createPlan(Long companyId, Long inspectId, String inspectName, PlanTimeInfo timeInfo) { + HotHiddenDangerPlanBo newPlan = new HotHiddenDangerPlanBo(); + newPlan.setCompanyId(companyId); + newPlan.setInspectId(inspectId); + newPlan.setInspectName(inspectName); + newPlan.setPlanName(timeInfo.getPlanName()); + newPlan.setStartDate(timeInfo.getTimeRange().getStartTime()); + newPlan.setEndDate(timeInfo.getTimeRange().getEndTime()); + newPlan.setProgress(0L); // 0=待处理 + newPlan.setIsAutoGeneratedByBackend(1L); + newPlan.setPlanDateKey(timeInfo.getPlanDateKey()); + newPlan.setRemark(inspectId == null ? "自动生成(待配置)" : "自动生成"); + + this.insertByBo(newPlan); + return newPlan; + } + + /** + * 同步计划详情 + */ + private void syncInspectionDetails(Long planId, Long companyId, HotHiddenDangerCheckConfigVo checkConfig, String companyName) { + if (planId == null || companyId == null || checkConfig == null) { + return; + } + + List existing = inspectionMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotHiddenDangerInspection::getCompanyId, companyId) + .eq(HotHiddenDangerInspection::getPlanId, planId) + .eq(HotHiddenDangerInspection::getIsDeleted, 0L) + ); + + // 4. 缓存已存在的检查项 + Map existsMap = new HashMap<>(); + if (existing != null) { + for (HotHiddenDangerInspection ins : existing) { + if (ins == null || ins.getProjectType() == null) { + continue; + } + String projectId = StringUtils.isBlank(ins.getProjectId()) ? "0" : ins.getProjectId(); + existsMap.put(ins.getProjectType() + ":" + projectId, ins); + } + } + + String companyJson = checkConfig.getCompanyInspectJson(); + if (StringUtils.isNotBlank(companyJson)) { + // key 规则:projectType:projectId + // 其中 projectType: 1=公司 2=车辆 3=人员;公司项目没有具体 projectId,统一用 "0" 占位 + String key = "1:0"; + HotHiddenDangerInspection ins = existsMap.get(key); + // 企业检查对象不存在则创建 + if (ins == null) { + HotHiddenDangerInspectionBo inspectionBo = new HotHiddenDangerInspectionBo(); + inspectionBo.setCompanyId(companyId); + inspectionBo.setPlanId(planId); + inspectionBo.setProjectName(companyName); + inspectionBo.setProjectType(1L); + inspectionBo.setProjectId("0"); + inspectionBo.setCheckContentJson(companyJson); + inspectionService.insertByBo(inspectionBo); + } else { + tryUpdateCheckContentJson(ins, companyJson); + } + } + + String vehicleJson = checkConfig.getVehicleInspectJson(); + if (StringUtils.isNotBlank(vehicleJson)) { + HotVehicleBo vehicleBo = new HotVehicleBo(); + vehicleBo.setCompanyId(companyId); + vehicleBo.setVehicleStatus(1L); + vehicleBo.setOperationStatus(1L); + List vehicles = vehicleService.queryList(vehicleBo); + if (vehicles != null) { + for (HotVehicleVo vehicle : vehicles) { + if (vehicle == null || vehicle.getId() == null) { + continue; + } + String key = "2:" + vehicle.getId(); + HotHiddenDangerInspection ins = existsMap.get(key); + if (ins == null) { + HotHiddenDangerInspectionBo inspectionBo = new HotHiddenDangerInspectionBo(); + inspectionBo.setCompanyId(companyId); + inspectionBo.setPlanId(planId); + inspectionBo.setProjectType(2L); + inspectionBo.setProjectName(vehicle.getPlateNumber() + " 隐患排查"); + inspectionBo.setProjectId(String.valueOf(vehicle.getId())); + inspectionBo.setCheckContentJson(vehicleJson); + inspectionService.insertByBo(inspectionBo); + } else { + tryUpdateCheckContentJson(ins, vehicleJson); + } + } + } + } + + String personJson = checkConfig.getPersonInspectJson(); + if (StringUtils.isNotBlank(personJson)) { + HotDriverBo driverBo = new HotDriverBo(); + driverBo.setCompanyId(companyId); + driverBo.setStatus(1L); + driverBo.setAuditStatus(1); + driverBo.setIsDeleted(0L); + List drivers = driverService.queryList(driverBo); + if (drivers != null) { + for (HotDriverVo driver : drivers) { + if (driver == null || StringUtils.isBlank(driver.getId())) { + continue; + } + String key = "3:" + driver.getId(); + HotHiddenDangerInspection ins = existsMap.get(key); + if (ins == null) { + HotHiddenDangerInspectionBo inspectionBo = new HotHiddenDangerInspectionBo(); + inspectionBo.setCompanyId(companyId); + inspectionBo.setPlanId(planId); + inspectionBo.setProjectType(3L); + inspectionBo.setProjectName(driver.getName() + " 隐患排查"); + inspectionBo.setProjectId(driver.getId()); + inspectionBo.setCheckContentJson(personJson); + inspectionService.insertByBo(inspectionBo); + } else { + tryUpdateCheckContentJson(ins, personJson); + } + } + } + } + } + + private void tryUpdateCheckContentJson(HotHiddenDangerInspection ins, String json) { + if (ins == null || ins.getId() == null || StringUtils.isBlank(json)) { + return; + } + if (StringUtils.isNotBlank(ins.getInstanceId()) || ins.getStatus() != null) { + return; + } + if (json.equals(ins.getCheckContentJson())) { + return; + } + inspectionMapper.update( + null, + Wrappers.lambdaUpdate() + .set(HotHiddenDangerInspection::getCheckContentJson, json) + .eq(HotHiddenDangerInspection::getId, ins.getId()) + ); + } + + private LambdaQueryWrapper buildQueryWrapper(HotHiddenDangerPlanBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotHiddenDangerPlan::getCreateTime); + lqw.eq(bo.getCompanyId() != null, HotHiddenDangerPlan::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getInspectId() != null, HotHiddenDangerPlan::getInspectId, bo.getInspectId()); + lqw.like(StringUtils.isNotBlank(bo.getPlanName()), HotHiddenDangerPlan::getPlanName, bo.getPlanName()); + lqw.eq(bo.getStartDate() != null, HotHiddenDangerPlan::getStartDate, bo.getStartDate()); + lqw.eq(bo.getEndDate() != null, HotHiddenDangerPlan::getEndDate, bo.getEndDate()); + lqw.eq(bo.getProgress() != null, HotHiddenDangerPlan::getProgress, bo.getProgress()); + lqw.eq(bo.getIsDeleted() != null, HotHiddenDangerPlan::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增隐患排查 + * + * @param bo 隐患排查 + * @return 是否新增成功 + */ + @Override + public Long insertByBo(HotHiddenDangerPlanBo bo) { + HotHiddenDangerPlan add = MapstructUtils.convert(bo, HotHiddenDangerPlan.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + if (add.getCompanyId() != null) { + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, add.getCompanyId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers != null && !managers.isEmpty()) { + List receiverIds = managers.stream().map(m -> String.valueOf(m.getId())).toList(); + String planName = StringUtils.blankToDefault(add.getPlanName(), "隐患排查"); + String content = planName + " 已创建,请及时进行排查。"; + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("隐患管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("创建公司: {} 隐患排查计划通知失败,计划ID: {}", add.getCompanyId(), add.getId(), e); + } + } + } + } + return bo.getId(); + } + + /** + * 修改隐患排查 + * + * @param bo 隐患排查 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotHiddenDangerPlanBo bo) { + HotHiddenDangerPlan before = null; + if (bo.getId() != null) { + before = baseMapper.selectById(bo.getId()); + } + HotHiddenDangerPlan update = MapstructUtils.convert(bo, HotHiddenDangerPlan.class); + validEntityBeforeSave(update); + boolean ok = baseMapper.updateById(update) > 0; + if (ok) { + Long companyId = update.getCompanyId() != null ? update.getCompanyId() : (before != null ? before.getCompanyId() : null); + if (companyId != null) { + List managers = managerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, companyId) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers != null && !managers.isEmpty()) { + List receiverIds = managers.stream().map(m -> String.valueOf(m.getId())).toList(); + String planName = StringUtils.blankToDefault( + update.getPlanName() != null ? update.getPlanName() : (before != null ? before.getPlanName() : null), + "隐患排查" + ); + String content = planName + " 已更新,请及时进行排查。"; + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("隐患管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("更新公司: {} 隐患排查计划通知失败,计划ID: {}", companyId, update.getId(), e); + } + } + } + } + return ok; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotHiddenDangerPlan entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除隐患排查信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 计划时间信息内部类 + */ + private static class PlanTimeInfo { + private final TimeRangeUtil.TimeRange timeRange; + private final String planName; + private final String planDateKey; + + public PlanTimeInfo(TimeRangeUtil.TimeRange timeRange, String planName, String planDateKey) { + this.timeRange = timeRange; + this.planName = planName; + this.planDateKey = planDateKey; + } + + public TimeRangeUtil.TimeRange getTimeRange() { + return timeRange; + } + + public String getPlanName() { + return planName; + } + + public String getPlanDateKey() { + return planDateKey; + } + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/task/HazardInspectionPlanTask.java b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/task/HazardInspectionPlanTask.java new file mode 100644 index 0000000..aa32fd9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/hiddenDangerPlan/task/HazardInspectionPlanTask.java @@ -0,0 +1,36 @@ +package com.hotwj.platform.securityManagement.hiddenDangerPlan.task; + +import com.hotwj.platform.securityManagement.hiddenDangerPlan.service.IHotHiddenDangerPlanService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +/** + * 隐患排查计划定时任务 + * + * @author shihongwei + * @date 2025-01-03 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class HazardInspectionPlanTask { + + private final IHotHiddenDangerPlanService hotHiddenDangerPlanService; + + /** + * 每天0点执行,生成隐患排查计划 + */ + @Scheduled(cron = "0 0 0 * * ?") + // @Scheduled(cron = "0 0/5 * * * ?") + public void generatePlans() { + log.info("开始生成隐患排查计划"); + try { + hotHiddenDangerPlanService.generatePlans(); + log.info("生成隐患排查计划完成"); + } catch (Exception e) { + log.error("生成隐患排查计划失败", e); + } + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/controller/HotSafetyInvestmentController.java b/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/controller/HotSafetyInvestmentController.java new file mode 100644 index 0000000..78ef59d --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/controller/HotSafetyInvestmentController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.securityManagement.safetyInvestment.controller; + +import com.hotwj.platform.securityManagement.safetyInvestment.domain.bo.HotSafetyInvestmentBo; +import com.hotwj.platform.securityManagement.safetyInvestment.domain.vo.HotSafetyInvestmentVo; +import com.hotwj.platform.securityManagement.safetyInvestment.service.IHotSafetyInvestmentService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 安全生产投入 + * + * @author shihongwei + * @date 2025-12-17 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/safetyInvestment") +@Tag(name = "安全生产投入", description = "安全生产投入管理") +public class HotSafetyInvestmentController extends BaseController { + + private final IHotSafetyInvestmentService hotSafetyInvestmentService; + + /** + * 查询安全生产投入列表 + */ + //@SaCheckPermission("securityManagement:safetyInvestment:list") + @GetMapping("/list") + @Operation(summary = "查询安全生产投入列表") + public TableDataInfo list(HotSafetyInvestmentBo bo, PageQuery pageQuery) { + return hotSafetyInvestmentService.queryPageList(bo, pageQuery); + } + + /** + * 导出安全生产投入列表 + */ + //@SaCheckPermission("securityManagement:safetyInvestment:export") + @Log(title = "安全生产投入", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出安全生产投入列表") + public void export(HotSafetyInvestmentBo bo, HttpServletResponse response) { + List list = hotSafetyInvestmentService.queryList(bo); + ExcelUtil.exportExcel(list, "安全生产投入", HotSafetyInvestmentVo.class, response); + } + + /** + * 获取安全生产投入详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:safetyInvestment:query") + @GetMapping("/{id}") + @Operation(summary = "获取安全生产投入详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotSafetyInvestmentService.queryById(id)); + } + + /** + * 新增安全生产投入 + */ + //@SaCheckPermission("securityManagement:safetyInvestment:add") + @Log(title = "安全生产投入", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增安全生产投入") + public R add(@Validated(AddGroup.class) @RequestBody HotSafetyInvestmentBo bo) { + return toAjax(hotSafetyInvestmentService.insertByBo(bo)); + } + + /** + * 修改安全生产投入 + */ + //@SaCheckPermission("securityManagement:safetyInvestment:edit") + @Log(title = "安全生产投入", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改安全生产投入") + public R edit(@Validated(EditGroup.class) @RequestBody HotSafetyInvestmentBo bo) { + return toAjax(hotSafetyInvestmentService.updateByBo(bo)); + } + + /** + * 删除安全生产投入 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:safetyInvestment:remove") + @Log(title = "安全生产投入", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除安全生产投入") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotSafetyInvestmentService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/domain/HotSafetyInvestment.java b/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/domain/HotSafetyInvestment.java new file mode 100644 index 0000000..a8a220b --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/domain/HotSafetyInvestment.java @@ -0,0 +1,96 @@ +package com.hotwj.platform.securityManagement.safetyInvestment.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 安全生产投入对象 hot_safety_investment + * + * @author shihongwei + * @date 2025-12-17 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_safety_investment") +public class HotSafetyInvestment extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 投入类型(字典) + */ + private String investmentType; + + /** + * 投入名称 + */ + private String investmentName; + + /** + * 数量 + */ + private Long quantity; + + /** + * 投入金额 + */ + private BigDecimal investmentAmount; + + /** + * 投入时间 + */ + private Date investmentTime; + + /** + * 票据(pdf、jpg,OSS文件id) + */ + private String receiptAttachments; + + /** + * 使用部门 + */ + private String useDept; + + /** + * 使用缘由 + */ + private String useReason; + + /** + * 备注 + */ + private String remark; + + /** + * 负责人签字OSS文件ID + */ + private String responsibleSignatureId; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/domain/bo/HotSafetyInvestmentBo.java b/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/domain/bo/HotSafetyInvestmentBo.java new file mode 100644 index 0000000..1f77fd9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/domain/bo/HotSafetyInvestmentBo.java @@ -0,0 +1,92 @@ +package com.hotwj.platform.securityManagement.safetyInvestment.domain.bo; + +import com.hotwj.platform.securityManagement.safetyInvestment.domain.HotSafetyInvestment; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 安全生产投入业务对象 hot_safety_investment + * + * @author shihongwei + * @date 2025-12-17 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotSafetyInvestment.class, reverseConvertGenerate = false) +public class HotSafetyInvestmentBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 投入类型(字典) + */ + private String investmentType; + + /** + * 投入名称 + */ + private String investmentName; + + /** + * 数量 + */ + private Long quantity; + + /** + * 投入金额 + */ + private BigDecimal investmentAmount; + + /** + * 投入时间 + */ + private Date investmentTime; + + /** + * 票据(pdf、jpg,OSS文件id) + */ + private String receiptAttachments; + + /** + * 使用部门 + */ + private String useDept; + + /** + * 使用缘由 + */ + private String useReason; + + /** + * 备注 + */ + private String remark; + + /** + * 负责人签字OSS文件ID + */ + private String responsibleSignatureId; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/domain/vo/HotSafetyInvestmentVo.java b/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/domain/vo/HotSafetyInvestmentVo.java new file mode 100644 index 0000000..73a8144 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/domain/vo/HotSafetyInvestmentVo.java @@ -0,0 +1,118 @@ +package com.hotwj.platform.securityManagement.safetyInvestment.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.safetyInvestment.domain.HotSafetyInvestment; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 安全生产投入视图对象 hot_safety_investment + * + * @author shihongwei + * @date 2025-12-17 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotSafetyInvestment.class) +public class HotSafetyInvestmentVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 投入类型(字典) + */ + @ExcelProperty(value = "投入类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "字=典") + private String investmentType; + + /** + * 投入名称 + */ + @ExcelProperty(value = "投入名称") + private String investmentName; + + /** + * 数量 + */ + @ExcelProperty(value = "数量") + private Long quantity; + + /** + * 投入金额 + */ + @ExcelProperty(value = "投入金额") + private BigDecimal investmentAmount; + + /** + * 投入时间 + */ + @ExcelProperty(value = "投入时间") + private Date investmentTime; + + /** + * 票据(pdf、jpg,OSS文件id) + */ + @ExcelProperty(value = "票据", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "p=df、jpg,OSS文件id") + private String receiptAttachments; + + /** + * 使用部门 + */ + @ExcelProperty(value = "使用部门") + private String useDept; + + /** + * 使用缘由 + */ + @ExcelProperty(value = "使用缘由") + private String useReason; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 负责人签字OSS文件ID + */ + @ExcelProperty(value = "负责人签字OSS文件ID") + private String responsibleSignatureId; + + /** + * 负责人签字图片URL + */ + @ExcelProperty(value = "负责人签字图片URL") + private String responsibleSignatureUrl; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/mapper/HotSafetyInvestmentMapper.java b/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/mapper/HotSafetyInvestmentMapper.java new file mode 100644 index 0000000..ac8cd54 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/mapper/HotSafetyInvestmentMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.safetyInvestment.mapper; + +import com.hotwj.platform.securityManagement.safetyInvestment.domain.HotSafetyInvestment; +import com.hotwj.platform.securityManagement.safetyInvestment.domain.vo.HotSafetyInvestmentVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 安全生产投入Mapper接口 + * + * @author shihongwei + * @date 2025-12-17 + */ +@Mapper +public interface HotSafetyInvestmentMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/service/IHotSafetyInvestmentService.java b/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/service/IHotSafetyInvestmentService.java new file mode 100644 index 0000000..e7cc3d6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/service/IHotSafetyInvestmentService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.securityManagement.safetyInvestment.service; + +import com.hotwj.platform.securityManagement.safetyInvestment.domain.bo.HotSafetyInvestmentBo; +import com.hotwj.platform.securityManagement.safetyInvestment.domain.vo.HotSafetyInvestmentVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 安全生产投入Service接口 + * + * @author shihongwei + * @date 2025-12-17 + */ +public interface IHotSafetyInvestmentService { + + /** + * 查询安全生产投入 + * + * @param id 主键 + * @return 安全生产投入 + */ + HotSafetyInvestmentVo queryById(Long id); + + /** + * 分页查询安全生产投入列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 安全生产投入分页列表 + */ + TableDataInfo queryPageList(HotSafetyInvestmentBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的安全生产投入列表 + * + * @param bo 查询条件 + * @return 安全生产投入列表 + */ + List queryList(HotSafetyInvestmentBo bo); + + /** + * 新增安全生产投入 + * + * @param bo 安全生产投入 + * @return 是否新增成功 + */ + Boolean insertByBo(HotSafetyInvestmentBo bo); + + /** + * 修改安全生产投入 + * + * @param bo 安全生产投入 + * @return 是否修改成功 + */ + Boolean updateByBo(HotSafetyInvestmentBo bo); + + /** + * 校验并批量删除安全生产投入信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/service/impl/HotSafetyInvestmentServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/service/impl/HotSafetyInvestmentServiceImpl.java new file mode 100644 index 0000000..4e6b5b5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/safetyInvestment/service/impl/HotSafetyInvestmentServiceImpl.java @@ -0,0 +1,152 @@ +package com.hotwj.platform.securityManagement.safetyInvestment.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.securityManagement.safetyInvestment.domain.HotSafetyInvestment; +import com.hotwj.platform.securityManagement.safetyInvestment.domain.bo.HotSafetyInvestmentBo; +import com.hotwj.platform.securityManagement.safetyInvestment.domain.vo.HotSafetyInvestmentVo; +import com.hotwj.platform.securityManagement.safetyInvestment.mapper.HotSafetyInvestmentMapper; +import com.hotwj.platform.securityManagement.safetyInvestment.service.IHotSafetyInvestmentService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 安全生产投入Service业务层处理 + * + * @author shihongwei + * @date 2025-12-17 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotSafetyInvestmentServiceImpl implements IHotSafetyInvestmentService { + + private final HotSafetyInvestmentMapper baseMapper; + + /** + * 查询安全生产投入 + * + * @param id 主键 + * @return 安全生产投入 + */ + @Override + public HotSafetyInvestmentVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询安全生产投入列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 安全生产投入分页列表 + */ + @Override + public TableDataInfo queryPageList(HotSafetyInvestmentBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的安全生产投入列表 + * + * @param bo 查询条件 + * @return 安全生产投入列表 + */ + @Override + public List queryList(HotSafetyInvestmentBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotSafetyInvestmentBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotSafetyInvestment::getId); + lqw.eq(bo.getCompanyId() != null, HotSafetyInvestment::getCompanyId, bo.getCompanyId()); + lqw.eq(StringUtils.isNotBlank(bo.getInvestmentType()), HotSafetyInvestment::getInvestmentType, bo.getInvestmentType()); + lqw.like(StringUtils.isNotBlank(bo.getInvestmentName()), HotSafetyInvestment::getInvestmentName, bo.getInvestmentName()); + lqw.eq(bo.getQuantity() != null, HotSafetyInvestment::getQuantity, bo.getQuantity()); + lqw.eq(bo.getInvestmentAmount() != null, HotSafetyInvestment::getInvestmentAmount, bo.getInvestmentAmount()); + lqw.eq(bo.getInvestmentTime() != null, HotSafetyInvestment::getInvestmentTime, bo.getInvestmentTime()); + lqw.eq(StringUtils.isNotBlank(bo.getReceiptAttachments()), HotSafetyInvestment::getReceiptAttachments, bo.getReceiptAttachments()); + lqw.eq(StringUtils.isNotBlank(bo.getUseDept()), HotSafetyInvestment::getUseDept, bo.getUseDept()); + lqw.eq(StringUtils.isNotBlank(bo.getUseReason()), HotSafetyInvestment::getUseReason, bo.getUseReason()); + lqw.eq(StringUtils.isNotBlank(bo.getResponsibleSignatureId()), HotSafetyInvestment::getResponsibleSignatureId, bo.getResponsibleSignatureId()); + lqw.eq(bo.getIsDeleted() != null, HotSafetyInvestment::getIsDeleted, bo.getIsDeleted()); + if (params != null) { + Object beginInvestmentTime = params.get("beginInvestmentTime"); + Object endInvestmentTime = params.get("endInvestmentTime"); + if (beginInvestmentTime instanceof java.util.Date) { + lqw.ge(HotSafetyInvestment::getInvestmentTime, (java.util.Date) beginInvestmentTime); + } + if (endInvestmentTime instanceof java.util.Date) { + lqw.le(HotSafetyInvestment::getInvestmentTime, (java.util.Date) endInvestmentTime); + } + } + return lqw; + } + + /** + * 新增安全生产投入 + * + * @param bo 安全生产投入 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotSafetyInvestmentBo bo) { + HotSafetyInvestment add = MapstructUtils.convert(bo, HotSafetyInvestment.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改安全生产投入 + * + * @param bo 安全生产投入 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotSafetyInvestmentBo bo) { + HotSafetyInvestment update = MapstructUtils.convert(bo, HotSafetyInvestment.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotSafetyInvestment entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除安全生产投入信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/controller/HotSecurityMeetingController.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/controller/HotSecurityMeetingController.java new file mode 100644 index 0000000..5beb0ec --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/controller/HotSecurityMeetingController.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.securityManagement.securityMeeting.controller; + +import com.hotwj.platform.securityManagement.securityMeeting.domain.bo.HotSecurityMeetingBo; +import com.hotwj.platform.securityManagement.securityMeeting.domain.vo.HotSecurityMeetingVo; +import com.hotwj.platform.securityManagement.securityMeeting.service.IHotSecurityMeetingService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 安全会议管理 + * + * @author shihongwei + * @date 2025-12-17 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/securityMeeting") +@Tag(name = "安全会议管理", description = "安全会议管理") +public class HotSecurityMeetingController extends BaseController { + + private final IHotSecurityMeetingService hotSecurityMeetingService; + + /** + * 查询安全会议管理列表 + */ + //@SaCheckPermission("securityManagement:securityMeeting:list") + @GetMapping("/list") + @Operation(summary = "查询安全会议管理列表") + public TableDataInfo list(HotSecurityMeetingBo bo, PageQuery pageQuery) { + return hotSecurityMeetingService.queryPageList(bo, pageQuery); + } + + /** + * 导出安全会议管理列表 + */ + //@SaCheckPermission("securityManagement:securityMeeting:export") + @Log(title = "安全会议管理", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出安全会议管理列表") + public void export(HotSecurityMeetingBo bo, HttpServletResponse response) { + List list = hotSecurityMeetingService.queryList(bo); + ExcelUtil.exportExcel(list, "安全会议管理", HotSecurityMeetingVo.class, response); + } + + /** + * 获取安全会议管理详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:securityMeeting:query") + @GetMapping("/{id}") + @Operation(summary = "获取安全会议管理详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotSecurityMeetingService.queryById(id)); + } + + /** + * 新增安全会议管理 + */ + //@SaCheckPermission("securityManagement:securityMeeting:add") + @Log(title = "安全会议管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增安全会议管理") + public R add(@Validated(AddGroup.class) @RequestBody HotSecurityMeetingBo bo) { + return toAjax(hotSecurityMeetingService.insertByBo(bo)); + } + + /** + * 修改安全会议管理 + */ + //@SaCheckPermission("securityManagement:securityMeeting:edit") + @Log(title = "安全会议管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改安全会议管理") + public R edit(@Validated(EditGroup.class) @RequestBody HotSecurityMeetingBo bo) { + return toAjax(hotSecurityMeetingService.updateByBo(bo)); + } + + /** + * 删除安全会议管理 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:securityMeeting:remove") + @Log(title = "安全会议管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除安全会议管理") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotSecurityMeetingService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/domain/HotSecurityMeeting.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/domain/HotSecurityMeeting.java new file mode 100644 index 0000000..97fc8ca --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/domain/HotSecurityMeeting.java @@ -0,0 +1,140 @@ +package com.hotwj.platform.securityManagement.securityMeeting.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 安全会议管理对象 hot_security_meeting + * + * @author shihongwei + * @date 2025-12-17 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_security_meeting") +public class HotSecurityMeeting extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 会议主题 + */ + private String meetingTopic; + + /** + * 会议种类(字典) + */ + private String meetingCategory; + + /** + * 会议类型(字典) + */ + private String meetingType; + + /** + * 会议类型(线下培训) + */ + private String offlineMeetingType; + + /** + * 会议开始时间 + */ + private Date startTime; + + /** + * 会议结束时间 + */ + private Date endTime; + + /** + * 主持人 + */ + private String hostName; + + /** + * 地点 + */ + private String location; + + /** + * 会议记录人id + */ + private Long recorderId; + + /** + * 会议记录人 + */ + private String recorderName; + + /** + * 会议附件(docx、pdf、pptx等,OSS附件id) + */ + private String fileAttachments; + + /** + * 视频附件(mp4,OSS附件id) + */ + private String videoAttachments; + + /** + * 线上会议链接 + */ + private String onlineMeetingUrl; + + /** + * 会议内容 + */ + private String meetingContent; + + /** + * 会议照片(png,jpg,pdf等,JSON或逗号分隔) + */ + private String photoAttachments; + + /** + * 下月工作安排 + */ + private String nextMonthPlan; + + /** + * 备注 + */ + private String remark; + + /** + * 会议状态(1=未开始、2=进行中、3=已结束) + */ + private String meetingStatus; + + /** + * 参与人员ID列表(逗号分隔) + */ + private String participantIds; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/domain/bo/HotSecurityMeetingBo.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/domain/bo/HotSecurityMeetingBo.java new file mode 100644 index 0000000..78b5ff4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/domain/bo/HotSecurityMeetingBo.java @@ -0,0 +1,136 @@ +package com.hotwj.platform.securityManagement.securityMeeting.domain.bo; + +import com.hotwj.platform.securityManagement.securityMeeting.domain.HotSecurityMeeting; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 安全会议管理业务对象 hot_security_meeting + * + * @author shihongwei + * @date 2025-12-17 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotSecurityMeeting.class, reverseConvertGenerate = false) +public class HotSecurityMeetingBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 会议主题 + */ + private String meetingTopic; + + /** + * 会议种类(字典) + */ + private String meetingCategory; + + /** + * 会议类型(字典) + */ + private String meetingType; + + /** + * 会议类型(线下培训) + */ + private String offlineMeetingType; + + /** + * 会议开始时间 + */ + private Date startTime; + + /** + * 会议结束时间 + */ + private Date endTime; + + /** + * 主持人 + */ + private String hostName; + + /** + * 地点 + */ + private String location; + + /** + * 会议记录人id + */ + private Long recorderId; + + /** + * 会议记录人 + */ + private String recorderName; + + /** + * 会议附件(docx、pdf、pptx等,OSS附件id) + */ + private String fileAttachments; + + /** + * 视频附件(mp4,OSS附件id) + */ + private String videoAttachments; + + /** + * 线上会议链接 + */ + private String onlineMeetingUrl; + + /** + * 会议内容 + */ + private String meetingContent; + + /** + * 会议照片(png,jpg,pdf等,JSON或逗号分隔) + */ + private String photoAttachments; + + /** + * 下月工作安排 + */ + private String nextMonthPlan; + + /** + * 备注 + */ + private String remark; + + /** + * 会议状态(1=未开始、2=进行中、3=已结束) + */ + private String meetingStatus; + + /** + * 参与人员ID列表(逗号分隔) + */ + private String participantIds; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/domain/vo/HotSecurityMeetingVo.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/domain/vo/HotSecurityMeetingVo.java new file mode 100644 index 0000000..f89047d --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/domain/vo/HotSecurityMeetingVo.java @@ -0,0 +1,168 @@ +package com.hotwj.platform.securityManagement.securityMeeting.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.securityMeeting.domain.HotSecurityMeeting; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 安全会议管理视图对象 hot_security_meeting + * + * @author shihongwei + * @date 2025-12-17 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotSecurityMeeting.class) +public class HotSecurityMeetingVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 会议主题 + */ + @ExcelProperty(value = "会议主题") + private String meetingTopic; + + /** + * 会议种类(字典) + */ + @ExcelProperty(value = "会议种类", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "字=典") + private String meetingCategory; + + /** + * 会议类型(字典) + */ + @ExcelProperty(value = "会议类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "字=典") + private String meetingType; + + /** + * 会议类型(线下培训) + */ + private String offlineMeetingType; + + /** + * 会议开始时间 + */ + @ExcelProperty(value = "会议开始时间") + private Date startTime; + + /** + * 会议结束时间 + */ + private Date endTime; + + /** + * 主持人 + */ + @ExcelProperty(value = "主持人") + private String hostName; + + /** + * 地点 + */ + @ExcelProperty(value = "地点") + private String location; + + /** + * 会议记录人id + */ + @ExcelProperty(value = "会议记录人id") + private Long recorderId; + + /** + * 会议记录人 + */ + @ExcelProperty(value = "会议记录人") + private String recorderName; + + /** + * 会议附件(docx、pdf、pptx等,OSS附件id) + */ + @ExcelProperty(value = "会议附件", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "d=ocx、pdf、pptx等,OSS附件id") + private String fileAttachments; + + /** + * 视频附件(mp4,OSS附件id) + */ + @ExcelProperty(value = "视频附件", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "m=p4,OSS附件id") + private String videoAttachments; + + /** + * 线上会议链接 + */ + @ExcelProperty(value = "线上会议链接") + private String onlineMeetingUrl; + + /** + * 会议内容 + */ + @ExcelProperty(value = "会议内容") + private String meetingContent; + + /** + * 会议照片(png,jpg,pdf等,JSON或逗号分隔) + */ + @ExcelProperty(value = "会议照片", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "p=ng,jpg,pdf等,JSON或逗号分隔") + private String photoAttachments; + + /** + * 下月工作安排 + */ + @ExcelProperty(value = "下月工作安排") + private String nextMonthPlan; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 会议状态(1=未开始、2=进行中、3=已结束) + */ + @ExcelProperty(value = "会议状态", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1==未开始、2=进行中、3=已结束") + private String meetingStatus; + + /** + * 参与人员ID列表(逗号分隔) + */ + @ExcelProperty(value = "参与人员ID列表", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逗=号分隔") + private String participantIds; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/mapper/HotSecurityMeetingMapper.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/mapper/HotSecurityMeetingMapper.java new file mode 100644 index 0000000..283197a --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/mapper/HotSecurityMeetingMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.securityMeeting.mapper; + +import com.hotwj.platform.securityManagement.securityMeeting.domain.HotSecurityMeeting; +import com.hotwj.platform.securityManagement.securityMeeting.domain.vo.HotSecurityMeetingVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 安全会议管理Mapper接口 + * + * @author shihongwei + * @date 2025-12-17 + */ +@Mapper +public interface HotSecurityMeetingMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/service/IHotSecurityMeetingService.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/service/IHotSecurityMeetingService.java new file mode 100644 index 0000000..6c88704 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/service/IHotSecurityMeetingService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.securityManagement.securityMeeting.service; + +import com.hotwj.platform.securityManagement.securityMeeting.domain.bo.HotSecurityMeetingBo; +import com.hotwj.platform.securityManagement.securityMeeting.domain.vo.HotSecurityMeetingVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 安全会议管理Service接口 + * + * @author shihongwei + * @date 2025-12-17 + */ +public interface IHotSecurityMeetingService { + + /** + * 查询安全会议管理 + * + * @param id 主键 + * @return 安全会议管理 + */ + HotSecurityMeetingVo queryById(Long id); + + /** + * 分页查询安全会议管理列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 安全会议管理分页列表 + */ + TableDataInfo queryPageList(HotSecurityMeetingBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的安全会议管理列表 + * + * @param bo 查询条件 + * @return 安全会议管理列表 + */ + List queryList(HotSecurityMeetingBo bo); + + /** + * 新增安全会议管理 + * + * @param bo 安全会议管理 + * @return 是否新增成功 + */ + Boolean insertByBo(HotSecurityMeetingBo bo); + + /** + * 修改安全会议管理 + * + * @param bo 安全会议管理 + * @return 是否修改成功 + */ + Boolean updateByBo(HotSecurityMeetingBo bo); + + /** + * 校验并批量删除安全会议管理信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/service/impl/HotSecurityMeetingServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/service/impl/HotSecurityMeetingServiceImpl.java new file mode 100644 index 0000000..8783d7e --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/service/impl/HotSecurityMeetingServiceImpl.java @@ -0,0 +1,493 @@ +package com.hotwj.platform.securityManagement.securityMeeting.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.flow.service.ISysFlowService; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import com.hotwj.platform.securityManagement.securityMeeting.domain.HotSecurityMeeting; +import com.hotwj.platform.securityManagement.securityMeeting.domain.bo.HotSecurityMeetingBo; +import com.hotwj.platform.securityManagement.securityMeeting.domain.vo.HotSecurityMeetingVo; +import com.hotwj.platform.securityManagement.securityMeeting.mapper.HotSecurityMeetingMapper; +import com.hotwj.platform.securityManagement.securityMeeting.service.IHotSecurityMeetingService; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.HotSecurityMeetingAttendee; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.mapper.HotSecurityMeetingAttendeeMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.DateUtils; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 安全会议管理Service业务层处理 + * + * @author shihongwei + * @date 2025-12-17 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotSecurityMeetingServiceImpl implements IHotSecurityMeetingService { + + private static final String OFFLINE_MEETING_TYPE = "offline"; + private static final String STATUS_IN_PROGRESS = "2"; + private static final String STATUS_FINISHED = "3"; + + private final HotSecurityMeetingMapper baseMapper; + private final HotSecurityMeetingAttendeeMapper attendeeMapper; + private final ISysFlowService flowService; + private final HotDriverMapper driverMapper; + private final HotCompanySafetyManagerMapper safetyManagerMapper; + private final IHotSystemNotificationService notificationService; + + /** + * 新增安全会议管理 + * + * @param bo 安全会议管理 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(HotSecurityMeetingBo bo) { + // 补充记录人名称 + if (bo.getRecorderId() != null) { + HotCompanySafetyManager manager = safetyManagerMapper.selectById(bo.getRecorderId()); + if (manager != null) { + bo.setRecorderName(manager.getName()); + } + } + HotSecurityMeeting add = MapstructUtils.convert(bo, HotSecurityMeeting.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + // 处理参会人员(统一逻辑) + // TODO 线下会议不需要推送 ? + manageAttendees(add, bo.getParticipantIds()); + if (StringUtils.isNotBlank(bo.getParticipantIds())) { + List driverIds = new ArrayList<>(); + List managerIds = new ArrayList<>(); + Arrays.stream(bo.getParticipantIds().split(",")) + .map(String::trim) + .filter(StringUtils::isNotBlank) + .forEach(pid -> { + if (driverMapper.selectById(pid) != null) { + driverIds.add(pid); + } else { + try { + Long mid = Long.valueOf(pid); + if (safetyManagerMapper.selectById(mid) != null) { + managerIds.add(String.valueOf(mid)); + } + } catch (NumberFormatException ignore) { + } + } + }); + String topic = StringUtils.blankToDefault(add.getMeetingTopic(), "未命名会议"); + String timeStr = add.getStartTime() != null ? DateUtils.formatDateTime(add.getStartTime()) : "未设置时间"; + String loc = StringUtils.blankToDefault(add.getLocation(), "未设置地点"); + String content = "新增安全会议【" + topic + "】,开始时间【" + timeStr + "】,地点【" + loc + "】。"; + if (!driverIds.isEmpty()) { + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("制度与会议"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("驾驶员"); + bos.setReceiverIds(driverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("会议新增通知发送失败(驾驶员) meetingId={} driverCount={}", add.getId(), driverIds.size(), e); + } + } + if (!managerIds.isEmpty()) { + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("制度与会议"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(managerIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("会议新增通知发送失败(管理员) meetingId={} managerCount={}", add.getId(), managerIds.size(), e); + } + } + } + } + return flag; + } + + /** + * 修改安全会议管理 + * + * @param bo 安全会议管理 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(HotSecurityMeetingBo bo) { + HotSecurityMeeting current = baseMapper.selectById(bo.getId()); + if (current == null) { + throw new ServiceException("会议不存在"); + } + validateEditable(current); + + // 补充记录人名称 + if (bo.getRecorderId() != null) { + HotCompanySafetyManager manager = safetyManagerMapper.selectById(bo.getRecorderId()); + if (manager != null) { + bo.setRecorderName(manager.getName()); + } + } + HotSecurityMeeting update = MapstructUtils.convert(bo, HotSecurityMeeting.class); + validEntityBeforeSave(update); + boolean flag = baseMapper.updateById(update) > 0; + + // 处理参会人员变更 + // TODO 线下会议不需要推送 ? + if (flag && bo.getParticipantIds() != null) { + // 获取完整的会议信息(用于推送标题) + HotSecurityMeeting meeting = baseMapper.selectById(bo.getId()); + if (meeting == null) { + meeting = update; + } + manageAttendees(meeting, bo.getParticipantIds()); + } + if (flag) { + HotSecurityMeeting meeting = baseMapper.selectById(bo.getId()); + if (meeting == null) { + meeting = update; + } + String pidStr = bo.getParticipantIds() != null ? bo.getParticipantIds() : meeting.getParticipantIds(); + if (StringUtils.isNotBlank(pidStr)) { + List driverIds = new ArrayList<>(); + List managerIds = new ArrayList<>(); + Arrays.stream(pidStr.split(",")) + .map(String::trim) + .filter(StringUtils::isNotBlank) + .forEach(pid -> { + if (driverMapper.selectById(pid) != null) { + driverIds.add(pid); + } else { + try { + Long mid = Long.valueOf(pid); + if (safetyManagerMapper.selectById(mid) != null) { + managerIds.add(String.valueOf(mid)); + } + } catch (NumberFormatException ignore) { + } + } + }); + String topic = StringUtils.blankToDefault(meeting.getMeetingTopic(), "未命名会议"); + String timeStr = meeting.getStartTime() != null ? DateUtils.formatDateTime(meeting.getStartTime()) : "未设置时间"; + String loc = StringUtils.blankToDefault(meeting.getLocation(), "未设置地点"); + String content = "安全会议【" + topic + "】已更新,开始时间【" + timeStr + "】,地点【" + loc + "】。"; + if (!driverIds.isEmpty()) { + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("制度与会议"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("驾驶员"); + bos.setReceiverIds(driverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("会议更新通知发送失败(驾驶员) meetingId={} driverCount={}", meeting.getId(), driverIds.size(), e); + } + } + if (!managerIds.isEmpty()) { + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("制度与会议"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(managerIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("会议更新通知发送失败(管理员) meetingId={} managerCount={}", meeting.getId(), managerIds.size(), e); + } + } + } + } + return flag; + } + + + /** + * 统一管理参会人员(新增、删除、恢复)及推送待办 + * + * @param meeting 会议实体 + * @param participantIds 参会人员ID字符串 + */ + private void manageAttendees(HotSecurityMeeting meeting, String participantIds) { + Long meetingId = meeting.getId(); + Long companyId = meeting.getCompanyId(); + String topic = meeting.getMeetingTopic(); + + // 1. 查询现有参会人员(包括逻辑删除的,以便恢复) + // 注意:MyBatis Plus默认过滤逻辑删除,需使用自定义SQL或特定Wrapper,此处简单起见先查活跃的 + // 若需支持"恢复",最好查询所有。但如果无法简单查所有,则insert时若有唯一索引会报错。 + // 假设无唯一索引或允许重复插入(id自增),或者我们只处理活跃状态的差异。 + // 为了健壮性,我们先查活跃的。对于"toAdd",我们尝试先恢复逻辑删除的记录,若无则插入。 + + List activeAttendees = attendeeMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotSecurityMeetingAttendee::getMeetingId, meetingId) + ); + Set activePersonIds = activeAttendees.stream() + .map(HotSecurityMeetingAttendee::getPersonId) + .collect(Collectors.toSet()); + + // 2. 获取目标参会人员ID集合 + Set targetPersonIds = new HashSet<>(); + if (StringUtils.isNotBlank(participantIds)) { + Arrays.stream(participantIds.split(",")) + .filter(StringUtils::isNotBlank) + .forEach(targetPersonIds::add); + } + + // 3. 计算差异 + List toRemove = activePersonIds.stream() + .filter(id -> !targetPersonIds.contains(id)) + .collect(Collectors.toList()); + + List toAdd = targetPersonIds.stream() + .filter(id -> !activePersonIds.contains(id)) + .collect(Collectors.toList()); + + // 4. 执行删除(逻辑删除) + if (!toRemove.isEmpty()) { + attendeeMapper.delete( + Wrappers.lambdaQuery() + .eq(HotSecurityMeetingAttendee::getMeetingId, meetingId) + .in(HotSecurityMeetingAttendee::getPersonId, toRemove) + ); + + // 删除相关待办任务 + for (String personId : toRemove) { + try { + flowService.deleteDirectTodo( + "MEETING_SIGN", + String.valueOf(meetingId), + personId + ); + } catch (Exception e) { + log.error("删除安全会议待办失败: meetingId={}, personId={}", meetingId, personId, e); + } + } + } + + // 5. 执行新增(或恢复)并推送待办 + for (String personId : toAdd) { + // Determine post, name, phone + String post = null; + String name = null; + String phone = null; + HotDriver driver = driverMapper.selectById(personId); + if (driver != null) { + post = "驾驶员"; + name = driver.getName(); + phone = driver.getPhone(); + } else { + try { + Long id = Long.valueOf(personId); + HotCompanySafetyManager manager = safetyManagerMapper.selectById(id); + if (manager != null) { + post = manager.getJobName(); + name = manager.getName(); + phone = manager.getPhone(); + } + } catch (NumberFormatException e) { + // Ignore + } + } + + // 尝试恢复逻辑删除的记录(如果存在) + // 这一步需要绕过MP的逻辑删除过滤,或者直接执行UPDATE语句设置is_deleted=0 + // 由于MP默认不方便操作逻辑删除字段,我们这里先检查是否存在物理记录(较复杂), + // 或者简单处理:直接insert。如果表设计允许(id主键自增,personId+meetingId无唯一索引),则会有多条记录。 + // 假设我们希望数据干净,最好是update is_deleted=0。 + // 使用UpdateWrapper直接更新: + boolean recovered = attendeeMapper.update(null, + Wrappers.lambdaUpdate() + .set(HotSecurityMeetingAttendee::getIsDeleted, 0) // 恢复 + .set(HotSecurityMeetingAttendee::getPost, post) // 更新岗位 + .set(HotSecurityMeetingAttendee::getName, name) // 更新姓名 + .set(HotSecurityMeetingAttendee::getPhone, phone) // 更新手机号 + .eq(HotSecurityMeetingAttendee::getMeetingId, meetingId) + .eq(HotSecurityMeetingAttendee::getPersonId, personId) + // 这里不加is_deleted=1条件,因为无论它之前是什么状态,都设为0 + ) > 0; + + if (!recovered) { + // 如果没有更新到记录(说明之前不存在,或MP拦截了UPDATE?MP UpdateWrapper通常可以更新逻辑删除字段吗? + // 通常需要自定义SQL来恢复。 + // 如果无法恢复,则执行插入。 + HotSecurityMeetingAttendee attendee = new HotSecurityMeetingAttendee(); + attendee.setMeetingId(meetingId); + attendee.setPersonId(personId); + attendee.setCompanyId(companyId); + attendee.setPost(post); + attendee.setName(name); + attendee.setPhone(phone); + attendeeMapper.insert(attendee); + } + + // 推送待办(检查是否已存在) + // 线下会议不需要推送待办 + if (!"offline".equals(meeting.getMeetingType())) { + try { + if (!flowService.hasPendingTask("MEETING_SIGN", String.valueOf(meetingId), personId)) { + flowService.pushDirectTodo( + "MEETING_SIGN", + String.valueOf(meetingId), + "安全会议:" + topic, + companyId, + personId + ); + } + } catch (Exception e) { + log.error("推送安全会议待办失败: meetingId={}, personId={}", meetingId, personId, e); + } + } + } + } + + @Override + public HotSecurityMeetingVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询安全会议管理列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 安全会议管理分页列表 + */ + @Override + public TableDataInfo queryPageList(HotSecurityMeetingBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的安全会议管理列表 + * + * @param bo 查询条件 + * @return 安全会议管理列表 + */ + @Override + public List queryList(HotSecurityMeetingBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotSecurityMeetingBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotSecurityMeeting::getId); + Object beginTime = params.get("beginTime"); + Object endTime = params.get("endTime"); + if (beginTime != null && endTime != null) { + lqw.between(HotSecurityMeeting::getStartTime, beginTime, endTime); + } else if (beginTime != null) { + lqw.ge(HotSecurityMeeting::getStartTime, beginTime); + } else if (endTime != null) { + lqw.le(HotSecurityMeeting::getStartTime, endTime); + } + lqw.eq(bo.getCompanyId() != null, HotSecurityMeeting::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getMeetingTopic()), HotSecurityMeeting::getMeetingTopic, bo.getMeetingTopic()); + lqw.eq(StringUtils.isNotBlank(bo.getMeetingCategory()), HotSecurityMeeting::getMeetingCategory, bo.getMeetingCategory()); + lqw.eq(StringUtils.isNotBlank(bo.getMeetingType()), HotSecurityMeeting::getMeetingType, bo.getMeetingType()); + lqw.eq(StringUtils.isNotBlank(bo.getOfflineMeetingType()), HotSecurityMeeting::getOfflineMeetingType, bo.getOfflineMeetingType()); + lqw.eq(bo.getStartTime() != null, HotSecurityMeeting::getStartTime, bo.getStartTime()); + lqw.like(StringUtils.isNotBlank(bo.getHostName()), HotSecurityMeeting::getHostName, bo.getHostName()); + lqw.eq(StringUtils.isNotBlank(bo.getLocation()), HotSecurityMeeting::getLocation, bo.getLocation()); + lqw.like(StringUtils.isNotBlank(bo.getRecorderName()), HotSecurityMeeting::getRecorderName, bo.getRecorderName()); + lqw.eq(StringUtils.isNotBlank(bo.getFileAttachments()), HotSecurityMeeting::getFileAttachments, bo.getFileAttachments()); + lqw.eq(StringUtils.isNotBlank(bo.getVideoAttachments()), HotSecurityMeeting::getVideoAttachments, bo.getVideoAttachments()); + lqw.eq(StringUtils.isNotBlank(bo.getOnlineMeetingUrl()), HotSecurityMeeting::getOnlineMeetingUrl, bo.getOnlineMeetingUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getMeetingContent()), HotSecurityMeeting::getMeetingContent, bo.getMeetingContent()); + lqw.eq(StringUtils.isNotBlank(bo.getPhotoAttachments()), HotSecurityMeeting::getPhotoAttachments, bo.getPhotoAttachments()); + lqw.eq(StringUtils.isNotBlank(bo.getNextMonthPlan()), HotSecurityMeeting::getNextMonthPlan, bo.getNextMonthPlan()); + lqw.eq(StringUtils.isNotBlank(bo.getMeetingStatus()), HotSecurityMeeting::getMeetingStatus, bo.getMeetingStatus()); + lqw.eq(StringUtils.isNotBlank(bo.getParticipantIds()), HotSecurityMeeting::getParticipantIds, bo.getParticipantIds()); + lqw.eq(bo.getIsDeleted() != null, HotSecurityMeeting::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotSecurityMeeting entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除安全会议管理信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + if (ids != null && !ids.isEmpty()) { + List meetings = baseMapper.selectByIds(ids); + meetings.forEach(this::validateDeletable); + } + } + return baseMapper.deleteByIds(ids) > 0; + } + + private void validateEditable(HotSecurityMeeting meeting) { + if (meeting == null) { + return; + } + if (isOfflineMeetingInProgressOrFinished(meeting)) { + throw new ServiceException("线下会议进行中或已结束,不允许编辑"); + } + } + + private void validateDeletable(HotSecurityMeeting meeting) { + if (meeting == null) { + return; + } + if (isOfflineMeetingInProgressOrFinished(meeting)) { + throw new ServiceException("线下会议进行中或已结束,不允许删除"); + } + } + + private boolean isOfflineMeetingInProgressOrFinished(HotSecurityMeeting meeting) { + return OFFLINE_MEETING_TYPE.equals(meeting.getMeetingType()) + && (STATUS_IN_PROGRESS.equals(meeting.getMeetingStatus()) || STATUS_FINISHED.equals(meeting.getMeetingStatus())); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/strategy/SecurityMeetingSignStrategy.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/strategy/SecurityMeetingSignStrategy.java new file mode 100644 index 0000000..6b50273 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/strategy/SecurityMeetingSignStrategy.java @@ -0,0 +1,44 @@ +package com.hotwj.platform.securityManagement.securityMeeting.strategy; + +import com.hotwj.platform.flow.domain.SysFlowInstance; +import com.hotwj.platform.flow.domain.dto.FlowNextDto; +import com.hotwj.platform.flow.strategy.IFlowStrategy; +import com.hotwj.platform.securityManagement.securityMeeting.domain.HotSecurityMeeting; +import com.hotwj.platform.securityManagement.securityMeeting.mapper.HotSecurityMeetingMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class SecurityMeetingSignStrategy implements IFlowStrategy { + + private final HotSecurityMeetingMapper meetingMapper; + + @Override + public String getFlowCode() { + return "MEETING_SIGN"; + } + + @Override + public String getFlowName() { + return "安全会议"; + } + + @Override + public FlowNextDto next(SysFlowInstance instance, String currentNodeCode) { + return null; + } + + @Override + public FlowNextDto getInitialNode(String businessId) { + String nodeName = "安全会议签到"; + if (businessId != null) { + HotSecurityMeeting meeting = meetingMapper.selectById(businessId); + if (meeting != null && meeting.getMeetingTopic() != null) { + nodeName = "安全会议:" + meeting.getMeetingTopic(); + } + } + return new FlowNextDto("NODE_MEETING_SIGN", nodeName, null); + } +} + diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/task/HotSecurityMeetingStatusTask.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/task/HotSecurityMeetingStatusTask.java new file mode 100644 index 0000000..a6b8efd --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeeting/task/HotSecurityMeetingStatusTask.java @@ -0,0 +1,86 @@ +package com.hotwj.platform.securityManagement.securityMeeting.task; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.hotwj.platform.securityManagement.securityMeeting.domain.HotSecurityMeeting; +import com.hotwj.platform.securityManagement.securityMeeting.mapper.HotSecurityMeetingMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Component +@RequiredArgsConstructor +public class HotSecurityMeetingStatusTask { + + private static final List TARGET_MEETING_TYPES = Arrays.asList("produce", "example", "special", "offline"); + private static final String STATUS_NOT_STARTED = "1"; + private static final String STATUS_IN_PROGRESS = "2"; + private static final String STATUS_FINISHED = "3"; + + private final HotSecurityMeetingMapper hotSecurityMeetingMapper; + + @Scheduled(cron = "0 * * * * ?") + public void syncMeetingStatus() { + Date now = new Date(); + updateToInProgress(now); + updateToFinished(now); + } + + private void updateToInProgress(Date now) { + List meetings = hotSecurityMeetingMapper.selectList( + Wrappers.lambdaQuery() + .in(HotSecurityMeeting::getMeetingType, TARGET_MEETING_TYPES) + .eq(HotSecurityMeeting::getMeetingStatus, STATUS_NOT_STARTED) + .isNotNull(HotSecurityMeeting::getStartTime) + .le(HotSecurityMeeting::getStartTime, now) + ); + if (meetings.isEmpty()) { + return; + } + List ids = meetings.stream().map(HotSecurityMeeting::getId).collect(Collectors.toList()); + int updated = hotSecurityMeetingMapper.update( + null, + Wrappers.lambdaUpdate() + .set(HotSecurityMeeting::getMeetingStatus, STATUS_IN_PROGRESS) + .in(HotSecurityMeeting::getId, ids) + .eq(HotSecurityMeeting::getMeetingStatus, STATUS_NOT_STARTED) + ); + if (updated > 0) { + log.info("安全会议状态更新为进行中,数量={}", updated); + } + } + + private void updateToFinished(Date now) { + Date threshold = Date.from(now.toInstant().minus(1, ChronoUnit.HOURS)); + List meetings2 = hotSecurityMeetingMapper.selectList(Wrappers.lambdaQuery() + .in(HotSecurityMeeting::getMeetingType, TARGET_MEETING_TYPES)); + List meetings = hotSecurityMeetingMapper.selectList( + Wrappers.lambdaQuery() + .in(HotSecurityMeeting::getMeetingType, TARGET_MEETING_TYPES) + .eq(HotSecurityMeeting::getMeetingStatus, STATUS_IN_PROGRESS) + .isNotNull(HotSecurityMeeting::getEndTime) + .le(HotSecurityMeeting::getEndTime, threshold) + ); + if (meetings.isEmpty()) { + return; + } + List ids = meetings.stream().map(HotSecurityMeeting::getId).collect(Collectors.toList()); + int updated = hotSecurityMeetingMapper.update( + null, + Wrappers.lambdaUpdate() + .set(HotSecurityMeeting::getMeetingStatus, STATUS_FINISHED) + .in(HotSecurityMeeting::getId, ids) + .eq(HotSecurityMeeting::getMeetingStatus, STATUS_IN_PROGRESS) + ); + if (updated > 0) { + log.info("安全会议状态更新为已结束,数量={}", updated); + } + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/controller/HotSecurityMeetingAttendeeController.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/controller/HotSecurityMeetingAttendeeController.java new file mode 100644 index 0000000..28bec42 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/controller/HotSecurityMeetingAttendeeController.java @@ -0,0 +1,143 @@ +package com.hotwj.platform.securityManagement.securityMeetingAttendee.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.bo.HotSecurityMeetingAttendeeBo; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.bo.HotSecurityMeetingMyQueryBo; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.bo.HotSecurityMeetingSignBo; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.vo.HotSecurityMeetingAttendeeVo; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.vo.HotSecurityMeetingMyVo; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.service.IHotSecurityMeetingAttendeeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 会议参会人员详情 + * + * @author shihongwei + * @date 2026-02-03 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/securityMeetingAttendee") +@Tag(name = "会议参会人员详情", description = "会议参会人员详情管理") +public class HotSecurityMeetingAttendeeController extends BaseController { + + private final IHotSecurityMeetingAttendeeService hotSecurityMeetingAttendeeService; + + /** + * 查询我的会议列表 + */ +// @SaCheckPermission("securityManagement:securityMeetingAttendee:list") + @GetMapping("/myList") + @Operation(summary = "查询我的会议列表") + public TableDataInfo myList(HotSecurityMeetingMyQueryBo bo, PageQuery pageQuery) { + return hotSecurityMeetingAttendeeService.queryMyMeetingList(bo, pageQuery); + } + + /** + * 查询会议参会人员详情列表 + */ + @SaCheckPermission("securityManagement:securityMeetingAttendee:list") + @GetMapping("/list") + @Operation(summary = "分页查询会议参会人员详情列表") + public TableDataInfo list(HotSecurityMeetingAttendeeBo bo, PageQuery pageQuery) { + return hotSecurityMeetingAttendeeService.queryPageList(bo, pageQuery); + } + + /** + * 导出会议参会人员详情列表 + */ + @SaCheckPermission("securityManagement:securityMeetingAttendee:export") + @Log(title = "会议参会人员详情", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出会议参会人员详情列表") + public void export(HotSecurityMeetingAttendeeBo bo, HttpServletResponse response) { + List list = hotSecurityMeetingAttendeeService.queryList(bo); + ExcelUtil.exportExcel(list, "会议参会人员详情", HotSecurityMeetingAttendeeVo.class, response); + } + + /** + * 获取会议参会人员详情详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("securityManagement:securityMeetingAttendee:query") + @GetMapping("/{id}") + @Operation(summary = "获取会议参会人员详情详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotSecurityMeetingAttendeeService.queryById(id)); + } + + /** + * 新增会议参会人员详情 + */ + @SaCheckPermission("securityManagement:securityMeetingAttendee:add") + @Log(title = "会议参会人员详情", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增会议参会人员详情") + public R add(@Validated(AddGroup.class) @RequestBody HotSecurityMeetingAttendeeBo bo) { + return toAjax(hotSecurityMeetingAttendeeService.insertByBo(bo)); + } + + /** + * 修改会议参会人员详情 + */ + @SaCheckPermission("securityManagement:securityMeetingAttendee:edit") + @Log(title = "会议参会人员详情", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改会议参会人员详情") + public R edit(@Validated(EditGroup.class) @RequestBody HotSecurityMeetingAttendeeBo bo) { + return toAjax(hotSecurityMeetingAttendeeService.updateByBo(bo)); + } + + /** + * 删除会议参会人员详情 + * + * @param ids 主键串 + */ + @SaCheckPermission("securityManagement:securityMeetingAttendee:remove") + @Log(title = "会议参会人员详情", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除会议参会人员详情") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotSecurityMeetingAttendeeService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 完成会议签到 + * + * @param bo 签到业务对象 + */ + @Log(title = "完成会议签到", businessType = BusinessType.UPDATE) + @PostMapping("/completeSign") + @Operation(summary = "完成会议签到") + public R completeSign(@Validated @RequestBody HotSecurityMeetingSignBo bo) { + return R.ok(hotSecurityMeetingAttendeeService.completeSign(bo.getTaskId(), bo.getPhotoOssId(), bo.getSignOssId())); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/HotSecurityMeetingAttendee.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/HotSecurityMeetingAttendee.java new file mode 100644 index 0000000..837a172 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/HotSecurityMeetingAttendee.java @@ -0,0 +1,85 @@ +package com.hotwj.platform.securityManagement.securityMeetingAttendee.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 会议参会人员详情对象 hot_security_meeting_attendee + * + * @author shihongwei + * @date 2026-02-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_security_meeting_attendee") +public class HotSecurityMeetingAttendee extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + private Long companyId; + + /** + * 人员ID + */ + private String personId; + + /** + * 姓名 + */ + private String name; + + /** + * 手机号 + */ + private String phone; + + /** + * 岗位 + */ + private String post; + + /** + * 会议ID + */ + private Long meetingId; + + /** + * 参会人员照片OSS ID + */ + private Long attendeePhotoOssId; + + /** + * 参会人员签名OSS ID + */ + private Long attendeeSignOssId; + + /** + * 完成时间 + */ + private Date finishTime; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/bo/HotSecurityMeetingAttendeeBo.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/bo/HotSecurityMeetingAttendeeBo.java new file mode 100644 index 0000000..b19ab67 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/bo/HotSecurityMeetingAttendeeBo.java @@ -0,0 +1,83 @@ +package com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.bo; + +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.HotSecurityMeetingAttendee; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 会议参会人员详情业务对象 hot_security_meeting_attendee + * + * @author shihongwei + * @date 2026-02-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotSecurityMeetingAttendee.class, reverseConvertGenerate = false) +public class HotSecurityMeetingAttendeeBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + @NotNull(message = "公司ID(由应用层保证必填)不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 人员ID + */ + private String personId; + + /** + * 姓名 + */ + private String name; + + /** + * 手机号 + */ + private String phone; + + /** + * 岗位 + */ + private String post; + + /** + * 会议ID + */ + private Long meetingId; + + /** + * 参会人员照片OSS ID + */ + private Long attendeePhotoOssId; + + /** + * 参会人员签名OSS ID + */ + private Long attendeeSignOssId; + + /** + * 完成时间 + */ + private Date finishTime; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/bo/HotSecurityMeetingMyQueryBo.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/bo/HotSecurityMeetingMyQueryBo.java new file mode 100644 index 0000000..4ad7183 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/bo/HotSecurityMeetingMyQueryBo.java @@ -0,0 +1,21 @@ +package com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 我的会议查询对象 + * + * @author shihongwei + * @date 2026-02-03 + */ +@Data +@Schema(description = "我的会议查询对象") +public class HotSecurityMeetingMyQueryBo { + + /** + * 状态(0=进行中,1=已完成) + */ + @Schema(description = "状态(0=进行中,1=已完成)") + private Integer status; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/bo/HotSecurityMeetingSignBo.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/bo/HotSecurityMeetingSignBo.java new file mode 100644 index 0000000..644e61c --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/bo/HotSecurityMeetingSignBo.java @@ -0,0 +1,38 @@ +package com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * 会议签到业务对象 + * + * @author shihongwei + * @date 2026-02-03 + */ +@Data +@Schema(description = "会议签到业务对象") +public class HotSecurityMeetingSignBo { + + /** + * 任务ID + */ + @Schema(description = "任务ID") + @NotBlank(message = "任务ID不能为空") + private String taskId; + + /** + * 参会照片OSS ID + */ + @Schema(description = "参会照片OSS ID") + @NotNull(message = "参会照片不能为空") + private Long photoOssId; + + /** + * 签名文件OSS ID + */ + @Schema(description = "签名文件OSS ID") + @NotNull(message = "签名文件不能为空") + private Long signOssId; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/vo/HotSecurityMeetingAttendeeVo.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/vo/HotSecurityMeetingAttendeeVo.java new file mode 100644 index 0000000..847c4e6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/vo/HotSecurityMeetingAttendeeVo.java @@ -0,0 +1,121 @@ +package com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.HotSecurityMeetingAttendee; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 会议参会人员详情视图对象 hot_security_meeting_attendee + * + * @author shihongwei + * @date 2026-02-03 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotSecurityMeetingAttendee.class) +public class HotSecurityMeetingAttendeeVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "由=应用层保证必填") + private Long companyId; + + /** + * 人员ID + */ + @ExcelProperty(value = "人员ID") + private String personId; + + /** + * 姓名 + */ + @ExcelProperty(value = "姓名") + private String name; + + /** + * 手机号 + */ + @ExcelProperty(value = "手机号") + private String phone; + + /** + * 岗位 + */ + private String post; + + /** + * 会议ID + */ + @ExcelProperty(value = "会议ID") + private Long meetingId; + + /** + * 参会人员照片OSS ID + */ + @ExcelProperty(value = "参会人员照片OSS ID") + private Long attendeePhotoOssId; + + /** + * 参会人员签名OSS ID + */ + @ExcelProperty(value = "参会人员签名OSS ID") + private Long attendeeSignOssId; + + /** + * 参会人员照片URL + */ + @ExcelProperty(value = "参会人员照片URL") + private String attendeePhotoUrl; + + /** + * 参会人员签名URL + */ + @ExcelProperty(value = "参会人员签名URL") + private String attendeeSignUrl; + + /** + * 完成时间 + */ + @ExcelProperty(value = "完成时间") + private Date finishTime; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/vo/HotSecurityMeetingMyVo.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/vo/HotSecurityMeetingMyVo.java new file mode 100644 index 0000000..6ea4908 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/domain/vo/HotSecurityMeetingMyVo.java @@ -0,0 +1,54 @@ +package com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.vo; + +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.securityMeeting.domain.vo.HotSecurityMeetingVo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 我的会议视图对象 + * + * @author shihongwei + * @date 2026-02-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(description = "我的会议视图对象") +public class HotSecurityMeetingMyVo extends HotSecurityMeetingVo { + + /** + * 参会记录ID + */ + @Schema(description = "参会记录ID") + @ExcelProperty(value = "参会记录ID") + private Long attendeeId; + + /** + * 参会人员照片OSS ID + */ + @Schema(description = "参会人员照片OSS ID") + @ExcelProperty(value = "参会人员照片OSS ID") + private Long attendeePhotoOssId; + + /** + * 参会人员签名OSS ID + */ + @Schema(description = "参会人员签名OSS ID") + @ExcelProperty(value = "参会人员签名OSS ID") + private Long attendeeSignOssId; + + /** + * 完成时间 + */ + @Schema(description = "完成时间") + @ExcelProperty(value = "完成时间") + private Date finishTime; + + /** + * 任务id + */ + private String taskId; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/mapper/HotSecurityMeetingAttendeeMapper.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/mapper/HotSecurityMeetingAttendeeMapper.java new file mode 100644 index 0000000..f300ce5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/mapper/HotSecurityMeetingAttendeeMapper.java @@ -0,0 +1,30 @@ +package com.hotwj.platform.securityManagement.securityMeetingAttendee.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.HotSecurityMeetingAttendee; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.vo.HotSecurityMeetingAttendeeVo; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.vo.HotSecurityMeetingMyVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 会议参会人员详情Mapper接口 + * + * @author shihongwei + * @date 2026-02-03 + */ +@Mapper +public interface HotSecurityMeetingAttendeeMapper extends BaseMapperPlus { + + /** + * 查询我的会议列表 + * + * @param page 分页对象 + * @param personId 人员ID + * @param status 状态 + * @return 结果 + */ + Page selectMyMeetingList(@Param("page") Page page, @Param("personId") String personId, @Param("status") Integer status); + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/service/IHotSecurityMeetingAttendeeService.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/service/IHotSecurityMeetingAttendeeService.java new file mode 100644 index 0000000..7f69233 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/service/IHotSecurityMeetingAttendeeService.java @@ -0,0 +1,89 @@ +package com.hotwj.platform.securityManagement.securityMeetingAttendee.service; + +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.bo.HotSecurityMeetingAttendeeBo; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.bo.HotSecurityMeetingMyQueryBo; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.vo.HotSecurityMeetingAttendeeVo; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.vo.HotSecurityMeetingMyVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 会议参会人员详情Service接口 + * + * @author shihongwei + * @date 2026-02-03 + */ +public interface IHotSecurityMeetingAttendeeService { + + /** + * 完成会议签到 + * + * @param taskId 任务ID + * @param photoOssId 参会照片OSS ID + * @param signOssId 签名文件OSS ID + * @return 结果 + */ + Boolean completeSign(String taskId, Long photoOssId, Long signOssId); + + /** + * 查询我的会议列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 结果 + */ + TableDataInfo queryMyMeetingList(HotSecurityMeetingMyQueryBo bo, PageQuery pageQuery); + + /** + * 查询会议参会人员详情 + * + * @param id 主键 + * @return 会议参会人员详情 + */ + HotSecurityMeetingAttendeeVo queryById(Long id); + + /** + * 分页查询会议参会人员详情列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 会议参会人员详情分页列表 + */ + TableDataInfo queryPageList(HotSecurityMeetingAttendeeBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的会议参会人员详情列表 + * + * @param bo 查询条件 + * @return 会议参会人员详情列表 + */ + List queryList(HotSecurityMeetingAttendeeBo bo); + + /** + * 新增会议参会人员详情 + * + * @param bo 会议参会人员详情 + * @return 是否新增成功 + */ + Boolean insertByBo(HotSecurityMeetingAttendeeBo bo); + + /** + * 修改会议参会人员详情 + * + * @param bo 会议参会人员详情 + * @return 是否修改成功 + */ + Boolean updateByBo(HotSecurityMeetingAttendeeBo bo); + + /** + * 校验并批量删除会议参会人员详情信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/service/impl/HotSecurityMeetingAttendeeServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/service/impl/HotSecurityMeetingAttendeeServiceImpl.java new file mode 100644 index 0000000..eb26e64 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/securityMeetingAttendee/service/impl/HotSecurityMeetingAttendeeServiceImpl.java @@ -0,0 +1,245 @@ +package com.hotwj.platform.securityManagement.securityMeetingAttendee.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.common.helper.DriverLoginContextHelper; +import com.hotwj.platform.flow.domain.SysFlowInstance; +import com.hotwj.platform.flow.domain.SysFlowTask; +import com.hotwj.platform.flow.service.ISysFlowService; +import com.hotwj.platform.integration.ocr.SignatureVerifyService; +import com.hotwj.platform.securityManagement.securityMeeting.domain.HotSecurityMeeting; +import com.hotwj.platform.securityManagement.securityMeeting.mapper.HotSecurityMeetingMapper; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.HotSecurityMeetingAttendee; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.bo.HotSecurityMeetingAttendeeBo; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.bo.HotSecurityMeetingMyQueryBo; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.vo.HotSecurityMeetingAttendeeVo; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.domain.vo.HotSecurityMeetingMyVo; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.mapper.HotSecurityMeetingAttendeeMapper; +import com.hotwj.platform.securityManagement.securityMeetingAttendee.service.IHotSecurityMeetingAttendeeService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 会议参会人员详情Service业务层处理 + * + * @author shihongwei + * @date 2026-02-03 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotSecurityMeetingAttendeeServiceImpl implements IHotSecurityMeetingAttendeeService { + + private final HotSecurityMeetingAttendeeMapper baseMapper; + private final ISysFlowService flowService; + private final HotSecurityMeetingMapper meetingMapper; + private final SignatureVerifyService signatureVerifyService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean completeSign(String taskId, Long photoOssId, Long signOssId) { + // 1. 获取任务详情 + SysFlowTask task = flowService.getTaskById(taskId); + if (task == null) { + throw new ServiceException("任务不存在"); + } + + // 2. 校验权限 + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser != null && !task.getApproverId().equals(String.valueOf(loginUser.getBusinessUserId()))) { + throw new ServiceException("无权操作此任务"); + } + + // 3. 获取会议ID (BusinessId) + SysFlowInstance instance = flowService.getInstanceByTaskId(taskId); + if (instance == null) { + throw new ServiceException("流程实例不存在"); + } + Long meetingId = Long.valueOf(instance.getBusinessId()); + String personId = task.getApproverId(); + + // 4. 校验参数 + if (photoOssId == null) { + throw new ServiceException("请上传参会照片"); + } + if (signOssId == null) { + throw new ServiceException("请上传签名"); + } + + // 4.1 校验会议开始时间(预定会议未到开始时间不可参会) + HotSecurityMeeting meeting = meetingMapper.selectById(meetingId); + if (meeting == null) { + throw new ServiceException("会议不存在"); + } + Date now = new Date(); + Date startTime = meeting.getStartTime(); + if (startTime == null || now.before(startTime)) { + throw new ServiceException("会议尚未开始,不能参会"); + } + + // 5. 更新参会人员记录 + // 查找记录 + HotSecurityMeetingAttendee attendee = baseMapper.selectOne( + Wrappers.lambdaQuery() + .eq(HotSecurityMeetingAttendee::getMeetingId, meetingId) + .eq(HotSecurityMeetingAttendee::getPersonId, personId) + .last("limit 1") + ); + + if (attendee == null) { + // 尝试仅通过meetingId和创建者(如果personId不对)? 不,必须匹配personId + // 如果找不到,可能是数据不一致 + throw new ServiceException("参会人员记录不存在"); + } + if (DriverLoginContextHelper.isDriverPort() && signOssId != null) { + if (StringUtils.isBlank(attendee.getName())) { + throw new ServiceException("参会人员姓名不能为空"); + } + signatureVerifyService.validateSelfSignature(signOssId, attendee.getName()); + } + + attendee.setAttendeePhotoOssId(photoOssId); + attendee.setAttendeeSignOssId(signOssId); + attendee.setFinishTime(new Date()); + baseMapper.updateById(attendee); + + // 6. 完成待办任务 + flowService.audit(taskId, true, "签到完成", personId); + + return true; + } + + /** + * 查询我的会议列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 结果 + */ + @Override + public TableDataInfo queryMyMeetingList(HotSecurityMeetingMyQueryBo bo, PageQuery pageQuery) { + String personId = String.valueOf(LoginHelper.getBusinessUserId()); + Page page = baseMapper.selectMyMeetingList(pageQuery.build(), personId, bo.getStatus()); + return TableDataInfo.build(page); + } + + /** + * 查询会议参会人员详情 + * + * @param id 主键 + * @return 会议参会人员详情 + */ + @Override + public HotSecurityMeetingAttendeeVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询会议参会人员详情列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 会议参会人员详情分页列表 + */ + @Override + public TableDataInfo queryPageList(HotSecurityMeetingAttendeeBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的会议参会人员详情列表 + * + * @param bo 查询条件 + * @return 会议参会人员详情列表 + */ + @Override + public List queryList(HotSecurityMeetingAttendeeBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotSecurityMeetingAttendeeBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotSecurityMeetingAttendee::getId); + lqw.eq(bo.getCompanyId() != null, HotSecurityMeetingAttendee::getCompanyId, bo.getCompanyId()); + lqw.eq(StringUtils.isNotBlank(bo.getPersonId()), HotSecurityMeetingAttendee::getPersonId, bo.getPersonId()); + lqw.eq(bo.getMeetingId() != null, HotSecurityMeetingAttendee::getMeetingId, bo.getMeetingId()); + lqw.eq(bo.getAttendeePhotoOssId() != null, HotSecurityMeetingAttendee::getAttendeePhotoOssId, bo.getAttendeePhotoOssId()); + lqw.eq(bo.getAttendeeSignOssId() != null, HotSecurityMeetingAttendee::getAttendeeSignOssId, bo.getAttendeeSignOssId()); + lqw.eq(bo.getFinishTime() != null, HotSecurityMeetingAttendee::getFinishTime, bo.getFinishTime()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotSecurityMeetingAttendee::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotSecurityMeetingAttendee::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotSecurityMeetingAttendee::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增会议参会人员详情 + * + * @param bo 会议参会人员详情 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotSecurityMeetingAttendeeBo bo) { + HotSecurityMeetingAttendee add = MapstructUtils.convert(bo, HotSecurityMeetingAttendee.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改会议参会人员详情 + * + * @param bo 会议参会人员详情 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotSecurityMeetingAttendeeBo bo) { + HotSecurityMeetingAttendee update = MapstructUtils.convert(bo, HotSecurityMeetingAttendee.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotSecurityMeetingAttendee entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除会议参会人员详情信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/controller/HotServiceQualityComplaintController.java b/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/controller/HotServiceQualityComplaintController.java new file mode 100644 index 0000000..49f3cc7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/controller/HotServiceQualityComplaintController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.securityManagement.serviceQualityComplaint.controller; + +import com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.bo.HotServiceQualityComplaintBo; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.vo.HotServiceQualityComplaintVo; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.service.IHotServiceQualityComplaintService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 服务质量投诉 + * + * @author shihongwei + * @date 2026-01-07 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/serviceQualityComplaint") +@Tag(name = "服务质量投诉", description = "服务质量投诉管理") +public class HotServiceQualityComplaintController extends BaseController { + + private final IHotServiceQualityComplaintService hotServiceQualityComplaintService; + + /** + * 查询服务质量投诉列表 + */ + //@SaCheckPermission("securityManagement:serviceQualityComplaint:list") + @GetMapping("/list") + @Operation(summary = "分页查询服务质量投诉列表") + public TableDataInfo list(HotServiceQualityComplaintBo bo, PageQuery pageQuery) { + return hotServiceQualityComplaintService.queryPageList(bo, pageQuery); + } + + /** + * 导出服务质量投诉列表 + */ + //@SaCheckPermission("securityManagement:serviceQualityComplaint:export") + @Log(title = "服务质量投诉", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出服务质量投诉列表") + public void export(HotServiceQualityComplaintBo bo, HttpServletResponse response) { + List list = hotServiceQualityComplaintService.queryList(bo); + ExcelUtil.exportExcel(list, "服务质量投诉", HotServiceQualityComplaintVo.class, response); + } + + /** + * 获取服务质量投诉详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:serviceQualityComplaint:query") + @GetMapping("/{id}") + @Operation(summary = "获取服务质量投诉详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotServiceQualityComplaintService.queryById(id)); + } + + /** + * 新增服务质量投诉 + */ + //@SaCheckPermission("securityManagement:serviceQualityComplaint:add") + @Log(title = "服务质量投诉", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增服务质量投诉") + public R add(@Validated(AddGroup.class) @RequestBody HotServiceQualityComplaintBo bo) { + return toAjax(hotServiceQualityComplaintService.insertByBo(bo)); + } + + /** + * 修改服务质量投诉 + */ + //@SaCheckPermission("securityManagement:serviceQualityComplaint:edit") + @Log(title = "服务质量投诉", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改服务质量投诉") + public R edit(@Validated(EditGroup.class) @RequestBody HotServiceQualityComplaintBo bo) { + return toAjax(hotServiceQualityComplaintService.updateByBo(bo)); + } + + /** + * 删除服务质量投诉 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:serviceQualityComplaint:remove") + @Log(title = "服务质量投诉", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除服务质量投诉") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotServiceQualityComplaintService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/domain/HotServiceQualityComplaint.java b/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/domain/HotServiceQualityComplaint.java new file mode 100644 index 0000000..289b0c8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/domain/HotServiceQualityComplaint.java @@ -0,0 +1,130 @@ +package com.hotwj.platform.securityManagement.serviceQualityComplaint.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 服务质量投诉对象 hot_service_quality_complaint + * + * @author shihongwei + * @date 2026-01-07 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_service_quality_complaint") +public class HotServiceQualityComplaint extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 投诉对象(1=人员 2=企业 3=车辆) + */ + private String complaintTarget; + + /** + * 受理人ID + */ + private Long acceptorId; + + /** + * 受理人 + */ + private String acceptorName; + + /** + * 处理人ID + */ + private Long handlerId; + + /** + * 处理人 + */ + private String handlerName; + + /** + * 案件发生时间 + */ + private Date caseTime; + + /** + * 投诉人 + */ + private String complainantName; + + /** + * 投诉人电话 + */ + private String complainantPhone; + + /** + * 投诉方式 + */ + private String complaintChannel; + + /** + * 投诉服务 + */ + private String complaintService; + + /** + * 案件发生地点 + */ + private String casePlace; + + /** + * 案件概况 + */ + private String caseSummary; + + /** + * 处理情况 + */ + private String handleResult; + + /** + * 投诉诉求 + */ + private String complaintRequest; + + /** + * 相关文件图片URL(多个图片,隔开) + */ + private String attachmentUrl; + + /** + * 受理人签名图片URL + */ + private String acceptorSignImage; + + /** + * 处理人签名图片URL + */ + private String handlerSignImage; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/domain/bo/HotServiceQualityComplaintBo.java b/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/domain/bo/HotServiceQualityComplaintBo.java new file mode 100644 index 0000000..8915592 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/domain/bo/HotServiceQualityComplaintBo.java @@ -0,0 +1,136 @@ +package com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.bo; + +import com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.HotServiceQualityComplaint; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 服务质量投诉业务对象 hot_service_quality_complaint + * + * @author shihongwei + * @date 2026-01-07 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotServiceQualityComplaint.class, reverseConvertGenerate = false) +public class HotServiceQualityComplaintBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 投诉对象(1=人员 2=企业 3=车辆) + */ + @NotBlank(message = "投诉对象(1=人员 2=企业 3=车辆)不能为空", groups = {AddGroup.class, EditGroup.class}) + private String complaintTarget; + + /** + * 受理人ID + */ + @NotNull(message = "受理人ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long acceptorId; + + /** + * 受理人 + */ + private String acceptorName; + + /** + * 处理人ID + */ + @NotNull(message = "处理人ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long handlerId; + + /** + * 处理人 + */ + private String handlerName; + + /** + * 案件发生时间 + */ + @NotNull(message = "案件发生时间不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date caseTime; + + /** + * 投诉人 + */ + @NotBlank(message = "投诉人不能为空", groups = {AddGroup.class, EditGroup.class}) + private String complainantName; + + /** + * 投诉人电话 + */ + @NotBlank(message = "投诉人电话不能为空", groups = {AddGroup.class, EditGroup.class}) + private String complainantPhone; + + /** + * 投诉方式 + */ + @NotBlank(message = "投诉方式不能为空", groups = {AddGroup.class, EditGroup.class}) + private String complaintChannel; + + /** + * 投诉服务 + */ + private String complaintService; + + /** + * 案件发生地点 + */ + private String casePlace; + + /** + * 案件概况 + */ + private String caseSummary; + + /** + * 处理情况 + */ + private String handleResult; + + /** + * 投诉诉求 + */ + private String complaintRequest; + + /** + * 相关文件图片URL(多个图片,隔开) + */ + private String attachmentUrl; + + /** + * 受理人签名图片URL + */ + private String acceptorSignImage; + + /** + * 处理人签名图片URL + */ + private String handlerSignImage; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/domain/vo/HotServiceQualityComplaintVo.java b/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/domain/vo/HotServiceQualityComplaintVo.java new file mode 100644 index 0000000..244cf91 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/domain/vo/HotServiceQualityComplaintVo.java @@ -0,0 +1,169 @@ +package com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.HotServiceQualityComplaint; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 服务质量投诉视图对象 hot_service_quality_complaint + * + * @author shihongwei + * @date 2026-01-07 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotServiceQualityComplaint.class) +public class HotServiceQualityComplaintVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 投诉对象(1=人员 2=企业 3=车辆) + */ + @ExcelProperty(value = "投诉对象(1=人员 2=企业 3=车辆)") + private String complaintTarget; + + /** + * 受理人ID + */ + @ExcelProperty(value = "受理人ID") + private Long acceptorId; + + /** + * 受理人 + */ + @ExcelProperty(value = "受理人") + private String acceptorName; + + /** + * 处理人ID + */ + @ExcelProperty(value = "处理人ID") + private Long handlerId; + + /** + * 处理人 + */ + @ExcelProperty(value = "处理人") + private String handlerName; + + /** + * 案件发生时间 + */ + @ExcelProperty(value = "案件发生时间") + private Date caseTime; + + /** + * 投诉人 + */ + @ExcelProperty(value = "投诉人") + private String complainantName; + + /** + * 投诉人电话 + */ + @ExcelProperty(value = "投诉人电话") + private String complainantPhone; + + /** + * 投诉方式 + */ + @ExcelProperty(value = "投诉方式") + private String complaintChannel; + + /** + * 投诉服务 + */ + @ExcelProperty(value = "投诉服务") + private String complaintService; + + /** + * 案件发生地点 + */ + @ExcelProperty(value = "案件发生地点") + private String casePlace; + + /** + * 案件概况 + */ + @ExcelProperty(value = "案件概况") + private String caseSummary; + + /** + * 处理情况 + */ + @ExcelProperty(value = "处理情况") + private String handleResult; + + /** + * 投诉诉求 + */ + @ExcelProperty(value = "投诉诉求") + private String complaintRequest; + + /** + * 相关文件图片URL(多个图片,隔开) + */ + @ExcelProperty(value = "相关文件图片URL", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "多=个图片,隔开") + private String attachmentUrl; + + /** + * 相关文件图片URL(多个图片,隔开)Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "attachmentUrl") + private String attachmentUrlUrl; + /** + * 受理人签名图片URL + */ + @ExcelProperty(value = "受理人签名图片URL") + private String acceptorSignImage; + + /** + * 受理人签名图片URLUrl + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "acceptorSignImage") + private String acceptorSignImageUrl; + /** + * 处理人签名图片URL + */ + @ExcelProperty(value = "处理人签名图片URL") + private String handlerSignImage; + + /** + * 处理人签名图片URLUrl + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "handlerSignImage") + private String handlerSignImageUrl; + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/mapper/HotServiceQualityComplaintMapper.java b/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/mapper/HotServiceQualityComplaintMapper.java new file mode 100644 index 0000000..d86a08a --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/mapper/HotServiceQualityComplaintMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.serviceQualityComplaint.mapper; + +import com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.HotServiceQualityComplaint; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.vo.HotServiceQualityComplaintVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 服务质量投诉Mapper接口 + * + * @author shihongwei + * @date 2026-01-07 + */ +@Mapper +public interface HotServiceQualityComplaintMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/service/IHotServiceQualityComplaintService.java b/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/service/IHotServiceQualityComplaintService.java new file mode 100644 index 0000000..fe61cf9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/service/IHotServiceQualityComplaintService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.securityManagement.serviceQualityComplaint.service; + +import com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.bo.HotServiceQualityComplaintBo; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.vo.HotServiceQualityComplaintVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 服务质量投诉Service接口 + * + * @author shihongwei + * @date 2026-01-07 + */ +public interface IHotServiceQualityComplaintService { + + /** + * 查询服务质量投诉 + * + * @param id 主键 + * @return 服务质量投诉 + */ + HotServiceQualityComplaintVo queryById(Long id); + + /** + * 分页查询服务质量投诉列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 服务质量投诉分页列表 + */ + TableDataInfo queryPageList(HotServiceQualityComplaintBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的服务质量投诉列表 + * + * @param bo 查询条件 + * @return 服务质量投诉列表 + */ + List queryList(HotServiceQualityComplaintBo bo); + + /** + * 新增服务质量投诉 + * + * @param bo 服务质量投诉 + * @return 是否新增成功 + */ + Boolean insertByBo(HotServiceQualityComplaintBo bo); + + /** + * 修改服务质量投诉 + * + * @param bo 服务质量投诉 + * @return 是否修改成功 + */ + Boolean updateByBo(HotServiceQualityComplaintBo bo); + + /** + * 校验并批量删除服务质量投诉信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/service/impl/HotServiceQualityComplaintServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/service/impl/HotServiceQualityComplaintServiceImpl.java new file mode 100644 index 0000000..e05d54f --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/serviceQualityComplaint/service/impl/HotServiceQualityComplaintServiceImpl.java @@ -0,0 +1,160 @@ +package com.hotwj.platform.securityManagement.serviceQualityComplaint.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.HotServiceQualityComplaint; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.bo.HotServiceQualityComplaintBo; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.domain.vo.HotServiceQualityComplaintVo; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.mapper.HotServiceQualityComplaintMapper; +import com.hotwj.platform.securityManagement.serviceQualityComplaint.service.IHotServiceQualityComplaintService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 服务质量投诉Service业务层处理 + * + * @author shihongwei + * @date 2026-01-07 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotServiceQualityComplaintServiceImpl implements IHotServiceQualityComplaintService { + + private final HotServiceQualityComplaintMapper baseMapper; + + /** + * 查询服务质量投诉 + * + * @param id 主键 + * @return 服务质量投诉 + */ + @Override + public HotServiceQualityComplaintVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询服务质量投诉列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 服务质量投诉分页列表 + */ + @Override + public TableDataInfo queryPageList(HotServiceQualityComplaintBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的服务质量投诉列表 + * + * @param bo 查询条件 + * @return 服务质量投诉列表 + */ + @Override + public List queryList(HotServiceQualityComplaintBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotServiceQualityComplaintBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotServiceQualityComplaint::getId); + lqw.eq(bo.getCompanyId() != null, HotServiceQualityComplaint::getCompanyId, bo.getCompanyId()); + lqw.eq(StringUtils.isNotBlank(bo.getComplaintTarget()), HotServiceQualityComplaint::getComplaintTarget, bo.getComplaintTarget()); + lqw.eq(bo.getAcceptorId() != null, HotServiceQualityComplaint::getAcceptorId, bo.getAcceptorId()); + lqw.like(StringUtils.isNotBlank(bo.getAcceptorName()), HotServiceQualityComplaint::getAcceptorName, bo.getAcceptorName()); + lqw.eq(bo.getHandlerId() != null, HotServiceQualityComplaint::getHandlerId, bo.getHandlerId()); + lqw.like(StringUtils.isNotBlank(bo.getHandlerName()), HotServiceQualityComplaint::getHandlerName, bo.getHandlerName()); + lqw.eq(bo.getCaseTime() != null, HotServiceQualityComplaint::getCaseTime, bo.getCaseTime()); + lqw.like(StringUtils.isNotBlank(bo.getComplainantName()), HotServiceQualityComplaint::getComplainantName, bo.getComplainantName()); + lqw.eq(StringUtils.isNotBlank(bo.getComplainantPhone()), HotServiceQualityComplaint::getComplainantPhone, bo.getComplainantPhone()); + lqw.eq(StringUtils.isNotBlank(bo.getComplaintChannel()), HotServiceQualityComplaint::getComplaintChannel, bo.getComplaintChannel()); + lqw.eq(StringUtils.isNotBlank(bo.getComplaintService()), HotServiceQualityComplaint::getComplaintService, bo.getComplaintService()); + lqw.eq(StringUtils.isNotBlank(bo.getCasePlace()), HotServiceQualityComplaint::getCasePlace, bo.getCasePlace()); + lqw.eq(StringUtils.isNotBlank(bo.getCaseSummary()), HotServiceQualityComplaint::getCaseSummary, bo.getCaseSummary()); + lqw.eq(StringUtils.isNotBlank(bo.getHandleResult()), HotServiceQualityComplaint::getHandleResult, bo.getHandleResult()); + lqw.eq(StringUtils.isNotBlank(bo.getComplaintRequest()), HotServiceQualityComplaint::getComplaintRequest, bo.getComplaintRequest()); + lqw.eq(StringUtils.isNotBlank(bo.getAttachmentUrl()), HotServiceQualityComplaint::getAttachmentUrl, bo.getAttachmentUrl()); + lqw.eq(StringUtils.isNotBlank(bo.getAcceptorSignImage()), HotServiceQualityComplaint::getAcceptorSignImage, bo.getAcceptorSignImage()); + lqw.eq(StringUtils.isNotBlank(bo.getHandlerSignImage()), HotServiceQualityComplaint::getHandlerSignImage, bo.getHandlerSignImage()); + lqw.eq(bo.getIsDeleted() != null, HotServiceQualityComplaint::getIsDeleted, bo.getIsDeleted()); + if (params != null) { + Object beginCaseTime = params.get("beginCaseTime"); + Object endCaseTime = params.get("endCaseTime"); + if (beginCaseTime instanceof java.util.Date) { + lqw.ge(HotServiceQualityComplaint::getCaseTime, (java.util.Date) beginCaseTime); + } + if (endCaseTime instanceof java.util.Date) { + lqw.le(HotServiceQualityComplaint::getCaseTime, (java.util.Date) endCaseTime); + } + } + return lqw; + } + + /** + * 新增服务质量投诉 + * + * @param bo 服务质量投诉 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotServiceQualityComplaintBo bo) { + HotServiceQualityComplaint add = MapstructUtils.convert(bo, HotServiceQualityComplaint.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改服务质量投诉 + * + * @param bo 服务质量投诉 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotServiceQualityComplaintBo bo) { + HotServiceQualityComplaint update = MapstructUtils.convert(bo, HotServiceQualityComplaint.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotServiceQualityComplaint entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除服务质量投诉信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/training/controller/HotTrainingController.java b/src/main/java/com/hotwj/platform/securityManagement/training/controller/HotTrainingController.java new file mode 100644 index 0000000..7eb3254 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/training/controller/HotTrainingController.java @@ -0,0 +1,378 @@ +package com.hotwj.platform.securityManagement.training.controller; + +import com.hotwj.platform.securityManagement.training.domain.bo.HotTrainingBo; +import com.hotwj.platform.securityManagement.training.domain.dto.RegisterVerifyDto; +import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo; +import com.hotwj.platform.securityManagement.training.service.IHotTrainingAnnualRecordExportService; +import com.hotwj.platform.securityManagement.training.service.IHotTrainingService; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.bo.HotTrainingCourseConfigBo; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.vo.HotTrainingCourseConfigVo; +import com.hotwj.platform.securityManagement.trainingCourseConfig.service.IHotTrainingCourseConfigService; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.bo.HotTrainingParticipantAddBo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.TrainingParticipantStatVo; +import com.hotwj.platform.securityManagement.trainingParticipant.service.IHotTrainingParticipantService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.redis.utils.RedisUtils; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 安全教育培训-培训主 + * + * @author shihongwei + * @date 2025-12-17 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/training") +@Tag(name = "安全教育培训", description = "安全教育培训-培训主管理") +public class HotTrainingController extends BaseController { + + private static final String VERIFY_OSS_KEY_PREFIX = "verify:oss:"; + private static final Duration VERIFY_OSS_TTL = Duration.ofDays(7); + + private final IHotTrainingService hotTrainingService; + private final IHotTrainingParticipantService hotTrainingParticipantService; + private final IHotTrainingCourseConfigService hotTrainingCourseConfigService; + private final IHotTrainingAnnualRecordExportService hotTrainingAnnualRecordExportService; + + /** + * 查询安全教育培训-培训主列表 + */ + //@SaCheckPermission("securityManagement:training:list") + @GetMapping("/list") + @Operation(summary = "查询安全教育培训-培训主列表") + public TableDataInfo list(HotTrainingBo bo, PageQuery pageQuery) { + TableDataInfo page = hotTrainingService.queryPageList(bo, pageQuery); + List rows = page.getRows(); + if (rows == null || rows.isEmpty()) { + return page; + } + List trainingIds = rows.stream() + .map(HotTrainingVo::getId) + .collect(Collectors.toList()); + List stats = hotTrainingParticipantService.queryTrainingParticipantStats(trainingIds); + if (stats == null || stats.isEmpty()) { + for (HotTrainingVo vo : rows) { + vo.setTotalCount(0L); + vo.setPassedCount(0L); + vo.setCompleteCount(0L); + vo.setIncompleteCount(0L); + vo.setPendingReviewCount(0L); + } + return page; + } + Map statMap = stats.stream() + .collect(Collectors.toMap(TrainingParticipantStatVo::getTrainingId, Function.identity())); + for (HotTrainingVo vo : rows) { + TrainingParticipantStatVo stat = statMap.get(vo.getId()); + if (stat == null) { + vo.setTotalCount(0L); + vo.setPassedCount(0L); + vo.setIncompleteCount(0L); + vo.setPendingReviewCount(0L); + vo.setCompleteCount(0L); + } else { + vo.setTotalCount(stat.getTotalCount()); + vo.setPassedCount(stat.getPassedCount()); + vo.setIncompleteCount(stat.getIncompleteCount()); + vo.setPendingReviewCount(stat.getPendingReviewCount()); + vo.setCompleteCount(stat.getCompleteCount()); + } + } + return page; + } + + @PostMapping("/registerVerify") + @Operation(summary = "注册核验数据") + public R registerVerify(@RequestBody @Validated RegisterVerifyDto dto) { + RedisUtils.setCacheObject(VERIFY_OSS_KEY_PREFIX + dto.getJsonOssId(), "1", VERIFY_OSS_TTL); + if (dto.getResourceOssIds() != null) { + for (String ossId : dto.getResourceOssIds()) { + if (org.dromara.common.core.utils.StringUtils.isBlank(ossId)) { + continue; + } + RedisUtils.setCacheObject(VERIFY_OSS_KEY_PREFIX + ossId, "1", VERIFY_OSS_TTL); + } + } + return R.ok(true); + } + + /** + * 导出安全教育培训-培训主列表 + */ + //@SaCheckPermission("securityManagement:training:export") + @Log(title = "安全教育培训-培训主", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出安全教育培训-培训主列表") + public void export(HotTrainingBo bo, HttpServletResponse response) { + List list = hotTrainingService.queryList(bo); + ExcelUtil.exportExcel(list, "安全教育培训-培训主", HotTrainingVo.class, response); + } + + /** + * 获取安全教育培训-培训主详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:training:query") + @GetMapping("/{id}") + @Operation(summary = "获取安全教育培训-培训主详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotTrainingService.queryById(id)); + } + + /** + * 新增安全教育培训-培训主 + */ + //@SaCheckPermission("securityManagement:training:add") + @Log(title = "安全教育培训-培训主", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增安全教育培训-培训主") + public R add(@Validated(AddGroup.class) @RequestBody HotTrainingBo bo) { + return toAjax(hotTrainingService.insertByBo(bo)); + } + + /** + * 修改安全教育培训-培训主 + */ + //@SaCheckPermission("securityManagement:training:edit") + @Log(title = "安全教育培训-培训主", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改安全教育培训-培训主") + public R edit(@Validated(EditGroup.class) @RequestBody HotTrainingBo bo) { + return toAjax(hotTrainingService.updateByBo(bo)); + } + + //@SaCheckPermission("securityManagement:training:add") + @Log(title = "安全教育培训-培训主", businessType = BusinessType.OTHER) + @RepeatSubmit() + @PostMapping("/issue") + @Operation(summary = "下发安全教育培训") + public R issue( + @RequestParam Long trainingId, + @RequestParam String companyIds + ) { + HotTrainingVo source = hotTrainingService.queryById(trainingId); + if (source == null) { + return R.fail("培训计划不存在"); + } + if (StringUtils.isBlank(source.getIndustryType())) { + return R.fail("下发失败:行业类型不能为空,请先完善培训计划"); + } + if (source.getClassHours() == null) { + return R.fail("下发失败:所需课时不能为空,请先完善培训计划"); + } + if (StringUtils.isBlank(source.getPaperIds())) { + return R.fail("下发失败:请先绑定试卷后再下发企业"); + } + if (companyIds == null || companyIds.isBlank()) { + return R.fail("企业ID列表不能为空"); + } + HotTrainingCourseConfigBo sourceCourseBo = new HotTrainingCourseConfigBo(); + sourceCourseBo.setCompanyId(source.getCompanyId()); + sourceCourseBo.setTrainingId(source.getId()); + sourceCourseBo.setIsDeleted(0L); + List sourceCourseConfigs = hotTrainingCourseConfigService.queryList(sourceCourseBo); + if (sourceCourseConfigs == null || sourceCourseConfigs.isEmpty()) { + return R.fail("下发失败:请先配置课程后再下发企业"); + } + String[] parts = companyIds.split(","); + int success = 0; + int fail = 0; + StringBuilder sb = new StringBuilder(); + for (String p : parts) { + String s = p.trim(); + if (s.isEmpty()) continue; + try { + Long cid = Long.valueOf(s); + HotTrainingBo bo = new HotTrainingBo(); + bo.setCompanyId(cid); + bo.setPlanName(source.getPlanName()); + bo.setPersonType(source.getPersonType()); + bo.setStartTime(source.getStartTime()); + bo.setEndTime(source.getEndTime()); + bo.setClassHours(source.getClassHours()); + bo.setPaperIds(source.getPaperIds()); + bo.setTrainingLocation(source.getTrainingLocation()); + bo.setTeacherName(source.getTeacherName()); + bo.setPlanOwner("总部"); + bo.setRemark(source.getRemark()); + bo.setIsEnabled(source.getIsEnabled()); + bo.setIsMakeUp(source.getIsMakeUp()); + bo.setCourseIds(source.getCourseIds()); + bo.setTrainingType(source.getTrainingType()); + bo.setParticipantIds(source.getParticipantIds()); + bo.setAssessmentDetail(source.getAssessmentDetail()); + bo.setReviewFlow(source.getReviewFlow()); + bo.setMeetingType(source.getMeetingType()); + bo.setMeetingCategory(source.getMeetingCategory()); + bo.setRecorderId(source.getRecorderId()); + bo.setRecorderName(source.getRecorderName()); + bo.setFiles(source.getFiles()); + bo.setPhotos(source.getPhotos()); + bo.setIsIssued(1L); + bo.setIsDeleted(0L); + if (hotTrainingService.insertByBo(bo)) { + try { + copyTrainingCourseConfig(sourceCourseConfigs, cid, bo.getId()); + success++; + } catch (Exception e) { + fail++; + sb.append("课程配置下发失败: ").append(cid).append(" -> ").append(e.getMessage()).append("\n"); + if (bo.getId() != null) { + hotTrainingService.deleteWithValidByIds(List.of(bo.getId()), false); + } + } + } else { + fail++; + sb.append("下发失败: ").append(cid).append("\n"); + } + } catch (Exception e) { + fail++; + sb.append("下发异常: ").append(s).append(" -> ").append(e.getMessage()).append("\n"); + } + } + HotTrainingBo upd = new HotTrainingBo(); + upd.setId(trainingId); + upd.setIsIssued(1L); + try { + hotTrainingService.updateByBo(upd); + } catch (Exception ignored) { + } + return R.ok("成功下发: " + success + " 家; 失败: " + fail + " 家" + (sb.length() > 0 ? ("\n" + sb) : "")); + } + + private void copyTrainingCourseConfig(List sourceCourseConfigs, Long companyId, Long trainingId) { + if (sourceCourseConfigs == null || sourceCourseConfigs.isEmpty()) { + return; + } + List insertedIds = new ArrayList<>(); + try { + for (HotTrainingCourseConfigVo sourceCourseConfig : sourceCourseConfigs) { + HotTrainingCourseConfigBo addBo = new HotTrainingCourseConfigBo(); + addBo.setCompanyId(companyId); + addBo.setTrainingId(trainingId); + addBo.setCourseName(sourceCourseConfig.getCourseName()); + addBo.setCourseId(sourceCourseConfig.getCourseId()); + addBo.setCourseBelong(sourceCourseConfig.getCourseBelong()); + addBo.setStudyDuration(sourceCourseConfig.getStudyDuration()); + addBo.setIsDeleted(0L); + boolean inserted = hotTrainingCourseConfigService.insertByBo(addBo); + if (!inserted) { + throw new IllegalStateException("课程插入失败"); + } + if (addBo.getId() != null) { + insertedIds.add(addBo.getId()); + } + } + } catch (Exception e) { + if (!insertedIds.isEmpty()) { + hotTrainingCourseConfigService.deleteWithValidByIds(insertedIds, false); + } + throw new IllegalStateException("课程配置复制失败", e); + } + } + + /** + * 删除安全教育培训-培训主 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:training:remove") + @Log(title = "安全教育培训-培训主", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除安全教育培训-培训主") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotTrainingService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 添加培训参与人员 + */ + //@SaCheckPermission("securityManagement:training:add") + @Log(title = "安全教育培训-培训主表", businessType = BusinessType.INSERT) + @PostMapping("/addParticipants") + @Operation( + summary = "添加培训参与人员", + description = "按人员类型批量添加参与者:type=driver(驾驶员) 或 manager(管理员)。" + + "当人员已存在且 is_deleted=1 时将恢复为 0;同步初始化/恢复汇总宽表(hot_training_archive_summary);" + + "根据人员类型发送系统通知。" + ) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "操作成功", content = @Content(schema = @Schema(implementation = Void.class))), + @ApiResponse(responseCode = "400", description = "请求参数错误", content = @Content) + }) + public R addParticipants( + @Validated + @RequestBody + @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = "添加参与人员请求体", + required = true, + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = HotTrainingParticipantAddBo.class), + examples = @ExampleObject( + name = "示例", + value = """ + { + "trainingId": 123, + "personList": [ + { "type": "driver", "ids": "10001,10002" }, + { "type": "manager", "ids": "20001" } + ] + } + """ + ) + ) + ) + HotTrainingParticipantAddBo bo + ) { + return toAjax(hotTrainingParticipantService.addParticipants(bo)); + } + + /** + * 导出个人年度培训记录(单人导出Excel,多人导出ZIP) + */ + @PostMapping("/exportPersonalAnnualRecord") + @Operation(summary = "导出个人年度培训记录") + public void exportPersonalAnnualRecord(@RequestParam String driverId, + @RequestParam(required = false) Integer year, + @RequestParam(required = false) String trainingType, + HttpServletResponse response) { + hotTrainingAnnualRecordExportService.exportPersonalAnnualRecord(driverId, year, trainingType, response); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/training/domain/HotTraining.java b/src/main/java/com/hotwj/platform/securityManagement/training/domain/HotTraining.java new file mode 100644 index 0000000..f6652d8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/training/domain/HotTraining.java @@ -0,0 +1,193 @@ +package com.hotwj.platform.securityManagement.training.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 安全教育培训-培训主对象 hot_training + * + * @author shihongwei + * @date 2025-12-17 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_training") +public class HotTraining extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 计划名称 + */ + private String planName; + + /** + * 人员类型(字典) + */ + private String personType; + + /** + * 起始时间 + */ + private Date startTime; + + /** + * 结束时间 + */ + private Date endTime; + + /** + * 课时(小时) + */ + private Long classHours; + + /** + * 绑定试卷ID列表(逗号分隔) + */ + private String paperIds; + + /** + * 培训地点 + */ + private String trainingLocation; + + /** + * 培训老师 + */ + private String teacherName; + + /** + * 计划所属 + */ + private String planOwner; + + /** + * 备注 + */ + private String remark; + + /** + * 总人数 + */ + private Long totalCount; + + /** + * 已通过 + */ + private Long passedCount; + + /** + * 未完成 + */ + private Long incompleteCount; + + /** + * 待审核 + */ + private Long pendingReviewCount; + + /** + * 是否启用:1是 0否 + */ + private Long isEnabled; + + /** + * 是否补学:1是 0否 + */ + private Long isMakeUp; + + /** + * 是否下发:1是 0否 + */ + private Long isIssued; + + /** + * 课程配置ID列表(逗号分隔) + */ + private String courseIds; + + /** + * 下发公司ID + */ + private String issuedCompanyId; + + /** + * 培训类型(岗前培训/日常培训等,字典) + */ + private String trainingType; + + /** + * 指定参与人员ID列表(逗号分隔) + */ + private String participantIds; + + /** + * 对应的考核明细配置(JSON) + */ + private String assessmentDetail; + + /** + * 对应的审核子表/流程配置(JSON) + */ + private String reviewFlow; + + /** + * 会议类型 + */ + private String meetingType; + + /** + * 会议种类 + */ + private String meetingCategory; + + /** + * 记录人ID + */ + private Long recorderId; + + /** + * 记录人姓名 + */ + private String recorderName; + + /** + * 附件 + */ + private String files; + + /** + * 照片 + */ + private String photos; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 行业类型 + */ + private String industryType; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/training/domain/bo/HotTrainingBo.java b/src/main/java/com/hotwj/platform/securityManagement/training/domain/bo/HotTrainingBo.java new file mode 100644 index 0000000..b9e66b1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/training/domain/bo/HotTrainingBo.java @@ -0,0 +1,189 @@ +package com.hotwj.platform.securityManagement.training.domain.bo; + +import com.hotwj.platform.securityManagement.training.domain.HotTraining; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 安全教育培训-培训主业务对象 hot_training + * + * @author shihongwei + * @date 2025-12-17 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotTraining.class, reverseConvertGenerate = false) +public class HotTrainingBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 计划名称 + */ + private String planName; + + /** + * 人员类型(字典) + */ + private String personType; + + /** + * 起始时间 + */ + private Date startTime; + + /** + * 结束时间 + */ + private Date endTime; + + /** + * 课时(小时) + */ + private Long classHours; + + /** + * 绑定试卷ID列表(逗号分隔) + */ + private String paperIds; + + /** + * 培训地点 + */ + private String trainingLocation; + + /** + * 培训老师 + */ + private String teacherName; + + /** + * 计划所属 + */ + private String planOwner; + + /** + * 备注 + */ + private String remark; + + /** + * 总人数 + */ + private Long totalCount; + + /** + * 已通过 + */ + private Long passedCount; + + /** + * 未完成 + */ + private Long incompleteCount; + + /** + * 待审核 + */ + private Long pendingReviewCount; + + /** + * 是否启用:1是 0否 + */ + private Long isEnabled; + + /** + * 是否补学:1是 0否 + */ + private Long isMakeUp; + + /** + * 是否下发:1是 0否 + */ + private Long isIssued; + + /** + * 课程配置ID列表(逗号分隔) + */ + private String courseIds; + + /** + * 下发公司ID + */ + private String issuedCompanyId; + + /** + * 培训类型(岗前培训/日常培训等,字典) + */ + private String trainingType; + + /** + * 指定参与人员ID列表(逗号分隔) + */ + private String participantIds; + + /** + * 对应的考核明细配置(JSON) + */ + private String assessmentDetail; + + /** + * 对应的审核子表/流程配置(JSON) + */ + private String reviewFlow; + + /** + * 会议类型 + */ + private String meetingType; + + /** + * 会议种类 + */ + private String meetingCategory; + + /** + * 记录人ID + */ + private Long recorderId; + + /** + * 记录人姓名 + */ + private String recorderName; + + /** + * 附件 + */ + private String files; + + /** + * 照片 + */ + private String photos; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + /** + * 行业类型 + */ + private String industryType; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/training/domain/dto/RegisterVerifyDto.java b/src/main/java/com/hotwj/platform/securityManagement/training/domain/dto/RegisterVerifyDto.java new file mode 100644 index 0000000..88922ff --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/training/domain/dto/RegisterVerifyDto.java @@ -0,0 +1,16 @@ +package com.hotwj.platform.securityManagement.training.domain.dto; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.util.List; + +@Data +public class RegisterVerifyDto { + + @NotBlank(message = "jsonOssId不能为空") + private String jsonOssId; + + private List resourceOssIds; +} + diff --git a/src/main/java/com/hotwj/platform/securityManagement/training/domain/vo/HotTrainingVo.java b/src/main/java/com/hotwj/platform/securityManagement/training/domain/vo/HotTrainingVo.java new file mode 100644 index 0000000..a8c96e4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/training/domain/vo/HotTrainingVo.java @@ -0,0 +1,236 @@ +package com.hotwj.platform.securityManagement.training.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.training.domain.HotTraining; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 安全教育培训-培训主视图对象 hot_training + * + * @author shihongwei + * @date 2025-12-17 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotTraining.class) +public class HotTrainingVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 计划名称 + */ + @ExcelProperty(value = "计划名称") + private String planName; + + /** + * 人员类型(字典) + */ + @ExcelProperty(value = "人员类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "字=典") + private String personType; + + /** + * 起始时间 + */ + @ExcelProperty(value = "起始时间") + private Date startTime; + + /** + * 结束时间 + */ + @ExcelProperty(value = "结束时间") + private Date endTime; + + /** + * 课时(小时) + */ + @ExcelProperty(value = "课时", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "小=时") + private Long classHours; + + /** + * 绑定试卷ID列表(逗号分隔) + */ + @ExcelProperty(value = "绑定试卷ID列表", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逗=号分隔") + private String paperIds; + + /** + * 培训地点 + */ + @ExcelProperty(value = "培训地点") + private String trainingLocation; + + /** + * 培训老师 + */ + @ExcelProperty(value = "培训老师") + private String teacherName; + + /** + * 计划所属 + */ + @ExcelProperty(value = "计划所属") + private String planOwner; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 总人数 + */ + @ExcelProperty(value = "总人数") + private Long totalCount; + + /** + * 已通过 + */ + @ExcelProperty(value = "已通过") + private Long passedCount; + + /** + * 已完成人数 + */ + private Long completeCount; + + /** + * 未完成 + */ + @ExcelProperty(value = "未完成") + private Long incompleteCount; + + /** + * 待审核 + */ + @ExcelProperty(value = "待审核") + private Long pendingReviewCount; + + /** + * 是否启用:1是 0否 + */ + @ExcelProperty(value = "是否启用:1是 0否") + private Long isEnabled; + + /** + * 是否下发:1是 0否 + */ + @ExcelProperty(value = "是否下发:1是 0否") + private Long isIssued; + /** + * 是否补学:1是 0否 + */ + @ExcelProperty(value = "是否补学:1是 0否") + private Long isMakeUp; + + /** + * 课程配置ID列表(逗号分隔) + */ + @ExcelProperty(value = "课程配置ID列表", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逗=号分隔") + private String courseIds; + + /** + * 下发公司ID + */ + @ExcelProperty(value = "下发公司ID") + private String issuedCompanyId; + + /** + * 培训类型(岗前培训/日常培训等,字典) + */ + @ExcelProperty(value = "培训类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "岗=前培训/日常培训等,字典") + private String trainingType; + + /** + * 指定参与人员ID列表(逗号分隔) + */ + @ExcelProperty(value = "指定参与人员ID列表", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "逗=号分隔") + private String participantIds; + + /** + * 对应的考核明细配置(JSON) + */ + @ExcelProperty(value = "对应的考核明细配置(JSON)") + private String assessmentDetail; + + /** + * 对应的审核子表/流程配置(JSON) + */ + @ExcelProperty(value = "对应的审核子表/流程配置(JSON)") + private String reviewFlow; + + /** + * 会议类型 + */ + @ExcelProperty(value = "会议类型") + private String meetingType; + + /** + * 会议种类 + */ + @ExcelProperty(value = "会议种类") + private String meetingCategory; + + /** + * 记录人ID + */ + @ExcelProperty(value = "记录人ID") + private Long recorderId; + + /** + * 记录人姓名 + */ + @ExcelProperty(value = "记录人姓名") + private String recorderName; + + /** + * 附件 + */ + @ExcelProperty(value = "附件") + private String files; + + /** + * 照片 + */ + @ExcelProperty(value = "照片") + private String photos; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 行业类型 + */ + private String industryType; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/training/mapper/HotTrainingMapper.java b/src/main/java/com/hotwj/platform/securityManagement/training/mapper/HotTrainingMapper.java new file mode 100644 index 0000000..8ccb0c9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/training/mapper/HotTrainingMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.training.mapper; + +import com.hotwj.platform.securityManagement.training.domain.HotTraining; +import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 安全教育培训-培训主Mapper接口 + * + * @author shihongwei + * @date 2025-12-17 + */ +@Mapper +public interface HotTrainingMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/training/service/IHotTrainingAnnualRecordExportService.java b/src/main/java/com/hotwj/platform/securityManagement/training/service/IHotTrainingAnnualRecordExportService.java new file mode 100644 index 0000000..6cf8d97 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/training/service/IHotTrainingAnnualRecordExportService.java @@ -0,0 +1,19 @@ +package com.hotwj.platform.securityManagement.training.service; + +import jakarta.servlet.http.HttpServletResponse; + +/** + * 个人年度培训记录导出服务 + */ +public interface IHotTrainingAnnualRecordExportService { + + /** + * 导出个人年度培训记录(单人导出Excel,多人导出ZIP) + * + * @param driverId 逗号分隔的人员ID + * @param year 年份(可空,默认当前年) + * @param trainingType 培训类型(可空) + * @param response 响应流 + */ + void exportPersonalAnnualRecord(String driverId, Integer year, String trainingType, HttpServletResponse response); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/training/service/IHotTrainingService.java b/src/main/java/com/hotwj/platform/securityManagement/training/service/IHotTrainingService.java new file mode 100644 index 0000000..8f10ee0 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/training/service/IHotTrainingService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.securityManagement.training.service; + +import com.hotwj.platform.securityManagement.training.domain.bo.HotTrainingBo; +import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 安全教育培训-培训主Service接口 + * + * @author shihongwei + * @date 2025-12-17 + */ +public interface IHotTrainingService { + + /** + * 查询安全教育培训-培训主 + * + * @param id 主键 + * @return 安全教育培训-培训主 + */ + HotTrainingVo queryById(Long id); + + /** + * 分页查询安全教育培训-培训主列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 安全教育培训-培训主分页列表 + */ + TableDataInfo queryPageList(HotTrainingBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的安全教育培训-培训主列表 + * + * @param bo 查询条件 + * @return 安全教育培训-培训主列表 + */ + List queryList(HotTrainingBo bo); + + /** + * 新增安全教育培训-培训主 + * + * @param bo 安全教育培训-培训主 + * @return 是否新增成功 + */ + Boolean insertByBo(HotTrainingBo bo); + + /** + * 修改安全教育培训-培训主 + * + * @param bo 安全教育培训-培训主 + * @return 是否修改成功 + */ + Boolean updateByBo(HotTrainingBo bo); + + /** + * 校验并批量删除安全教育培训-培训主信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/training/service/impl/HotTrainingAnnualRecordExportServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/training/service/impl/HotTrainingAnnualRecordExportServiceImpl.java new file mode 100644 index 0000000..4bc4568 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/training/service/impl/HotTrainingAnnualRecordExportServiceImpl.java @@ -0,0 +1,504 @@ +package com.hotwj.platform.securityManagement.training.service.impl; + +import cn.hutool.core.io.resource.ClassPathResource; +import cn.idev.excel.ExcelWriter; +import cn.idev.excel.FastExcel; +import cn.idev.excel.write.metadata.WriteSheet; +import cn.idev.excel.write.metadata.fill.FillConfig; +import cn.idev.excel.write.metadata.fill.FillWrapper; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.securityManagement.training.service.IHotTrainingAnnualRecordExportService; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.vo.PersonalAnnualRecordCourseVo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.service.IHotTrainingCourseRecordService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.dromara.common.core.service.DictService; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.file.FileUtils; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.server.ResponseStatusException; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +@Service +@RequiredArgsConstructor +public class HotTrainingAnnualRecordExportServiceImpl implements IHotTrainingAnnualRecordExportService { + + private static final String PERSONAL_ANNUAL_TEMPLATE_PATH = "excelTemplates/personalAnnualTrainingRecord.xlsx"; + private static final String PERSONAL_ANNUAL_POST_DEFAULT = "驾驶员/押运员"; + private static final String PERSONAL_ANNUAL_ZIP_NAME = "个人年度培训记录.zip"; + private static final String TRAINING_ORGANIZATION = "HOT交通安全管理清单平台"; + private static final String DEFAULT_COMPANY_NAME = "未知单位"; + private static final String DEFAULT_LEADER = "未知负责人"; + private static final DateTimeFormatter BIRTH_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy年MM月dd日"); + private static final String ROW_HAS_COURSE_KEY = "hasCourse"; + + private final IHotDriverService hotDriverService; + private final IHotTrainingCourseRecordService hotTrainingCourseRecordService; + private final DictService dictService; + private final ISysCompanyService sysCompanyService; + + @Override + public void exportPersonalAnnualRecord(String driverId, Integer year, String trainingType, HttpServletResponse response) { + List driverIds = parseDriverIds(driverId); + int targetYear = year == null ? LocalDate.now().getYear() : year; + Date startTime = Date.from(LocalDate.of(targetYear, 1, 1).atStartOfDay(ZoneId.systemDefault()).toInstant()); + Date endTime = Date.from(LocalDate.of(targetYear + 1, 1, 1).atStartOfDay(ZoneId.systemDefault()).toInstant()); + + List records = new ArrayList<>(); + for (String id : driverIds) { + HotDriverVo driver = hotDriverService.queryById(id); + validateDriver(id, driver); + List courses = hotTrainingCourseRecordService.queryPersonalAnnualRecordCourses(id, startTime, endTime, trainingType); + records.add(new ExportDriverRecord(driver, courses == null ? List.of() : courses)); + } + + try { + if (records.size() == 1) { + exportSingleExcel(records.get(0), response); + } else { + exportZip(records, response); + } + } catch (IOException e) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "导出失败,请稍后重试", e); + } + } + + private void exportSingleExcel(ExportDriverRecord record, HttpServletResponse response) throws IOException { + String filename = buildExcelFileName(record.driver()); + byte[] content = buildExcelBytes(record); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + FileUtils.setAttachmentResponseHeader(response, filename); + try (OutputStream outputStream = response.getOutputStream()) { + outputStream.write(content); + outputStream.flush(); + } + } + + private void exportZip(List records, HttpServletResponse response) throws IOException { + response.setContentType("application/zip"); + FileUtils.setAttachmentResponseHeader(response, PERSONAL_ANNUAL_ZIP_NAME); + try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) { + for (ExportDriverRecord record : records) { + byte[] content = buildExcelBytes(record); + ZipEntry entry = new ZipEntry(buildExcelFileName(record.driver())); + zos.putNextEntry(entry); + zos.write(content); + zos.closeEntry(); + } + zos.finish(); + } + } + + private byte[] buildExcelBytes(ExportDriverRecord record) throws IOException { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + Map data = new LinkedHashMap<>(buildTemplateData(record)); + Object rowsObj = data.remove("rows"); + Collection rows = rowsObj instanceof Collection collection ? collection : List.of(); + + ClassPathResource templateResource = new ClassPathResource(PERSONAL_ANNUAL_TEMPLATE_PATH); + ExcelWriter excelWriter = FastExcel.write(baos) + .withTemplate(templateResource.getStream()) + .autoCloseStream(false) + .build(); + WriteSheet writeSheet = FastExcel.writerSheet().build(); + excelWriter.fill(data, writeSheet); + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + excelWriter.fill(new FillWrapper("rows", rows), fillConfig, writeSheet); + excelWriter.finish(); + return postProcessWorkbook(baos.toByteArray(), rows, data); + } + } + + private Map buildTemplateData(ExportDriverRecord record) { + HotDriverVo driver = record.driver(); + SysCompanyVo company = resolveCompany(driver.getCompanyId()); + Map data = new LinkedHashMap<>(); + data.put("name", StringUtils.defaultString(driver.getName(), "")); + data.put("gender", resolveGender(driver.getGender())); + data.put("birthMonth", resolveBirthMonth(driver)); + data.put("educationLevel", resolveEducationLevel(driver.getEducation())); + data.put("post", resolvePost(driver.getPersonType())); + data.put("dutyPost", ""); + data.put("certificateObtainTime", ""); + data.put("companyName", resolveCompanyName(company)); + data.put("leader", resolveCompanyLeader(company)); + data.put("filler", ""); + + List> rows = new ArrayList<>(); + for (PersonalAnnualRecordCourseVo course : record.courses()) { + Map row = new LinkedHashMap<>(); + row.put("trainingTime", StringUtils.defaultString(course.getTrainingTime(), "")); + row.put("courseName", StringUtils.defaultString(course.getCourseName(), "")); + row.put("evaluation", normalizeEvaluation(course.getEvaluation())); + row.put("hours", StringUtils.defaultString(course.getHours(), "")); + row.put("organization", TRAINING_ORGANIZATION); + row.put("remark", ""); + row.put(ROW_HAS_COURSE_KEY, Boolean.TRUE); + rows.add(row); + } + if (rows.isEmpty()) { + Map emptyRow = new LinkedHashMap<>(); + emptyRow.put("trainingTime", ""); + emptyRow.put("courseName", ""); + emptyRow.put("evaluation", ""); + emptyRow.put("hours", ""); + emptyRow.put("organization", ""); + emptyRow.put("remark", ""); + emptyRow.put(ROW_HAS_COURSE_KEY, Boolean.FALSE); + rows.add(emptyRow); + } + data.put("rows", rows); + return data; + } + + private String resolveGender(String gender) { + if (StringUtils.isBlank(gender)) { + return ""; + } + return switch (gender) { + case "0" -> "男"; + case "1" -> "女"; + default -> gender; + }; + } + + private String resolveEducationLevel(String education) { + if (StringUtils.isBlank(education)) { + return ""; + } + String label = dictService.getDictLabel("hot_academic", education); + return StringUtils.isNotBlank(label) ? label : education; + } + + private String formatBirthDate(Date date) { + if (date == null) { + return ""; + } + return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().format(BIRTH_DATE_FORMATTER); + } + + private String resolveBirthMonth(HotDriverVo driver) { + String birth = formatBirthDate(driver.getBirthDate()); + if (StringUtils.isNotBlank(birth)) { + return birth; + } + return parseBirthDateFromIdCard(driver.getIdCardNo()); + } + + private String parseBirthDateFromIdCard(String idCardNo) { + if (StringUtils.isBlank(idCardNo)) { + return ""; + } + String raw = idCardNo.trim(); + try { + if (raw.length() == 18) { + String birthPart = raw.substring(6, 14); + LocalDate birthDate = LocalDate.parse(birthPart, DateTimeFormatter.ofPattern("yyyyMMdd")); + return birthDate.format(BIRTH_DATE_FORMATTER); + } + if (raw.length() == 15) { + String birthPart = "19" + raw.substring(6, 12); + LocalDate birthDate = LocalDate.parse(birthPart, DateTimeFormatter.ofPattern("yyyyMMdd")); + return birthDate.format(BIRTH_DATE_FORMATTER); + } + } catch (Exception ignored) { + // ignore invalid id card format and fallback to empty + } + return ""; + } + + private String normalizeEvaluation(String evaluation) { + return "及格"; + } + + private String resolvePost(String personType) { + if (StringUtils.isBlank(personType)) { + return PERSONAL_ANNUAL_POST_DEFAULT; + } + String label = dictService.getDictLabel("hot_person_type", personType); + if (StringUtils.isNotBlank(label)) { + return label; + } + return personType; + } + + private List parseDriverIds(String driverId) { + if (StringUtils.isBlank(driverId)) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "driverId不能为空"); + } + Set ids = new LinkedHashSet<>(); + for (String id : driverId.split(",")) { + String trimmed = id == null ? "" : id.trim(); + if (StringUtils.isNotBlank(trimmed)) { + ids.add(trimmed); + } + } + if (ids.isEmpty()) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "driverId不能为空"); + } + return new ArrayList<>(ids); + } + + private void validateDriver(String driverId, HotDriverVo driver) { + if (driver == null) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "无效driverId: " + driverId); + } + boolean enabled = Long.valueOf(1L).equals(driver.getStatus()); + boolean notDeleted = !Long.valueOf(1L).equals(driver.getIsDeleted()); + boolean auditPassed = driver.getAuditStatus() != null && driver.getAuditStatus() == 1; + if (!enabled || !notDeleted || !auditPassed) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "driverId状态无效: " + driverId); + } + } + + private String buildExcelFileName(HotDriverVo driver) { + String driverName = safeFilePart(StringUtils.defaultString(driver.getName(), "未知姓名")); + return driverName + "年度培训记录.xlsx"; + } + + private String safeFilePart(String value) { + return value.replaceAll("[\\\\/:*?\"<>|]", "_"); + } + + private SysCompanyVo resolveCompany(Long companyId) { + if (companyId == null) { + return null; + } + return sysCompanyService.queryById(companyId); + } + + private String resolveCompanyName(SysCompanyVo company) { + if (company == null || StringUtils.isBlank(company.getCompanyName())) { + return DEFAULT_COMPANY_NAME; + } + return company.getCompanyName(); + } + + private String resolveCompanyLeader(SysCompanyVo company) { + if (company == null) { + return DEFAULT_LEADER; + } + if (StringUtils.isNotBlank(company.getResponsibleName())) { + return company.getResponsibleName(); + } + if (StringUtils.isNotBlank(company.getLegalPersonName())) { + return company.getLegalPersonName(); + } + return DEFAULT_LEADER; + } + + private byte[] postProcessWorkbook(byte[] rawBytes, Collection rows, Map data) throws IOException { + try (XSSFWorkbook workbook = new XSSFWorkbook(new java.io.ByteArrayInputStream(rawBytes)); + ByteArrayOutputStream out = new ByteArrayOutputStream()) { + Sheet sheet = workbook.getSheetAt(0); + replaceInlinePlaceholders(sheet, data); + int dataStartRow = findDataStartRow(sheet); + int rowCount = rows == null ? 0 : rows.size(); + if (dataStartRow >= 0 && rowCount > 0) { + Row templateRow = sheet.getRow(dataStartRow); + CellStyle contentTextStyle = buildContentTextStyle(workbook, templateRow); + for (int i = 0; i < rowCount; i++) { + Row row = sheet.getRow(dataStartRow + i); + if (row == null) { + row = sheet.createRow(dataStartRow + i); + } + if (templateRow != null) { + row.setHeight(templateRow.getHeight()); + } + copyTemplateStyles(templateRow, row); + setCellAsText(row, 1, getCellText(row, 1), contentTextStyle); + if (hasCourseRow(rows, i)) { + setCellValue(row, 3, TRAINING_ORGANIZATION); + setCellValue(row, 4, "及格"); + } + mergeTrainingContentCells(sheet, dataStartRow + i); + alignRowCellsLeft(row, 0, 6); + } + } + workbook.write(out); + return out.toByteArray(); + } + } + + private boolean hasCourseRow(Collection rows, int index) { + if (!(rows instanceof List rowList) || index < 0 || index >= rowList.size()) { + return false; + } + Object rowObj = rowList.get(index); + if (!(rowObj instanceof Map rowMap)) { + return false; + } + return Boolean.TRUE.equals(rowMap.get(ROW_HAS_COURSE_KEY)); + } + + private void replaceInlinePlaceholders(Sheet sheet, Map data) { + if (data == null || data.isEmpty()) { + return; + } + for (int i = 0; i <= sheet.getLastRowNum(); i++) { + Row row = sheet.getRow(i); + if (row == null) { + continue; + } + for (int cellNum = row.getFirstCellNum(); cellNum < row.getLastCellNum(); cellNum++) { + if (cellNum < 0) { + continue; + } + Cell cell = row.getCell(cellNum); + if (cell == null || cell.getCellType() != CellType.STRING) { + continue; + } + String text = cell.getStringCellValue(); + if (StringUtils.isBlank(text) || !text.contains("{")) { + continue; + } + String replaced = text; + for (Map.Entry entry : data.entrySet()) { + Object value = entry.getValue(); + if (value instanceof Collection) { + continue; + } + String key = "{" + entry.getKey() + "}"; + replaced = replaced.replace(key, StringUtils.defaultString(Objects.toString(value, ""), "")); + } + if (!Objects.equals(text, replaced)) { + cell.setCellValue(replaced); + } + } + } + } + + private CellStyle buildContentTextStyle(Workbook workbook, Row templateRow) { + CellStyle textStyle = workbook.createCellStyle(); + short textFormat = workbook.createDataFormat().getFormat("@"); + if (templateRow != null && templateRow.getCell(1) != null && templateRow.getCell(1).getCellStyle() != null) { + textStyle.cloneStyleFrom(templateRow.getCell(1).getCellStyle()); + } + textStyle.setDataFormat(textFormat); + return textStyle; + } + + private void copyTemplateStyles(Row templateRow, Row targetRow) { + if (templateRow == null) { + return; + } + for (int col = 0; col <= 6; col++) { + Cell templateCell = templateRow.getCell(col); + if (templateCell == null || templateCell.getCellStyle() == null) { + continue; + } + Cell targetCell = targetRow.getCell(col); + if (targetCell == null) { + targetCell = targetRow.createCell(col); + } + targetCell.setCellStyle(templateCell.getCellStyle()); + } + } + + private void setCellAsText(Row row, int index, String value, CellStyle style) { + Cell cell = row.getCell(index); + if (cell == null) { + cell = row.createCell(index); + } + cell.setCellStyle(style); + cell.setCellValue(StringUtils.defaultString(value, "")); + } + + private void setCellValue(Row row, int index, String value) { + Cell cell = row.getCell(index); + if (cell == null) { + cell = row.createCell(index); + } + cell.setCellValue(StringUtils.defaultString(value, "")); + } + + private void mergeTrainingContentCells(Sheet sheet, int rowIndex) { + CellRangeAddress merge = new CellRangeAddress(rowIndex, rowIndex, 1, 2); + if (!isMergedRegionExists(sheet, merge)) { + sheet.addMergedRegion(merge); + } + } + + private boolean isMergedRegionExists(Sheet sheet, CellRangeAddress target) { + for (int i = 0; i < sheet.getNumMergedRegions(); i++) { + CellRangeAddress region = sheet.getMergedRegion(i); + if (region.getFirstRow() == target.getFirstRow() + && region.getLastRow() == target.getLastRow() + && region.getFirstColumn() == target.getFirstColumn() + && region.getLastColumn() == target.getLastColumn()) { + return true; + } + } + return false; + } + + private void alignRowCellsLeft(Row row, int startCol, int endCol) { + for (int col = startCol; col <= endCol; col++) { + Cell cell = row.getCell(col); + if (cell == null) { + continue; + } + CellStyle sourceStyle = cell.getCellStyle(); + if (sourceStyle == null) { + continue; + } + Workbook workbook = row.getSheet().getWorkbook(); + CellStyle leftStyle = workbook.createCellStyle(); + leftStyle.cloneStyleFrom(sourceStyle); + if (col == 0 || col == 5) { + leftStyle.setAlignment(HorizontalAlignment.CENTER); + } else { + leftStyle.setAlignment(HorizontalAlignment.LEFT); + } + leftStyle.setVerticalAlignment(VerticalAlignment.CENTER); + cell.setCellStyle(leftStyle); + } + } + + private int findDataStartRow(Sheet sheet) { + for (int i = 0; i <= sheet.getLastRowNum(); i++) { + Row row = sheet.getRow(i); + if (row == null) { + continue; + } + String c0 = getCellText(row, 0); + String c1 = getCellText(row, 1); + if ("培训时间".equals(c0) && "培训内容".equals(c1)) { + return i + 1; + } + } + return -1; + } + + private String getCellText(Row row, int index) { + Cell cell = row.getCell(index); + if (cell == null) { + return ""; + } + return switch (cell.getCellType()) { + case STRING -> cell.getStringCellValue(); + case NUMERIC -> String.valueOf(cell.getNumericCellValue()); + case BOOLEAN -> String.valueOf(cell.getBooleanCellValue()); + default -> ""; + }; + } + + private record ExportDriverRecord(HotDriverVo driver, List courses) { + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/training/service/impl/HotTrainingServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/training/service/impl/HotTrainingServiceImpl.java new file mode 100644 index 0000000..0fd7938 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/training/service/impl/HotTrainingServiceImpl.java @@ -0,0 +1,216 @@ +package com.hotwj.platform.securityManagement.training.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import com.hotwj.platform.securityManagement.training.domain.HotTraining; +import com.hotwj.platform.securityManagement.training.domain.bo.HotTrainingBo; +import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo; +import com.hotwj.platform.securityManagement.training.mapper.HotTrainingMapper; +import com.hotwj.platform.securityManagement.training.service.IHotTrainingService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 安全教育培训-培训主Service业务层处理 + * + * @author shihongwei + * @date 2025-12-17 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotTrainingServiceImpl implements IHotTrainingService { + + private final HotTrainingMapper baseMapper; + private final HotCompanySafetyManagerMapper managerMapper; + private final IHotSystemNotificationService notificationService; + + /** + * 查询安全教育培训-培训主 + * + * @param id 主键 + * @return 安全教育培训-培训主 + */ + @Override + public HotTrainingVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询安全教育培训-培训主列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 安全教育培训-培训主分页列表 + */ + @Override + public TableDataInfo queryPageList(HotTrainingBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的安全教育培训-培训主列表 + * + * @param bo 查询条件 + * @return 安全教育培训-培训主列表 + */ + @Override + public List queryList(HotTrainingBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotTrainingBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotTraining::getCreateTime); + lqw.eq(bo.getCompanyId() != null, HotTraining::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getPlanName()), HotTraining::getPlanName, bo.getPlanName()); + lqw.eq(StringUtils.isNotBlank(bo.getPersonType()), HotTraining::getPersonType, bo.getPersonType()); + lqw.eq(bo.getStartTime() != null, HotTraining::getStartTime, bo.getStartTime()); + lqw.eq(bo.getEndTime() != null, HotTraining::getEndTime, bo.getEndTime()); + lqw.eq(bo.getClassHours() != null, HotTraining::getClassHours, bo.getClassHours()); + lqw.eq(StringUtils.isNotBlank(bo.getPaperIds()), HotTraining::getPaperIds, bo.getPaperIds()); + lqw.eq(StringUtils.isNotBlank(bo.getTrainingLocation()), HotTraining::getTrainingLocation, bo.getTrainingLocation()); + lqw.like(StringUtils.isNotBlank(bo.getTeacherName()), HotTraining::getTeacherName, bo.getTeacherName()); + lqw.eq(StringUtils.isNotBlank(bo.getPlanOwner()), HotTraining::getPlanOwner, bo.getPlanOwner()); + lqw.eq(bo.getTotalCount() != null, HotTraining::getTotalCount, bo.getTotalCount()); + lqw.eq(bo.getPassedCount() != null, HotTraining::getPassedCount, bo.getPassedCount()); + lqw.eq(bo.getIncompleteCount() != null, HotTraining::getIncompleteCount, bo.getIncompleteCount()); + lqw.eq(bo.getPendingReviewCount() != null, HotTraining::getPendingReviewCount, bo.getPendingReviewCount()); + lqw.eq(bo.getIsEnabled() != null, HotTraining::getIsEnabled, bo.getIsEnabled()); + lqw.eq(bo.getIsMakeUp() != null, HotTraining::getIsMakeUp, bo.getIsMakeUp()); + lqw.eq(bo.getIsIssued() != null, HotTraining::getIsIssued, bo.getIsIssued()); + lqw.eq(bo.getIssuedCompanyId() != null, HotTraining::getIssuedCompanyId, bo.getIssuedCompanyId()); + lqw.eq(StringUtils.isNotBlank(bo.getCourseIds()), HotTraining::getCourseIds, bo.getCourseIds()); + lqw.eq(StringUtils.isNotBlank(bo.getTrainingType()), HotTraining::getTrainingType, bo.getTrainingType()); + lqw.eq(StringUtils.isNotBlank(bo.getParticipantIds()), HotTraining::getParticipantIds, bo.getParticipantIds()); + lqw.eq(StringUtils.isNotBlank(bo.getAssessmentDetail()), HotTraining::getAssessmentDetail, bo.getAssessmentDetail()); + lqw.eq(StringUtils.isNotBlank(bo.getReviewFlow()), HotTraining::getReviewFlow, bo.getReviewFlow()); + lqw.eq(StringUtils.isNotBlank(bo.getMeetingType()), HotTraining::getMeetingType, bo.getMeetingType()); + lqw.eq(StringUtils.isNotBlank(bo.getMeetingCategory()), HotTraining::getMeetingCategory, bo.getMeetingCategory()); + lqw.eq(StringUtils.isNotBlank(bo.getIndustryType()), HotTraining::getIndustryType, bo.getIndustryType()); + + if (params != null) { + Object startBeginDate = params.get("startBeginDate"); + Object startEndDate = params.get("startEndDate"); + Object endBeginDate = params.get("endBeginDate"); + Object endEndDate = params.get("endEndDate"); + if (startBeginDate instanceof java.util.Date) { + lqw.ge(HotTraining::getStartTime, (java.util.Date) startBeginDate); + } + if (startEndDate instanceof java.util.Date) { + lqw.le(HotTraining::getStartTime, (java.util.Date) startEndDate); + } + if (endBeginDate instanceof java.util.Date) { + lqw.ge(HotTraining::getEndTime, (java.util.Date) endBeginDate); + } + if (endEndDate instanceof java.util.Date) { + lqw.le(HotTraining::getEndTime, (java.util.Date) endEndDate); + } + } + lqw.eq(bo.getIsDeleted() != null, HotTraining::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增安全教育培训-培训主 + * + * @param bo 安全教育培训-培训主 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotTrainingBo bo) { + HotTraining add = MapstructUtils.convert(bo, HotTraining.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改安全教育培训-培训主 + * + * @param bo 安全教育培训-培训主 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotTrainingBo bo) { + HotTraining update = MapstructUtils.convert(bo, HotTraining.class); + validEntityBeforeSave(update); + HotTraining before = baseMapper.selectById(update.getId()); + boolean ok = baseMapper.updateById(update) > 0; + if (ok && before != null) { + Long oldHours = before.getClassHours(); + Long newHours = update.getClassHours(); + boolean hoursConfigured = newHours != null && !newHours.equals(oldHours); + if (hoursConfigured && update.getCompanyId() != null) { + java.util.List managers = managerMapper.selectList( + com.baomidou.mybatisplus.core.toolkit.Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, update.getCompanyId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers != null && !managers.isEmpty()) { + java.util.List receiverIds = managers.stream().map(m -> String.valueOf(m.getId())).toList(); + String planName = org.dromara.common.core.utils.StringUtils.blankToDefault(update.getPlanName(), "培训计划"); + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(planName + "培训计划课时已配置,当前计划已可启用下发。"); + bos.setSourceType("培训计划"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("培训课时配置通知失败 companyId={} trainingId={}", update.getCompanyId(), update.getId(), e); + } + } + } + } + return ok; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotTraining entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除安全教育培训-培训主信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/controller/HotTrainingAnswerRecordController.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/controller/HotTrainingAnswerRecordController.java new file mode 100644 index 0000000..037a33b --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/controller/HotTrainingAnswerRecordController.java @@ -0,0 +1,141 @@ +package com.hotwj.platform.securityManagement.trainingAnswer.controller; + +import com.hotwj.platform.securityManagement.trainingAnswer.domain.bo.SubmitTrainingAnswerBo; +import com.hotwj.platform.securityManagement.trainingAnswer.domain.bo.TrainingAnswerRecordBo; +import com.hotwj.platform.securityManagement.trainingAnswer.domain.vo.HotTrainingAnswerRecordVo; +import com.hotwj.platform.securityManagement.trainingAnswer.service.IHotTrainingAnswerRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 培训课程答题详情记录 + * + * @author shihongwei + * @date 2026-02-16 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/courseAnswer/trainingAnswerRecord") +@Tag(name = "培训课程答题详情记录", description = "培训课程答题详情记录管理") +public class HotTrainingAnswerRecordController extends BaseController { + + private final IHotTrainingAnswerRecordService hotTrainingAnswerRecordService; + + /** + * 查询培训课程答题详情记录列表 + */ + //@SaCheckPermission("courseAnswer:trainingAnswerRecord:list") + @GetMapping("/list") + @Operation(summary = "分页查询培训课程答题详情记录列表") + public TableDataInfo list(TrainingAnswerRecordBo bo, PageQuery pageQuery) { + return hotTrainingAnswerRecordService.queryPageList(bo, pageQuery); + } + + //@SaCheckPermission("courseAnswer:trainingAnswerRecord:list") + @GetMapping("/studyDetail") + @Operation(summary = "查询培训计划答题明细(按培训+用户)") + public R> studyDetail( + @RequestParam Long companyId, + @RequestParam Long trainingId, + @RequestParam String userId + ) { + return R.ok(hotTrainingAnswerRecordService.queryStudyDetailByTrainingAndUser(companyId, trainingId, userId)); + } + + /** + * 导出培训课程答题详情记录列表 + */ + //@SaCheckPermission("courseAnswer:trainingAnswerRecord:export") + @Log(title = "培训课程答题详情记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出培训课程答题详情记录列表") + public void export(TrainingAnswerRecordBo bo, HttpServletResponse response) { + List list = hotTrainingAnswerRecordService.queryList(bo); + ExcelUtil.exportExcel(list, "培训课程答题详情记录", HotTrainingAnswerRecordVo.class, response); + } + + /** + * 获取培训课程答题详情记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("courseAnswer:trainingAnswerRecord:query") + @GetMapping("/{id}") + @Operation(summary = "获取培训课程答题详情记录详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotTrainingAnswerRecordService.queryById(id)); + } + + /** + * 新增培训课程答题详情记录 + */ + //@SaCheckPermission("courseAnswer:trainingAnswerRecord:add") + @Log(title = "培训课程答题详情记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增培训课程答题详情记录") + public R add(@Validated(AddGroup.class) @RequestBody TrainingAnswerRecordBo bo) { + return toAjax(hotTrainingAnswerRecordService.insertByBo(bo)); + } + + /** + * 修改培训课程答题详情记录 + */ + //@SaCheckPermission("courseAnswer:trainingAnswerRecord:edit") + @Log(title = "培训课程答题详情记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改培训课程答题详情记录") + public R edit(@Validated(EditGroup.class) @RequestBody TrainingAnswerRecordBo bo) { + return toAjax(hotTrainingAnswerRecordService.updateByBo(bo)); + } + + /** + * 删除培训课程答题详情记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("courseAnswer:trainingAnswerRecord:remove") + @Log(title = "培训课程答题详情记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除培训课程答题详情记录") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotTrainingAnswerRecordService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 批量提交某次答题的整套题目作答记录 + */ + //@SaCheckPermission("courseAnswer:trainingAnswerRecord:add") + @Log(title = "培训课程答题详情记录-批量提交", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/submit") + @Operation(summary = "批量提交某次答题的整套题目作答记录(支持随堂练习/培训计划答题)") + public R submit(@RequestBody @Validated(AddGroup.class) SubmitTrainingAnswerBo bo) { + return toAjax(hotTrainingAnswerRecordService.submitWithSignature(bo)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/domain/HotTrainingAnswerRecord.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/domain/HotTrainingAnswerRecord.java new file mode 100644 index 0000000..62fdf15 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/domain/HotTrainingAnswerRecord.java @@ -0,0 +1,140 @@ +package com.hotwj.platform.securityManagement.trainingAnswer.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 培训课程答题详情记录对象 hot_training_answer_record + * + * @author shihongwei + * @date 2026-02-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_training_answer_record") +public class HotTrainingAnswerRecord extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + private Long companyId; + + /** + * 答题场景:LESSON=课程随堂题 PLAN=计划配置题 + */ + private String sceneType; + + /** + * 业务ID:当scene_type=LESSON时为课程ID,当scene_type=PLAN时为培训计划ID + */ + private Long businessId; + + /** + * 题目来源:课程章节ID或计划配置项ID + */ + private Long questionSource; + + /** + * 培训计划ID + */ + private Long trainingId; + + /** + * 课程ID + */ + private Long courseId; + + /** + * 试卷ID + */ + private Long examId; + + /** + * 培训人员ID + */ + private String userId; + + /** + * 培训人员姓名 + */ + private String userName; + + /** + * 题库题目ID(hot_question_bank.id) + */ + private Long questionId; + + /** + * 试卷-题目关联ID(hot_exam_paper_question.id,可选) + */ + private Long paperQuestionId; + + /** + * 题型:1=单选 2=多选 3=判断 + */ + private Long questionType; + + /** + * 该题总分 + */ + private Long questionScore; + + /** + * 试卷中题目序号 + */ + private Long questionOrder; + + /** + * 标准答案(如 A,B) + */ + private String correctAnswers; + + /** + * 用户作答选项(如 A,B) + */ + private String selectedAnswers; + + /** + * 是否答对:1=正确 0=错误 + */ + private Long isCorrect; + + /** + * 本题得分 + */ + private Long gainedScore; + + /** + * 答题用时(秒) + */ + private Long answerDuration; + + /** + * 提交答题时间 + */ + private Date answerTime; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/domain/bo/SubmitTrainingAnswerBo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/domain/bo/SubmitTrainingAnswerBo.java new file mode 100644 index 0000000..3c00981 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/domain/bo/SubmitTrainingAnswerBo.java @@ -0,0 +1,43 @@ +package com.hotwj.platform.securityManagement.trainingAnswer.domain.bo; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Data +public class SubmitTrainingAnswerBo { + + @NotNull + private Long companyId; + + /** + * 答题场景:LESSON=课程随堂题 PLAN=计划配置题 + */ + @NotNull + private String sceneType; + + /** + * 业务ID:当scene_type=LESSON时为课程ID,当scene_type=PLAN时为培训计划ID + */ + @NotNull + private Long businessId; + + private Long trainingId; + + private Long courseId; + + private Long examId; + + @NotNull + private String userId; + + private String userName; + + @NotNull + private Long signatureOssId; + + @NotNull + private List answers; + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/domain/bo/TrainingAnswerRecordBo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/domain/bo/TrainingAnswerRecordBo.java new file mode 100644 index 0000000..8375708 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/domain/bo/TrainingAnswerRecordBo.java @@ -0,0 +1,79 @@ +package com.hotwj.platform.securityManagement.trainingAnswer.domain.bo; + +import com.hotwj.platform.securityManagement.trainingAnswer.domain.HotTrainingAnswerRecord; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 培训课程答题详情记录业务对象 hot_training_answer_record + * + * @author shihongwei + * @date 2026-02-16 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotTrainingAnswerRecord.class, reverseConvertGenerate = false) +public class TrainingAnswerRecordBo extends BaseEntity { + + @NotNull(message = "主键不能为空", groups = { EditGroup.class }) + private Long id; + + private Long companyId; + + /** + * 答题场景:LESSON=课程随堂题 PLAN=计划配置题 + */ + private String sceneType; + + /** + * 业务ID:当scene_type=LESSON时为课程ID,当scene_type=PLAN时为培训计划ID + */ + private Long businessId; + + /** + * 题目来源:课程章节ID或计划配置项ID + */ + private Long questionSource; + + private Long trainingId; + + private Long courseId; + + private Long examId; + + private String userId; + + private String userName; + + private Long questionId; + + private Long paperQuestionId; + + private Long questionType; + + private Long questionScore; + + private Long questionOrder; + + private String correctAnswers; + + private String selectedAnswers; + + private Long isCorrect; + + private Long gainedScore; + + private Long answerDuration; + + private Date answerTime; + + private Long isDeleted; + +} + diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/domain/enums/TrainingSceneType.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/domain/enums/TrainingSceneType.java new file mode 100644 index 0000000..cc5bf99 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/domain/enums/TrainingSceneType.java @@ -0,0 +1,29 @@ +package com.hotwj.platform.securityManagement.trainingAnswer.domain.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 答题场景类型 + * + * @author shihongwei + * @date 2026-02-16 + */ +@Getter +@AllArgsConstructor +public enum TrainingSceneType { + + /** + * 课程随堂题 + */ + LESSON("LESSON", "课程随堂题"), + + /** + * 计划配置题 + */ + PLAN("PLAN", "计划配置题"); + + private final String code; + private final String info; + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/domain/vo/HotTrainingAnswerRecordVo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/domain/vo/HotTrainingAnswerRecordVo.java new file mode 100644 index 0000000..1ba174f --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/domain/vo/HotTrainingAnswerRecordVo.java @@ -0,0 +1,220 @@ +package com.hotwj.platform.securityManagement.trainingAnswer.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.trainingAnswer.domain.HotTrainingAnswerRecord; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 培训课程答题详情记录视图对象 hot_training_answer_record + * + * @author shihongwei + * @date 2026-02-16 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotTrainingAnswerRecord.class) +public class HotTrainingAnswerRecordVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "由=应用层保证必填") + private Long companyId; + + /** + * 答题场景:LESSON=课程随堂题 PLAN=计划配置题 + */ + @ExcelProperty(value = "答题场景") + private String sceneType; + + /** + * 业务ID:当scene_type=LESSON时为课程ID,当scene_type=PLAN时为培训计划ID + */ + @ExcelProperty(value = "业务ID") + private Long businessId; + + /** + * 题目来源:课程章节ID或计划配置项ID + */ + @ExcelProperty(value = "题目来源") + private Long questionSource; + + /** + * 培训计划ID + */ + @ExcelProperty(value = "培训计划ID") + private Long trainingId; + + /** + * 课程ID + */ + @ExcelProperty(value = "课程ID") + private Long courseId; + + /** + * 试卷ID + */ + @ExcelProperty(value = "试卷ID") + private Long examId; + + /** + * 培训人员ID + */ + @ExcelProperty(value = "培训人员ID") + private String userId; + + /** + * 培训人员姓名 + */ + @ExcelProperty(value = "培训人员姓名") + private String userName; + + /** + * 题库题目ID(hot_question_bank.id) + */ + @ExcelProperty(value = "题库题目ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "h=ot_question_bank.id") + private Long questionId; + + /** + * 试卷-题目关联ID(hot_exam_paper_question.id,可选) + */ + @ExcelProperty(value = "试卷-题目关联ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "h=ot_exam_paper_question.id,可选") + private Long paperQuestionId; + + /** + * 题型:1=单选 2=多选 3=判断 + */ + @ExcelProperty(value = "题型:1=单选 2=多选 3=判断") + private Long questionType; + + /** + * 该题总分 + */ + @ExcelProperty(value = "该题总分") + private Long questionScore; + + /** + * 试卷中题目序号 + */ + @ExcelProperty(value = "试卷中题目序号") + private Long questionOrder; + + /** + * 标准答案(如 A,B) + */ + @ExcelProperty(value = "标准答案(如 A,B)") + private String correctAnswers; + + /** + * 用户作答选项(如 A,B) + */ + @ExcelProperty(value = "用户作答选项(如 A,B)") + private String selectedAnswers; + + /** + * 是否答对:1=正确 0=错误 + */ + @ExcelProperty(value = "是否答对:1=正确 0=错误") + private Long isCorrect; + + /** + * 本题得分 + */ + @ExcelProperty(value = "本题得分") + private Long gainedScore; + + /** + * 答题用时(秒) + */ + @ExcelProperty(value = "答题用时(秒)") + private Long answerDuration; + + /** + * 提交答题时间 + */ + @ExcelProperty(value = "提交答题时间") + private Date answerTime; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + private String trainingName; + + private String courseName; + + private String examName; + + private Long paperTotalScore; + + private Long passScore; + + private Long examScore; + + private Long learnDurationMin; + + private Date completeTime; + + private String questionDesc; + + private String optionA; + + private String optionB; + + private String optionC; + + private String optionD; + + private String optionE; + + private String optionF; + + private Long difficulty; + + private Long questionOriginalScore; + + private Long singleChoiceCount; + + private Long judgeCount; + + private Long totalQuestionScore; + + private Boolean passed; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/mapper/HotTrainingAnswerRecordMapper.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/mapper/HotTrainingAnswerRecordMapper.java new file mode 100644 index 0000000..8eaf67f --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/mapper/HotTrainingAnswerRecordMapper.java @@ -0,0 +1,30 @@ +package com.hotwj.platform.securityManagement.trainingAnswer.mapper; + +import com.hotwj.platform.securityManagement.trainingAnswer.domain.HotTrainingAnswerRecord; +import com.hotwj.platform.securityManagement.trainingAnswer.domain.vo.HotTrainingAnswerRecordVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 培训课程答题详情记录Mapper接口 + * + * @author shihongwei + * @date 2026-02-16 + */ +@Mapper +public interface HotTrainingAnswerRecordMapper extends BaseMapperPlus { + + java.util.List selectStudyDetailByTrainingAndUser( + @Param("companyId") Long companyId, + @Param("trainingId") Long trainingId, + @Param("userId") String userId + ); + + java.util.List selectPlanStudyDetailByTrainingAndUser( + @Param("companyId") Long companyId, + @Param("trainingId") Long trainingId, + @Param("userId") String userId + ); + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/service/IHotTrainingAnswerRecordService.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/service/IHotTrainingAnswerRecordService.java new file mode 100644 index 0000000..2325872 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/service/IHotTrainingAnswerRecordService.java @@ -0,0 +1,87 @@ +package com.hotwj.platform.securityManagement.trainingAnswer.service; + +import com.hotwj.platform.securityManagement.trainingAnswer.domain.bo.TrainingAnswerRecordBo; +import com.hotwj.platform.securityManagement.trainingAnswer.domain.bo.SubmitTrainingAnswerBo; +import com.hotwj.platform.securityManagement.trainingAnswer.domain.vo.HotTrainingAnswerRecordVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 培训课程答题详情记录Service接口 + * + * @author shihongwei + * @date 2026-02-16 + */ +public interface IHotTrainingAnswerRecordService { + + /** + * 查询培训课程答题详情记录 + * + * @param id 主键 + * @return 培训课程答题详情记录 + */ + HotTrainingAnswerRecordVo queryById(Long id); + + /** + * 分页查询培训课程答题详情记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训课程答题详情记录分页列表 + */ + TableDataInfo queryPageList(TrainingAnswerRecordBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的培训课程答题详情记录列表 + * + * @param bo 查询条件 + * @return 培训课程答题详情记录列表 + */ + List queryList(TrainingAnswerRecordBo bo); + + List queryStudyDetailByTrainingAndUser(Long companyId, Long trainingId, String userId); + + /** + * 新增培训课程答题详情记录 + * + * @param bo 培训课程答题详情记录 + * @return 是否新增成功 + */ + Boolean insertByBo(TrainingAnswerRecordBo bo); + + /** + * 修改培训课程答题详情记录 + * + * @param bo 培训课程答题详情记录 + * @return 是否修改成功 + */ + Boolean updateByBo(TrainingAnswerRecordBo bo); + + /** + * 校验并批量删除培训课程答题详情记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 批量新增某次答题的整套题目作答记录 + * + * @param bos 作答记录集合 + * @return 是否新增成功 + */ + Boolean batchInsert(List bos); + + /** + * 提交整套答题并记录学员签名 + * + * @param bo 提交数据 + * @return 是否成功 + */ + Boolean submitWithSignature(SubmitTrainingAnswerBo bo); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/service/impl/HotTrainingAnswerRecordServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/service/impl/HotTrainingAnswerRecordServiceImpl.java new file mode 100644 index 0000000..d00b4be --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAnswer/service/impl/HotTrainingAnswerRecordServiceImpl.java @@ -0,0 +1,441 @@ +package com.hotwj.platform.securityManagement.trainingAnswer.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.flow.service.ISysFlowService; +import com.hotwj.platform.integration.ocr.SignatureVerifyService; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.vo.HotCompanySafetyManagerVo; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo; +import com.hotwj.platform.securityManagement.training.mapper.HotTrainingMapper; +import com.hotwj.platform.securityManagement.trainingAnswer.domain.HotTrainingAnswerRecord; +import com.hotwj.platform.securityManagement.trainingAnswer.domain.bo.SubmitTrainingAnswerBo; +import com.hotwj.platform.securityManagement.trainingAnswer.domain.bo.TrainingAnswerRecordBo; +import com.hotwj.platform.securityManagement.trainingAnswer.domain.enums.TrainingSceneType; +import com.hotwj.platform.securityManagement.trainingAnswer.domain.vo.HotTrainingAnswerRecordVo; +import com.hotwj.platform.securityManagement.trainingAnswer.mapper.HotTrainingAnswerRecordMapper; +import com.hotwj.platform.securityManagement.trainingAnswer.service.IHotTrainingAnswerRecordService; +import com.hotwj.platform.securityManagement.trainingAudit.domain.HotTrainingAudit; +import com.hotwj.platform.securityManagement.trainingAudit.mapper.HotTrainingAuditMapper; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.bo.HotTrainingCourseRecordBo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.vo.HotTrainingCourseRecordVo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.service.IHotTrainingCourseRecordService; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.HotTrainingParticipant; +import com.hotwj.platform.securityManagement.trainingParticipant.mapper.HotTrainingParticipantMapper; +import com.hotwj.platform.securityManagement.trainingProgress.service.TrainingProgressService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 培训课程答题详情记录Service业务层处理 + * + * @author shihongwei + * @date 2026-02-16 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotTrainingAnswerRecordServiceImpl implements IHotTrainingAnswerRecordService { + + private static final String FLOW_CODE_TRAINING_STUDY = "TRAINING_STUDY"; + private final HotTrainingAnswerRecordMapper baseMapper; + private final IHotTrainingCourseRecordService trainingCourseRecordService; + private final HotTrainingParticipantMapper trainingParticipantMapper; + private final ISysFlowService flowService; + private final IHotSystemNotificationService notificationService; + private final HotTrainingMapper trainingMapper; + private final HotCompanySafetyManagerMapper companySafetyManagerMapper; + private final TrainingProgressService trainingProgressService; + private final HotTrainingAuditMapper trainingAuditMapper; + private final SignatureVerifyService signatureVerifyService; + + /** + * 查询培训课程答题详情记录 + * + * @param id 主键 + * @return 培训课程答题详情记录 + */ + @Override + public HotTrainingAnswerRecordVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询培训课程答题详情记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训课程答题详情记录分页列表 + */ + @Override + public TableDataInfo queryPageList(TrainingAnswerRecordBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的培训课程答题详情记录列表 + * + * @param bo 查询条件 + * @return 培训课程答题详情记录列表 + */ + @Override + public List queryList(TrainingAnswerRecordBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + @Override + public List queryStudyDetailByTrainingAndUser(Long companyId, Long trainingId, String userId) { + if (companyId == null || trainingId == null || StringUtils.isBlank(userId)) { + return List.of(); + } + List list = baseMapper.selectPlanStudyDetailByTrainingAndUser(companyId, trainingId, userId); + if (list == null || list.isEmpty()) { + return List.of(); + } + long examScore = 0L; + Date completeTime = null; + for (HotTrainingAnswerRecordVo vo : list) { + if (vo.getGainedScore() != null) { + examScore += vo.getGainedScore(); + } + if (vo.getAnswerTime() != null) { + if (completeTime == null || vo.getAnswerTime().after(completeTime)) { + completeTime = vo.getAnswerTime(); + } + } + } + Boolean passed = null; + HotTrainingAnswerRecordVo head = list.get(0); + if (head.getPassScore() != null) { + passed = examScore >= head.getPassScore(); + } + for (HotTrainingAnswerRecordVo vo : list) { + vo.setExamScore(examScore); + if (completeTime != null) { + vo.setCompleteTime(completeTime); + } + if (passed != null) { + vo.setPassed(passed); + } + } + return list; + } + + private LambdaQueryWrapper buildQueryWrapper(TrainingAnswerRecordBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotTrainingAnswerRecord::getId); + lqw.eq(bo.getCompanyId() != null, HotTrainingAnswerRecord::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getTrainingId() != null, HotTrainingAnswerRecord::getTrainingId, bo.getTrainingId()); + lqw.eq(bo.getCourseId() != null, HotTrainingAnswerRecord::getCourseId, bo.getCourseId()); + lqw.eq(bo.getExamId() != null, HotTrainingAnswerRecord::getExamId, bo.getExamId()); + lqw.eq(bo.getUserId() != null, HotTrainingAnswerRecord::getUserId, bo.getUserId()); + lqw.like(StringUtils.isNotBlank(bo.getUserName()), HotTrainingAnswerRecord::getUserName, bo.getUserName()); + lqw.eq(bo.getQuestionId() != null, HotTrainingAnswerRecord::getQuestionId, bo.getQuestionId()); + lqw.eq(bo.getPaperQuestionId() != null, HotTrainingAnswerRecord::getPaperQuestionId, bo.getPaperQuestionId()); + lqw.eq(bo.getQuestionType() != null, HotTrainingAnswerRecord::getQuestionType, bo.getQuestionType()); + lqw.eq(bo.getQuestionScore() != null, HotTrainingAnswerRecord::getQuestionScore, bo.getQuestionScore()); + lqw.eq(bo.getQuestionOrder() != null, HotTrainingAnswerRecord::getQuestionOrder, bo.getQuestionOrder()); + lqw.eq(StringUtils.isNotBlank(bo.getCorrectAnswers()), HotTrainingAnswerRecord::getCorrectAnswers, bo.getCorrectAnswers()); + lqw.eq(StringUtils.isNotBlank(bo.getSelectedAnswers()), HotTrainingAnswerRecord::getSelectedAnswers, bo.getSelectedAnswers()); + lqw.eq(bo.getIsCorrect() != null, HotTrainingAnswerRecord::getIsCorrect, bo.getIsCorrect()); + lqw.eq(bo.getGainedScore() != null, HotTrainingAnswerRecord::getGainedScore, bo.getGainedScore()); + lqw.eq(bo.getAnswerDuration() != null, HotTrainingAnswerRecord::getAnswerDuration, bo.getAnswerDuration()); + lqw.eq(bo.getAnswerTime() != null, HotTrainingAnswerRecord::getAnswerTime, bo.getAnswerTime()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotTrainingAnswerRecord::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotTrainingAnswerRecord::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotTrainingAnswerRecord::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增培训课程答题详情记录 + * + * @param bo 培训课程答题详情记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(TrainingAnswerRecordBo bo) { + HotTrainingAnswerRecord add = MapstructUtils.convert(bo, HotTrainingAnswerRecord.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改培训课程答题详情记录 + * + * @param bo 培训课程答题详情记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(TrainingAnswerRecordBo bo) { + HotTrainingAnswerRecord update = MapstructUtils.convert(bo, HotTrainingAnswerRecord.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotTrainingAnswerRecord entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除培训课程答题详情记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + public Boolean batchInsert(List bos) { + if (bos == null || bos.isEmpty()) { + return Boolean.TRUE; + } + for (TrainingAnswerRecordBo bo : bos) { + HotTrainingAnswerRecord entity = MapstructUtils.convert(bo, HotTrainingAnswerRecord.class); + validEntityBeforeSave(entity); + baseMapper.insert(entity); + } + return Boolean.TRUE; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean submitWithSignature(SubmitTrainingAnswerBo bo) { + if (bo == null || bo.getAnswers() == null || bo.getAnswers().isEmpty()) { + return Boolean.FALSE; + } + + if (TrainingSceneType.PLAN.getCode().equals(bo.getSceneType())) { + String submitterName = resolveSubmitterName(bo); + bo.setUserName(submitterName); + signatureVerifyService.validateSelfSignature(bo.getSignatureOssId(), submitterName); + } + + + // 1. 处理答题明细(存在则更新,不存在则插入) + // 获取当前用户在该场景下的所有答题记录,用于比对 + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.eq(HotTrainingAnswerRecord::getUserId, bo.getUserId()) + .eq(HotTrainingAnswerRecord::getSceneType, bo.getSceneType()) + .eq(HotTrainingAnswerRecord::getBusinessId, bo.getBusinessId()); + + List existingRecords = baseMapper.selectList(lqw); + Map existingMap = existingRecords.stream() + .collect(Collectors.toMap(HotTrainingAnswerRecord::getQuestionSource, Function.identity(), (v1, v2) -> v2)); + + List toInsert = new ArrayList<>(); + List toUpdate = new ArrayList<>(); + + // 计算总分 + long totalScore = 0L; + + for (TrainingAnswerRecordBo answerBo : bo.getAnswers()) { + // 设置通用字段 + answerBo.setCompanyId(bo.getCompanyId()); + answerBo.setSceneType(bo.getSceneType()); + answerBo.setBusinessId(bo.getBusinessId()); + answerBo.setUserId(bo.getUserId()); + answerBo.setUserName(bo.getUserName()); + answerBo.setTrainingId(bo.getTrainingId()); + answerBo.setExamId(bo.getExamId()); + // courseId 仅在 LESSON 场景或有值时设置 + if (bo.getCourseId() != null) { + answerBo.setCourseId(bo.getCourseId()); + } + + // 累加分数 (假设 isCorrect 1=正确) + if (Long.valueOf(1L).equals(answerBo.getIsCorrect()) && answerBo.getGainedScore() != null) { + totalScore += answerBo.getGainedScore(); + } + + HotTrainingAnswerRecord entity = MapstructUtils.convert(answerBo, HotTrainingAnswerRecord.class); + + if (entity != null && entity.getQuestionSource() != null && existingMap.containsKey(entity.getQuestionSource())) { + // 更新 + HotTrainingAnswerRecord existing = existingMap.get(entity.getQuestionSource()); + entity.setId(existing.getId()); + toUpdate.add(entity); + } else { + // 插入 + toInsert.add(entity); + } + } + + if (!toInsert.isEmpty() || !toUpdate.isEmpty()) { + for (HotTrainingAnswerRecord entity : toInsert) { + validEntityBeforeSave(entity); + baseMapper.insert(entity); + } + for (HotTrainingAnswerRecord entity : toUpdate) { + validEntityBeforeSave(entity); + baseMapper.updateById(entity); + } + } + + // 2. 根据场景更新对应的记录表(保证 userId+trainingId+courseId 唯一) + if (TrainingSceneType.LESSON.getCode().equals(bo.getSceneType())) { + HotTrainingCourseRecordBo courseRecordBo = new HotTrainingCourseRecordBo(); + courseRecordBo.setCompanyId(bo.getCompanyId()); + courseRecordBo.setTrainingId(bo.getTrainingId()); + courseRecordBo.setTrainingCourseId(bo.getCourseId()); + courseRecordBo.setUserId(bo.getUserId()); + courseRecordBo.setExamId(bo.getExamId()); + courseRecordBo.setSignatureOssId(bo.getSignatureOssId()); + courseRecordBo.setExamScore(new BigDecimal(totalScore)); + courseRecordBo.setCheckStatus(1L); + HotTrainingCourseRecordBo queryBo = new HotTrainingCourseRecordBo(); + queryBo.setCompanyId(bo.getCompanyId()); + queryBo.setTrainingId(bo.getTrainingId()); + queryBo.setTrainingCourseId(bo.getBusinessId()); + queryBo.setUserId(bo.getUserId()); + + HotTrainingCourseRecordVo latest = trainingCourseRecordService.queryLatestByUserAndCourse(queryBo); + if (latest != null) { + courseRecordBo.setId(latest.getId()); + trainingCourseRecordService.updateByBo(courseRecordBo); + } else { + trainingCourseRecordService.insertByBo(courseRecordBo); + } + + trainingProgressService.syncProgress(bo.getCompanyId(), bo.getTrainingId(), bo.getUserId()); + + } else if (TrainingSceneType.PLAN.getCode().equals(bo.getSceneType())) { + clearPlanAuditRecord(bo); + // 更新培训计划参与记录 (HotTrainingParticipant) + // PLAN 场景下,businessId 通常是 trainingId + LambdaQueryWrapper participantLqw = new LambdaQueryWrapper<>(); + participantLqw.eq(HotTrainingParticipant::getCompanyId, bo.getCompanyId()) + .eq(HotTrainingParticipant::getTrainingId, bo.getTrainingId()) + .eq(HotTrainingParticipant::getUserId, bo.getUserId()) + .eq(HotTrainingParticipant::getIsDeleted, 0L); + + HotTrainingParticipant participant = trainingParticipantMapper.selectOne(participantLqw); + + if (participant != null) { + trainingParticipantMapper.update( + null, + Wrappers.lambdaUpdate() + .eq(HotTrainingParticipant::getId, participant.getId()) + .set(HotTrainingParticipant::getIsCompleted, 1L) + .set(HotTrainingParticipant::getCompleteTime, new Date()) + .set(HotTrainingParticipant::getIsPass, null) + .set(HotTrainingParticipant::getSignatureOssId, bo.getSignatureOssId()) + .set(HotTrainingParticipant::getLearnProgress, BigDecimal.valueOf(100)) + ); + completeTrainingTodo(participant.getTrainingId(), bo.getUserId()); +// trainingProgressService.syncProgress(bo.getCompanyId(), bo.getTrainingId(), bo.getUserId()); + + HotTrainingVo training = trainingMapper.selectVoById(bo.getTrainingId()); + String planName = training != null ? training.getPlanName() : "培训"; + List managers = companySafetyManagerMapper.selectVoList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, bo.getCompanyId()) + .eq(HotCompanySafetyManager::getIsResigned, 0) + .eq(HotCompanySafetyManager::getStatus, 1) + .eq(HotCompanySafetyManager::getIsDeleted, 0) + ); + List receiverIds = managers == null ? List.of() : + managers.stream().map(m -> String.valueOf(m.getId())).collect(Collectors.toList()); + if (receiverIds != null && !receiverIds.isEmpty()) { + HotSystemNotificationGroupBo msg = new HotSystemNotificationGroupBo(); + msg.setLevel("普通"); + msg.setSourceType("安全教育培训"); + msg.setSenderType("SYSTEM"); + msg.setReceiverType("管理员"); + msg.setReceiverIds(receiverIds); + String traineeName = bo.getUserName(); + msg.setContent((traineeName != null ? traineeName : "驾驶员") + "已完成" + planName + "培训,需要及时审核,也可进行报表打印查看"); + msg.setIsDeleted(0L); + try { + notificationService.insertByBo(msg); + } catch (Exception e) { + log.warn("培训完成后发送管理员系统消息失败 trainingId={} companyId={} err={}", bo.getTrainingId(), bo.getCompanyId(), e.getMessage()); + } + } + } + } + + return Boolean.TRUE; + } + + private void clearPlanAuditRecord(SubmitTrainingAnswerBo bo) { + if (bo.getCompanyId() == null || bo.getTrainingId() == null || StringUtils.isBlank(bo.getUserId())) { + return; + } + LambdaQueryWrapper auditLqw = new LambdaQueryWrapper<>(); + auditLqw.eq(HotTrainingAudit::getCompanyId, bo.getCompanyId()) + .eq(HotTrainingAudit::getTrainingId, bo.getTrainingId()) + .eq(HotTrainingAudit::getUserId, bo.getUserId()) + .eq(HotTrainingAudit::getIsDeleted, 0L); + trainingAuditMapper.delete(auditLqw); + } + + private void completeTrainingTodo(Long trainingId, String userId) { + if (trainingId == null || StringUtils.isBlank(userId)) { + return; + } + try { + String taskId = flowService.getTaskIdByBusinessId(FLOW_CODE_TRAINING_STUDY, String.valueOf(trainingId), userId); + if (StringUtils.isBlank(taskId)) { + return; + } + flowService.audit(taskId, true, "综合检测已提交", userId); + } catch (Exception e) { + log.warn("综合检测提交后完成待办失败 trainingId={} userId={} err={}", trainingId, userId, e.getMessage()); + } + } + + private String resolveSubmitterName(SubmitTrainingAnswerBo bo) { + if (StringUtils.isNotBlank(bo.getUserName())) { + return bo.getUserName().trim(); + } + if (bo.getCompanyId() != null && bo.getTrainingId() != null && StringUtils.isNotBlank(bo.getUserId())) { + HotTrainingParticipant participant = trainingParticipantMapper.selectOne( + Wrappers.lambdaQuery() + .eq(HotTrainingParticipant::getCompanyId, bo.getCompanyId()) + .eq(HotTrainingParticipant::getTrainingId, bo.getTrainingId()) + .eq(HotTrainingParticipant::getUserId, bo.getUserId()) + .eq(HotTrainingParticipant::getIsDeleted, 0L) + .last("limit 1") + ); + if (participant != null && StringUtils.isNotBlank(participant.getUserName())) { + return participant.getUserName().trim(); + } + } + throw new ServiceException("无法获取提交人姓名,签名校验失败"); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/controller/HotTrainingAuditController.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/controller/HotTrainingAuditController.java new file mode 100644 index 0000000..a8aeb4c --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/controller/HotTrainingAuditController.java @@ -0,0 +1,129 @@ +package com.hotwj.platform.securityManagement.trainingAudit.controller; + +import com.hotwj.platform.securityManagement.trainingAudit.domain.bo.HotTrainingAuditBatchBo; +import com.hotwj.platform.securityManagement.trainingAudit.domain.bo.HotTrainingAuditBo; +import com.hotwj.platform.securityManagement.trainingAudit.domain.vo.HotTrainingAuditVo; +import com.hotwj.platform.securityManagement.trainingAudit.service.IHotTrainingAuditService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 培训审核 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/trainingAudit") +@Tag(name = "培训审核", description = "培训审核管理") +public class HotTrainingAuditController extends BaseController { + + private final IHotTrainingAuditService hotTrainingAuditService; + + /** + * 查询培训审核列表 + */ + //@SaCheckPermission("securityManagement:trainingAudit:list") + @GetMapping("/list") + @Operation(summary = "分页查询培训审核列表") + public TableDataInfo list(HotTrainingAuditBo bo, PageQuery pageQuery) { + return hotTrainingAuditService.queryPageList(bo, pageQuery); + } + + /** + * 导出培训审核列表 + */ + //@SaCheckPermission("securityManagement:trainingAudit:export") + @Log(title = "培训审核", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出培训审核列表") + public void export(HotTrainingAuditBo bo, HttpServletResponse response) { + List list = hotTrainingAuditService.queryList(bo); + ExcelUtil.exportExcel(list, "培训审核", HotTrainingAuditVo.class, response); + } + + /** + * 获取培训审核详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:trainingAudit:query") + @GetMapping("/{id}") + @Operation(summary = "获取培训审核详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotTrainingAuditService.queryById(id)); + } + + /** + * 新增培训审核 + */ + //@SaCheckPermission("securityManagement:trainingAudit:add") + @Log(title = "培训审核", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增培训审核") + public R add(@Validated(AddGroup.class) @RequestBody HotTrainingAuditBo bo) { + return toAjax(hotTrainingAuditService.insertByBo(bo)); + } + + /** + * 修改培训审核 + */ + //@SaCheckPermission("securityManagement:trainingAudit:edit") + @Log(title = "培训审核", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改培训审核") + public R edit(@Validated(EditGroup.class) @RequestBody HotTrainingAuditBo bo) { + return toAjax(hotTrainingAuditService.updateByBo(bo)); + } + + /** + * 批量审核培训参与人员 + */ + @Log(title = "培训审核", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/batchAudit") + @Operation(summary = "批量审核培训参与人员") + public R batchAudit(@Validated @RequestBody HotTrainingAuditBatchBo bo) { + return toAjax(hotTrainingAuditService.batchAudit(bo)); + } + + /** + * 删除培训审核 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:trainingAudit:remove") + @Log(title = "培训审核", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除培训审核") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotTrainingAuditService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/domain/HotTrainingAudit.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/domain/HotTrainingAudit.java new file mode 100644 index 0000000..54e36ad --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/domain/HotTrainingAudit.java @@ -0,0 +1,85 @@ +package com.hotwj.platform.securityManagement.trainingAudit.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 培训审核对象 hot_training_audit + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_training_audit") +public class HotTrainingAudit extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 培训ID + */ + private Long trainingId; + + /** + * 用户ID + */ + private String userId; + + /** + * 是否通过 1=通过 0=未通过 + */ + private Long isPass; + + /** + * 备注 + */ + private String remark; + + /** + * 审核人ID + */ + private String auditorId; + + /** + * 审核人姓名 + */ + private String auditorName; + + /** + * 审核时间 + */ + private Date auditTime; + + /** + * 审核人签字图片url + */ + private String signImgUrl; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/domain/bo/HotTrainingAuditBatchBo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/domain/bo/HotTrainingAuditBatchBo.java new file mode 100644 index 0000000..eb88e13 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/domain/bo/HotTrainingAuditBatchBo.java @@ -0,0 +1,51 @@ +package com.hotwj.platform.securityManagement.trainingAudit.domain.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +/** + * 培训审核批量审核业务对象 + * + * @author shihongwei + * @date 2026-04-27 + */ +@Data +@Schema(description = "培训审核批量审核业务对象") +public class HotTrainingAuditBatchBo { + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空") + private Long companyId; + + /** + * 培训ID + */ + @NotNull(message = "培训ID不能为空") + private Long trainingId; + + /** + * 是否补学 + */ + private Long isMakeUp; + + /** + * 审核人签字图片url + */ + @NotBlank(message = "审核人签字图片url不能为空") + private String signImgUrl; + + /** + * 批量审核明细 + */ + @Valid + @NotEmpty(message = "审核明细不能为空") + private List auditItems; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/domain/bo/HotTrainingAuditBo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/domain/bo/HotTrainingAuditBo.java new file mode 100644 index 0000000..4815c5e --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/domain/bo/HotTrainingAuditBo.java @@ -0,0 +1,88 @@ +package com.hotwj.platform.securityManagement.trainingAudit.domain.bo; + +import com.hotwj.platform.securityManagement.trainingAudit.domain.HotTrainingAudit; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 培训审核业务对象 hot_training_audit + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotTrainingAudit.class, reverseConvertGenerate = false) +public class HotTrainingAuditBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 培训ID + */ + @NotNull(message = "培训ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long trainingId; + + /** + * 用户ID + */ + private String userId; + + /** + * 是否通过 1=通过 0=未通过 + */ + @NotNull(message = "是否通过 1=通过 0=未通过不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isPass; + + /** + * 备注 + */ + private String remark; + + /** + * 审核人ID + */ + private String auditorId; + + /** + * 审核人姓名 + */ + private String auditorName; + + /** + * 审核时间 + */ + private Date auditTime; + + /** + * 审核人签字图片url + */ + @NotBlank(message = "审核人签字图片url不能为空", groups = {AddGroup.class, EditGroup.class}) + private String signImgUrl; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/domain/vo/HotTrainingAuditVo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/domain/vo/HotTrainingAuditVo.java new file mode 100644 index 0000000..8a651e4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/domain/vo/HotTrainingAuditVo.java @@ -0,0 +1,95 @@ +package com.hotwj.platform.securityManagement.trainingAudit.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.trainingAudit.domain.HotTrainingAudit; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 培训审核视图对象 hot_training_audit + * + * @author shihongwei + * @date 2025-12-29 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotTrainingAudit.class) +public class HotTrainingAuditVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 培训ID + */ + @ExcelProperty(value = "培训ID") + private Long trainingId; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户ID") + private String userId; + + /** + * 是否通过 1=通过 0=未通过 + */ + @ExcelProperty(value = "是否通过 1=通过 0=未通过") + private Long isPass; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 审核人ID + */ + @ExcelProperty(value = "审核人ID") + private String auditorId; + + /** + * 审核人姓名 + */ + @ExcelProperty(value = "审核人姓名") + private String auditorName; + + /** + * 审核时间 + */ + @ExcelProperty(value = "审核时间") + private Date auditTime; + + /** + * 审核人签字图片url + */ + @ExcelProperty(value = "审核人签字图片url") + private String signImgUrl; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/mapper/HotTrainingAuditMapper.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/mapper/HotTrainingAuditMapper.java new file mode 100644 index 0000000..2666a42 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/mapper/HotTrainingAuditMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.trainingAudit.mapper; + +import com.hotwj.platform.securityManagement.trainingAudit.domain.HotTrainingAudit; +import com.hotwj.platform.securityManagement.trainingAudit.domain.vo.HotTrainingAuditVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 培训审核Mapper接口 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Mapper +public interface HotTrainingAuditMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/service/IHotTrainingAuditService.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/service/IHotTrainingAuditService.java new file mode 100644 index 0000000..20ff21e --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/service/IHotTrainingAuditService.java @@ -0,0 +1,77 @@ +package com.hotwj.platform.securityManagement.trainingAudit.service; + +import com.hotwj.platform.securityManagement.trainingAudit.domain.bo.HotTrainingAuditBatchBo; +import com.hotwj.platform.securityManagement.trainingAudit.domain.bo.HotTrainingAuditBo; +import com.hotwj.platform.securityManagement.trainingAudit.domain.vo.HotTrainingAuditVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 培训审核Service接口 + * + * @author shihongwei + * @date 2025-12-29 + */ +public interface IHotTrainingAuditService { + + /** + * 查询培训审核 + * + * @param id 主键 + * @return 培训审核 + */ + HotTrainingAuditVo queryById(Long id); + + /** + * 分页查询培训审核列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训审核分页列表 + */ + TableDataInfo queryPageList(HotTrainingAuditBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的培训审核列表 + * + * @param bo 查询条件 + * @return 培训审核列表 + */ + List queryList(HotTrainingAuditBo bo); + + /** + * 新增培训审核 + * + * @param bo 培训审核 + * @return 是否新增成功 + */ + Boolean insertByBo(HotTrainingAuditBo bo); + + /** + * 修改培训审核 + * + * @param bo 培训审核 + * @return 是否修改成功 + */ + Boolean updateByBo(HotTrainingAuditBo bo); + + /** + * 批量审核培训参与人员 + * + * @param bo 批量审核参数 + * @return 是否成功 + */ + Boolean batchAudit(HotTrainingAuditBatchBo bo); + + /** + * 校验并批量删除培训审核信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/service/impl/HotTrainingAuditServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/service/impl/HotTrainingAuditServiceImpl.java new file mode 100644 index 0000000..65dcf5e --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingAudit/service/impl/HotTrainingAuditServiceImpl.java @@ -0,0 +1,250 @@ +package com.hotwj.platform.securityManagement.trainingAudit.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.securityManagement.trainingAudit.domain.HotTrainingAudit; +import com.hotwj.platform.securityManagement.trainingAudit.domain.bo.HotTrainingAuditBatchBo; +import com.hotwj.platform.securityManagement.trainingAudit.domain.bo.HotTrainingAuditBo; +import com.hotwj.platform.securityManagement.trainingAudit.domain.vo.HotTrainingAuditVo; +import com.hotwj.platform.securityManagement.trainingAudit.mapper.HotTrainingAuditMapper; +import com.hotwj.platform.securityManagement.trainingAudit.service.IHotTrainingAuditService; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.HotTrainingParticipant; +import com.hotwj.platform.securityManagement.trainingParticipant.mapper.HotTrainingParticipantMapper; +import com.hotwj.platform.securityManagement.trainingProgress.service.TrainingProgressService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 培训审核Service业务层处理 + * + * @author shihongwei + * @date 2025-12-29 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotTrainingAuditServiceImpl implements IHotTrainingAuditService { + + private final HotTrainingAuditMapper baseMapper; + private final HotTrainingParticipantMapper trainingParticipantMapper; + private final TrainingProgressService trainingProgressService; + + /** + * 查询培训审核 + * + * @param id 主键 + * @return 培训审核 + */ + @Override + public HotTrainingAuditVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询培训审核列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训审核分页列表 + */ + @Override + public TableDataInfo queryPageList(HotTrainingAuditBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的培训审核列表 + * + * @param bo 查询条件 + * @return 培训审核列表 + */ + @Override + public List queryList(HotTrainingAuditBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotTrainingAuditBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotTrainingAudit::getId); + lqw.eq(bo.getCompanyId() != null, HotTrainingAudit::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getTrainingId() != null, HotTrainingAudit::getTrainingId, bo.getTrainingId()); + lqw.eq(bo.getUserId() != null, HotTrainingAudit::getUserId, bo.getUserId()); + lqw.eq(bo.getIsPass() != null, HotTrainingAudit::getIsPass, bo.getIsPass()); + lqw.eq(bo.getAuditorId() != null, HotTrainingAudit::getAuditorId, bo.getAuditorId()); + lqw.like(StringUtils.isNotBlank(bo.getAuditorName()), HotTrainingAudit::getAuditorName, bo.getAuditorName()); + lqw.eq(bo.getAuditTime() != null, HotTrainingAudit::getAuditTime, bo.getAuditTime()); + lqw.eq(StringUtils.isNotBlank(bo.getSignImgUrl()), HotTrainingAudit::getSignImgUrl, bo.getSignImgUrl()); + lqw.eq(bo.getIsDeleted() != null, HotTrainingAudit::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增培训审核 + * + * @param bo 培训审核 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotTrainingAuditBo bo) { + fillAuditInfo(bo); + HotTrainingAudit add = MapstructUtils.convert(bo, HotTrainingAudit.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + syncParticipantPassStatus(bo); + } + return flag; + } + + /** + * 修改培训审核 + * + * @param bo 培训审核 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotTrainingAuditBo bo) { + fillAuditInfo(bo); + HotTrainingAudit update = MapstructUtils.convert(bo, HotTrainingAudit.class); + validEntityBeforeSave(update); + boolean flag = baseMapper.updateById(update) > 0; + if (flag) { + syncParticipantPassStatus(bo); + } + return flag; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchAudit(HotTrainingAuditBatchBo bo) { + if (bo.getCompanyId() == null || bo.getTrainingId() == null + || StringUtils.isBlank(bo.getSignImgUrl()) + || bo.getAuditItems() == null || bo.getAuditItems().isEmpty()) { + return false; + } + String auditorId = LoginHelper.getBusinessUserId(); + String auditorName = LoginHelper.getUsername(); + Date auditTime = new Date(); + int affected = 0; + for (HotTrainingAuditBo item : bo.getAuditItems()) { + if (item == null || StringUtils.isBlank(item.getUserId()) || item.getIsPass() == null) { + continue; + } + String userId = item.getUserId().trim(); + if (StringUtils.isBlank(userId)) { + continue; + } + LambdaQueryWrapper participantLqw = Wrappers.lambdaQuery(); + participantLqw.eq(HotTrainingParticipant::getCompanyId, bo.getCompanyId()) + .eq(HotTrainingParticipant::getTrainingId, bo.getTrainingId()) + .eq(HotTrainingParticipant::getUserId, userId) + .eq(HotTrainingParticipant::getIsDeleted, 0L) + .last("limit 1"); + HotTrainingParticipant participant = trainingParticipantMapper.selectOne(participantLqw); + if (participant == null) { + continue; + } + LambdaQueryWrapper auditLqw = Wrappers.lambdaQuery(); + auditLqw.eq(HotTrainingAudit::getCompanyId, bo.getCompanyId()) + .eq(HotTrainingAudit::getTrainingId, bo.getTrainingId()) + .eq(HotTrainingAudit::getUserId, userId) + .eq(HotTrainingAudit::getIsDeleted, 0L) + .last("limit 1"); + HotTrainingAudit existing = baseMapper.selectOne(auditLqw); + + HotTrainingAuditBo auditBo = new HotTrainingAuditBo(); + if (existing != null) { + auditBo.setId(existing.getId()); + } + auditBo.setCompanyId(bo.getCompanyId()); + auditBo.setTrainingId(bo.getTrainingId()); + auditBo.setUserId(userId); + auditBo.setIsPass(item.getIsPass()); + auditBo.setRemark(item.getRemark()); + auditBo.setSignImgUrl(bo.getSignImgUrl()); + auditBo.setAuditorId(auditorId); + auditBo.setAuditorName(auditorName); + auditBo.setAuditTime(auditTime); + + boolean ok = existing == null ? insertByBo(auditBo) : updateByBo(auditBo); + if (ok) { + affected++; + } + } + return affected > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotTrainingAudit entity) { + //TODO 做一些数据校验,如唯一约束 + } + + private void fillAuditInfo(HotTrainingAuditBo bo) { + String userId = LoginHelper.getBusinessUserId(); + String username = LoginHelper.getUsername(); + if (userId != null) { + bo.setAuditorId(userId); + } + if (StringUtils.isNotBlank(username)) { + bo.setAuditorName(username); + } + bo.setAuditTime(new Date()); + } + + private void syncParticipantPassStatus(HotTrainingAuditBo bo) { + if (bo.getCompanyId() == null || bo.getTrainingId() == null || StringUtils.isBlank(bo.getUserId())) { + return; + } + LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); + lqw.eq(HotTrainingParticipant::getCompanyId, bo.getCompanyId()) + .eq(HotTrainingParticipant::getTrainingId, bo.getTrainingId()) + .eq(HotTrainingParticipant::getUserId, bo.getUserId()) + .eq(HotTrainingParticipant::getIsDeleted, 0L); + HotTrainingParticipant participant = trainingParticipantMapper.selectOne(lqw); + if (participant == null) { + return; + } + participant.setIsPass(bo.getIsPass()); + if (Long.valueOf(0L).equals(bo.getIsPass())) { + participant.setIsCompleted(0L); + participant.setCompleteTime(null); + } + trainingParticipantMapper.updateById(participant); + trainingProgressService.syncProgress(bo.getCompanyId(), bo.getTrainingId(), bo.getUserId()); + } + + /** + * 校验并批量删除培训审核信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/controller/HotTrainingCourseConfigController.java b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/controller/HotTrainingCourseConfigController.java new file mode 100644 index 0000000..36ac832 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/controller/HotTrainingCourseConfigController.java @@ -0,0 +1,275 @@ +package com.hotwj.platform.securityManagement.trainingCourseConfig.controller; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.hotwj.platform.config.courseResource.domain.bo.HotCourseResourceBo; +import com.hotwj.platform.config.courseResource.service.IHotCourseResourceService; +import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo; +import com.hotwj.platform.securityManagement.training.service.IHotTrainingService; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.bo.HotTrainingCourseConfigBo; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.vo.HotTrainingCourseConfigVo; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.vo.TrainingUserCourseStudyDetailVo; +import com.hotwj.platform.securityManagement.trainingCourseConfig.service.IHotTrainingCourseConfigService; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.bo.HotTrainingCourseRecordBo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.vo.HotTrainingCourseRecordVo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.service.IHotTrainingCourseRecordService; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.bo.HotTrainingParticipantBo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.HotTrainingParticipantVo; +import com.hotwj.platform.securityManagement.trainingParticipant.service.IHotTrainingParticipantService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.util.*; + +/** + * 企业课程配置 + * + * @author shihongwei + * @date 2026-01-24 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/trainingCourseConfig") +@Tag(name = "企业课程配置", description = "企业课程配置管理") +public class HotTrainingCourseConfigController extends BaseController { + + private final IHotTrainingCourseConfigService hotTrainingCourseConfigService; + private final IHotTrainingCourseRecordService hotTrainingCourseRecordService; + private final IHotCourseResourceService hotCourseResourceService; + private final IHotTrainingParticipantService hotTrainingParticipantService; + private final IHotTrainingService hotTrainingService; + + /** + * 查询企业课程配置列表 + */ + //@SaCheckPermission("securityManagement:trainingCourseConfig:list") + @GetMapping("/list") + @Operation(summary = "分页查询企业课程配置列表") + public TableDataInfo list(HotTrainingCourseConfigBo bo, PageQuery pageQuery) { + TableDataInfo tableDataInfo = hotTrainingCourseConfigService.queryPageList(bo, pageQuery); + List rows = tableDataInfo.getRows(); + if (rows == null || rows.isEmpty()) { + return tableDataInfo; + } + for (HotTrainingCourseConfigVo vo : rows) { + HotCourseResourceBo courseResourceBo = new HotCourseResourceBo(); + courseResourceBo.setCourseId(vo.getCourseId()); + courseResourceBo.setResourceType(3L); + boolean hasLessonQuiz = !hotCourseResourceService.queryList(courseResourceBo).isEmpty(); + vo.setHasLessonQuiz(hasLessonQuiz); + if (!hasLessonQuiz) { + vo.setLessonQuizCompleted(null); + } + if (bo.getUserId() != null && !bo.getUserId().isEmpty()) { + HotTrainingCourseRecordBo recordBo = new HotTrainingCourseRecordBo(); + recordBo.setCompanyId(vo.getCompanyId()); + recordBo.setTrainingId(vo.getTrainingId()); + recordBo.setTrainingCourseId(vo.getId()); + recordBo.setUserId(bo.getUserId()); + HotTrainingCourseRecordVo latest = hotTrainingCourseRecordService.queryLatestByUserAndCourse(recordBo); + if (latest != null) { + if (latest.getLearnDurationMin() != null) { + vo.setLearnDurationMin(latest.getLearnDurationMin().intValue()); + } + vo.setProgressRate(latest.getProgressRate()); + vo.setCompleted(latest.getCheckStatus() != null && latest.getCheckStatus() == 1L); + vo.setCompleteTime(latest.getCompleteTime()); + vo.setLessonQuizCompleted(latest.getCheckStatus() != null && latest.getCheckStatus() == 1L); + } + } + + if (bo.getUserId() != null && !bo.getUserId().isEmpty()) { + HotTrainingParticipantBo pbo = new HotTrainingParticipantBo(); + pbo.setCompanyId(vo.getCompanyId()); + pbo.setTrainingId(vo.getTrainingId()); + pbo.setUserId(bo.getUserId()); + pbo.setIsDeleted(0L); + List participants = hotTrainingParticipantService.queryList(pbo); + if (participants != null && !participants.isEmpty()) { + String json = participants.get(0).getStudyPhotoOssId(); + if (json != null && !json.isEmpty()) { + try { + Map map = JsonUtils.parseObject(json, new TypeReference<>() { + }); + if (map != null) { + Long ossId = map.get(vo.getId()); + if (ossId != null) { + vo.setStudyPhotoOssId(ossId); + } + } + } catch (Exception ignored) { + } + } + } + } + } + return tableDataInfo; + } + + @GetMapping("/trainingStudyDetail") + @Operation(summary = "培训学习明细聚合(含培训信息与课程明细)") + public R> trainingStudyDetail( + @RequestParam Long companyId, + @RequestParam Long trainingId, + @RequestParam String userId + ) { + HotTrainingVo training = hotTrainingService.queryById(trainingId); + Long isMakeUp = training == null ? null : training.getIsMakeUp(); + + Map photoMap = Collections.emptyMap(); + + HotTrainingParticipantBo participantBo = new HotTrainingParticipantBo(); + participantBo.setCompanyId(companyId); + participantBo.setTrainingId(trainingId); + participantBo.setUserId(userId); + participantBo.setIsDeleted(0L); + List participants = hotTrainingParticipantService.queryList(participantBo); + Long isCompleted = null; + Long isPass = null; + if (participants != null && !participants.isEmpty()) { + isCompleted = participants.get(0).getIsCompleted(); + isPass = participants.get(0).getIsPass(); + String json = participants.get(0).getStudyPhotoOssId(); + if (json != null && !json.isEmpty()) { + try { + Map parsed = JsonUtils.parseObject(json, new TypeReference<>() { + }); + if (parsed != null) { + photoMap = parsed; + } + } catch (Exception ignored) { + } + } + } + + List rows = hotTrainingCourseConfigService.queryTrainingUserCourseStudyDetail(companyId, trainingId, userId); + if (rows == null) { + rows = List.of(); + } + + List> courseDetailList = new ArrayList<>(); + for (TrainingUserCourseStudyDetailVo row : rows) { + if (row == null) { + continue; + } + + Long trainingCourseId = row.getTrainingCourseId(); + String courseName = row.getCourseName(); + BigDecimal progressRate = row.getProgressRate() == null ? BigDecimal.ZERO : row.getProgressRate(); + java.util.Date completeTime = row.getCompleteTime(); + Long studyPhotoOssId = trainingCourseId == null ? null : photoMap.get(trainingCourseId); + + Map item = new HashMap<>(); + item.put("courseName", courseName); + item.put("progressRate", progressRate); + item.put("completeTime", completeTime); + item.put("isMakeUp", isMakeUp); + item.put("studyPhotoOssId", studyPhotoOssId); + courseDetailList.add(item); + } + + Map resp = new HashMap<>(); + resp.put("traing", training); + resp.put("isCompleted", isCompleted); + resp.put("isPass", isPass); + resp.put("courseDetailList", courseDetailList); + return R.ok(resp); + } + + /** + * 导出企业课程配置列表 + */ + //@SaCheckPermission("securityManagement:trainingCourseConfig:export") + @Log(title = "企业课程配置", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出企业课程配置列表") + public void export(HotTrainingCourseConfigBo bo, HttpServletResponse response) { + List list = hotTrainingCourseConfigService.queryList(bo); + ExcelUtil.exportExcel(list, "企业课程配置", HotTrainingCourseConfigVo.class, response); + } + + /** + * 获取企业课程配置详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:trainingCourseConfig:query") + @GetMapping("/{id}") + @Operation(summary = "获取企业课程配置详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotTrainingCourseConfigService.queryById(id)); + } + + /** + * 新增企业课程配置 + */ + //@SaCheckPermission("securityManagement:trainingCourseConfig:add") + @Log(title = "企业课程配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增企业课程配置") + public R add(@Validated(AddGroup.class) @RequestBody HotTrainingCourseConfigBo bo) { + return toAjax(hotTrainingCourseConfigService.insertByBo(bo)); + } + + /** + * 修改企业课程配置 + */ + //@SaCheckPermission("securityManagement:trainingCourseConfig:edit") + @Log(title = "企业课程配置", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改企业课程配置") + public R edit(@Validated(EditGroup.class) @RequestBody HotTrainingCourseConfigBo bo) { + return toAjax(hotTrainingCourseConfigService.updateByBo(bo)); + } + + //@SaCheckPermission("securityManagement:trainingCourseConfig:edit") + @Log(title = "企业课程配置", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/updateStudyPhotoOssId") + @Operation(summary = "修改企业课程配置学习照片") + public R updateStudyPhotoOssId( + @RequestParam Long participantId, + @RequestParam Long trainingCourseId, + @RequestParam Long studyPhotoOssId + ) { + return toAjax(hotTrainingParticipantService.updateStudyPhotoOssId(participantId, trainingCourseId, studyPhotoOssId)); + } + + /** + * 删除企业课程配置 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:trainingCourseConfig:remove") + @Log(title = "企业课程配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除企业课程配置") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotTrainingCourseConfigService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/domain/HotTrainingCourseConfig.java b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/domain/HotTrainingCourseConfig.java new file mode 100644 index 0000000..7da1b52 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/domain/HotTrainingCourseConfig.java @@ -0,0 +1,84 @@ +package com.hotwj.platform.securityManagement.trainingCourseConfig.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 企业课程配置对象 hot_training_course_config + * + * @author shihongwei + * @date 2025-12-24 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_training_course_config") +public class HotTrainingCourseConfig extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 课程名称 + */ + private String courseName; + + /** + * 所属课程ID + */ + private Long courseId; + + /** + * 课程所属,例如: 企业/下发 + */ + private String courseBelong; + + /** + * 学习时长,单位分钟 + */ + private Long studyDuration; + + /** + * 排序序号,数字越小越靠前 + */ + private Long sortNo; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 培训id + */ + private Long trainingId; + + /** + * 创建人显示名称 + */ + private String createByName; + + /** + * 最后修改人显示名称 + */ + private String updateByName; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/domain/bo/HotTrainingCourseConfigBo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/domain/bo/HotTrainingCourseConfigBo.java new file mode 100644 index 0000000..de024d4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/domain/bo/HotTrainingCourseConfigBo.java @@ -0,0 +1,85 @@ +package com.hotwj.platform.securityManagement.trainingCourseConfig.domain.bo; + +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.HotTrainingCourseConfig; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 企业课程配置业务对象 hot_training_course_config + * + * @author shihongwei + * @date 2026-01-24 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotTrainingCourseConfig.class, reverseConvertGenerate = false) +public class HotTrainingCourseConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 课程名称 + */ + @NotBlank(message = "课程名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String courseName; + + /** + * 所属课程ID + */ + @NotNull(message = "所属课程ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long courseId; + + /** + * 课程所属,例如: 企业/下发 + */ + private String courseBelong; + + /** + * 学习时长,单位分钟 + */ + private Long studyDuration; + + /** + * 排序序号,数字越小越靠前 + */ + private Long sortNo; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + /** + * 培训id + */ + private Long trainingId; + + /** + * 创建人显示名称 + */ + private String createByName; + + /** + * 最后修改人显示名称 + */ + private String updateByName; + + private String userId; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/domain/vo/HotTrainingCourseConfigVo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/domain/vo/HotTrainingCourseConfigVo.java new file mode 100644 index 0000000..681dac3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/domain/vo/HotTrainingCourseConfigVo.java @@ -0,0 +1,114 @@ +package com.hotwj.platform.securityManagement.trainingCourseConfig.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.HotTrainingCourseConfig; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 企业课程配置视图对象 hot_training_course_config + * + * @author shihongwei + * @date 2026-01-24 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotTrainingCourseConfig.class) +public class HotTrainingCourseConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 课程名称 + */ + @ExcelProperty(value = "课程名称") + private String courseName; + + /** + * 所属课程ID + */ + @ExcelProperty(value = "所属课程ID") + private Long courseId; + + /** + * 课程所属,例如: 企业/下发 + */ + @ExcelProperty(value = "课程所属,例如: 企业/下发") + private String courseBelong; + + /** + * 学习时长,单位分钟 + */ + @ExcelProperty(value = "学习时长,单位分钟") + private Long studyDuration; + + /** + * 排序序号,数字越小越靠前 + */ + @ExcelProperty(value = "排序序号,数字越小越靠前") + private Long sortNo; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 培训id + */ + @ExcelProperty(value = "培训id") + private Long trainingId; + + /** + * 学习照片OSS文件ID + */ + @ExcelProperty(value = "学习照片OSS文件ID") + private Long studyPhotoOssId; + + /** + * 创建人显示名称 + */ + @ExcelProperty(value = "创建人显示名称") + private String createByName; + + /** + * 最后修改人显示名称 + */ + @ExcelProperty(value = "最后修改人显示名称") + private String updateByName; + + private Date createTime; + + private Integer learnDurationMin; + + private BigDecimal progressRate; + + private Boolean completed; + + private Date completeTime; + + private Boolean lessonQuizCompleted; + + private Boolean hasLessonQuiz; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/domain/vo/TrainingUserCourseStudyDetailVo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/domain/vo/TrainingUserCourseStudyDetailVo.java new file mode 100644 index 0000000..a666925 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/domain/vo/TrainingUserCourseStudyDetailVo.java @@ -0,0 +1,15 @@ +package com.hotwj.platform.securityManagement.trainingCourseConfig.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class TrainingUserCourseStudyDetailVo { + private Long trainingCourseId; + private String courseName; + private Long sortNo; + private BigDecimal progressRate; + private Date completeTime; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/mapper/HotTrainingCourseConfigMapper.java b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/mapper/HotTrainingCourseConfigMapper.java new file mode 100644 index 0000000..a544887 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/mapper/HotTrainingCourseConfigMapper.java @@ -0,0 +1,26 @@ +package com.hotwj.platform.securityManagement.trainingCourseConfig.mapper; + +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.HotTrainingCourseConfig; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.vo.HotTrainingCourseConfigVo; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.vo.TrainingUserCourseStudyDetailVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +import java.util.List; + +/** + * 企业课程配置Mapper接口 + * + * @author shihongwei + * @date 2026-01-24 + */ +@Mapper +public interface HotTrainingCourseConfigMapper extends BaseMapperPlus { + + List selectTrainingUserCourseStudyDetail( + @Param("companyId") Long companyId, + @Param("trainingId") Long trainingId, + @Param("userId") String userId + ); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/service/IHotTrainingCourseConfigService.java b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/service/IHotTrainingCourseConfigService.java new file mode 100644 index 0000000..cbdbbba --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/service/IHotTrainingCourseConfigService.java @@ -0,0 +1,71 @@ +package com.hotwj.platform.securityManagement.trainingCourseConfig.service; + +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.bo.HotTrainingCourseConfigBo; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.vo.HotTrainingCourseConfigVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 企业课程配置Service接口 + * + * @author shihongwei + * @date 2026-01-24 + */ +public interface IHotTrainingCourseConfigService { + + /** + * 查询企业课程配置 + * + * @param id 主键 + * @return 企业课程配置 + */ + HotTrainingCourseConfigVo queryById(Long id); + + /** + * 分页查询企业课程配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 企业课程配置分页列表 + */ + TableDataInfo queryPageList(HotTrainingCourseConfigBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的企业课程配置列表 + * + * @param bo 查询条件 + * @return 企业课程配置列表 + */ + List queryList(HotTrainingCourseConfigBo bo); + + java.util.List + queryTrainingUserCourseStudyDetail(Long companyId, Long trainingId, String userId); + + /** + * 新增企业课程配置 + * + * @param bo 企业课程配置 + * @return 是否新增成功 + */ + Boolean insertByBo(HotTrainingCourseConfigBo bo); + + /** + * 修改企业课程配置 + * + * @param bo 企业课程配置 + * @return 是否修改成功 + */ + Boolean updateByBo(HotTrainingCourseConfigBo bo); + + /** + * 校验并批量删除企业课程配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/service/impl/HotTrainingCourseConfigServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/service/impl/HotTrainingCourseConfigServiceImpl.java new file mode 100644 index 0000000..1dd4f40 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseConfig/service/impl/HotTrainingCourseConfigServiceImpl.java @@ -0,0 +1,159 @@ +package com.hotwj.platform.securityManagement.trainingCourseConfig.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.HotTrainingCourseConfig; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.bo.HotTrainingCourseConfigBo; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.vo.HotTrainingCourseConfigVo; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.vo.TrainingUserCourseStudyDetailVo; +import com.hotwj.platform.securityManagement.trainingCourseConfig.mapper.HotTrainingCourseConfigMapper; +import com.hotwj.platform.securityManagement.trainingCourseConfig.service.IHotTrainingCourseConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 企业课程配置Service业务层处理 + * + * @author shihongwei + * @date 2026-01-24 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotTrainingCourseConfigServiceImpl implements IHotTrainingCourseConfigService { + + private final HotTrainingCourseConfigMapper baseMapper; + + /** + * 查询企业课程配置 + * + * @param id 主键 + * @return 企业课程配置 + */ + @Override + public HotTrainingCourseConfigVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询企业课程配置列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 企业课程配置分页列表 + */ + @Override + public TableDataInfo queryPageList(HotTrainingCourseConfigBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的企业课程配置列表 + * + * @param bo 查询条件 + * @return 企业课程配置列表 + */ + @Override + public List queryList(HotTrainingCourseConfigBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + @Override + public List queryTrainingUserCourseStudyDetail(Long companyId, Long trainingId, String userId) { + return baseMapper.selectTrainingUserCourseStudyDetail(companyId, trainingId, userId); + } + + private LambdaQueryWrapper buildQueryWrapper(HotTrainingCourseConfigBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotTrainingCourseConfig::getSortNo); + lqw.eq(bo.getCompanyId() != null, HotTrainingCourseConfig::getCompanyId, bo.getCompanyId()); + lqw.like(StringUtils.isNotBlank(bo.getCourseName()), HotTrainingCourseConfig::getCourseName, bo.getCourseName()); + lqw.eq(bo.getCourseId() != null, HotTrainingCourseConfig::getCourseId, bo.getCourseId()); + lqw.eq(StringUtils.isNotBlank(bo.getCourseBelong()), HotTrainingCourseConfig::getCourseBelong, bo.getCourseBelong()); + lqw.eq(bo.getStudyDuration() != null, HotTrainingCourseConfig::getStudyDuration, bo.getStudyDuration()); + lqw.eq(bo.getSortNo() != null, HotTrainingCourseConfig::getSortNo, bo.getSortNo()); + lqw.eq(bo.getIsDeleted() != null, HotTrainingCourseConfig::getIsDeleted, bo.getIsDeleted()); + lqw.eq(bo.getTrainingId() != null, HotTrainingCourseConfig::getTrainingId, bo.getTrainingId()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotTrainingCourseConfig::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotTrainingCourseConfig::getUpdateByName, bo.getUpdateByName()); + return lqw; + } + + /** + * 新增企业课程配置 + * + * @param bo 企业课程配置 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotTrainingCourseConfigBo bo) { + HotTrainingCourseConfig add = MapstructUtils.convert(bo, HotTrainingCourseConfig.class); + HotTrainingCourseConfig maxSort = baseMapper.selectOne( + Wrappers.lambdaQuery() + .select(HotTrainingCourseConfig::getSortNo) + .eq(HotTrainingCourseConfig::getCompanyId, bo.getCompanyId()) + .eq(bo.getTrainingId() != null, HotTrainingCourseConfig::getTrainingId, bo.getTrainingId()) + .orderByDesc(HotTrainingCourseConfig::getSortNo) + .last("limit 1") + ); + long max = (maxSort != null && maxSort.getSortNo() != null) ? maxSort.getSortNo() : 0L; + add.setSortNo(max + 1); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改企业课程配置 + * + * @param bo 企业课程配置 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotTrainingCourseConfigBo bo) { + HotTrainingCourseConfig update = MapstructUtils.convert(bo, HotTrainingCourseConfig.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotTrainingCourseConfig entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除企业课程配置信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/controller/HotTrainingCourseRecordController.java b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/controller/HotTrainingCourseRecordController.java new file mode 100644 index 0000000..8ea9c07 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/controller/HotTrainingCourseRecordController.java @@ -0,0 +1,133 @@ +package com.hotwj.platform.securityManagement.trainingCourseRecord.controller; + +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.bo.HotTrainingCourseRecordBo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.vo.HotTrainingCourseRecordVo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.service.IHotTrainingCourseRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 培训人员课程学习记录 + * + * @author shihongwei + * @date 2026-02-02 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/trainingCourseRecord") +@Tag(name = "培训人员课程学习记录", description = "培训人员课程学习记录管理") +public class HotTrainingCourseRecordController extends BaseController { + + private final IHotTrainingCourseRecordService hotTrainingCourseRecordService; + + /** + * 查询培训人员课程学习记录列表 + */ + //@SaCheckPermission("securityManagement:trainingCourseRecord:list") + @GetMapping("/list") + @Operation(summary = "分页查询培训人员课程学习记录列表") + public TableDataInfo list(HotTrainingCourseRecordBo bo, PageQuery pageQuery) { + return hotTrainingCourseRecordService.queryPageList(bo, pageQuery); + } + + /** + * 导出培训人员课程学习记录列表 + */ + //@SaCheckPermission("securityManagement:trainingCourseRecord:export") + @Log(title = "培训人员课程学习记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出培训人员课程学习记录列表") + public void export(HotTrainingCourseRecordBo bo, HttpServletResponse response) { + List list = hotTrainingCourseRecordService.queryList(bo); + ExcelUtil.exportExcel(list, "培训人员课程学习记录", HotTrainingCourseRecordVo.class, response); + } + + /** + * 获取培训人员课程学习记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:trainingCourseRecord:query") + @GetMapping("/{id}") + @Operation(summary = "获取培训人员课程学习记录详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotTrainingCourseRecordService.queryById(id)); + } + + /** + * 新增培训人员课程学习记录 + */ + //@SaCheckPermission("securityManagement:trainingCourseRecord:add") + @Log(title = "培训人员课程学习记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增培训人员课程学习记录") + public R add(@Validated(AddGroup.class) @RequestBody HotTrainingCourseRecordBo bo) { + Long id = hotTrainingCourseRecordService.insertByBo(bo); + return R.ok(id); + } + + /** + * 修改培训人员课程学习记录 + */ + //@SaCheckPermission("securityManagement:trainingCourseRecord:edit") + @Log(title = "培训人员课程学习记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改培训人员课程学习记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotTrainingCourseRecordBo bo) { + return toAjax(hotTrainingCourseRecordService.updateByBo(bo)); + } + + /** + * 显式扣减学习记录对应的学时套餐 + * + * @param id 学习记录ID + */ + @Log(title = "培训人员课程学习记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/{id}/deductHourPackage") + @Operation(summary = "显式扣减学习记录对应的学时套餐") + public R deductHourPackage(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "学习记录ID", required = true, example = "1") + @PathVariable Long id) { + return toAjax(hotTrainingCourseRecordService.deductHourPackage(id)); + } + + /** + * 删除培训人员课程学习记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:trainingCourseRecord:remove") + @Log(title = "培训人员课程学习记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除培训人员课程学习记录") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotTrainingCourseRecordService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/domain/HotTrainingCourseRecord.java b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/domain/HotTrainingCourseRecord.java new file mode 100644 index 0000000..a223808 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/domain/HotTrainingCourseRecord.java @@ -0,0 +1,126 @@ +package com.hotwj.platform.securityManagement.trainingCourseRecord.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 培训人员课程学习记录对象 hot_training_course_record + * + * @author shihongwei + * @date 2026-02-02 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_training_course_record") +public class HotTrainingCourseRecord extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + private Long companyId; + + /** + * 培训计划ID + */ + private Long trainingId; + + /** + * 培训人员ID + */ + private String userId; + + /** + * 培训人员姓名 + */ + private String userName; + + /** + * 课程ID + */ + private Long trainingCourseId; + + /** + * 试卷ID + */ + private Long examId; + + /** + * 试卷分数 + */ + private BigDecimal examScore; + + /** + * 检测状态:0=未完成 1=已完成 + */ + private Long checkStatus; + + /** + * 需学习时长(秒) + */ + private Long studyDuration; + + /** + * 学习进度百分比,如 75.50 + */ + private BigDecimal progressRate; + + /** + * 累计学习时长(分钟) + */ + private Long learnDurationMin; + + /** + * 完成时间 + */ + private Date completeTime; + + /** + * 学员签名OSS文件ID + */ + private Long signatureOssId; + + /** + * 是否已使用购买课时套餐:0=否 1=是 + */ + private Long isUseHourPackage; + + /** + * 已扣减课时数 + */ + private Long hourPackageUsedHours; + + /** + * 创建者姓名 + */ + private String createByName; + + /** + * 更新者姓名 + */ + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/domain/bo/HotTrainingCourseRecordBo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/domain/bo/HotTrainingCourseRecordBo.java new file mode 100644 index 0000000..3b9415d --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/domain/bo/HotTrainingCourseRecordBo.java @@ -0,0 +1,122 @@ +package com.hotwj.platform.securityManagement.trainingCourseRecord.domain.bo; + +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.HotTrainingCourseRecord; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 培训人员课程学习记录业务对象 hot_training_course_record + * + * @author shihongwei + * @date 2026-02-02 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotTrainingCourseRecord.class, reverseConvertGenerate = false) +public class HotTrainingCourseRecordBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + private Long companyId; + + /** + * 培训计划ID + */ + private Long trainingId; + + /** + * 培训人员ID + */ + private String userId; + + /** + * 培训人员姓名 + */ + private String userName; + + /** + * 课程ID + */ + private Long trainingCourseId; + + /** + * 试卷ID + */ + private Long examId; + + /** + * 试卷分数 + */ + private BigDecimal examScore; + + /** + * 检测状态:0=未完成 1=已完成 + */ + private Long checkStatus; + + /** + * 需学习时长(秒) + */ + private Long studyDuration; + + /** + * 学习进度百分比,如 75.50 + */ + private BigDecimal progressRate; + + /** + * 累计学习时长(分钟) + */ + private Long learnDurationMin; + + /** + * 完成时间 + */ + private Date completeTime; + + /** + * 学员签名OSS文件ID + */ + private Long signatureOssId; + + /** + * 是否已使用购买课时套餐:0=否 1=是 + */ + private Long isUseHourPackage; + + /** + * 已扣减课时数 + */ + private Long hourPackageUsedHours; + + /** + * 创建者姓名 + */ + private String createByName; + + /** + * 更新者姓名 + */ + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/domain/vo/HotTrainingCourseRecordVo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/domain/vo/HotTrainingCourseRecordVo.java new file mode 100644 index 0000000..f7764ae --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/domain/vo/HotTrainingCourseRecordVo.java @@ -0,0 +1,150 @@ +package com.hotwj.platform.securityManagement.trainingCourseRecord.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.HotTrainingCourseRecord; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import org.dromara.common.json.handler.BigDecimalTwoDecimalSerializer; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 培训人员课程学习记录视图对象 hot_training_course_record + * + * @author shihongwei + * @date 2026-02-02 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotTrainingCourseRecord.class) +public class HotTrainingCourseRecordVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID(由应用层保证必填) + */ + @ExcelProperty(value = "公司ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "由=应用层保证必填") + private Long companyId; + + /** + * 培训计划ID + */ + @ExcelProperty(value = "培训计划ID") + private Long trainingId; + + /** + * 培训人员ID + */ + @ExcelProperty(value = "培训人员ID") + private String userId; + + /** + * 培训人员姓名 + */ + @ExcelProperty(value = "培训人员姓名") + private String userName; + + /** + * 课程ID + */ + @ExcelProperty(value = "课程ID") + private Long trainingCourseId; + + /** + * 试卷ID + */ + @ExcelProperty(value = "试卷ID") + private Long examId; + + /** + * 试卷分数 + */ + @ExcelProperty(value = "试卷分数") + private BigDecimal examScore; + + /** + * 检测状态:0=未完成 1=已完成 + */ + @ExcelProperty(value = "检测状态:0=未完成 1=已完成") + private Long checkStatus; + + /** + * 需学习时长(秒) + */ + @ExcelProperty(value = "需学习时长(秒)") + private Long studyDuration; + + /** + * 学习进度百分比,如 75.50 + */ + @ExcelProperty(value = "学习进度百分比,如 75.50") + @JsonSerialize(using = BigDecimalTwoDecimalSerializer.class) + private BigDecimal progressRate; + + /** + * 累计学习时长(分钟) + */ + @ExcelProperty(value = "累计学习时长(分钟)") + private Long learnDurationMin; + + /** + * 完成时间 + */ + @ExcelProperty(value = "完成时间") + private Date completeTime; + + /** + * 学员签名OSS文件ID + */ + @ExcelProperty(value = "学员签名OSS文件ID") + private Long signatureOssId; + + /** + * 是否已使用购买课时套餐:0=否 1=是 + */ + @ExcelProperty(value = "是否已使用购买课时套餐") + private Long isUseHourPackage; + + /** + * 已扣减课时数 + */ + @ExcelProperty(value = "已扣减课时数") + private Long hourPackageUsedHours; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/domain/vo/PersonalAnnualRecordCourseVo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/domain/vo/PersonalAnnualRecordCourseVo.java new file mode 100644 index 0000000..f3c1931 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/domain/vo/PersonalAnnualRecordCourseVo.java @@ -0,0 +1,36 @@ +package com.hotwj.platform.securityManagement.trainingCourseRecord.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 个人年度培训记录导出课程视图对象 + */ +@Data +public class PersonalAnnualRecordCourseVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 课程名称 + */ + private String courseName; + + /** + * 培训时间,格式 yyyy.MM.dd + */ + private String trainingTime; + + /** + * 培训评价,仅显示“及格/不及格” + */ + private String evaluation; + + /** + * 学时 + */ + private String hours; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/mapper/HotTrainingCourseRecordMapper.java b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/mapper/HotTrainingCourseRecordMapper.java new file mode 100644 index 0000000..21e03a4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/mapper/HotTrainingCourseRecordMapper.java @@ -0,0 +1,26 @@ +package com.hotwj.platform.securityManagement.trainingCourseRecord.mapper; + +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.HotTrainingCourseRecord; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.vo.HotTrainingCourseRecordVo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.vo.PersonalAnnualRecordCourseVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +import java.util.Date; +import java.util.List; + +/** + * 培训人员课程学习记录Mapper接口 + * + * @author shihongwei + * @date 2026-02-02 + */ +@Mapper +public interface HotTrainingCourseRecordMapper extends BaseMapperPlus { + + List selectPersonalAnnualRecordCourses(@Param("userId") String userId, + @Param("startTime") Date startTime, + @Param("endTime") Date endTime, + @Param("trainingType") String trainingType); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/service/IHotTrainingCourseRecordService.java b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/service/IHotTrainingCourseRecordService.java new file mode 100644 index 0000000..695f51b --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/service/IHotTrainingCourseRecordService.java @@ -0,0 +1,91 @@ +package com.hotwj.platform.securityManagement.trainingCourseRecord.service; + +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.bo.HotTrainingCourseRecordBo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.vo.HotTrainingCourseRecordVo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.vo.PersonalAnnualRecordCourseVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.Date; +import java.util.List; + +/** + * 培训人员课程学习记录Service接口 + * + * @author shihongwei + * @date 2026-02-02 + */ +public interface IHotTrainingCourseRecordService { + + /** + * 查询培训人员课程学习记录 + * + * @param id 主键 + * @return 培训人员课程学习记录 + */ + HotTrainingCourseRecordVo queryById(Long id); + + /** + * 分页查询培训人员课程学习记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训人员课程学习记录分页列表 + */ + TableDataInfo queryPageList(HotTrainingCourseRecordBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的培训人员课程学习记录列表 + * + * @param bo 查询条件 + * @return 培训人员课程学习记录列表 + */ + List queryList(HotTrainingCourseRecordBo bo); + + /** + * 新增培训人员课程学习记录 + * + * @param bo 培训人员课程学习记录 + * @return 是否新增成功 + */ + Long insertByBo(HotTrainingCourseRecordBo bo); + + /** + * 修改培训人员课程学习记录 + * + * @param bo 培训人员课程学习记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotTrainingCourseRecordBo bo); + + /** + * 按学习记录显式扣减学时套餐 + * + * @param id 学习记录ID + * @return 是否扣减成功 + */ + Boolean deductHourPackage(Long id); + + /** + * 校验并批量删除培训人员课程学习记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 查询某个用户在某培训某课程下最新的一条课程学习记录 + * + * @param bo 查询条件(需至少包含 companyId、trainingId、courseId、userId) + * @return 最新的一条课程学习记录;若不存在则返回 null + */ + HotTrainingCourseRecordVo queryLatestByUserAndCourse(HotTrainingCourseRecordBo bo); + + /** + * 查询个人年度培训记录导出课程列表 + */ + List queryPersonalAnnualRecordCourses(String userId, Date startTime, Date endTime, String trainingType); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/service/impl/HotTrainingCourseRecordServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/service/impl/HotTrainingCourseRecordServiceImpl.java new file mode 100644 index 0000000..dfe35f2 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/service/impl/HotTrainingCourseRecordServiceImpl.java @@ -0,0 +1,479 @@ +package com.hotwj.platform.securityManagement.trainingCourseRecord.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.operationManagement.hourPurchaseDetail.domain.HotHourPurchaseDetail; +import com.hotwj.platform.operationManagement.hourPurchaseDetail.mapper.HotHourPurchaseDetailMapper; +import com.hotwj.platform.operationManagement.hourUsageDetail.domain.HotHourUsageDetail; +import com.hotwj.platform.operationManagement.hourUsageDetail.mapper.HotHourUsageDetailMapper; +import com.hotwj.platform.securityManagement.training.domain.HotTraining; +import com.hotwj.platform.securityManagement.training.mapper.HotTrainingMapper; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.HotTrainingCourseConfig; +import com.hotwj.platform.securityManagement.trainingCourseConfig.mapper.HotTrainingCourseConfigMapper; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.HotTrainingCourseRecord; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.bo.HotTrainingCourseRecordBo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.vo.HotTrainingCourseRecordVo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.vo.PersonalAnnualRecordCourseVo; +import com.hotwj.platform.securityManagement.trainingCourseRecord.mapper.HotTrainingCourseRecordMapper; +import com.hotwj.platform.securityManagement.trainingCourseRecord.service.IHotTrainingCourseRecordService; +import com.hotwj.platform.securityManagement.trainingProgress.service.TrainingProgressService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.*; + +/** + * 培训人员课程学习记录Service业务层处理 + * + * @author shihongwei + * @date 2026-02-02 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotTrainingCourseRecordServiceImpl implements IHotTrainingCourseRecordService { + + private static final Long PURCHASE_DETAIL_STATUS_EFFECTIVE = 1L; + private static final Long PURCHASE_DETAIL_STATUS_USED_UP = 2L; + private static final Long USE_HOUR_PACKAGE_NO = 0L; + private static final Long USE_HOUR_PACKAGE_YES = 1L; + private static final String TRAINING_TYPE_DAILY = "daily"; + + private final HotTrainingCourseRecordMapper baseMapper; + private final TrainingProgressService trainingProgressService; + private final HotHourPurchaseDetailMapper hotHourPurchaseDetailMapper; + private final HotHourUsageDetailMapper hotHourUsageDetailMapper; + private final HotTrainingMapper hotTrainingMapper; + private final HotTrainingCourseConfigMapper hotTrainingCourseConfigMapper; + + /** + * 查询培训人员课程学习记录 + * + * @param id 主键 + * @return 培训人员课程学习记录 + */ + @Override + public HotTrainingCourseRecordVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询培训人员课程学习记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训人员课程学习记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotTrainingCourseRecordBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的培训人员课程学习记录列表 + * + * @param bo 查询条件 + * @return 培训人员课程学习记录列表 + */ + @Override + public List queryList(HotTrainingCourseRecordBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + /** + * 查询某个用户在某培训某课程下最新的一条课程学习记录 + * + * @param bo 查询条件(需至少包含 companyId、trainingId、courseId、userId) + * @return 最新的一条课程学习记录;若不存在则返回 null + */ + @Override + public HotTrainingCourseRecordVo queryLatestByUserAndCourse(HotTrainingCourseRecordBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(bo.getCompanyId() != null, HotTrainingCourseRecord::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getTrainingId() != null, HotTrainingCourseRecord::getTrainingId, bo.getTrainingId()); + lqw.eq(StringUtils.isNotBlank(bo.getUserId()), HotTrainingCourseRecord::getUserId, bo.getUserId()); + lqw.eq(bo.getTrainingCourseId() != null, HotTrainingCourseRecord::getTrainingCourseId, bo.getTrainingCourseId()); + lqw.orderByDesc(HotTrainingCourseRecord::getCompleteTime); + lqw.orderByDesc(HotTrainingCourseRecord::getCreateTime); + Page page = baseMapper.selectVoPage(new Page<>(1, 1), lqw); + List records = page.getRecords(); + if (records == null || records.isEmpty()) { + return null; + } + return records.get(0); + } + + @Override + public List queryPersonalAnnualRecordCourses(String userId, Date startTime, Date endTime, String trainingType) { + return baseMapper.selectPersonalAnnualRecordCourses(userId, startTime, endTime, trainingType); + } + + private LambdaQueryWrapper buildQueryWrapper(HotTrainingCourseRecordBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotTrainingCourseRecord::getCreateTime); + lqw.eq(bo.getCompanyId() != null, HotTrainingCourseRecord::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getTrainingId() != null, HotTrainingCourseRecord::getTrainingId, bo.getTrainingId()); + lqw.eq(StringUtils.isNotBlank(bo.getUserId()), HotTrainingCourseRecord::getUserId, bo.getUserId()); + lqw.like(StringUtils.isNotBlank(bo.getUserName()), HotTrainingCourseRecord::getUserName, bo.getUserName()); + lqw.eq(bo.getTrainingCourseId() != null, HotTrainingCourseRecord::getTrainingCourseId, bo.getTrainingCourseId()); + lqw.eq(bo.getExamId() != null, HotTrainingCourseRecord::getExamId, bo.getExamId()); + lqw.eq(bo.getExamScore() != null, HotTrainingCourseRecord::getExamScore, bo.getExamScore()); + lqw.eq(bo.getCheckStatus() != null, HotTrainingCourseRecord::getCheckStatus, bo.getCheckStatus()); + lqw.eq(bo.getStudyDuration() != null, HotTrainingCourseRecord::getStudyDuration, bo.getStudyDuration()); + lqw.eq(bo.getProgressRate() != null, HotTrainingCourseRecord::getProgressRate, bo.getProgressRate()); + lqw.eq(bo.getLearnDurationMin() != null, HotTrainingCourseRecord::getLearnDurationMin, bo.getLearnDurationMin()); + lqw.eq(bo.getCompleteTime() != null, HotTrainingCourseRecord::getCompleteTime, bo.getCompleteTime()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotTrainingCourseRecord::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotTrainingCourseRecord::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotTrainingCourseRecord::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增培训人员课程学习记录 + * + * @param bo 培训人员课程学习记录 + * @return 是否新增成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Long insertByBo(HotTrainingCourseRecordBo bo) { + initHourPackageUsageFieldsForInsert(bo); + HotTrainingCourseRecord add = MapstructUtils.convert(bo, HotTrainingCourseRecord.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + + if (flag && bo.getCompanyId() != null && bo.getTrainingId() != null && StringUtils.isNotBlank(bo.getUserId())) { + trainingProgressService.syncProgress(bo.getCompanyId(), bo.getTrainingId(), bo.getUserId()); + } + + return add.getId(); + } + + /** + * 修改培训人员课程学习记录 + * + * @param bo 培训人员课程学习记录 + * @return 是否修改成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(HotTrainingCourseRecordBo bo) { + HotTrainingCourseRecord update = MapstructUtils.convert(bo, HotTrainingCourseRecord.class); + HotTrainingCourseRecord before = null; + if (bo.getId() != null) { + before = baseMapper.selectById(bo.getId()); + if (before != null && before.getProgressRate() != null && update.getProgressRate() != null) { + // 仅操作单条记录时,确保视频进度单调不下降 + if (update.getProgressRate().compareTo(before.getProgressRate()) < 0) { + update.setProgressRate(before.getProgressRate()); + } + } + } + initHourPackageUsageFieldsForUpdate(update, before); + validEntityBeforeSave(update); + boolean ok = baseMapper.updateById(update) > 0; + if (ok && before != null && before.getCompanyId() != null && before.getTrainingId() != null && StringUtils.isNotBlank(before.getUserId())) { + trainingProgressService.syncProgress(before.getCompanyId(), before.getTrainingId(), before.getUserId()); + } + return ok; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deductHourPackage(Long id) { + if (id == null) { + throw new ServiceException("学习记录ID不能为空"); + } + HotTrainingCourseRecord record = baseMapper.selectById(id); + if (record == null) { + throw new ServiceException("学习记录不存在"); + } + if (Objects.equals(record.getIsUseHourPackage(), USE_HOUR_PACKAGE_YES)) { + return true; + } + + Long needUseHours = resolveNeedUseHours(record.getTrainingCourseId()); + if (!canDeductHourPackage(record, needUseHours)) { + throw new ServiceException("当前学习记录未满足课时套餐扣减条件"); + } + + HotTrainingCourseRecord update = new HotTrainingCourseRecord(); + update.setIsUseHourPackage(USE_HOUR_PACKAGE_YES); + update.setHourPackageUsedHours(needUseHours); + boolean updated = baseMapper.update(update, Wrappers.lambdaUpdate() + .eq(HotTrainingCourseRecord::getId, id) + .eq(HotTrainingCourseRecord::getIsDeleted, USE_HOUR_PACKAGE_NO) + .and(wrapper -> wrapper.isNull(HotTrainingCourseRecord::getIsUseHourPackage) + .or() + .eq(HotTrainingCourseRecord::getIsUseHourPackage, USE_HOUR_PACKAGE_NO))) > 0; + if (!updated) { + HotTrainingCourseRecord latest = baseMapper.selectById(id); + if (latest != null && Objects.equals(latest.getIsUseHourPackage(), USE_HOUR_PACKAGE_YES)) { + return true; + } + throw new ServiceException("学习记录状态已变更,请刷新后重试"); + } + + record.setIsUseHourPackage(USE_HOUR_PACKAGE_YES); + record.setHourPackageUsedHours(needUseHours); + saveHourPackageUsageIfNeeded(record); + return true; + } + + private void initHourPackageUsageFieldsForInsert(HotTrainingCourseRecordBo bo) { + if (bo == null) { + return; + } + if (bo.getIsUseHourPackage() == null) { + bo.setIsUseHourPackage(USE_HOUR_PACKAGE_NO); + } + if (bo.getHourPackageUsedHours() == null) { + bo.setHourPackageUsedHours(0L); + } + } + + private void initHourPackageUsageFieldsForUpdate(HotTrainingCourseRecord update, HotTrainingCourseRecord before) { + if (before != null && update.getIsUseHourPackage() == null) { + update.setIsUseHourPackage(before.getIsUseHourPackage()); + } + if (before != null && update.getHourPackageUsedHours() == null) { + update.setHourPackageUsedHours(before.getHourPackageUsedHours()); + } + } + + private boolean canDeductHourPackage(HotTrainingCourseRecord record, Long needUseHours) { + if (record == null || record.getCompanyId() == null || record.getTrainingId() == null || record.getTrainingCourseId() == null || StringUtils.isBlank(record.getUserId())) { + return false; + } + if (Objects.equals(record.getIsUseHourPackage(), USE_HOUR_PACKAGE_YES)) { + return false; + } + if (!Objects.equals(record.getCheckStatus(), 1L)) { + return false; + } + HotTraining training = hotTrainingMapper.selectById(record.getTrainingId()); + if (training == null || !StringUtils.equals(TRAINING_TYPE_DAILY, training.getTrainingType())) { + return false; + } + return needUseHours != null && needUseHours > 0; + } + + private Long resolveNeedUseHours(Long trainingCourseId) { + if (trainingCourseId == null) { + return 0L; + } + HotTrainingCourseConfig courseConfig = hotTrainingCourseConfigMapper.selectById(trainingCourseId); + if (courseConfig == null || courseConfig.getStudyDuration() == null || courseConfig.getStudyDuration() <= 0) { + return 0L; + } + return courseConfig.getStudyDuration(); + } + + private void saveHourPackageUsageIfNeeded(HotTrainingCourseRecord record) { + if (record == null || record.getId() == null) { + return; + } + if (!Objects.equals(record.getIsUseHourPackage(), USE_HOUR_PACKAGE_YES)) { + return; + } + Long needUseHours = record.getHourPackageUsedHours(); + if (needUseHours == null || needUseHours <= 0) { + return; + } + long usedTotal = hotHourUsageDetailMapper.selectList(Wrappers.lambdaQuery() + .eq(HotHourUsageDetail::getBizOrderId, record.getId()) + .eq(HotHourUsageDetail::getUseType, buildUseType(record.getTrainingId())) + .eq(HotHourUsageDetail::getUserId, parseLong(record.getUserId())) + .eq(HotHourUsageDetail::getIsDeleted, 0L)) + .stream() + .map(HotHourUsageDetail::getUsedHours) + .filter(Objects::nonNull) + .mapToLong(Long::longValue) + .sum(); + if (usedTotal >= needUseHours) { + return; + } + + long remainingNeed = needUseHours - usedTotal; + List detailList = hotHourPurchaseDetailMapper.selectList(Wrappers.lambdaQuery() + .eq(HotHourPurchaseDetail::getCompanyId, record.getCompanyId()) + .eq(HotHourPurchaseDetail::getStatus, PURCHASE_DETAIL_STATUS_EFFECTIVE) + .gt(HotHourPurchaseDetail::getRemainingHours, 0L) + .orderByAsc(HotHourPurchaseDetail::getId)); + if (detailList == null || detailList.isEmpty()) { + throw new ServiceException("可用学时套餐不足,请先购买学时套餐"); + } + + Long operatorId = parseLong(record.getUserId()); + String useType = buildUseType(record.getTrainingId()); + Date now = record.getCompleteTime() != null ? record.getCompleteTime() : new Date(); + + for (HotHourPurchaseDetail detail : detailList) { + if (remainingNeed <= 0) { + break; + } + Long currentRemaining = detail.getRemainingHours(); + if (currentRemaining == null || currentRemaining <= 0) { + continue; + } + long currentUse = Math.min(remainingNeed, currentRemaining); + if (!deductPurchaseDetail(detail, currentUse, now)) { + throw new ServiceException("学时套餐已变更,请刷新后重试"); + } + HotHourUsageDetail usageDetail = buildUsageDetail(record, detail, currentUse, useType, operatorId, now); + if (hotHourUsageDetailMapper.insert(usageDetail) <= 0) { + throw new ServiceException("写入学时套餐使用记录失败"); + } + remainingNeed -= currentUse; + } + if (remainingNeed > 0) { + throw new ServiceException("可用学时套餐不足,请先购买学时套餐"); + } + } + + private boolean deductPurchaseDetail(HotHourPurchaseDetail detail, long useHours, Date useTime) { + long beforeRemaining = detail.getRemainingHours() == null ? 0L : detail.getRemainingHours(); + long beforeUsed = detail.getUsedHours() == null ? 0L : detail.getUsedHours(); + long afterRemaining = beforeRemaining - useHours; + long afterUsed = beforeUsed + useHours; + + HotHourPurchaseDetail update = new HotHourPurchaseDetail(); + update.setUsedHours(afterUsed); + update.setRemainingHours(afterRemaining); + update.setLastUseTime(useTime); + update.setStatus(afterRemaining == 0 ? PURCHASE_DETAIL_STATUS_USED_UP : PURCHASE_DETAIL_STATUS_EFFECTIVE); + update.setVersion(detail.getVersion() == null ? 1L : detail.getVersion() + 1); + + LambdaUpdateWrapper wrapper = Wrappers.lambdaUpdate() + .eq(HotHourPurchaseDetail::getId, detail.getId()) + .eq(HotHourPurchaseDetail::getVersion, detail.getVersion()) + .eq(HotHourPurchaseDetail::getRemainingHours, beforeRemaining) + .gt(HotHourPurchaseDetail::getRemainingHours, 0L); + boolean success = hotHourPurchaseDetailMapper.update(update, wrapper) > 0; + if (success) { + detail.setUsedHours(afterUsed); + detail.setRemainingHours(afterRemaining); + detail.setLastUseTime(useTime); + detail.setStatus(update.getStatus()); + detail.setVersion(update.getVersion()); + } + return success; + } + + private HotHourUsageDetail buildUsageDetail(HotTrainingCourseRecord record, HotHourPurchaseDetail detail, long usedHours, + String useType, Long operatorId, Date useDate) { + HotHourUsageDetail usageDetail = new HotHourUsageDetail(); + usageDetail.setCompanyId(record.getCompanyId()); + usageDetail.setPurchaseDetailId(detail.getId()); + usageDetail.setAccountId(detail.getAccountId()); + usageDetail.setUserId(parseLong(record.getUserId())); + usageDetail.setUserName(StringUtils.defaultIfBlank(record.getUserName(), record.getCreateByName())); + usageDetail.setOperatorId(operatorId); + usageDetail.setOperatorName(StringUtils.defaultIfBlank(record.getUpdateByName(), StringUtils.defaultIfBlank(record.getUserName(), record.getCreateByName()))); + usageDetail.setUseType(useType); + usageDetail.setPackageId(detail.getPackageId()); + usageDetail.setPackageName(detail.getPackageName()); + usageDetail.setUsedHours(usedHours); + usageDetail.setBeforeRemainingHours(detail.getRemainingHours() + usedHours); + usageDetail.setAfterRemainingHours(detail.getRemainingHours()); + usageDetail.setRemainingHours(detail.getRemainingHours()); + usageDetail.setUseDate(useDate); + usageDetail.setBizOrderId(record.getId()); + usageDetail.setBizOrderNo(String.valueOf(record.getId())); + usageDetail.setRemark("安全学习扣减学时套餐"); + return usageDetail; + } + + private String buildUseType(Long trainingId) { + if (trainingId == null) { + return "日常培训"; + } + HotTraining training = hotTrainingMapper.selectById(trainingId); + return mapTrainingTypeLabel(training == null ? null : training.getTrainingType()); + } + + private String mapTrainingTypeLabel(String trainingType) { + if (StringUtils.equals("pre-job", trainingType)) { + return "岗前培训"; + } + if (StringUtils.equals("daily", trainingType)) { + return "日常培训"; + } + if (StringUtils.equals("accident", trainingType)) { + return "事故培训"; + } + if (StringUtils.equals("special", trainingType)) { + return "专题培训"; + } + if (StringUtils.equals("violation", trainingType)) { + return "违章培训"; + } + if (StringUtils.equals("emergency", trainingType)) { + return "应急培训"; + } + if (StringUtils.equals("prevention", trainingType)) { + return "双重预防学习"; + } + if (StringUtils.equals("education", trainingType)) { + return "再教育培训"; + } + if (StringUtils.equals("offline", trainingType)) { + return "线下培训"; + } + return "其他培训"; + } + + private Long parseLong(String value) { + if (StringUtils.isBlank(value) || !StringUtils.isNumeric(value)) { + return null; + } + return Long.valueOf(value); + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotTrainingCourseRecord entity) { + BigDecimal progressRate = entity.getProgressRate(); + if (progressRate != null) { + if (progressRate.compareTo(BigDecimal.ZERO) < 0) { + entity.setProgressRate(BigDecimal.ZERO); + } else if (progressRate.compareTo(new BigDecimal("100")) > 0) { + entity.setProgressRate(new BigDecimal("100")); + } + } + } + + /** + * 校验并批量删除培训人员课程学习记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/training-course-record-hour-package.md b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/training-course-record-hour-package.md new file mode 100644 index 0000000..ea77ff4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingCourseRecord/training-course-record-hour-package.md @@ -0,0 +1,196 @@ +# 安全学习使用学时套餐接口说明 + +## 1. 功能说明 + +- 场景:移动端驾驶员进行安全学习时,前端在学习完成后调用独立接口扣减已购买的学时套餐 +- 扣减顺序:按照学时套餐购买先后顺序依次扣减,先买先用 +- 扣减时机:课程学习记录完成后,由前端显式调用扣减接口触发 +- 幂等控制:学习记录增加标识字段,已经扣减过的记录不会重复扣减 +- 使用记录:每次扣减都会生成学时套餐使用明细 + +说明: + +- 前端需要在学习完成后新增一次“扣减学时套餐”的请求动作 +- 学习记录新增/修改接口只负责保存学习状态 +- 后端会在独立扣减接口中处理套餐扣减、明细写入、剩余学时更新 + +## 2. 影响范围 + +本次主要影响以下接口: + +### 2.1 学习记录接口 + +- `POST /securityManagement/trainingCourseRecord` +- `PUT /securityManagement/trainingCourseRecord` +- `GET /securityManagement/trainingCourseRecord/{id}` +- `GET /securityManagement/trainingCourseRecord/list` +- `POST /securityManagement/trainingCourseRecord/{id}/deductHourPackage` + +说明: + +- 新增/修改学习记录时,仅保存学习记录本身,不自动扣减学时套餐 +- 查询学习记录详情和列表时,返回新增的套餐扣减标识字段 +- 前端在确认课程已完成后,再调用独立扣减接口触发扣减 + +### 2.2 答题提交接口 + +- `POST /courseAnswer/trainingAnswerRecord/submit` + +说明: + +- 如果移动端通过“提交随堂练习/提交课程答题”来完成课程学习,后端内部也会更新学习记录 +- 答题提交本身不再自动触发学时套餐扣减 +- 前端需要在答题完成且学习记录已完成后,再调用扣减接口 + +## 3. 扣减规则 + +- 仅在前端显式调用扣减接口,且课程学习记录满足“已完成”时触发扣减 +- 同一条课程学习记录只扣减一次 +- 已扣减过的记录,通过 `isUseHourPackage` 标识防止重复扣减 +- 扣减数量按课程配置的学习时长 `studyDuration` 处理 +- 学时套餐从 `hot_hour_purchase_detail` 中按 `id` 升序依次扣减 +- 扣减成功后,同时更新: + - 学时套餐购买明细的 `remainingHours` + - 学时套餐购买明细的 `usedHours` + - 学时套餐购买明细的 `lastUseTime` + - 学时套餐使用明细表 `hot_hour_usage_detail` + +## 4. 前端请求参数说明 + +### 4.1 学习记录保存接口无新增字段 + +前端调用以下接口时,无需新增任何参数: + +- `POST /securityManagement/trainingCourseRecord` +- `PUT /securityManagement/trainingCourseRecord` +- `POST /courseAnswer/trainingAnswerRecord/submit` + +特别说明: + +- 不需要前端传 `isUseHourPackage` +- 不需要前端传 `hourPackageUsedHours` +- 不需要前端传“套餐ID”“购买记录ID”“扣减顺序”等参数 + +这些字段不会在保存学习记录时自动扣减,由独立扣减接口处理。 + +### 4.2 扣减接口 + +- 地址:`POST /securityManagement/trainingCourseRecord/{id}/deductHourPackage` +- 说明:按学习记录ID显式扣减学时套餐 +- 路径参数: + - `id`:学习记录ID + +请求示例: + +```http +POST /securityManagement/trainingCourseRecord/9001/deductHourPackage +``` + +### 4.3 学习记录原有请求示例 + +```json +{ + "companyId": 1, + "trainingId": 1001, + "userId": "20001", + "userName": "张三", + "trainingCourseId": 3001, + "checkStatus": 1, + "progressRate": 100, + "learnDurationMin": 45, + "completeTime": "2026-05-12 10:30:00" +} +``` + +说明: + +- 仍按原有方式提交即可 +- 当 `checkStatus=1` 时,表示学习记录已完成;前端应在此后调用独立扣减接口 + +## 5. 新增返回字段 + +学习记录详情接口、学习记录列表接口会新增以下字段: + +| 字段名 | 类型 | 说明 | +|------------------------|--------|-------------------------| +| `isUseHourPackage` | `Long` | 是否已使用购买课时套餐:`0=否`,`1=是` | +| `hourPackageUsedHours` | `Long` | 本条学习记录已扣减课时数 | + +### 5.1 返回示例 + +```json +{ + "code": 200, + "data": { + "id": 9001, + "companyId": 1, + "trainingId": 1001, + "userId": "20001", + "userName": "张三", + "trainingCourseId": 3001, + "checkStatus": 1, + "progressRate": 100.00, + "learnDurationMin": 45, + "completeTime": "2026-05-12 10:30:00", + "signatureOssId": 8888, + "isUseHourPackage": 1, + "hourPackageUsedHours": 45 + }, + "msg": "操作成功" +} +``` + +## 6. 前端展示建议 + +### 6.1 学习记录页面 + +如果页面需要展示本次学习是否已使用套餐,可直接使用: + +- `isUseHourPackage=1` 显示“已使用套餐学时” +- `isUseHourPackage=0` 显示“未使用套餐学时” + +### 6.2 已扣减学时展示 + +- 当 `hourPackageUsedHours > 0` 时,可显示“本次已扣减 XX 课时” +- 当 `hourPackageUsedHours = 0` 或为空时,可显示 `-` + +### 6.3 前端是否需要防重 + +- 前端仍建议保留按钮防连点、重复提交控制 +- 但最终防重复扣减由后端 `isUseHourPackage` 字段保证 + +## 7. 失败提示 + +如果学习完成时需要扣减学时套餐,但当前没有可用套餐,接口可能返回以下错误: + +```json +{ + "code": 500, + "msg": "可用学时套餐不足,请先购买学时套餐", + "data": null +} +``` + +前端建议处理方式: + +- 直接 toast 提示后端返回的 `msg` +- 引导用户前往学时套餐购买页面 +- 不要在前端本地自行计算剩余可用课时 + +## 8. 联调要点 + +- 前端联调时重点确认课程完成后会调用独立扣减接口 +- 同一课程记录重复调用扣减接口时,不应重复扣减 +- 学习记录查询接口应能看到: + - `isUseHourPackage` + - `hourPackageUsedHours` +- 若学时套餐余额不足,应正确提示失败信息 + +## 9. 前端结论 + +- 前端请求参数:学习记录接口无新增;扣减接口新增 `id` 路径参数 +- 前端调用流程:学习完成后新增一次扣减接口调用 +- 前端返回处理:新增关注两个字段 + - `isUseHourPackage` + - `hourPackageUsedHours` +- 前端异常处理:直接展示后端返回的套餐不足提示 diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/controller/HotTrainingOutlineController.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/controller/HotTrainingOutlineController.java new file mode 100644 index 0000000..75629cc --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/controller/HotTrainingOutlineController.java @@ -0,0 +1,130 @@ +package com.hotwj.platform.securityManagement.trainingOutline.controller; + +import com.hotwj.platform.securityManagement.trainingOutline.domain.bo.HotTrainingOutlineBo; +import com.hotwj.platform.securityManagement.trainingOutline.domain.vo.HotTrainingOutlineVo; +import com.hotwj.platform.securityManagement.trainingOutline.service.IHotTrainingOutlinePrintService; +import com.hotwj.platform.securityManagement.trainingOutline.service.IHotTrainingOutlineService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 培训大纲 + * + * @author shihongwei + * @date 2026-01-14 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/trainingOutline") +@Tag(name = "培训大纲", description = "培训大纲管理") +public class HotTrainingOutlineController extends BaseController { + + private final IHotTrainingOutlineService hotTrainingOutlineService; + private final IHotTrainingOutlinePrintService printService; + + /** + * 查询培训大纲列表 + */ + //@SaCheckPermission("securityManagement:trainingOutline:list") + @GetMapping("/list") + @Operation(summary = "分页查询培训大纲列表") + public TableDataInfo list(HotTrainingOutlineBo bo, PageQuery pageQuery) { + return hotTrainingOutlineService.queryPageList(bo, pageQuery); + } + + /** + * 导出培训大纲列表 + */ + //@SaCheckPermission("securityManagement:trainingOutline:export") + @Log(title = "培训大纲", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出培训大纲列表") + public void export(HotTrainingOutlineBo bo, HttpServletResponse response) { + List list = hotTrainingOutlineService.queryList(bo); + ExcelUtil.exportExcel(list, "培训大纲", HotTrainingOutlineVo.class, response); + } + + /** + * 导出培训大纲PDF + */ + //@SaCheckPermission("securityManagement:trainingOutline:print") + @Log(title = "培训大纲打印", businessType = BusinessType.EXPORT) + @PostMapping("/exportPdf") + @Operation(summary = "导出培训大纲PDF") + public void exportPdf(HotTrainingOutlineBo bo, HttpServletResponse response) { + printService.exportPdf(bo, response); + } + + /** + * 获取培训大纲详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:trainingOutline:query") + @GetMapping("/{id}") + @Operation(summary = "获取培训大纲详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotTrainingOutlineService.queryById(id)); + } + + /** + * 新增培训大纲 + */ + //@SaCheckPermission("securityManagement:trainingOutline:add") + @Log(title = "培训大纲", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增培训大纲") + public R add(@Validated(AddGroup.class) @RequestBody HotTrainingOutlineBo bo) { + return toAjax(hotTrainingOutlineService.insertByBo(bo)); + } + + /** + * 修改培训大纲 + */ + //@SaCheckPermission("securityManagement:trainingOutline:edit") + @Log(title = "培训大纲", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改培训大纲") + public R edit(@Validated(EditGroup.class) @RequestBody HotTrainingOutlineBo bo) { + return toAjax(hotTrainingOutlineService.updateByBo(bo)); + } + + /** + * 删除培训大纲 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:trainingOutline:remove") + @Log(title = "培训大纲", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除培训大纲") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotTrainingOutlineService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/domain/HotTrainingOutline.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/domain/HotTrainingOutline.java new file mode 100644 index 0000000..0651f80 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/domain/HotTrainingOutline.java @@ -0,0 +1,64 @@ +package com.hotwj.platform.securityManagement.trainingOutline.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 培训大纲对象 hot_training_outline + * + * @author shihongwei + * @date 2026-01-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_training_outline") +public class HotTrainingOutline extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 年度 + */ + private Long outlineYear; + + /** + * 培训类型组合,逗号分隔存储 + */ + private String outlineTypes; + + /** + * 是否启用 0=否,1=是 + */ + private Long isEnabled; + + /** + * 月度培训计划ID组合,逗号分隔存储 + */ + private String monthPlanIds; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/domain/bo/HotTrainingOutlineBo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/domain/bo/HotTrainingOutlineBo.java new file mode 100644 index 0000000..572dde7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/domain/bo/HotTrainingOutlineBo.java @@ -0,0 +1,61 @@ +package com.hotwj.platform.securityManagement.trainingOutline.domain.bo; + +import com.hotwj.platform.securityManagement.trainingOutline.domain.HotTrainingOutline; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 培训大纲业务对象 hot_training_outline + * + * @author shihongwei + * @date 2026-01-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotTrainingOutline.class, reverseConvertGenerate = false) +public class HotTrainingOutlineBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 年度 + */ + @NotNull(message = "年度不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long outlineYear; + + /** + * 培训类型组合,逗号分隔存储 + */ + private String outlineTypes; + + /** + * 是否启用 0=否,1=是 + */ + private Long isEnabled; + + /** + * 月度培训计划ID组合,逗号分隔存储 + */ + private String monthPlanIds; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/domain/vo/HotTrainingOutlineVo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/domain/vo/HotTrainingOutlineVo.java new file mode 100644 index 0000000..554854f --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/domain/vo/HotTrainingOutlineVo.java @@ -0,0 +1,70 @@ +package com.hotwj.platform.securityManagement.trainingOutline.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.trainingOutline.domain.HotTrainingOutline; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 培训大纲视图对象 hot_training_outline + * + * @author shihongwei + * @date 2026-01-14 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotTrainingOutline.class) +public class HotTrainingOutlineVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 年度 + */ + @ExcelProperty(value = "年度") + private Long outlineYear; + + /** + * 培训类型组合,逗号分隔存储 + */ + @ExcelProperty(value = "培训类型组合,逗号分隔存储") + private String outlineTypes; + + /** + * 是否启用 0=否,1=是 + */ + @ExcelProperty(value = "是否启用 0=否,1=是") + private Long isEnabled; + + /** + * 月度培训计划ID组合,逗号分隔存储 + */ + @ExcelProperty(value = "月度培训计划ID组合,逗号分隔存储") + private String monthPlanIds; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/mapper/HotTrainingOutlineMapper.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/mapper/HotTrainingOutlineMapper.java new file mode 100644 index 0000000..75d3b15 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/mapper/HotTrainingOutlineMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.trainingOutline.mapper; + +import com.hotwj.platform.securityManagement.trainingOutline.domain.HotTrainingOutline; +import com.hotwj.platform.securityManagement.trainingOutline.domain.vo.HotTrainingOutlineVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 培训大纲Mapper接口 + * + * @author shihongwei + * @date 2026-01-14 + */ +@Mapper +public interface HotTrainingOutlineMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/service/IHotTrainingOutlinePrintService.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/service/IHotTrainingOutlinePrintService.java new file mode 100644 index 0000000..f476b2a --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/service/IHotTrainingOutlinePrintService.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.trainingOutline.service; + +import com.hotwj.platform.securityManagement.trainingOutline.domain.bo.HotTrainingOutlineBo; +import jakarta.servlet.http.HttpServletResponse; + +/** + * 培训大纲打印服务接口 + */ +public interface IHotTrainingOutlinePrintService { + /** + * 导出PDF + * + * @param bo 查询参数 + * @param response 响应对象 + */ + void exportPdf(HotTrainingOutlineBo bo, HttpServletResponse response); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/service/IHotTrainingOutlineService.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/service/IHotTrainingOutlineService.java new file mode 100644 index 0000000..c9a8b3c --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/service/IHotTrainingOutlineService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.securityManagement.trainingOutline.service; + +import com.hotwj.platform.securityManagement.trainingOutline.domain.bo.HotTrainingOutlineBo; +import com.hotwj.platform.securityManagement.trainingOutline.domain.vo.HotTrainingOutlineVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 培训大纲Service接口 + * + * @author shihongwei + * @date 2026-01-14 + */ +public interface IHotTrainingOutlineService { + + /** + * 查询培训大纲 + * + * @param id 主键 + * @return 培训大纲 + */ + HotTrainingOutlineVo queryById(Long id); + + /** + * 分页查询培训大纲列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训大纲分页列表 + */ + TableDataInfo queryPageList(HotTrainingOutlineBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的培训大纲列表 + * + * @param bo 查询条件 + * @return 培训大纲列表 + */ + List queryList(HotTrainingOutlineBo bo); + + /** + * 新增培训大纲 + * + * @param bo 培训大纲 + * @return 是否新增成功 + */ + Boolean insertByBo(HotTrainingOutlineBo bo); + + /** + * 修改培训大纲 + * + * @param bo 培训大纲 + * @return 是否修改成功 + */ + Boolean updateByBo(HotTrainingOutlineBo bo); + + /** + * 校验并批量删除培训大纲信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/service/impl/HotTrainingOutlinePrintServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/service/impl/HotTrainingOutlinePrintServiceImpl.java new file mode 100644 index 0000000..69c0c7b --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/service/impl/HotTrainingOutlinePrintServiceImpl.java @@ -0,0 +1,192 @@ +package com.hotwj.platform.securityManagement.trainingOutline.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.hotwj.platform.common.utils.PdfWatermarkUtil; +import com.hotwj.platform.reportStatistics.integration.GotenbergClient; +import com.hotwj.platform.reportStatistics.template.VelocityTemplateRenderer; +import com.hotwj.platform.securityManagement.trainingOutline.domain.HotTrainingOutline; +import com.hotwj.platform.securityManagement.trainingOutline.domain.bo.HotTrainingOutlineBo; +import com.hotwj.platform.securityManagement.trainingOutline.domain.vo.HotTrainingOutlineVo; +import com.hotwj.platform.securityManagement.trainingOutline.mapper.HotTrainingOutlineMapper; +import com.hotwj.platform.securityManagement.trainingOutline.service.IHotTrainingOutlinePrintService; +import com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.HotTrainingOutlineCourse; +import com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.vo.HotTrainingOutlineCourseVo; +import com.hotwj.platform.securityManagement.trainingOutlineCourse.mapper.HotTrainingOutlineCourseMapper; +import com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.HotTrainingOutlineMonth; +import com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.vo.HotTrainingOutlineMonthVo; +import com.hotwj.platform.securityManagement.trainingOutlineMonth.mapper.HotTrainingOutlineMonthMapper; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.velocity.VelocityContext; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.file.FileUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.FileWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Slf4j +@Service +@RequiredArgsConstructor +public class HotTrainingOutlinePrintServiceImpl implements IHotTrainingOutlinePrintService { + + private final HotTrainingOutlineMapper outlineMapper; + private final HotTrainingOutlineMonthMapper outlineMonthMapper; + private final HotTrainingOutlineCourseMapper outlineCourseMapper; + private final VelocityTemplateRenderer velocityTemplateRenderer; + private final GotenbergClient gotenbergClient; + + @Value("${dromara.app.name:HOT交通安全管理清单平台}") + private String appName; + + @Override + public void exportPdf(HotTrainingOutlineBo bo, HttpServletResponse response) { + // 1. 查询大纲列表 + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotTrainingOutline::getId); + lqw.eq(bo.getOutlineYear() != null, HotTrainingOutline::getOutlineYear, bo.getOutlineYear()); + lqw.eq(StringUtils.isNotBlank(bo.getOutlineTypes()), HotTrainingOutline::getOutlineTypes, bo.getOutlineTypes()); + lqw.eq(bo.getIsEnabled() != null, HotTrainingOutline::getIsEnabled, bo.getIsEnabled()); + lqw.eq(StringUtils.isNotBlank(bo.getMonthPlanIds()), HotTrainingOutline::getMonthPlanIds, bo.getMonthPlanIds()); + lqw.eq(bo.getIsDeleted() != null, HotTrainingOutline::getIsDeleted, bo.getIsDeleted()); + + List outlineList = outlineMapper.selectVoList(lqw); + if (CollUtil.isEmpty(outlineList)) { + throw new ServiceException("未查询到培训大纲数据"); + } + + // 假设只打印第一个匹配的大纲,或者需要循环打印。通常"导出PDF"如果是列表查询,可能导出列表。 + // 但根据前端逻辑,它似乎是在查看详情时打印,或者是针对特定年份的打印。 + // 如果是列表导出,我们可能需要一个包含多个大纲的 PDF。 + // 这里假设按照 vehicleThreeInspect 的逻辑,它导出的是一个列表。 + + // 但前端传参是 queryParams,可能包含 id。 + // 如果 bo.getId() 不为空,则只导出一个。 + + List> printDataList = new ArrayList<>(); + + for (HotTrainingOutlineVo outline : outlineList) { + Map outlineData = new HashMap<>(); + outlineData.put("outlineYear", outline.getOutlineYear()); + + // 查询月度计划 + List months = outlineMonthMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotTrainingOutlineMonth::getOutlineId, outline.getId()) + .eq(HotTrainingOutlineMonth::getIsDeleted, 0) + .orderByAsc(HotTrainingOutlineMonth::getSortNo) // 假设有排序 + ); + List monthVos = MapstructUtils.convert(months, HotTrainingOutlineMonthVo.class); + + List> flatRows = new ArrayList<>(); + + if (CollUtil.isNotEmpty(monthVos)) { + for (HotTrainingOutlineMonthVo month : monthVos) { + // 查询课程 + List courses = outlineCourseMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotTrainingOutlineCourse::getMonthOutlineId, month.getId()) + .eq(HotTrainingOutlineCourse::getIsDeleted, 0) + .orderByAsc(HotTrainingOutlineCourse::getSortNo) + ); + List courseVos = MapstructUtils.convert(courses, HotTrainingOutlineCourseVo.class); + + if (CollUtil.isNotEmpty(courseVos)) { + for (int i = 0; i < courseVos.size(); i++) { + HotTrainingOutlineCourseVo course = courseVos.get(i); + Map row = new HashMap<>(); + row.put("planMonth", month.getPlanMonth()); + row.put("topic", month.getTopic()); + row.put("courseName", course.getCourseName()); + row.put("durationMinute", course.getDurationMinute()); + row.put("courseDesc", course.getCourseDesc()); + + // 设置 rowSpan + if (i == 0) { + row.put("monthRowSpan", courseVos.size()); + } else { + row.put("monthRowSpan", 0); + } + flatRows.add(row); + } + } else { + // 如果没有课程,也显示月度信息 + Map row = new HashMap<>(); + row.put("planMonth", month.getPlanMonth()); + row.put("topic", month.getTopic()); + row.put("courseName", ""); + row.put("durationMinute", ""); + row.put("courseDesc", ""); + row.put("monthRowSpan", 1); + flatRows.add(row); + } + } + } + outlineData.put("rows", flatRows); + printDataList.add(outlineData); + } + + // 渲染 HTML + VelocityContext context = new VelocityContext(); + context.put("printTime", DateUtil.now()); + context.put("list", printDataList); + context.put("appName", appName); + + String bodyHtml = velocityTemplateRenderer.render("templates/securityManage/trainingOutline.html.vm", context); + + VelocityContext layoutContext = new VelocityContext(); + layoutContext.put("title", "年度培训计划大纲"); + List bodies = new ArrayList<>(); + bodies.add(bodyHtml); + layoutContext.put("bodies", bodies); + + String finalHtml = velocityTemplateRenderer.render("templates/layout/printLayout.html.vm", layoutContext); + + try { + byte[] pdfBytes = convertAndWatermark(finalHtml); + response.setContentType("application/pdf"); + String fileName = "年度培训计划大纲.pdf"; + FileUtils.setAttachmentResponseHeader(response, fileName); + response.getOutputStream().write(pdfBytes); + } catch (Exception e) { + log.error("Export PDF failed", e); + throw new ServiceException("导出PDF失败"); + } + } + + private byte[] convertAndWatermark(String htmlContent) throws Exception { + File temp = Files.createTempFile("training-outline-print-", ".html").toFile(); + try { + try (FileWriter fw = new FileWriter(temp, StandardCharsets.UTF_8)) { + fw.write(htmlContent); + } + String footer = """ +
+ 平台信息依实为准,信息使用自判 + / +
+ """; + byte[] pdf = gotenbergClient.convertHtmlToPdf(temp, footer); + return PdfWatermarkUtil.addWatermark(pdf, appName); + } finally { + if (temp.exists()) { + if (!temp.delete()) { + log.warn("Failed to delete temp file: {}", temp.getAbsolutePath()); + } + } + } + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/service/impl/HotTrainingOutlineServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/service/impl/HotTrainingOutlineServiceImpl.java new file mode 100644 index 0000000..2a0832b --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutline/service/impl/HotTrainingOutlineServiceImpl.java @@ -0,0 +1,136 @@ +package com.hotwj.platform.securityManagement.trainingOutline.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.securityManagement.trainingOutline.domain.HotTrainingOutline; +import com.hotwj.platform.securityManagement.trainingOutline.domain.bo.HotTrainingOutlineBo; +import com.hotwj.platform.securityManagement.trainingOutline.domain.vo.HotTrainingOutlineVo; +import com.hotwj.platform.securityManagement.trainingOutline.mapper.HotTrainingOutlineMapper; +import com.hotwj.platform.securityManagement.trainingOutline.service.IHotTrainingOutlineService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 培训大纲Service业务层处理 + * + * @author shihongwei + * @date 2026-01-14 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotTrainingOutlineServiceImpl implements IHotTrainingOutlineService { + + private final HotTrainingOutlineMapper baseMapper; + + /** + * 查询培训大纲 + * + * @param id 主键 + * @return 培训大纲 + */ + @Override + public HotTrainingOutlineVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询培训大纲列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训大纲分页列表 + */ + @Override + public TableDataInfo queryPageList(HotTrainingOutlineBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的培训大纲列表 + * + * @param bo 查询条件 + * @return 培训大纲列表 + */ + @Override + public List queryList(HotTrainingOutlineBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotTrainingOutlineBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotTrainingOutline::getId); + lqw.eq(bo.getOutlineYear() != null, HotTrainingOutline::getOutlineYear, bo.getOutlineYear()); + lqw.eq(StringUtils.isNotBlank(bo.getOutlineTypes()), HotTrainingOutline::getOutlineTypes, bo.getOutlineTypes()); + lqw.eq(bo.getIsEnabled() != null, HotTrainingOutline::getIsEnabled, bo.getIsEnabled()); + lqw.eq(StringUtils.isNotBlank(bo.getMonthPlanIds()), HotTrainingOutline::getMonthPlanIds, bo.getMonthPlanIds()); + lqw.eq(bo.getIsDeleted() != null, HotTrainingOutline::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增培训大纲 + * + * @param bo 培训大纲 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotTrainingOutlineBo bo) { + HotTrainingOutline add = MapstructUtils.convert(bo, HotTrainingOutline.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改培训大纲 + * + * @param bo 培训大纲 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotTrainingOutlineBo bo) { + HotTrainingOutline update = MapstructUtils.convert(bo, HotTrainingOutline.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotTrainingOutline entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除培训大纲信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/controller/HotTrainingOutlineCourseController.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/controller/HotTrainingOutlineCourseController.java new file mode 100644 index 0000000..0b3ca59 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/controller/HotTrainingOutlineCourseController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.securityManagement.trainingOutlineCourse.controller; + +import com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.bo.HotTrainingOutlineCourseBo; +import com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.vo.HotTrainingOutlineCourseVo; +import com.hotwj.platform.securityManagement.trainingOutlineCourse.service.IHotTrainingOutlineCourseService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 培训大纲-课程大纲 + * + * @author shihongwei + * @date 2026-01-14 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/trainingOutlineCourse") +@Tag(name = "培训大纲-课程大纲", description = "培训大纲-课程大纲管理") +public class HotTrainingOutlineCourseController extends BaseController { + + private final IHotTrainingOutlineCourseService hotTrainingOutlineCourseService; + + /** + * 查询培训大纲-课程大纲列表 + */ + //@SaCheckPermission("securityManagement:trainingOutlineCourse:list") + @GetMapping("/list") + @Operation(summary = "分页查询培训大纲-课程大纲列表") + public TableDataInfo list(HotTrainingOutlineCourseBo bo, PageQuery pageQuery) { + return hotTrainingOutlineCourseService.queryPageList(bo, pageQuery); + } + + /** + * 导出培训大纲-课程大纲列表 + */ + //@SaCheckPermission("securityManagement:trainingOutlineCourse:export") + @Log(title = "培训大纲-课程大纲", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出培训大纲-课程大纲列表") + public void export(HotTrainingOutlineCourseBo bo, HttpServletResponse response) { + List list = hotTrainingOutlineCourseService.queryList(bo); + ExcelUtil.exportExcel(list, "培训大纲-课程大纲", HotTrainingOutlineCourseVo.class, response); + } + + /** + * 获取培训大纲-课程大纲详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:trainingOutlineCourse:query") + @GetMapping("/{id}") + @Operation(summary = "获取培训大纲-课程大纲详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotTrainingOutlineCourseService.queryById(id)); + } + + /** + * 新增培训大纲-课程大纲 + */ + //@SaCheckPermission("securityManagement:trainingOutlineCourse:add") + @Log(title = "培训大纲-课程大纲", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增培训大纲-课程大纲") + public R add(@Validated(AddGroup.class) @RequestBody HotTrainingOutlineCourseBo bo) { + return toAjax(hotTrainingOutlineCourseService.insertByBo(bo)); + } + + /** + * 修改培训大纲-课程大纲 + */ + //@SaCheckPermission("securityManagement:trainingOutlineCourse:edit") + @Log(title = "培训大纲-课程大纲", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改培训大纲-课程大纲") + public R edit(@Validated(EditGroup.class) @RequestBody HotTrainingOutlineCourseBo bo) { + return toAjax(hotTrainingOutlineCourseService.updateByBo(bo)); + } + + /** + * 删除培训大纲-课程大纲 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:trainingOutlineCourse:remove") + @Log(title = "培训大纲-课程大纲", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除培训大纲-课程大纲") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotTrainingOutlineCourseService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/domain/HotTrainingOutlineCourse.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/domain/HotTrainingOutlineCourse.java new file mode 100644 index 0000000..e8c7226 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/domain/HotTrainingOutlineCourse.java @@ -0,0 +1,74 @@ +package com.hotwj.platform.securityManagement.trainingOutlineCourse.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 培训大纲-课程大纲对象 hot_training_outline_course + * + * @author shihongwei + * @date 2026-01-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_training_outline_course") +public class HotTrainingOutlineCourse extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 月度计划ID + */ + private Long monthOutlineId; + + /** + * 排序 + */ + private Long sortNo; + + /** + * 课程名称 + */ + private String courseName; + + /** + * 所属课程id + */ + private String belongCourseId; + + /** + * 课程描述 + */ + private String courseDesc; + + /** + * 时长(分钟) + */ + private Long durationMinute; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/domain/bo/HotTrainingOutlineCourseBo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/domain/bo/HotTrainingOutlineCourseBo.java new file mode 100644 index 0000000..c07016a --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/domain/bo/HotTrainingOutlineCourseBo.java @@ -0,0 +1,74 @@ +package com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.bo; + +import com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.HotTrainingOutlineCourse; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 培训大纲-课程大纲业务对象 hot_training_outline_course + * + * @author shihongwei + * @date 2026-01-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotTrainingOutlineCourse.class, reverseConvertGenerate = false) +public class HotTrainingOutlineCourseBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 月度计划ID + */ + @NotNull(message = "月度计划ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long monthOutlineId; + + /** + * 排序 + */ + private Long sortNo; + + /** + * 课程名称 + */ + @NotBlank(message = "课程名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String courseName; + + /** + * 所属课程id + */ + @NotBlank(message = "所属课程id不能为空", groups = {AddGroup.class, EditGroup.class}) + private String belongCourseId; + + /** + * 课程描述 + */ + private String courseDesc; + + /** + * 时长(分钟) + */ + private Long durationMinute; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/domain/vo/HotTrainingOutlineCourseVo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/domain/vo/HotTrainingOutlineCourseVo.java new file mode 100644 index 0000000..c9ae078 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/domain/vo/HotTrainingOutlineCourseVo.java @@ -0,0 +1,82 @@ +package com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.HotTrainingOutlineCourse; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 培训大纲-课程大纲视图对象 hot_training_outline_course + * + * @author shihongwei + * @date 2026-01-14 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotTrainingOutlineCourse.class) +public class HotTrainingOutlineCourseVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 月度计划ID + */ + @ExcelProperty(value = "月度计划ID") + private Long monthOutlineId; + + /** + * 排序 + */ + @ExcelProperty(value = "排序") + private Long sortNo; + + /** + * 课程名称 + */ + @ExcelProperty(value = "课程名称") + private String courseName; + + /** + * 所属课程id + */ + @ExcelProperty(value = "所属课程id") + private String belongCourseId; + + /** + * 课程描述 + */ + @ExcelProperty(value = "课程描述") + private String courseDesc; + + /** + * 时长(分钟) + */ + @ExcelProperty(value = "时长(分钟)") + private Long durationMinute; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/mapper/HotTrainingOutlineCourseMapper.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/mapper/HotTrainingOutlineCourseMapper.java new file mode 100644 index 0000000..792c00c --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/mapper/HotTrainingOutlineCourseMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.trainingOutlineCourse.mapper; + +import com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.HotTrainingOutlineCourse; +import com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.vo.HotTrainingOutlineCourseVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 培训大纲-课程大纲Mapper接口 + * + * @author shihongwei + * @date 2026-01-14 + */ +@Mapper +public interface HotTrainingOutlineCourseMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/service/IHotTrainingOutlineCourseService.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/service/IHotTrainingOutlineCourseService.java new file mode 100644 index 0000000..62944e4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/service/IHotTrainingOutlineCourseService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.securityManagement.trainingOutlineCourse.service; + +import com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.bo.HotTrainingOutlineCourseBo; +import com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.vo.HotTrainingOutlineCourseVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 培训大纲-课程大纲Service接口 + * + * @author shihongwei + * @date 2026-01-14 + */ +public interface IHotTrainingOutlineCourseService { + + /** + * 查询培训大纲-课程大纲 + * + * @param id 主键 + * @return 培训大纲-课程大纲 + */ + HotTrainingOutlineCourseVo queryById(Long id); + + /** + * 分页查询培训大纲-课程大纲列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训大纲-课程大纲分页列表 + */ + TableDataInfo queryPageList(HotTrainingOutlineCourseBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的培训大纲-课程大纲列表 + * + * @param bo 查询条件 + * @return 培训大纲-课程大纲列表 + */ + List queryList(HotTrainingOutlineCourseBo bo); + + /** + * 新增培训大纲-课程大纲 + * + * @param bo 培训大纲-课程大纲 + * @return 是否新增成功 + */ + Boolean insertByBo(HotTrainingOutlineCourseBo bo); + + /** + * 修改培训大纲-课程大纲 + * + * @param bo 培训大纲-课程大纲 + * @return 是否修改成功 + */ + Boolean updateByBo(HotTrainingOutlineCourseBo bo); + + /** + * 校验并批量删除培训大纲-课程大纲信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/service/impl/HotTrainingOutlineCourseServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/service/impl/HotTrainingOutlineCourseServiceImpl.java new file mode 100644 index 0000000..dcf9915 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineCourse/service/impl/HotTrainingOutlineCourseServiceImpl.java @@ -0,0 +1,138 @@ +package com.hotwj.platform.securityManagement.trainingOutlineCourse.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.HotTrainingOutlineCourse; +import com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.bo.HotTrainingOutlineCourseBo; +import com.hotwj.platform.securityManagement.trainingOutlineCourse.domain.vo.HotTrainingOutlineCourseVo; +import com.hotwj.platform.securityManagement.trainingOutlineCourse.mapper.HotTrainingOutlineCourseMapper; +import com.hotwj.platform.securityManagement.trainingOutlineCourse.service.IHotTrainingOutlineCourseService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 培训大纲-课程大纲Service业务层处理 + * + * @author shihongwei + * @date 2026-01-14 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotTrainingOutlineCourseServiceImpl implements IHotTrainingOutlineCourseService { + + private final HotTrainingOutlineCourseMapper baseMapper; + + /** + * 查询培训大纲-课程大纲 + * + * @param id 主键 + * @return 培训大纲-课程大纲 + */ + @Override + public HotTrainingOutlineCourseVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询培训大纲-课程大纲列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训大纲-课程大纲分页列表 + */ + @Override + public TableDataInfo queryPageList(HotTrainingOutlineCourseBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的培训大纲-课程大纲列表 + * + * @param bo 查询条件 + * @return 培训大纲-课程大纲列表 + */ + @Override + public List queryList(HotTrainingOutlineCourseBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotTrainingOutlineCourseBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotTrainingOutlineCourse::getId); + lqw.eq(bo.getMonthOutlineId() != null, HotTrainingOutlineCourse::getMonthOutlineId, bo.getMonthOutlineId()); + lqw.eq(bo.getSortNo() != null, HotTrainingOutlineCourse::getSortNo, bo.getSortNo()); + lqw.like(StringUtils.isNotBlank(bo.getCourseName()), HotTrainingOutlineCourse::getCourseName, bo.getCourseName()); + lqw.eq(StringUtils.isNotBlank(bo.getBelongCourseId()), HotTrainingOutlineCourse::getBelongCourseId, bo.getBelongCourseId()); + lqw.eq(StringUtils.isNotBlank(bo.getCourseDesc()), HotTrainingOutlineCourse::getCourseDesc, bo.getCourseDesc()); + lqw.eq(bo.getDurationMinute() != null, HotTrainingOutlineCourse::getDurationMinute, bo.getDurationMinute()); + lqw.eq(bo.getIsDeleted() != null, HotTrainingOutlineCourse::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增培训大纲-课程大纲 + * + * @param bo 培训大纲-课程大纲 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotTrainingOutlineCourseBo bo) { + HotTrainingOutlineCourse add = MapstructUtils.convert(bo, HotTrainingOutlineCourse.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改培训大纲-课程大纲 + * + * @param bo 培训大纲-课程大纲 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotTrainingOutlineCourseBo bo) { + HotTrainingOutlineCourse update = MapstructUtils.convert(bo, HotTrainingOutlineCourse.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotTrainingOutlineCourse entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除培训大纲-课程大纲信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/controller/HotTrainingOutlineMonthController.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/controller/HotTrainingOutlineMonthController.java new file mode 100644 index 0000000..215355d --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/controller/HotTrainingOutlineMonthController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.securityManagement.trainingOutlineMonth.controller; + +import com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.bo.HotTrainingOutlineMonthBo; +import com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.vo.HotTrainingOutlineMonthVo; +import com.hotwj.platform.securityManagement.trainingOutlineMonth.service.IHotTrainingOutlineMonthService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 培训大纲-月度计划 + * + * @author shihongwei + * @date 2026-01-14 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/trainingOutlineMonth") +@Tag(name = "培训大纲-月度计划", description = "培训大纲-月度计划管理") +public class HotTrainingOutlineMonthController extends BaseController { + + private final IHotTrainingOutlineMonthService hotTrainingOutlineMonthService; + + /** + * 查询培训大纲-月度计划列表 + */ + //@SaCheckPermission("securityManagement:trainingOutlineMonth:list") + @GetMapping("/list") + @Operation(summary = "分页查询培训大纲-月度计划列表") + public TableDataInfo list(HotTrainingOutlineMonthBo bo, PageQuery pageQuery) { + return hotTrainingOutlineMonthService.queryPageList(bo, pageQuery); + } + + /** + * 导出培训大纲-月度计划列表 + */ + //@SaCheckPermission("securityManagement:trainingOutlineMonth:export") + @Log(title = "培训大纲-月度计划", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出培训大纲-月度计划列表") + public void export(HotTrainingOutlineMonthBo bo, HttpServletResponse response) { + List list = hotTrainingOutlineMonthService.queryList(bo); + ExcelUtil.exportExcel(list, "培训大纲-月度计划", HotTrainingOutlineMonthVo.class, response); + } + + /** + * 获取培训大纲-月度计划详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:trainingOutlineMonth:query") + @GetMapping("/{id}") + @Operation(summary = "获取培训大纲-月度计划详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotTrainingOutlineMonthService.queryById(id)); + } + + /** + * 新增培训大纲-月度计划 + */ + //@SaCheckPermission("securityManagement:trainingOutlineMonth:add") + @Log(title = "培训大纲-月度计划", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增培训大纲-月度计划") + public R add(@Validated(AddGroup.class) @RequestBody HotTrainingOutlineMonthBo bo) { + return toAjax(hotTrainingOutlineMonthService.insertByBo(bo)); + } + + /** + * 修改培训大纲-月度计划 + */ + //@SaCheckPermission("securityManagement:trainingOutlineMonth:edit") + @Log(title = "培训大纲-月度计划", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改培训大纲-月度计划") + public R edit(@Validated(EditGroup.class) @RequestBody HotTrainingOutlineMonthBo bo) { + return toAjax(hotTrainingOutlineMonthService.updateByBo(bo)); + } + + /** + * 删除培训大纲-月度计划 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:trainingOutlineMonth:remove") + @Log(title = "培训大纲-月度计划", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除培训大纲-月度计划") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotTrainingOutlineMonthService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/domain/HotTrainingOutlineMonth.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/domain/HotTrainingOutlineMonth.java new file mode 100644 index 0000000..1421439 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/domain/HotTrainingOutlineMonth.java @@ -0,0 +1,69 @@ +package com.hotwj.platform.securityManagement.trainingOutlineMonth.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; + +/** + * 培训大纲-月度计划对象 hot_training_outline_month + * + * @author shihongwei + * @date 2026-01-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_training_outline_month") +public class HotTrainingOutlineMonth extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 培训大纲ID + */ + private Long outlineId; + + /** + * 排序 + */ + private Long sortNo; + + /** + * 月份,格式YYYY-MM + */ + private String planMonth; + + /** + * 主题 + */ + private String topic; + + /** + * 课程大纲id集合,逗号分隔存储 + */ + private String outlineIds; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/domain/bo/HotTrainingOutlineMonthBo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/domain/bo/HotTrainingOutlineMonthBo.java new file mode 100644 index 0000000..b2aa8ab --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/domain/bo/HotTrainingOutlineMonthBo.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.bo; + +import com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.HotTrainingOutlineMonth; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 培训大纲-月度计划业务对象 hot_training_outline_month + * + * @author shihongwei + * @date 2026-01-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotTrainingOutlineMonth.class, reverseConvertGenerate = false) +public class HotTrainingOutlineMonthBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 培训大纲ID + */ + @NotNull(message = "培训大纲ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long outlineId; + + /** + * 排序 + */ + private Long sortNo; + + /** + * 月份,格式YYYY-MM + */ + @NotBlank(message = "月份,格式YYYY-MM不能为空", groups = {AddGroup.class, EditGroup.class}) + private String planMonth; + + /** + * 主题 + */ + private String topic; + + /** + * 课程大纲id集合,逗号分隔存储 + */ + private String outlineIds; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/domain/vo/HotTrainingOutlineMonthVo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/domain/vo/HotTrainingOutlineMonthVo.java new file mode 100644 index 0000000..b934574 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/domain/vo/HotTrainingOutlineMonthVo.java @@ -0,0 +1,76 @@ +package com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.HotTrainingOutlineMonth; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 培训大纲-月度计划视图对象 hot_training_outline_month + * + * @author shihongwei + * @date 2026-01-14 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotTrainingOutlineMonth.class) +public class HotTrainingOutlineMonthVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 培训大纲ID + */ + @ExcelProperty(value = "培训大纲ID") + private Long outlineId; + + /** + * 排序 + */ + @ExcelProperty(value = "排序") + private Long sortNo; + + /** + * 月份,格式YYYY-MM + */ + @ExcelProperty(value = "月份,格式YYYY-MM") + private String planMonth; + + /** + * 主题 + */ + @ExcelProperty(value = "主题") + private String topic; + + /** + * 课程大纲id集合,逗号分隔存储 + */ + @ExcelProperty(value = "课程大纲id集合,逗号分隔存储") + private String outlineIds; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/mapper/HotTrainingOutlineMonthMapper.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/mapper/HotTrainingOutlineMonthMapper.java new file mode 100644 index 0000000..23a608d --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/mapper/HotTrainingOutlineMonthMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.trainingOutlineMonth.mapper; + +import com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.HotTrainingOutlineMonth; +import com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.vo.HotTrainingOutlineMonthVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 培训大纲-月度计划Mapper接口 + * + * @author shihongwei + * @date 2026-01-14 + */ +@Mapper +public interface HotTrainingOutlineMonthMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/service/IHotTrainingOutlineMonthService.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/service/IHotTrainingOutlineMonthService.java new file mode 100644 index 0000000..b938cbf --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/service/IHotTrainingOutlineMonthService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.securityManagement.trainingOutlineMonth.service; + +import com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.bo.HotTrainingOutlineMonthBo; +import com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.vo.HotTrainingOutlineMonthVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 培训大纲-月度计划Service接口 + * + * @author shihongwei + * @date 2026-01-14 + */ +public interface IHotTrainingOutlineMonthService { + + /** + * 查询培训大纲-月度计划 + * + * @param id 主键 + * @return 培训大纲-月度计划 + */ + HotTrainingOutlineMonthVo queryById(Long id); + + /** + * 分页查询培训大纲-月度计划列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训大纲-月度计划分页列表 + */ + TableDataInfo queryPageList(HotTrainingOutlineMonthBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的培训大纲-月度计划列表 + * + * @param bo 查询条件 + * @return 培训大纲-月度计划列表 + */ + List queryList(HotTrainingOutlineMonthBo bo); + + /** + * 新增培训大纲-月度计划 + * + * @param bo 培训大纲-月度计划 + * @return 是否新增成功 + */ + Boolean insertByBo(HotTrainingOutlineMonthBo bo); + + /** + * 修改培训大纲-月度计划 + * + * @param bo 培训大纲-月度计划 + * @return 是否修改成功 + */ + Boolean updateByBo(HotTrainingOutlineMonthBo bo); + + /** + * 校验并批量删除培训大纲-月度计划信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/service/impl/HotTrainingOutlineMonthServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/service/impl/HotTrainingOutlineMonthServiceImpl.java new file mode 100644 index 0000000..8230111 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingOutlineMonth/service/impl/HotTrainingOutlineMonthServiceImpl.java @@ -0,0 +1,137 @@ +package com.hotwj.platform.securityManagement.trainingOutlineMonth.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.HotTrainingOutlineMonth; +import com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.bo.HotTrainingOutlineMonthBo; +import com.hotwj.platform.securityManagement.trainingOutlineMonth.domain.vo.HotTrainingOutlineMonthVo; +import com.hotwj.platform.securityManagement.trainingOutlineMonth.mapper.HotTrainingOutlineMonthMapper; +import com.hotwj.platform.securityManagement.trainingOutlineMonth.service.IHotTrainingOutlineMonthService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 培训大纲-月度计划Service业务层处理 + * + * @author shihongwei + * @date 2026-01-14 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotTrainingOutlineMonthServiceImpl implements IHotTrainingOutlineMonthService { + + private final HotTrainingOutlineMonthMapper baseMapper; + + /** + * 查询培训大纲-月度计划 + * + * @param id 主键 + * @return 培训大纲-月度计划 + */ + @Override + public HotTrainingOutlineMonthVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询培训大纲-月度计划列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训大纲-月度计划分页列表 + */ + @Override + public TableDataInfo queryPageList(HotTrainingOutlineMonthBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的培训大纲-月度计划列表 + * + * @param bo 查询条件 + * @return 培训大纲-月度计划列表 + */ + @Override + public List queryList(HotTrainingOutlineMonthBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotTrainingOutlineMonthBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotTrainingOutlineMonth::getId); + lqw.eq(bo.getOutlineId() != null, HotTrainingOutlineMonth::getOutlineId, bo.getOutlineId()); + lqw.eq(bo.getSortNo() != null, HotTrainingOutlineMonth::getSortNo, bo.getSortNo()); + lqw.eq(StringUtils.isNotBlank(bo.getPlanMonth()), HotTrainingOutlineMonth::getPlanMonth, bo.getPlanMonth()); + lqw.eq(StringUtils.isNotBlank(bo.getTopic()), HotTrainingOutlineMonth::getTopic, bo.getTopic()); + lqw.eq(StringUtils.isNotBlank(bo.getOutlineIds()), HotTrainingOutlineMonth::getOutlineIds, bo.getOutlineIds()); + lqw.eq(bo.getIsDeleted() != null, HotTrainingOutlineMonth::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增培训大纲-月度计划 + * + * @param bo 培训大纲-月度计划 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotTrainingOutlineMonthBo bo) { + HotTrainingOutlineMonth add = MapstructUtils.convert(bo, HotTrainingOutlineMonth.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改培训大纲-月度计划 + * + * @param bo 培训大纲-月度计划 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotTrainingOutlineMonthBo bo) { + HotTrainingOutlineMonth update = MapstructUtils.convert(bo, HotTrainingOutlineMonth.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotTrainingOutlineMonth entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除培训大纲-月度计划信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/controller/HotTrainingParticipantController.java b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/controller/HotTrainingParticipantController.java new file mode 100644 index 0000000..1d958ec --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/controller/HotTrainingParticipantController.java @@ -0,0 +1,142 @@ +package com.hotwj.platform.securityManagement.trainingParticipant.controller; + +import com.hotwj.platform.securityManagement.trainingParticipant.domain.bo.HotTrainingParticipantBo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.DriverLearningRecordVo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.HotTrainingParticipantVo; +import com.hotwj.platform.securityManagement.trainingParticipant.service.IHotTrainingParticipantPrintService; +import com.hotwj.platform.securityManagement.trainingParticipant.service.IHotTrainingParticipantService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 培训参与人员 + * + * @author shihongwei + * @date 2025-12-26 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/trainingParticipant") +@Tag(name = "培训参与人员", description = "培训参与人员管理") +public class HotTrainingParticipantController extends BaseController { + + private final IHotTrainingParticipantService hotTrainingParticipantService; + private final IHotTrainingParticipantPrintService printService; + + /** + * 查询培训参与人员列表 + */ + //@SaCheckPermission("securityManagement:trainingParticipant:list") + @GetMapping("/list") + @Operation(summary = "分页查询培训参与人员列表") + public TableDataInfo list(HotTrainingParticipantBo bo, PageQuery pageQuery) { + return hotTrainingParticipantService.queryPageList(bo, pageQuery); + } + + @GetMapping("/listWithPlan") + //@SaCheckPermission("securityManagement:trainingParticipant:list") + @Operation(summary = "驾驶员学习记录列表(参与培训计划+计划信息)") + public TableDataInfo listWithPlan( + @RequestParam Long companyId, + @RequestParam String userId, + PageQuery pageQuery + ) { + return hotTrainingParticipantService.queryDriverLearningRecordPage(companyId, userId, pageQuery); + } + + /** + * 导出培训参与人员列表 + */ + //@SaCheckPermission("securityManagement:trainingParticipant:export") + @Log(title = "培训参与人员", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出培训参与人员列表") + public void export(HotTrainingParticipantBo bo, HttpServletResponse response) { + List list = hotTrainingParticipantService.queryList(bo); + ExcelUtil.exportExcel(list, "培训参与人员", HotTrainingParticipantVo.class, response); + } + + /** + * 导出培训参与人员列表PDF + */ + //@SaCheckPermission("securityManagement:trainingParticipant:query") + @Log(title = "培训参与人员打印", businessType = BusinessType.EXPORT) + @PostMapping("/exportPdf") + @Operation(summary = "导出培训参与人员列表PDF") + public void exportPdf(HotTrainingParticipantBo bo, HttpServletResponse response) { + printService.exportPdf(bo, response); + } + + /** + * 获取培训参与人员详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:trainingParticipant:query") + @GetMapping("/{id}") + @Operation(summary = "获取培训参与人员详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotTrainingParticipantService.queryById(id)); + } + + /** + * 新增培训参与人员 + */ + //@SaCheckPermission("securityManagement:trainingParticipant:add") + @Log(title = "培训参与人员", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增培训参与人员") + public R add(@Validated(AddGroup.class) @RequestBody HotTrainingParticipantBo bo) { + return toAjax(hotTrainingParticipantService.insertByBo(bo)); + } + + /** + * 修改培训参与人员 + */ + //@SaCheckPermission("securityManagement:trainingParticipant:edit") + @Log(title = "培训参与人员", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改培训参与人员") + public R edit(@Validated(EditGroup.class) @RequestBody HotTrainingParticipantBo bo) { + return toAjax(hotTrainingParticipantService.updateByBo(bo)); + } + + /** + * 删除培训参与人员 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:trainingParticipant:remove") + @Log(title = "培训参与人员", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除培训参与人员") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotTrainingParticipantService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/HotTrainingParticipant.java b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/HotTrainingParticipant.java new file mode 100644 index 0000000..f9d61d1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/HotTrainingParticipant.java @@ -0,0 +1,121 @@ +package com.hotwj.platform.securityManagement.trainingParticipant.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 培训参与人员对象 hot_training_participant + * + * @author shihongwei + * @date 2025-12-26 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_training_participant") +public class HotTrainingParticipant extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 培训ID + */ + private Long trainingId; + + /** + * 用户ID + */ + private String userId; + + /** + * 姓名 + */ + private String userName; + + /** + * 性别,男/女 + */ + private String gender; + + /** + * 身份证号 + */ + private String idCardNo; + + /** + * 学习进度,0-100,单位% + */ + private BigDecimal learnProgress; + + /** + * 是否完成学习 0=否 1=是 + */ + private Long isCompleted; + + /** + * 完成时间 + */ + private Date completeTime; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 人员类型 + */ + private Long personType; + + /** + * 手机号 + */ + private String phone; + + /** + * 是否通过 1=通过 0=未通过 + */ + private Long isPass; + + /** + * 学员签名OSS文件ID + */ + private Long signatureOssId; + + /** + * 学习照片OSS文件id(json) + */ + private String studyPhotoOssId; + + /** + * 创建人显示名称 + */ + private String createByName; + + /** + * 最后修改人显示名称 + */ + private String updateByName; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/bo/HotTrainingParticipantAddBo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/bo/HotTrainingParticipantAddBo.java new file mode 100644 index 0000000..961f13a --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/bo/HotTrainingParticipantAddBo.java @@ -0,0 +1,40 @@ +package com.hotwj.platform.securityManagement.trainingParticipant.domain.bo; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +/** + * 培训参与人员添加BO + * + * @author shihongwei + * @date 2026-03-10 + */ +@Data +public class HotTrainingParticipantAddBo { + + /** + * 培训计划ID + */ + @NotNull(message = "培训计划ID不能为空") + private Long trainingId; + + /** + * 人员列表 + */ + private List personList; + + @Data + public static class ParticipantItem { + /** + * 人员类型: driver=驾驶员, manager=管理员 + */ + private String type; + + /** + * 人员ID列表,逗号分隔 + */ + private String ids; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/bo/HotTrainingParticipantBo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/bo/HotTrainingParticipantBo.java new file mode 100644 index 0000000..9913ede --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/bo/HotTrainingParticipantBo.java @@ -0,0 +1,127 @@ +package com.hotwj.platform.securityManagement.trainingParticipant.domain.bo; + +import com.hotwj.platform.securityManagement.trainingParticipant.domain.HotTrainingParticipant; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 培训参与人员业务对象 hot_training_participant + * + * @author shihongwei + * @date 2025-12-26 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotTrainingParticipant.class, reverseConvertGenerate = false) +public class HotTrainingParticipantBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 培训ID + */ + @NotNull(message = "培训ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long trainingId; + + /** + * 用户ID + */ + @NotNull(message = "用户ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private String userId; + + /** + * 姓名 + */ + private String userName; + + /** + * 性别,男/女 + */ + private String gender; + + /** + * 身份证号 + */ + private String idCardNo; + + /** + * 学习进度,0-100,单位% + */ + private BigDecimal learnProgress; + + /** + * 是否完成学习 0=否 1=是 + */ + private Long isCompleted; + + /** + * 完成时间 + */ + private Date completeTime; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long isDeleted; + + /** + * 人员类型 + */ + private Long personType; + + /** + * 手机号 + */ + private String phone; + + /** + * 是否通过 1=通过 0=未通过 + */ + private Long isPass; + + /** + * 学员签名OSS文件ID + */ + private Long signatureOssId; + + /** + * 学习照片OSS文件id(json) + */ + private String studyPhotoOssId; + + /** + * 创建人显示名称 + */ + private String createByName; + + /** + * 最后修改人显示名称 + */ + private String updateByName; + + /** + * 是否可审核 1=是 0=否 + */ + private Long canAudit; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/vo/DriverLearningRecordVo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/vo/DriverLearningRecordVo.java new file mode 100644 index 0000000..8dd2e22 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/vo/DriverLearningRecordVo.java @@ -0,0 +1,35 @@ +package com.hotwj.platform.securityManagement.trainingParticipant.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +@Data +public class DriverLearningRecordVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + + private Long trainingId; + + private String planName; + + private Date startTime; + + private Date endTime; + + private Long isEnabled; + + private Long isMakeUp; + + private Integer learnProgress; + + private Long isPassed; + + private Long isCompleted; +} + diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/vo/HotTrainingParticipantVo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/vo/HotTrainingParticipantVo.java new file mode 100644 index 0000000..e2625a6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/vo/HotTrainingParticipantVo.java @@ -0,0 +1,149 @@ +package com.hotwj.platform.securityManagement.trainingParticipant.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.HotTrainingParticipant; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.json.handler.BigDecimalTwoDecimalSerializer; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 培训参与人员视图对象 hot_training_participant + * + * @author shihongwei + * @date 2025-12-26 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotTrainingParticipant.class) +public class HotTrainingParticipantVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 培训ID + */ + @ExcelProperty(value = "培训ID") + private Long trainingId; + + /** + * 用户ID + */ + @ExcelProperty(value = "用户ID") + private String userId; + + /** + * 姓名 + */ + @ExcelProperty(value = "姓名") + private String userName; + + /** + * 性别,男/女 + */ + @ExcelProperty(value = "性别,男/女") + private String gender; + + /** + * 身份证号 + */ + @ExcelProperty(value = "身份证号") + private String idCardNo; + + /** + * 学习进度,0-100,单位% + */ + @ExcelProperty(value = "学习进度,0-100,单位%") + @JsonSerialize(using = BigDecimalTwoDecimalSerializer.class) + private BigDecimal learnProgress; + + /** + * 是否完成学习 0=否 1=是 + */ + @ExcelProperty(value = "是否完成学习 0=否 1=是") + private Long isCompleted; + + /** + * 完成时间 + */ + @ExcelProperty(value = "完成时间") + private Date completeTime; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 人员类型 + */ + @ExcelProperty(value = "人员类型") + private Long personType; + + /** + * 手机号 + */ + @ExcelProperty(value = "手机号") + private String phone; + + /** + * 是否通过 1=通过 0=未通过 + */ + @ExcelProperty(value = "是否通过 1=通过 0=未通过") + private Long isPass; + + /** + * 创建人显示名称 + */ + @ExcelProperty(value = "创建人显示名称") + private String createByName; + + /** + * 最后修改人显示名称 + */ + @ExcelProperty(value = "最后修改人显示名称") + private String updateByName; + + + /** + * 学员签名OSS文件ID + */ + private Long signatureOssId; + + /** + * 学习照片OSS文件id(json) + */ + private String studyPhotoOssId; + + /** + * 是否合格 1=是 0=否 + */ + private Long isQualified; + + /** + * 是否可审核 1=是 0=否 + */ + private Long canAudit; + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/vo/TrainingParticipantStatVo.java b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/vo/TrainingParticipantStatVo.java new file mode 100644 index 0000000..def2c38 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/domain/vo/TrainingParticipantStatVo.java @@ -0,0 +1,38 @@ +package com.hotwj.platform.securityManagement.trainingParticipant.domain.vo; + +import lombok.Data; + +@Data +public class TrainingParticipantStatVo { + + /** + * 培训ID + */ + private Long trainingId; + + /** + * 总人数 + */ + private Long totalCount; + + /** + * 已通过人数 + */ + private Long passedCount; + + /** + * 已完成人数 + */ + private Long completeCount; + + /** + * 未完成人数 + */ + private Long incompleteCount; + + /** + * 待审核人数 + */ + private Long pendingReviewCount; +} + diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/mapper/HotTrainingParticipantMapper.java b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/mapper/HotTrainingParticipantMapper.java new file mode 100644 index 0000000..e889475 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/mapper/HotTrainingParticipantMapper.java @@ -0,0 +1,24 @@ +package com.hotwj.platform.securityManagement.trainingParticipant.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.HotTrainingParticipant; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.DriverLearningRecordVo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.HotTrainingParticipantVo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.TrainingParticipantStatVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +import java.util.Date; +import java.util.List; + +@Mapper +public interface HotTrainingParticipantMapper extends BaseMapperPlus { + + List selectDriverLearningRecordPage(Page page, + @Param("companyId") Long companyId, + @Param("userId") String userId, + @Param("now") Date now); + + List selectTrainingParticipantStats(@Param("trainingIds") List trainingIds); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/service/IHotTrainingParticipantPrintService.java b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/service/IHotTrainingParticipantPrintService.java new file mode 100644 index 0000000..a7b9b96 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/service/IHotTrainingParticipantPrintService.java @@ -0,0 +1,21 @@ +package com.hotwj.platform.securityManagement.trainingParticipant.service; + +import com.hotwj.platform.securityManagement.trainingParticipant.domain.bo.HotTrainingParticipantBo; +import jakarta.servlet.http.HttpServletResponse; + +/** + * 培训参与人员打印服务接口 + * + * @author shihongwei + * @date 2026-03-10 + */ +public interface IHotTrainingParticipantPrintService { + + /** + * 导出PDF + * + * @param bo 查询参数 + * @param response 响应对象 + */ + void exportPdf(HotTrainingParticipantBo bo, HttpServletResponse response); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/service/IHotTrainingParticipantService.java b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/service/IHotTrainingParticipantService.java new file mode 100644 index 0000000..5f66ef7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/service/IHotTrainingParticipantService.java @@ -0,0 +1,110 @@ +package com.hotwj.platform.securityManagement.trainingParticipant.service; + +import com.hotwj.platform.securityManagement.trainingParticipant.domain.bo.HotTrainingParticipantAddBo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.bo.HotTrainingParticipantBo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.DriverLearningRecordVo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.HotTrainingParticipantVo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.TrainingParticipantStatVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 培训参与人员Service接口 + * + * @author shihongwei + * @date 2025-12-26 + */ +public interface IHotTrainingParticipantService { + + /** + * 查询培训参与人员 + * + * @param id 主键 + * @return 培训参与人员 + */ + HotTrainingParticipantVo queryById(Long id); + + /** + * 分页查询培训参与人员列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训参与人员分页列表 + */ + TableDataInfo queryPageList(HotTrainingParticipantBo bo, PageQuery pageQuery); + + /** + * 驾驶员学习记录列表(参与培训计划+计划信息) + */ + TableDataInfo queryDriverLearningRecordPage(Long companyId, String userId, PageQuery pageQuery); + + /** + * 按培训ID聚合统计参与人数及完成情况 + */ + List queryTrainingParticipantStats(List trainingIds); + + /** + * 查询符合条件的培训参与人员列表 + * + * @param bo 查询条件 + * @return 培训参与人员列表 + */ + List queryList(HotTrainingParticipantBo bo); + + /** + * 新增培训参与人员 + * + * @param bo 培训参与人员 + * @return 是否新增成功 + */ + Boolean insertByBo(HotTrainingParticipantBo bo); + + /** + * 批量添加培训参与人员 + * + * @param bo 添加参数 + * @return 是否添加成功 + */ + Boolean addParticipants(HotTrainingParticipantAddBo bo); + + /** + * 修改培训参与人员 + * + * @param bo 培训参与人员 + * @return 是否修改成功 + */ + Boolean updateByBo(HotTrainingParticipantBo bo); + + /** + * 校验并批量删除培训参与人员信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 更新学员某个课程的人脸识别照片 + * + * @param participantId 培训参与人员ID + * @param trainingCourseId 企业课程配置ID + * @param studyPhotoOssId 学习照片OSS文件ID + * @return 是否更新成功 + */ + boolean updateStudyPhotoOssId(Long participantId, Long trainingCourseId, Long studyPhotoOssId); + + /** + * 批量计算培训计划学员进度(返回百分比字符串,如 "85%") + * + * @param trainingId 培训ID + * @param companyId 企业ID + * @param userIds 学员ID列表(如果为空则不限制) + * @return Map + */ + Map calculateProgressBatch(Long trainingId, Long companyId, List userIds); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/service/impl/HotTrainingParticipantPrintServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/service/impl/HotTrainingParticipantPrintServiceImpl.java new file mode 100644 index 0000000..609bca6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/service/impl/HotTrainingParticipantPrintServiceImpl.java @@ -0,0 +1,149 @@ +package com.hotwj.platform.securityManagement.trainingParticipant.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import com.hotwj.platform.reportStatistics.integration.GotenbergClient; +import com.hotwj.platform.reportStatistics.template.VelocityTemplateRenderer; +import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo; +import com.hotwj.platform.securityManagement.training.service.IHotTrainingService; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.bo.HotTrainingParticipantBo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.HotTrainingParticipantVo; +import com.hotwj.platform.securityManagement.trainingParticipant.service.IHotTrainingParticipantPrintService; +import com.hotwj.platform.securityManagement.trainingParticipant.service.IHotTrainingParticipantService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.velocity.VelocityContext; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.service.OssService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 培训参与人员打印服务实现 + * + * @author shihongwei + * @date 2026-03-10 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class HotTrainingParticipantPrintServiceImpl implements IHotTrainingParticipantPrintService { + + private final IHotTrainingParticipantService trainingParticipantService; + private final IHotTrainingService trainingService; + private final OssService ossService; + private final GotenbergClient gotenbergClient; + private final VelocityTemplateRenderer velocityTemplateRenderer; + + @Value("${dromara.app.name:HOT交通安全管理清单平台}") + private String appName; + + @Override + public void exportPdf(HotTrainingParticipantBo bo, HttpServletResponse response) { + // 1. Query Data + List list = trainingParticipantService.queryList(bo); + if (CollUtil.isEmpty(list)) { + throw new ServiceException("数据不存在"); + } + + // 2. Query Training Info for isMakeUp + Long isMakeUp = 0L; + if (bo.getTrainingId() != null) { + HotTrainingVo training = trainingService.queryById(bo.getTrainingId()); + if (training != null) { + isMakeUp = training.getIsMakeUp(); + } + } + + // 3. Process Data for Template + List> printList = new ArrayList<>(); + for (HotTrainingParticipantVo vo : list) { + Map map = new HashMap<>(); + map.put("userName", vo.getUserName()); + map.put("phone", vo.getPhone()); + + String personTypeStr = "未知"; + if (vo.getPersonType() != null) { + if (vo.getPersonType() == 1L) personTypeStr = "驾驶员"; + else if (vo.getPersonType() == 2L) personTypeStr = "管理员"; + } + map.put("personTypeStr", personTypeStr); + + map.put("completeTime", vo.getCompleteTime() != null ? DateUtil.formatDateTime(vo.getCompleteTime()) : ""); + + // Signature Image + String signatureUrl = ""; + if (vo.getSignatureOssId() != null) { + signatureUrl = resolveMaybeOssUrl(String.valueOf(vo.getSignatureOssId())); + } + map.put("signatureUrl", signatureUrl); + + map.put("isMakeUpStr", isMakeUp != null && isMakeUp == 1L ? "是" : "否"); + map.put("isCompletedStr", (vo.getIsCompleted() != null && vo.getIsCompleted() == 1L) ? "已完成" : "未完成"); + + printList.add(map); + } + + // 4. Generate PDF + VelocityContext context = new VelocityContext(); + context.put("list", printList); + context.put("appName", appName); + context.put("printTime", DateUtil.now()); + + String bodyHtml = velocityTemplateRenderer.render("templates/securityManage/trainingAssessmentDetail.html.vm", context); + + // Merge into layout (Portrait A4) - Assuming printLayout.html.vm is for portrait + // If table is wide, use printLayoutLandscape.html.vm + // The columns are: Name, Phone, Type, Time, Sign, MakeUp, Completed. 7 columns. Portrait might be tight but ok. + // Let's use Landscape to be safe if signatures are wide. + // Actually, let's use Portrait first, similar to frontend manual implementation. + // Wait, manual implementation used A4. + + VelocityContext layoutContext = new VelocityContext(); + layoutContext.put("title", "考核明细表"); + List bodies = new ArrayList<>(); + bodies.add(bodyHtml); + layoutContext.put("bodies", bodies); + + String finalHtml = velocityTemplateRenderer.render("templates/layout/printLayout.html.vm", layoutContext); + + try { + byte[] pdfBytes = gotenbergClient.convertHtmlToPdf(finalHtml); + + response.setContentType("application/pdf"); + response.setHeader("Content-Disposition", "attachment; filename=\"training-assessment-detail.pdf\""); + response.getOutputStream().write(pdfBytes); + } catch (Exception e) { + log.error("Export PDF failed", e); + throw new ServiceException("导出PDF失败"); + } + } + + private String resolveMaybeOssUrl(String value) { + if (StringUtils.isBlank(value)) { + return null; + } + String first = value.split(",")[0].trim(); + if (StringUtils.isBlank(first)) { + return null; + } + if (StringUtils.ishttp(first)) { + return first; + } + if (first.matches("^\\d+$")) { + String urls = ossService.selectUrlByIds(first); + if (StringUtils.isBlank(urls)) { + return null; + } + return urls.split(",")[0].trim(); + } + return first; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/service/impl/HotTrainingParticipantServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/service/impl/HotTrainingParticipantServiceImpl.java new file mode 100644 index 0000000..4d817d3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingParticipant/service/impl/HotTrainingParticipantServiceImpl.java @@ -0,0 +1,631 @@ +package com.hotwj.platform.securityManagement.trainingParticipant.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fasterxml.jackson.core.type.TypeReference; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.flow.service.ISysFlowService; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.vo.HotCompanySafetyManagerVo; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo; +import com.hotwj.platform.securityManagement.training.mapper.HotTrainingMapper; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.HotTrainingParticipant; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.bo.HotTrainingParticipantAddBo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.bo.HotTrainingParticipantBo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.DriverLearningRecordVo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.HotTrainingParticipantVo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.TrainingParticipantStatVo; +import com.hotwj.platform.securityManagement.trainingParticipant.mapper.HotTrainingParticipantMapper; +import com.hotwj.platform.securityManagement.trainingParticipant.service.IHotTrainingParticipantService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.*; + +/** + * 培训参与人员Service业务层处理 + * + * @author shihongwei + * @date 2025-12-26 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotTrainingParticipantServiceImpl implements IHotTrainingParticipantService { + + private static final String FLOW_CODE_TRAINING_STUDY = "TRAINING_STUDY"; + private final HotTrainingParticipantMapper baseMapper; + private final IHotSystemNotificationService notificationService; + private final ISysFlowService flowService; + private final HotTrainingMapper trainingMapper; + private final HotCompanySafetyManagerMapper managerMapper; + private final HotDriverMapper driverMapper; + + /** + * 查询培训参与人员 + * + * @param id 主键 + * @return 培训参与人员 + */ + @Override + public HotTrainingParticipantVo queryById(Long id) { + HotTrainingParticipantVo vo = baseMapper.selectVoById(id); + fillCanAuditStatus(vo); + return vo; + } + + /** + * 分页查询培训参与人员列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 培训参与人员分页列表 + */ + @Override + public TableDataInfo queryPageList(HotTrainingParticipantBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + if (result.getRecords() != null) { + result.getRecords().forEach(this::fillCanAuditStatus); + } + return TableDataInfo.build(result); + } + + @Override + public TableDataInfo queryDriverLearningRecordPage(Long companyId, String userId, PageQuery pageQuery) { + Page page = pageQuery.build(); + List rows = baseMapper.selectDriverLearningRecordPage(page, companyId, userId, new Date()); + page.setRecords(rows); + return TableDataInfo.build(page); + } + + @Override + public List queryTrainingParticipantStats(List trainingIds) { + if (trainingIds == null || trainingIds.isEmpty()) { + return List.of(); + } + return baseMapper.selectTrainingParticipantStats(trainingIds); + } + + /** + * 查询符合条件的培训参与人员列表 + * + * @param bo 查询条件 + * @return 培训参与人员列表 + */ + @Override + public List queryList(HotTrainingParticipantBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + List list = baseMapper.selectVoList(lqw); + if (list != null) { + list.forEach(this::fillCanAuditStatus); + } + return list; + } + + private void fillCanAuditStatus(HotTrainingParticipantVo vo) { + if (vo == null) { + return; + } + boolean canAudit = Long.valueOf(1L).equals(vo.getIsCompleted()) && vo.getIsPass() == null; + vo.setCanAudit(canAudit ? 1L : 0L); + } + + private LambdaQueryWrapper buildQueryWrapper(HotTrainingParticipantBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotTrainingParticipant::getId); + lqw.eq(bo.getCompanyId() != null, HotTrainingParticipant::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getTrainingId() != null, HotTrainingParticipant::getTrainingId, bo.getTrainingId()); + lqw.eq(StringUtils.isNotBlank(bo.getUserId()), HotTrainingParticipant::getUserId, bo.getUserId()); + lqw.like(StringUtils.isNotBlank(bo.getUserName()), HotTrainingParticipant::getUserName, bo.getUserName()); + lqw.eq(StringUtils.isNotBlank(bo.getGender()), HotTrainingParticipant::getGender, bo.getGender()); + lqw.eq(StringUtils.isNotBlank(bo.getIdCardNo()), HotTrainingParticipant::getIdCardNo, bo.getIdCardNo()); + lqw.eq(bo.getLearnProgress() != null, HotTrainingParticipant::getLearnProgress, bo.getLearnProgress()); + lqw.eq(bo.getIsCompleted() != null, HotTrainingParticipant::getIsCompleted, bo.getIsCompleted()); + lqw.eq(bo.getCompleteTime() != null, HotTrainingParticipant::getCompleteTime, bo.getCompleteTime()); + lqw.eq(bo.getIsDeleted() != null, HotTrainingParticipant::getIsDeleted, bo.getIsDeleted()); + lqw.eq(bo.getPersonType() != null, HotTrainingParticipant::getPersonType, bo.getPersonType()); + lqw.like(StringUtils.isNotBlank(bo.getPhone()), HotTrainingParticipant::getPhone, bo.getPhone()); + lqw.eq(bo.getIsPass() != null, HotTrainingParticipant::getIsPass, bo.getIsPass()); + if (bo.getCanAudit() != null) { + if (Long.valueOf(1L).equals(bo.getCanAudit())) { + lqw.eq(HotTrainingParticipant::getIsCompleted, 1L) + .isNull(HotTrainingParticipant::getIsPass); + } else if (Long.valueOf(0L).equals(bo.getCanAudit())) { + lqw.and(w -> w.ne(HotTrainingParticipant::getIsCompleted, 1L) + .or() + .isNotNull(HotTrainingParticipant::getIsPass) + .or() + .isNull(HotTrainingParticipant::getIsCompleted)); + } + } + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotTrainingParticipant::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotTrainingParticipant::getUpdateByName, bo.getUpdateByName()); + return lqw; + } + + /** + * 新增培训参与人员 + * + * @param bo 培训参与人员 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotTrainingParticipantBo bo) { + if (bo.getLearnProgress() != null) { + bo.setLearnProgress(limitLearnProgress(bo.getLearnProgress())); + } + HotTrainingParticipant add = MapstructUtils.convert(bo, HotTrainingParticipant.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + HotTrainingVo training = trainingMapper.selectVoById(add.getTrainingId()); + String planName = training != null ? training.getPlanName() : "培训"; + pushTrainingTodo(add.getTrainingId(), add.getCompanyId(), add.getUserId(), planName); + HotSystemNotificationGroupBo msg = new HotSystemNotificationGroupBo(); + msg.setLevel("普通"); + msg.setSourceType("安全教育培训"); + msg.setSenderType("SYSTEM"); + msg.setReceiverType("驾驶员"); + msg.setReceiverIds(java.util.List.of(add.getUserId())); + msg.setContent("您有一个" + planName + "培训需要进行学习"); + msg.setIsDeleted(0L); + try { + notificationService.insertByBo(msg); + } catch (Exception e) { + log.warn("分配培训计划后发送系统消息失败 trainingId={} userId={} err={}", add.getTrainingId(), add.getUserId(), e.getMessage()); + } + } + return flag; + } + + /** + * 修改培训参与人员 + * + * @param bo 培训参与人员 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotTrainingParticipantBo bo) { + if (bo.getLearnProgress() != null) { + bo.setLearnProgress(limitLearnProgress(bo.getLearnProgress())); + } + HotTrainingParticipant before = null; + if (bo.getId() != null) { + before = baseMapper.selectById(bo.getId()); + } + HotTrainingParticipant update = MapstructUtils.convert(bo, HotTrainingParticipant.class); + validEntityBeforeSave(update); + boolean ok = baseMapper.updateById(update) > 0; + if (ok) { + boolean notify = false; + if (before != null) { + boolean trainingChanged = update.getTrainingId() != null && !update.getTrainingId().equals(before.getTrainingId()); + boolean userChanged = update.getUserId() != null && !update.getUserId().equals(before.getUserId()); + boolean reopened = update.getIsCompleted() != null && update.getIsCompleted() == 0L && (before.getIsCompleted() == null || before.getIsCompleted() == 1L); + boolean progressReset = update.getLearnProgress() != null && update.getLearnProgress().compareTo(java.math.BigDecimal.valueOf(100)) < 0 && (before.getLearnProgress() == null || before.getLearnProgress().compareTo(java.math.BigDecimal.valueOf(100)) >= 0); + notify = trainingChanged || userChanged || reopened || progressReset; + } else { + notify = true; + } + if (notify) { + HotTrainingVo training = trainingMapper.selectVoById(update.getTrainingId()); + String planName = training != null ? training.getPlanName() : "培训"; + String receiver = update.getUserId(); + if (org.dromara.common.core.utils.StringUtils.isNotBlank(receiver)) { + Long participantId = update.getId(); + if (participantId == null && before != null) { + participantId = before.getId(); + } + Long todoTrainingId = update.getTrainingId(); + if (todoTrainingId == null && before != null) { + todoTrainingId = before.getTrainingId(); + } + pushTrainingTodo(todoTrainingId, update.getCompanyId(), receiver, planName); + HotSystemNotificationGroupBo msg = new HotSystemNotificationGroupBo(); + msg.setLevel("普通"); + msg.setSourceType("安全教育培训"); + msg.setSenderType("SYSTEM"); + msg.setReceiverType("驾驶员"); + msg.setReceiverIds(java.util.List.of(receiver)); + msg.setContent("您有一个" + planName + "培训需要进行学习"); + msg.setIsDeleted(0L); + try { + notificationService.insertByBo(msg); + } catch (Exception e) { + log.warn("更新参与人员后发送系统消息失败 trainingId={} userId={} err={}", update.getTrainingId(), receiver, e.getMessage()); + } + } + } + } + return ok; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotTrainingParticipant entity) { + //TODO 做一些数据校验,如唯一约束 + } + + private BigDecimal limitLearnProgress(BigDecimal value) { + if (value == null) { + return null; + } + if (value.compareTo(BigDecimal.ZERO) < 0) { + return BigDecimal.ZERO; + } + if (value.compareTo(BigDecimal.valueOf(100)) > 0) { + return BigDecimal.valueOf(100); + } + return value; + } + + /** + * 批量添加培训参与人员 + * + * @param bo 添加参数 + * @return 是否添加成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean addParticipants(HotTrainingParticipantAddBo bo) { + if (bo == null || bo.getTrainingId() == null || bo.getPersonList() == null || bo.getPersonList().isEmpty()) { + return false; + } + + HotTrainingVo training = trainingMapper.selectVoById(bo.getTrainingId()); + if (training == null) { + throw new RuntimeException("培训计划不存在"); + } + + // 解析前端传入的人员列表,拆解出三份数据结构: + // 1) userIdSet:去重后的所有用户ID(字符串形式,适用于驾驶员与管理员统一使用) + // 2) userTypeMap:用户ID -> 人员类型(driver/manager),用于分支逻辑 + // 3) userIdLongMap:仅管理员使用的ID映射(manager 的 Long 型数值ID),driver 不存入此表 + ParsedPersons parsed = parseParticipantItems(bo.getPersonList()); + Set userIdSet = parsed.userIdSet; + Map userTypeMap = parsed.userTypeMap; + Map userIdLongMap = parsed.managerIdMap; + + if (userIdSet.isEmpty()) { + return true; + } + + // 加载已存在的参与人员(含已删),构建 userId -> participant 的映射 + Map existingParticipantMap = loadExistingParticipants(bo.getTrainingId(), userIdSet); + + + // 计算课程总数(作为汇总宽表的初始 totalCourses) + + List addList = new ArrayList<>(); + List notifyDriverIds = new ArrayList<>(); + List notifyManagerIds = new ArrayList<>(); + + for (String userIdStr : userIdSet) { + String type = userTypeMap.get(userIdStr); + Long userIdLong = userIdLongMap.get(userIdStr); // 仅管理员使用 + + // 查询人员基础信息(姓名/证件号/电话、人员类型) + PersonInfo person = loadPersonInfo(type, userIdStr, userIdLong); + if (person == null) { + continue; + } + String userName = person.userName; + String idCardNo = person.idCardNo; + String phone = person.phone; + Long personType = person.personType; + + HotTrainingParticipant existing = existingParticipantMap.get(userIdStr); + boolean activated = false; + if (existing == null) { + // 新增参与人员(初始状态) + HotTrainingParticipant participant = new HotTrainingParticipant(); + participant.setCompanyId(training.getCompanyId()); + participant.setTrainingId(bo.getTrainingId()); + participant.setUserId(userIdStr); + participant.setUserName(userName); + participant.setIdCardNo(idCardNo); + participant.setPhone(phone); + participant.setPersonType(personType); + participant.setLearnProgress(BigDecimal.ZERO); + participant.setIsCompleted(0L); + participant.setIsDeleted(0L); + participant.setIsPass(null); + addList.add(participant); + activated = true; + } else if (existing.getIsDeleted() != null && existing.getIsDeleted() == 1L) { + int updated = baseMapper.update( + null, + Wrappers.lambdaUpdate() + .set(HotTrainingParticipant::getIsDeleted, 0L) + .set(StringUtils.isNotBlank(userName), HotTrainingParticipant::getUserName, userName) + .set(StringUtils.isNotBlank(idCardNo), HotTrainingParticipant::getIdCardNo, idCardNo) + .set(StringUtils.isNotBlank(phone), HotTrainingParticipant::getPhone, phone) + .set(HotTrainingParticipant::getPersonType, personType) + .eq(HotTrainingParticipant::getTrainingId, bo.getTrainingId()) + .set(HotTrainingParticipant::getLearnProgress, BigDecimal.ZERO) + .set(HotTrainingParticipant::getIsCompleted, 0L) + .set(HotTrainingParticipant::getCompleteTime, null) + .set(HotTrainingParticipant::getIsPass, null) + .set(HotTrainingParticipant::getSignatureOssId, null) + .set(HotTrainingParticipant::getStudyPhotoOssId, null) + .eq(HotTrainingParticipant::getUserId, userIdStr) + .eq(HotTrainingParticipant::getIsDeleted, 1L) + ); + activated = updated > 0; + } + + + if (activated) { + pushTrainingTodo(bo.getTrainingId(), training.getCompanyId(), userIdStr, training.getPlanName()); + if ("driver".equals(type)) { + notifyDriverIds.add(userIdStr); + } else if ("manager".equals(type)) { + notifyManagerIds.add(userIdStr); + } + } + } + + boolean success = true; + if (!addList.isEmpty()) { + success = baseMapper.insertBatch(addList); + } + + if (!notifyManagerIds.isEmpty()) { + HotSystemNotificationGroupBo msg = new HotSystemNotificationGroupBo(); + msg.setLevel("普通"); + msg.setSourceType("安全教育培训"); + msg.setSenderType("SYSTEM"); + msg.setReceiverType("管理员"); + msg.setReceiverIds(notifyManagerIds); + msg.setContent("您有一个" + training.getPlanName() + "培训需要进行学习"); + msg.setIsDeleted(0L); + try { + notificationService.insertByBo(msg); + } catch (Exception e) { + log.warn("分配培训计划后发送系统消息失败 trainingId={} type=manager count={} err={}", bo.getTrainingId(), notifyManagerIds.size(), e.getMessage()); + } + } + if (!notifyDriverIds.isEmpty()) { + HotSystemNotificationGroupBo msg = new HotSystemNotificationGroupBo(); + msg.setLevel("普通"); + msg.setSourceType("安全教育培训"); + msg.setSenderType("SYSTEM"); + msg.setReceiverType("驾驶员"); + msg.setReceiverIds(notifyDriverIds); + msg.setContent("您有一个" + training.getPlanName() + "培训需要进行学习"); + msg.setIsDeleted(0L); + try { + notificationService.insertByBo(msg); + } catch (Exception e) { + log.warn("分配培训计划后发送系统消息失败 trainingId={} type=driver count={} err={}", bo.getTrainingId(), notifyDriverIds.size(), e.getMessage()); + } + } + + return success; + } + + /** + * 解析前端传入的人员列表,构建用户ID集合/类型映射/管理员ID映射 + */ + private ParsedPersons parseParticipantItems(List items) { + ParsedPersons result = new ParsedPersons(); + if (items == null) { + return result; + } + for (HotTrainingParticipantAddBo.ParticipantItem item : items) { + if (item == null || StringUtils.isBlank(item.getType()) || StringUtils.isBlank(item.getIds())) { + continue; + } + String[] ids = item.getIds().split(","); + for (String raw : ids) { + if (StringUtils.isBlank(raw)) { + continue; + } + String uid = raw.trim(); + // 驾驶员ID为32位uuid字符串;管理员ID为Long数值 + if ("driver".equals(item.getType())) { + result.userIdSet.add(uid); + result.userTypeMap.put(uid, "driver"); + } else if ("manager".equals(item.getType())) { + try { + Long userIdLong = Long.valueOf(uid); + result.userIdSet.add(uid); + result.userTypeMap.put(uid, "manager"); + result.managerIdMap.put(uid, userIdLong); + } catch (Exception e) { + log.warn("添加培训参与人员时管理员ID格式错误: {}", uid); + } + } else { + log.warn("未知的人员类型: {}, id={}", item.getType(), uid); + } + } + } + return result; + } + + /** + * 查询人员基础信息(姓名/证件号/电话、人员类型) + */ + private PersonInfo loadPersonInfo(String type, String userIdStr, Long userIdLong) { + if ("driver".equals(type)) { + HotDriverVo driver = driverMapper.selectVoById(userIdStr); + if (driver == null) return null; + PersonInfo p = new PersonInfo(); + p.userName = driver.getName(); + p.idCardNo = driver.getIdCardNo(); + p.phone = driver.getPhone(); + p.personType = 1L; + return p; + } else if ("manager".equals(type)) { + if (userIdLong == null) return null; + HotCompanySafetyManagerVo manager = managerMapper.selectVoById(userIdLong); + if (manager == null) return null; + PersonInfo p = new PersonInfo(); + p.userName = manager.getName(); + p.idCardNo = manager.getIdCardNo(); + p.phone = manager.getPhone(); + p.personType = 2L; + return p; + } + return null; + } + + /** + * 加载已存在的参与人员(含已删除) + */ + private Map loadExistingParticipants(Long trainingId, Set userIdSet) { + Map map = new HashMap<>(); + List list = baseMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotTrainingParticipant::getTrainingId, trainingId) + .in(HotTrainingParticipant::getUserId, userIdSet) + .in(HotTrainingParticipant::getIsDeleted, 0L, 1L) + ); + if (list != null) { + for (HotTrainingParticipant p : list) { + if (p != null && StringUtils.isNotBlank(p.getUserId())) { + map.put(p.getUserId(), p); + } + } + } + return map; + } + + private void pushTrainingTodo(Long trainingId, Long companyId, String userId, String planName) { + if (trainingId == null || companyId == null || StringUtils.isBlank(userId)) { + return; + } + String businessId = String.valueOf(trainingId); + if (flowService.hasPendingTask(FLOW_CODE_TRAINING_STUDY, businessId, userId)) { + return; + } + String title = "安全教育培训:" + StringUtils.blankToDefault(planName, "培训"); + try { + flowService.pushDirectTodo(FLOW_CODE_TRAINING_STUDY, businessId, title, companyId, userId); + } catch (Exception e) { + log.warn("推送安全教育培训待办失败 trainingId={} userId={} err={}", trainingId, userId, e.getMessage()); + } + } + + /** + * 人员信息载体(用于封装姓名/证件/电话/人员类型) + */ + private static class PersonInfo { + String userName; + String idCardNo; + String phone; + Long personType; + } + + /** + * 解析人员ID结果载体 + */ + private static class ParsedPersons { + Set userIdSet = new HashSet<>(); + Map userTypeMap = new HashMap<>(); + Map managerIdMap = new HashMap<>(); + } + + /** + * 校验并批量删除培训参与人员信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + public boolean updateStudyPhotoOssId(Long participantId, Long trainingCourseId, Long studyPhotoOssId) { + if (participantId == null || trainingCourseId == null || studyPhotoOssId == null) { + return false; + } + HotTrainingParticipant participant = baseMapper.selectById(participantId); + if (participant == null) { + return false; + } + Map photoMap = null; + if (StringUtils.isNotBlank(participant.getStudyPhotoOssId())) { + try { + photoMap = JsonUtils.parseObject(participant.getStudyPhotoOssId(), new TypeReference>() { + }); + } catch (Exception e) { + photoMap = new HashMap<>(); + } + } + if (photoMap == null) { + photoMap = new HashMap<>(); + } + photoMap.put(trainingCourseId, studyPhotoOssId); + participant.setStudyPhotoOssId(JsonUtils.toJsonString(photoMap)); + return baseMapper.updateById(participant) > 0; + } + + @Override + public Map calculateProgressBatch(Long trainingId, Long companyId, List userIds) { + Map result = new HashMap<>(); + if (trainingId == null || companyId == null) { + return result; + } + + HotTrainingParticipantBo participantBo = new HotTrainingParticipantBo(); + participantBo.setTrainingId(trainingId); + participantBo.setCompanyId(companyId); + participantBo.setIsDeleted(0L); + List participants = queryList(participantBo); + if (participants == null || participants.isEmpty()) { + return result; + } + + Set userIdSet = null; + if (userIds != null && !userIds.isEmpty()) { + userIdSet = new HashSet<>(userIds.size()); + for (String uid : userIds) { + if (StringUtils.isNotBlank(uid)) { + userIdSet.add(uid); + } + } + } + + for (HotTrainingParticipantVo p : participants) { + if (p == null || StringUtils.isBlank(p.getUserId())) { + continue; + } + if (userIdSet != null && !userIdSet.contains(p.getUserId())) { + continue; + } + + BigDecimal progress = p.getLearnProgress() == null ? BigDecimal.ZERO : p.getLearnProgress(); + if (progress.compareTo(BigDecimal.ZERO) < 0) progress = BigDecimal.ZERO; + if (progress.compareTo(BigDecimal.valueOf(100)) > 0) progress = BigDecimal.valueOf(100); + + BigDecimal rounded = progress.setScale(0, RoundingMode.HALF_UP); + result.put(p.getUserId(), rounded.toPlainString() + "%"); + } + + return result; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/trainingProgress/service/TrainingProgressService.java b/src/main/java/com/hotwj/platform/securityManagement/trainingProgress/service/TrainingProgressService.java new file mode 100644 index 0000000..4550e0d --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/trainingProgress/service/TrainingProgressService.java @@ -0,0 +1,218 @@ +package com.hotwj.platform.securityManagement.trainingProgress.service; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.hotwj.platform.config.courseResource.domain.bo.HotCourseResourceBo; +import com.hotwj.platform.config.courseResource.domain.vo.HotCourseResourceVo; +import com.hotwj.platform.config.courseResource.service.IHotCourseResourceService; +import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo; +import com.hotwj.platform.securityManagement.training.mapper.HotTrainingMapper; +import com.hotwj.platform.securityManagement.trainingAnswer.mapper.HotTrainingAnswerRecordMapper; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.bo.HotTrainingCourseConfigBo; +import com.hotwj.platform.securityManagement.trainingCourseConfig.domain.vo.HotTrainingCourseConfigVo; +import com.hotwj.platform.securityManagement.trainingCourseConfig.service.IHotTrainingCourseConfigService; +import com.hotwj.platform.securityManagement.trainingCourseRecord.domain.HotTrainingCourseRecord; +import com.hotwj.platform.securityManagement.trainingCourseRecord.mapper.HotTrainingCourseRecordMapper; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.HotTrainingParticipant; +import com.hotwj.platform.securityManagement.trainingParticipant.mapper.HotTrainingParticipantMapper; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class TrainingProgressService { + + private final HotTrainingMapper trainingMapper; + private final IHotTrainingCourseConfigService trainingCourseConfigService; + private final IHotCourseResourceService courseResourceService; + private final HotTrainingCourseRecordMapper trainingCourseRecordMapper; + private final HotTrainingParticipantMapper trainingParticipantMapper; + private final HotTrainingAnswerRecordMapper trainingAnswerRecordMapper; + + /** + * 同步并计算培训进度 + *

+ * 算法说明: + * 1. 总体进度 = (Σ所有课程进度 + 最终考试进度) / 总指标数 + * 2. 单门课程进度计算: + * - 有随堂练习:视频观看比例 * 75% + 随堂练习完成状态(0或1) * 25% + * - 无随堂练习:视频观看比例 * 100% + * 3. 最终考试进度:完成考试计 100%,未完成计 0% (仅在培训配置了试卷时计入总指标) + *

+ * 示例: + * 假设培训包含 2 门课程和 1 份试卷(总指标数 = 3): + * - 课程1(有练习):视频看满且练习已做 -> 100% + * - 课程2(无练习):视频看了一半 -> 50% + * - 最终试卷:尚未参加 -> 0% + * - 最终同步进度 = (100 + 50 + 0) / 3 = 50% + * + * @param companyId 公司ID + * @param trainingId 培训ID + * @param userId 用户ID + */ + public void syncProgress(Long companyId, Long trainingId, String userId) { + if (companyId == null || trainingId == null || StringUtils.isBlank(userId)) { + return; + } + + HotTrainingParticipant participant = trainingParticipantMapper.selectOne( + Wrappers.lambdaQuery() + .eq(HotTrainingParticipant::getCompanyId, companyId) + .eq(HotTrainingParticipant::getTrainingId, trainingId) + .eq(HotTrainingParticipant::getUserId, userId) + .eq(HotTrainingParticipant::getIsDeleted, 0L) + ); + if (participant == null) { + return; + } + + HotTrainingVo training = trainingMapper.selectVoById(trainingId); + boolean hasPaper = training != null && StringUtils.isNotBlank(training.getPaperIds()); + + HotTrainingCourseConfigBo courseConfigBo = new HotTrainingCourseConfigBo(); + courseConfigBo.setTrainingId(trainingId); + courseConfigBo.setCompanyId(companyId); + courseConfigBo.setIsDeleted(0L); + List courseConfigs = trainingCourseConfigService.queryList(courseConfigBo); + if (courseConfigs == null || courseConfigs.isEmpty()) { + updateParticipantLearnProgress(companyId, trainingId, userId, BigDecimal.ZERO); + return; + } + + Set trainingCourseIds = courseConfigs.stream() + .map(HotTrainingCourseConfigVo::getId) + .filter(v -> v != null) + .collect(Collectors.toSet()); + + Map latestRecordMap = loadLatestCourseRecords(companyId, trainingId, userId, trainingCourseIds); + + // 加载课程是否有随堂练习 + Map courseHasQuizMap = loadCourseHasQuizMap(courseConfigs); + + int courseMetricCount = 0; + BigDecimal totalProgressSum = BigDecimal.ZERO; + for (HotTrainingCourseConfigVo cfg : courseConfigs) { + if (cfg == null || cfg.getId() == null) { + continue; + } + + HotTrainingCourseRecord record = latestRecordMap.get(cfg.getId()); + double videoRatio = 0.0; + if (record != null && record.getProgressRate() != null) { + videoRatio = record.getProgressRate().doubleValue() / 100.0; + } + if (videoRatio < 0.0) videoRatio = 0.0; + if (videoRatio > 1.0) videoRatio = 1.0; + + boolean hasQuiz = Boolean.TRUE.equals(courseHasQuizMap.get(cfg.getId())); + boolean quizDone = hasQuiz && record != null && record.getCheckStatus() != null && record.getCheckStatus() == 1L; + + BigDecimal courseProgress; + if (hasQuiz) { + courseProgress = BigDecimal.valueOf(videoRatio) + .multiply(BigDecimal.valueOf(75.0)) + .add(BigDecimal.valueOf(quizDone ? 25.0 : 0.0)); + } else { + courseProgress = BigDecimal.valueOf(videoRatio).multiply(BigDecimal.valueOf(100.0)); + } + totalProgressSum = totalProgressSum.add(courseProgress); + courseMetricCount++; + } + + boolean paperDone = hasPaper && participant.getIsCompleted().equals(1L); + + int totalMetrics = courseMetricCount + (hasPaper ? 1 : 0); + if (totalMetrics <= 0) { + updateParticipantLearnProgress(companyId, trainingId, userId, BigDecimal.ZERO); + return; + } + + if (hasPaper && paperDone) { + totalProgressSum = totalProgressSum.add(BigDecimal.valueOf(100)); + } + + BigDecimal progress = totalProgressSum.divide(BigDecimal.valueOf(totalMetrics), 2, RoundingMode.HALF_UP); + if (progress.compareTo(BigDecimal.ZERO) < 0) progress = BigDecimal.ZERO; + if (progress.compareTo(BigDecimal.valueOf(100)) > 0) progress = BigDecimal.valueOf(100); + updateParticipantLearnProgress(companyId, trainingId, userId, progress); + } + + /** + * 加载学员最近的课程学习记录 + */ + private Map loadLatestCourseRecords(Long companyId, + Long trainingId, + String userId, + Set trainingCourseIds) { + if (trainingCourseIds == null || trainingCourseIds.isEmpty()) { + return Map.of(); + } + List records = trainingCourseRecordMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotTrainingCourseRecord::getCompanyId, companyId) + .eq(HotTrainingCourseRecord::getTrainingId, trainingId) + .eq(HotTrainingCourseRecord::getUserId, userId) + .in(HotTrainingCourseRecord::getTrainingCourseId, trainingCourseIds) + .eq(HotTrainingCourseRecord::getIsDeleted, 0L) + .orderByDesc(HotTrainingCourseRecord::getCreateTime) + ); + if (records == null || records.isEmpty()) { + return Map.of(); + } + Map map = new HashMap<>(); + for (HotTrainingCourseRecord r : records) { + if (r.getTrainingCourseId() == null) { + continue; + } + map.putIfAbsent(r.getTrainingCourseId(), r); + } + return map; + } + + /** + * 加载课程是否有随堂检测(试卷) + */ + private Map loadCourseHasQuizMap(List courseConfigs) { + Map hasQuizMap = new HashMap<>(); + Map cache = new HashMap<>(); + if (courseConfigs == null) { + return hasQuizMap; + } + for (HotTrainingCourseConfigVo cfg : courseConfigs) { + if (cfg == null || cfg.getId() == null || cfg.getCourseId() == null) { + continue; + } + Boolean hasQuiz = cache.get(cfg.getCourseId()); + if (hasQuiz == null) { + HotCourseResourceBo bo = new HotCourseResourceBo(); + bo.setCourseId(cfg.getCourseId()); + bo.setResourceType(3L); + List resources = courseResourceService.queryList(bo); + hasQuiz = resources != null && !resources.isEmpty(); + cache.put(cfg.getCourseId(), hasQuiz); + } + hasQuizMap.put(cfg.getId(), hasQuiz); + } + return hasQuizMap; + } + + private void updateParticipantLearnProgress(Long companyId, Long trainingId, String userId, BigDecimal progress) { + trainingParticipantMapper.update( + null, + Wrappers.lambdaUpdate() + .set(HotTrainingParticipant::getLearnProgress, progress) + .eq(HotTrainingParticipant::getCompanyId, companyId) + .eq(HotTrainingParticipant::getTrainingId, trainingId) + .eq(HotTrainingParticipant::getUserId, userId) + .eq(HotTrainingParticipant::getIsDeleted, 0L) + ); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/controller/HotVehicleEntryExitController.java b/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/controller/HotVehicleEntryExitController.java new file mode 100644 index 0000000..9bbabc5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/controller/HotVehicleEntryExitController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.securityManagement.vehicleEntryExit.controller; + +import com.hotwj.platform.securityManagement.vehicleEntryExit.domain.bo.HotVehicleEntryExitBo; +import com.hotwj.platform.securityManagement.vehicleEntryExit.domain.vo.HotVehicleEntryExitVo; +import com.hotwj.platform.securityManagement.vehicleEntryExit.service.IHotVehicleEntryExitService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 车辆进出场记录 + * + * @author shihongwei + * @date 2026-02-12 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/vehicleEntryExit") +@Tag(name = "车辆进出场记录", description = "车辆进出场记录管理") +public class HotVehicleEntryExitController extends BaseController { + + private final IHotVehicleEntryExitService hotVehicleEntryExitService; + + /** + * 查询车辆进出场记录列表 + */ + //@SaCheckPermission("securityManagement:vehicleInoutCheck:list") + @GetMapping("/list") + @Operation(summary = "分页查询车辆进出场记录列表") + public TableDataInfo list(HotVehicleEntryExitBo bo, PageQuery pageQuery) { + return hotVehicleEntryExitService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆进出场记录列表 + */ + //@SaCheckPermission("securityManagement:vehicleInoutCheck:export") + @Log(title = "车辆进出场记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆进出场记录列表") + public void export(HotVehicleEntryExitBo bo, HttpServletResponse response) { + List list = hotVehicleEntryExitService.queryList(bo); + ExcelUtil.exportExcel(list, "车辆进出场记录", HotVehicleEntryExitVo.class, response); + } + + /** + * 获取车辆进出场记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:vehicleInoutCheck:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆进出场记录详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotVehicleEntryExitService.queryById(id)); + } + + /** + * 新增车辆进出场记录 + */ + //@SaCheckPermission("securityManagement:vehicleInoutCheck:add") + @Log(title = "车辆进出场记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆进出场记录") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleEntryExitBo bo) { + return toAjax(hotVehicleEntryExitService.insertByBo(bo)); + } + + /** + * 修改车辆进出场记录 + */ + //@SaCheckPermission("securityManagement:vehicleInoutCheck:edit") + @Log(title = "车辆进出场记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆进出场记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleEntryExitBo bo) { + return toAjax(hotVehicleEntryExitService.updateByBo(bo)); + } + + /** + * 删除车辆进出场记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:vehicleInoutCheck:remove") + @Log(title = "车辆进出场记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆进出场记录") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotVehicleEntryExitService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/domain/HotVehicleEntryExit.java b/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/domain/HotVehicleEntryExit.java new file mode 100644 index 0000000..15b6c4a --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/domain/HotVehicleEntryExit.java @@ -0,0 +1,55 @@ +package com.hotwj.platform.securityManagement.vehicleEntryExit.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 车辆进出场记录对象 hot_vehicle_entry_exit + * + * @author shihongwei + * @date 2026-02-12 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_entry_exit") +public class HotVehicleEntryExit extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 检查日期 + */ + private Date checkTime; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/domain/bo/HotVehicleEntryExitBo.java b/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/domain/bo/HotVehicleEntryExitBo.java new file mode 100644 index 0000000..e63800f --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/domain/bo/HotVehicleEntryExitBo.java @@ -0,0 +1,51 @@ +package com.hotwj.platform.securityManagement.vehicleEntryExit.domain.bo; + +import com.hotwj.platform.securityManagement.vehicleEntryExit.domain.HotVehicleEntryExit; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 车辆进出场记录业务对象 hot_vehicle_entry_exit + * + * @author shihongwei + * @date 2026-02-12 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleEntryExit.class, reverseConvertGenerate = false) +public class HotVehicleEntryExitBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 检查日期 + */ + private Date checkTime; + + /** + * 备注 + */ + private String remark; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/domain/vo/HotVehicleEntryExitVo.java b/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/domain/vo/HotVehicleEntryExitVo.java new file mode 100644 index 0000000..8713c81 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/domain/vo/HotVehicleEntryExitVo.java @@ -0,0 +1,71 @@ +package com.hotwj.platform.securityManagement.vehicleEntryExit.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.vehicleEntryExit.domain.HotVehicleEntryExit; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 车辆进出场记录视图对象 hot_vehicle_entry_exit + * + * @author shihongwei + * @date 2026-02-12 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleEntryExit.class) +public class HotVehicleEntryExitVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 检查日期 + */ + @ExcelProperty(value = "检查日期") + private Date checkTime; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 创建者姓名 + */ + @ExcelProperty(value = "创建者姓名") + private String createByName; + + /** + * 更新者姓名 + */ + @ExcelProperty(value = "更新者姓名") + private String updateByName; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/mapper/HotVehicleEntryExitMapper.java b/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/mapper/HotVehicleEntryExitMapper.java new file mode 100644 index 0000000..89047c3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/mapper/HotVehicleEntryExitMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.vehicleEntryExit.mapper; + +import com.hotwj.platform.securityManagement.vehicleEntryExit.domain.HotVehicleEntryExit; +import com.hotwj.platform.securityManagement.vehicleEntryExit.domain.vo.HotVehicleEntryExitVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆进出场记录Mapper接口 + * + * @author shihongwei + * @date 2026-02-12 + */ +@Mapper +public interface HotVehicleEntryExitMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/service/IHotVehicleEntryExitService.java b/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/service/IHotVehicleEntryExitService.java new file mode 100644 index 0000000..51b786b --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/service/IHotVehicleEntryExitService.java @@ -0,0 +1,77 @@ +package com.hotwj.platform.securityManagement.vehicleEntryExit.service; + +import com.hotwj.platform.securityManagement.vehicleEntryExit.domain.bo.HotVehicleEntryExitBo; +import com.hotwj.platform.securityManagement.vehicleEntryExit.domain.vo.HotVehicleEntryExitVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆进出场记录Service接口 + * + * @author shihongwei + * @date 2026-02-12 + */ +public interface IHotVehicleEntryExitService { + + /** + * 查询车辆进出场记录 + * + * @param id 主键 + * @return 车辆进出场记录 + */ + HotVehicleEntryExitVo queryById(Long id); + + /** + * 分页查询车辆进出场记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆进出场记录分页列表 + */ + TableDataInfo queryPageList(HotVehicleEntryExitBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆进出场记录列表 + * + * @param bo 查询条件 + * @return 车辆进出场记录列表 + */ + List queryList(HotVehicleEntryExitBo bo); + + /** + * 根据检查日期查询车辆进出场记录 + * + * @param companyId 公司ID + * @param checkDate 检查日期 + * @return 车辆进出场记录 + */ + HotVehicleEntryExitVo selectByCheckDate(Long companyId, java.util.Date checkDate); + + /** + * 新增车辆进出场记录 + * + * @param bo 车辆进出场记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleEntryExitBo bo); + + /** + * 修改车辆进出场记录 + * + * @param bo 车辆进出场记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleEntryExitBo bo); + + /** + * 校验并批量删除车辆进出场记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/service/impl/HotVehicleEntryExitServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/service/impl/HotVehicleEntryExitServiceImpl.java new file mode 100644 index 0000000..1ae99ca --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/vehicleEntryExit/service/impl/HotVehicleEntryExitServiceImpl.java @@ -0,0 +1,150 @@ +package com.hotwj.platform.securityManagement.vehicleEntryExit.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.securityManagement.vehicleEntryExit.domain.HotVehicleEntryExit; +import com.hotwj.platform.securityManagement.vehicleEntryExit.domain.bo.HotVehicleEntryExitBo; +import com.hotwj.platform.securityManagement.vehicleEntryExit.domain.vo.HotVehicleEntryExitVo; +import com.hotwj.platform.securityManagement.vehicleEntryExit.mapper.HotVehicleEntryExitMapper; +import com.hotwj.platform.securityManagement.vehicleEntryExit.service.IHotVehicleEntryExitService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 车辆进出场记录Service业务层处理 + * + * @author shihongwei + * @date 2026-02-12 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleEntryExitServiceImpl implements IHotVehicleEntryExitService { + + private final HotVehicleEntryExitMapper baseMapper; + + /** + * 查询车辆进出场记录 + * + * @param id 主键 + * @return 车辆进出场记录 + */ + @Override + public HotVehicleEntryExitVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆进出场记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆进出场记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleEntryExitBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆进出场记录列表 + * + * @param bo 查询条件 + * @return 车辆进出场记录列表 + */ + @Override + public List queryList(HotVehicleEntryExitBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + @Override + public HotVehicleEntryExitVo selectByCheckDate(Long companyId, java.util.Date checkDate) { + if (companyId == null || checkDate == null) { + return null; + } + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(HotVehicleEntryExit::getCompanyId, companyId); + // 使用 DateUtil 获取当天的开始和结束时间 + lqw.ge(HotVehicleEntryExit::getCheckTime, cn.hutool.core.date.DateUtil.beginOfDay(checkDate)); + lqw.le(HotVehicleEntryExit::getCheckTime, cn.hutool.core.date.DateUtil.endOfDay(checkDate)); + lqw.last("LIMIT 1"); + return baseMapper.selectVoOne(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleEntryExitBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotVehicleEntryExit::getCreateTime); + lqw.eq(bo.getCompanyId() != null, HotVehicleEntryExit::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getCheckTime() != null, HotVehicleEntryExit::getCheckTime, bo.getCheckTime()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotVehicleEntryExit::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotVehicleEntryExit::getUpdateByName, bo.getUpdateByName()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleEntryExit::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增车辆进出场记录 + * + * @param bo 车辆进出场记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleEntryExitBo bo) { + HotVehicleEntryExit add = MapstructUtils.convert(bo, HotVehicleEntryExit.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改车辆进出场记录 + * + * @param bo 车辆进出场记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleEntryExitBo bo) { + HotVehicleEntryExit update = MapstructUtils.convert(bo, HotVehicleEntryExit.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleEntryExit entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除车辆进出场记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/controller/HotVehicleInoutCheckController.java b/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/controller/HotVehicleInoutCheckController.java new file mode 100644 index 0000000..961fb15 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/controller/HotVehicleInoutCheckController.java @@ -0,0 +1,117 @@ +package com.hotwj.platform.securityManagement.vehicleInoutCheck.controller; + +import com.hotwj.platform.securityManagement.vehicleInoutCheck.domain.bo.HotVehicleInoutCheckBo; +import com.hotwj.platform.securityManagement.vehicleInoutCheck.domain.vo.HotVehicleInoutCheckVo; +import com.hotwj.platform.securityManagement.vehicleInoutCheck.service.IHotVehicleInoutCheckService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 车辆进出场检查明细 + * + * @author shihongwei + * @date 2026-01-05 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/vehicleInoutCheck") +@Tag(name = "车辆进出场检查明细", description = "车辆进出场检查明细管理") +public class HotVehicleInoutCheckController extends BaseController { + + private final IHotVehicleInoutCheckService hotVehicleInoutCheckService; + + /** + * 查询车辆进出场检查明细列表 + */ + //@SaCheckPermission("securityManagement:vehicleInoutCheck:list") + @GetMapping("/list") + @Operation(summary = "分页查询车辆进出场检查明细列表") + public TableDataInfo list(HotVehicleInoutCheckBo bo, PageQuery pageQuery) { + return hotVehicleInoutCheckService.queryPageList(bo, pageQuery); + } + + /** + * 导出车辆进出场检查明细列表 + */ + //@SaCheckPermission("securityManagement:vehicleInoutCheck:export") + @Log(title = "车辆进出场检查明细", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出车辆进出场检查明细列表") + public void export(HotVehicleInoutCheckBo bo, HttpServletResponse response) { + List list = hotVehicleInoutCheckService.queryList(bo); + ExcelUtil.exportExcel(list, "车辆进出场检查", HotVehicleInoutCheckVo.class, response); + } + + /** + * 获取车辆进出场检查详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:vehicleInoutCheck:query") + @GetMapping("/{id}") + @Operation(summary = "获取车辆进出场检查明细详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotVehicleInoutCheckService.queryById(id)); + } + + /** + * 新增车辆进出场检查 + */ + //@SaCheckPermission("securityManagement:vehicleInoutCheck:add") + @Log(title = "车辆进出场检查", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增车辆进出场检查") + public R add(@Validated(AddGroup.class) @RequestBody HotVehicleInoutCheckBo bo) { + return toAjax(hotVehicleInoutCheckService.insertByBo(bo)); + } + + /** + * 修改车辆进出场检查 + */ + //@SaCheckPermission("securityManagement:vehicleInoutCheck:edit") + @Log(title = "车辆进出场检查", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改车辆进出场检查") + public R edit(@Validated(EditGroup.class) @RequestBody HotVehicleInoutCheckBo bo) { + return toAjax(hotVehicleInoutCheckService.updateByBo(bo)); + } + + /** + * 删除车辆进出场检查 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:vehicleInoutCheck:remove") + @Log(title = "车辆进出场检查", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除车辆进出场检查") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotVehicleInoutCheckService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/domain/HotVehicleInoutCheck.java b/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/domain/HotVehicleInoutCheck.java new file mode 100644 index 0000000..5e9dd05 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/domain/HotVehicleInoutCheck.java @@ -0,0 +1,123 @@ +package com.hotwj.platform.securityManagement.vehicleInoutCheck.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 车辆进出场检查对象 hot_vehicle_inout_check + * + * @author shihongwei + * @date 2026-01-05 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_vehicle_inout_check") +public class HotVehicleInoutCheck extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 进出场检查主表id + */ + private Long recordId; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆ID + */ + private Long vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 检查人员ID + */ + private Long inspectorId; + + /** + * 检查人员姓名 + */ + private String inspectorName; + + /** + * 行驶证是否正常 1=正常 0=异常 + */ + private Long drivingLicenseStatus; + + /** + * 驾驶证是否正常 1=正常 0=异常 + */ + private Long driverLicenseStatus; + + /** + * 道路运输证是否正常 1=正常 0=异常 + */ + private Long roadTransportCertStatus; + + /** + * 从业资格证是否正常 1=正常 0=异常 + */ + private Long qualificationCertStatus; + + /** + * 检查日期 + */ + private Date checkDate; + + /** + * 出场时间 + */ + private Date departureTime; + + /** + * 回场时间 + */ + private Date returnTime; + + /** + * 运行路线 + */ + private String route; + + /** + * 备注 + */ + private String remark; + + /** + * 结论 + */ + private String conclusion; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + /** + * 回场结论 + */ + private String returnRemark; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/domain/bo/HotVehicleInoutCheckBo.java b/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/domain/bo/HotVehicleInoutCheckBo.java new file mode 100644 index 0000000..ec185d6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/domain/bo/HotVehicleInoutCheckBo.java @@ -0,0 +1,134 @@ +package com.hotwj.platform.securityManagement.vehicleInoutCheck.domain.bo; + +import com.hotwj.platform.securityManagement.vehicleInoutCheck.domain.HotVehicleInoutCheck; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 车辆进出场检查业务 明细 对象 hot_vehicle_inout_check + * + * @author shihongwei + * @date 2026-01-05 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotVehicleInoutCheck.class, reverseConvertGenerate = false) +public class HotVehicleInoutCheckBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 进出场检查主表id + */ + private Long recordId; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 车辆ID + */ + @NotNull(message = "车辆ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 检查人员ID + */ + @NotNull(message = "检查人员ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long inspectorId; + + /** + * 检查人员姓名 + */ + private String inspectorName; + + /** + * 行驶证是否正常 1=正常 0=异常 + */ + @NotNull(message = "行驶证是否正常 1=正常 0=异常不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long drivingLicenseStatus; + + /** + * 驾驶证是否正常 1=正常 0=异常 + */ + @NotNull(message = "驾驶证是否正常 1=正常 0=异常不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long driverLicenseStatus; + + /** + * 道路运输证是否正常 1=正常 0=异常 + */ + @NotNull(message = "道路运输证是否正常 1=正常 0=异常不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long roadTransportCertStatus; + + /** + * 从业资格证是否正常 1=正常 0=异常 + */ + @NotNull(message = "从业资格证是否正常 1=正常 0=异常不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long qualificationCertStatus; + + /** + * 检查日期 + */ +// @NotNull(message = "检查日期不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date checkDate; + + /** + * 出场时间 + */ + @NotNull(message = "出场时间不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date departureTime; + + /** + * 回场时间 + */ + private Date returnTime; + + /** + * 运行路线 + */ + @NotBlank(message = "运行路线不能为空", groups = {AddGroup.class, EditGroup.class}) + private String route; + + /** + * 备注 + */ + private String remark; + + /** + * 结论 + */ + private String conclusion; + + /** + * 回场结论 + */ + private String returnRemark; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/domain/vo/HotVehicleInoutCheckVo.java b/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/domain/vo/HotVehicleInoutCheckVo.java new file mode 100644 index 0000000..2ce880d --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/domain/vo/HotVehicleInoutCheckVo.java @@ -0,0 +1,143 @@ +package com.hotwj.platform.securityManagement.vehicleInoutCheck.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.vehicleInoutCheck.domain.HotVehicleInoutCheck; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 车辆进出场检查视图对象 hot_vehicle_inout_check + * + * @author shihongwei + * @date 2026-01-05 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotVehicleInoutCheck.class) +public class HotVehicleInoutCheckVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 进出场检查主表id + */ + @ExcelProperty(value = "进出场检查主表id") + private Long recordId; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 车辆ID + */ + @ExcelProperty(value = "车辆ID") + private Long vehicleId; + + /** + * 车牌号 + */ + @ExcelProperty(value = "车牌号") + private String plateNumber; + + /** + * 检查人员ID + */ + @ExcelProperty(value = "检查人员ID") + private Long inspectorId; + + /** + * 检查人员姓名 + */ + @ExcelProperty(value = "检查人员姓名") + private String inspectorName; + + /** + * 行驶证是否正常 1=正常 0=异常 + */ + @ExcelProperty(value = "行驶证是否正常 1=正常 0=异常") + private Long drivingLicenseStatus; + + /** + * 驾驶证是否正常 1=正常 0=异常 + */ + @ExcelProperty(value = "驾驶证是否正常 1=正常 0=异常") + private Long driverLicenseStatus; + + /** + * 道路运输证是否正常 1=正常 0=异常 + */ + @ExcelProperty(value = "道路运输证是否正常 1=正常 0=异常") + private Long roadTransportCertStatus; + + /** + * 从业资格证是否正常 1=正常 0=异常 + */ + @ExcelProperty(value = "从业资格证是否正常 1=正常 0=异常") + private Long qualificationCertStatus; + + /** + * 检查日期 + */ + @ExcelProperty(value = "检查日期") + private Date checkDate; + + /** + * 出场时间 + */ + @ExcelProperty(value = "出场时间") + private Date departureTime; + + /** + * 回场时间 + */ + @ExcelProperty(value = "回场时间") + private Date returnTime; + + /** + * 运行路线 + */ + @ExcelProperty(value = "运行路线") + private String route; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 结论 + */ + @ExcelProperty(value = "结论") + private String conclusion; + + /** + * 回场结论 + */ + @ExcelProperty(value = "回场结论") + private String returnRemark; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/mapper/HotVehicleInoutCheckMapper.java b/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/mapper/HotVehicleInoutCheckMapper.java new file mode 100644 index 0000000..e0b2e69 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/mapper/HotVehicleInoutCheckMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.vehicleInoutCheck.mapper; + +import com.hotwj.platform.securityManagement.vehicleInoutCheck.domain.HotVehicleInoutCheck; +import com.hotwj.platform.securityManagement.vehicleInoutCheck.domain.vo.HotVehicleInoutCheckVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 车辆进出场检查Mapper接口 + * + * @author shihongwei + * @date 2026-01-05 + */ +@Mapper +public interface HotVehicleInoutCheckMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/service/IHotVehicleInoutCheckService.java b/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/service/IHotVehicleInoutCheckService.java new file mode 100644 index 0000000..66a0f95 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/service/IHotVehicleInoutCheckService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.securityManagement.vehicleInoutCheck.service; + +import com.hotwj.platform.securityManagement.vehicleInoutCheck.domain.bo.HotVehicleInoutCheckBo; +import com.hotwj.platform.securityManagement.vehicleInoutCheck.domain.vo.HotVehicleInoutCheckVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 车辆进出场检查Service接口 + * + * @author shihongwei + * @date 2026-01-05 + */ +public interface IHotVehicleInoutCheckService { + + /** + * 查询车辆进出场检查 + * + * @param id 主键 + * @return 车辆进出场检查 + */ + HotVehicleInoutCheckVo queryById(Long id); + + /** + * 分页查询车辆进出场检查列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆进出场检查分页列表 + */ + TableDataInfo queryPageList(HotVehicleInoutCheckBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的车辆进出场检查列表 + * + * @param bo 查询条件 + * @return 车辆进出场检查列表 + */ + List queryList(HotVehicleInoutCheckBo bo); + + /** + * 新增车辆进出场检查 + * + * @param bo 车辆进出场检查 + * @return 是否新增成功 + */ + Boolean insertByBo(HotVehicleInoutCheckBo bo); + + /** + * 修改车辆进出场检查 + * + * @param bo 车辆进出场检查 + * @return 是否修改成功 + */ + Boolean updateByBo(HotVehicleInoutCheckBo bo); + + /** + * 校验并批量删除车辆进出场检查信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/service/impl/HotVehicleInoutCheckServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/service/impl/HotVehicleInoutCheckServiceImpl.java new file mode 100644 index 0000000..9b4f753 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/vehicleInoutCheck/service/impl/HotVehicleInoutCheckServiceImpl.java @@ -0,0 +1,257 @@ +package com.hotwj.platform.securityManagement.vehicleInoutCheck.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle; +import com.hotwj.platform.resourceManagement.vehicleManagement.mapper.HotVehicleMapper; +import com.hotwj.platform.securityManagement.vehicleEntryExit.domain.bo.HotVehicleEntryExitBo; +import com.hotwj.platform.securityManagement.vehicleEntryExit.service.IHotVehicleEntryExitService; +import com.hotwj.platform.securityManagement.vehicleInoutCheck.domain.HotVehicleInoutCheck; +import com.hotwj.platform.securityManagement.vehicleInoutCheck.domain.bo.HotVehicleInoutCheckBo; +import com.hotwj.platform.securityManagement.vehicleInoutCheck.domain.vo.HotVehicleInoutCheckVo; +import com.hotwj.platform.securityManagement.vehicleInoutCheck.mapper.HotVehicleInoutCheckMapper; +import com.hotwj.platform.securityManagement.vehicleInoutCheck.service.IHotVehicleInoutCheckService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.DateUtils; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 车辆进出场检查Service业务层处理 + * + * @author shihongwei + * @date 2026-01-05 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotVehicleInoutCheckServiceImpl implements IHotVehicleInoutCheckService { + + private final HotVehicleInoutCheckMapper baseMapper; + private final IHotVehicleEntryExitService vehicleEntryExitService; + private final HotVehicleMapper vehicleMapper; + private final HotCompanySafetyManagerMapper safetyManagerMapper; + private final IHotSystemNotificationService notificationService; + + /** + * 查询车辆进出场检查 + * + * @param id 主键 + * @return 车辆进出场检查 + */ + @Override + public HotVehicleInoutCheckVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询车辆进出场检查列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 车辆进出场检查分页列表 + */ + @Override + public TableDataInfo queryPageList(HotVehicleInoutCheckBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的车辆进出场检查列表 + * + * @param bo 查询条件 + * @return 车辆进出场检查列表 + */ + @Override + public List queryList(HotVehicleInoutCheckBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotVehicleInoutCheckBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotVehicleInoutCheck::getCheckDate); + lqw.eq(bo.getRecordId() != null, HotVehicleInoutCheck::getRecordId, bo.getRecordId()); + lqw.eq(bo.getCompanyId() != null, HotVehicleInoutCheck::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getVehicleId() != null, HotVehicleInoutCheck::getVehicleId, bo.getVehicleId()); + lqw.eq(StringUtils.isNotBlank(bo.getPlateNumber()), HotVehicleInoutCheck::getPlateNumber, bo.getPlateNumber()); + lqw.eq(bo.getInspectorId() != null, HotVehicleInoutCheck::getInspectorId, bo.getInspectorId()); + lqw.like(StringUtils.isNotBlank(bo.getInspectorName()), HotVehicleInoutCheck::getInspectorName, bo.getInspectorName()); + lqw.eq(bo.getDrivingLicenseStatus() != null, HotVehicleInoutCheck::getDrivingLicenseStatus, bo.getDrivingLicenseStatus()); + lqw.eq(bo.getDriverLicenseStatus() != null, HotVehicleInoutCheck::getDriverLicenseStatus, bo.getDriverLicenseStatus()); + lqw.eq(bo.getRoadTransportCertStatus() != null, HotVehicleInoutCheck::getRoadTransportCertStatus, bo.getRoadTransportCertStatus()); + lqw.eq(bo.getQualificationCertStatus() != null, HotVehicleInoutCheck::getQualificationCertStatus, bo.getQualificationCertStatus()); + lqw.eq(bo.getCheckDate() != null, HotVehicleInoutCheck::getCheckDate, bo.getCheckDate()); + lqw.eq(bo.getDepartureTime() != null, HotVehicleInoutCheck::getDepartureTime, bo.getDepartureTime()); + lqw.eq(bo.getReturnTime() != null, HotVehicleInoutCheck::getReturnTime, bo.getReturnTime()); + lqw.eq(StringUtils.isNotBlank(bo.getRoute()), HotVehicleInoutCheck::getRoute, bo.getRoute()); + lqw.eq(StringUtils.isNotBlank(bo.getConclusion()), HotVehicleInoutCheck::getConclusion, bo.getConclusion()); + lqw.like(StringUtils.isNotBlank(bo.getReturnRemark()), HotVehicleInoutCheck::getReturnRemark, bo.getReturnRemark()); + lqw.eq(bo.getIsDeleted() != null, HotVehicleInoutCheck::getIsDeleted, bo.getIsDeleted()); + lqw.like(StringUtils.isNotBlank(bo.getCreateByName()), HotVehicleInoutCheck::getCreateByName, bo.getCreateByName()); + lqw.like(StringUtils.isNotBlank(bo.getUpdateByName()), HotVehicleInoutCheck::getUpdateByName, bo.getUpdateByName()); + return lqw; + } + + /** + * 新增车辆进出场检查 + * + * @param bo 车辆进出场检查 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotVehicleInoutCheckBo bo) { + if (bo.getCheckDate() == null) { + bo.setCheckDate(new Date()); + } + + // 填充冗余字段(车牌号、检查人员姓名) + fillRedundantFields(bo); + + // 只有在新增未完成的出场记录时(returnTime == null),才校验是否已存在未回场记录 + // 如果是补录已完成的历史记录(returnTime != null),则不受此限制 + if (bo.getVehicleId() != null && bo.getReturnTime() == null) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(HotVehicleInoutCheck::getVehicleId, bo.getVehicleId()); + queryWrapper.isNotNull(HotVehicleInoutCheck::getDepartureTime); + queryWrapper.isNull(HotVehicleInoutCheck::getReturnTime); + // 严谨逻辑:查询该车是否存在任意一条“已出场但未回场”的记录,防止历史脏数据导致的状态错误 + if (baseMapper.selectCount(queryWrapper) > 0) { + throw new ServiceException("该车尚有未回场的记录,请先处理"); + } + } + // 如果存在检查日期存在,则不创建新表,复用之前查到的HotVehicleInoutCheck + if (bo.getRecordId() == null) { + com.hotwj.platform.securityManagement.vehicleEntryExit.domain.vo.HotVehicleEntryExitVo entryExitVo = vehicleEntryExitService.selectByCheckDate(bo.getCompanyId(), bo.getCheckDate()); + if (entryExitVo != null) { + bo.setRecordId(entryExitVo.getId()); + } else { + HotVehicleEntryExitBo entryExitBo = new HotVehicleEntryExitBo(); + entryExitBo.setCompanyId(bo.getCompanyId()); + entryExitBo.setCheckTime(bo.getCheckDate()); + vehicleEntryExitService.insertByBo(entryExitBo); + bo.setRecordId(entryExitBo.getId()); + } + } + HotVehicleInoutCheck add = MapstructUtils.convert(bo, HotVehicleInoutCheck.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + boolean abnormal = isAbnormal(add); + if (abnormal && add.getCompanyId() != null) { + List managers = safetyManagerMapper.selectList( + Wrappers.lambdaQuery() + .eq(HotCompanySafetyManager::getCompanyId, add.getCompanyId()) + .eq(HotCompanySafetyManager::getIsDeleted, 0L) + .eq(HotCompanySafetyManager::getIsResigned, 0L) + .eq(HotCompanySafetyManager::getStatus, 1L) + ); + if (managers != null && !managers.isEmpty()) { + List receiverIds = managers.stream().map(m -> String.valueOf(m.getId())).toList(); + String plate = StringUtils.blankToDefault(add.getPlateNumber(), "未知车牌"); + String dateStr = add.getCheckDate() != null ? DateUtils.formatDate(add.getCheckDate()) : "未知日期"; + String content = "车辆【" + plate + "】在【" + dateStr + "】进出场检查发现异常。"; + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("车辆管理"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("管理员"); + bos.setReceiverIds(receiverIds); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送进出场异常通知失败 companyId={} vehicleId={}", add.getCompanyId(), add.getVehicleId(), e); + } + } + } + } + return flag; + } + + /** + * 修改车辆进出场检查 + * + * @param bo 车辆进出场检查 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotVehicleInoutCheckBo bo) { + // 填充冗余字段(车牌号、检查人员姓名) + fillRedundantFields(bo); + + HotVehicleInoutCheck update = MapstructUtils.convert(bo, HotVehicleInoutCheck.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 填充冗余字段:车牌号、检查人员姓名 + */ + private void fillRedundantFields(HotVehicleInoutCheckBo bo) { + // 填充车牌号 + if (StringUtils.isBlank(bo.getPlateNumber()) && bo.getVehicleId() != null) { + // HotVehicle 主键 ID 定义为 String,需转换 + HotVehicle vehicle = vehicleMapper.selectById(String.valueOf(bo.getVehicleId())); + if (vehicle != null) { + bo.setPlateNumber(vehicle.getPlateNumber()); + } + } + // 填充检查人员姓名(安全管理人员) + if (StringUtils.isBlank(bo.getInspectorName()) && bo.getInspectorId() != null) { + HotCompanySafetyManager safetyManager = safetyManagerMapper.selectById(bo.getInspectorId()); + if (safetyManager != null) { + bo.setInspectorName(safetyManager.getName()); + } + } + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotVehicleInoutCheck entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除车辆进出场检查信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + private boolean isAbnormal(HotVehicleInoutCheck entity) { + return (entity.getDrivingLicenseStatus() != null && Long.valueOf(0L).equals(entity.getDrivingLicenseStatus())) + || (entity.getDriverLicenseStatus() != null && Long.valueOf(0L).equals(entity.getDriverLicenseStatus())) + || (entity.getRoadTransportCertStatus() != null && Long.valueOf(0L).equals(entity.getRoadTransportCertStatus())) + || (entity.getQualificationCertStatus() != null && Long.valueOf(0L).equals(entity.getQualificationCertStatus())); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInfo/callback/ViolationHandleCallback.java b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/callback/ViolationHandleCallback.java new file mode 100644 index 0000000..69a8838 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/callback/ViolationHandleCallback.java @@ -0,0 +1,41 @@ +package com.hotwj.platform.securityManagement.violationInfo.callback; + +import com.hotwj.platform.flow.callback.IFlowCallback; +import com.hotwj.platform.securityManagement.violationInfo.domain.HotViolationInfo; +import com.hotwj.platform.securityManagement.violationInfo.mapper.HotViolationInfoMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class ViolationHandleCallback implements IFlowCallback { + + private final HotViolationInfoMapper violationInfoMapper; + + public ViolationHandleCallback(HotViolationInfoMapper violationInfoMapper) { + this.violationInfoMapper = violationInfoMapper; + } + + @Override + public String getFlowCode() { + return "VIOLATION_HANDLE"; + } + + @Override + public void onProcessEnd(String instanceId, String businessId, String flowCode, boolean success) { + Long id = Long.parseLong(businessId); + HotViolationInfo info = violationInfoMapper.selectById(id); + if (info == null) { + return; + } + if (success) { + info.setProcessStatus(2L); + } else { + if (info.getProcessStatus() == null || info.getProcessStatus() == 2L) { + info.setProcessStatus(1L); + } + } + violationInfoMapper.updateById(info); + log.info("违规处理流程结束回调: businessId={}, success={}, instanceId={}", businessId, success, instanceId); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInfo/controller/HotViolationInfoController.java b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/controller/HotViolationInfoController.java new file mode 100644 index 0000000..f866935 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/controller/HotViolationInfoController.java @@ -0,0 +1,138 @@ +package com.hotwj.platform.securityManagement.violationInfo.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.hotwj.platform.securityManagement.violationInfo.domain.bo.HotViolationAuditBo; +import com.hotwj.platform.securityManagement.violationInfo.domain.bo.HotViolationInfoBo; +import com.hotwj.platform.securityManagement.violationInfo.domain.vo.HotViolationInfoVo; +import com.hotwj.platform.securityManagement.violationInfo.service.IHotViolationInfoService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 违规信息 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/violationInfo") +@Tag(name = "违规信息", description = "违规信息管理") +public class HotViolationInfoController extends BaseController { + + private final IHotViolationInfoService hotViolationInfoService; + + /** + * 查询违规信息列表 + */ + //@SaCheckPermission("securityManagement:violationInfo:list") + @GetMapping("/list") + @Operation(summary = "分页查询违规信息列表") + public TableDataInfo list(HotViolationInfoBo bo, PageQuery pageQuery) { + return hotViolationInfoService.queryPageList(bo, pageQuery); + } + + /** + * 导出违规信息列表 + */ + //@SaCheckPermission("securityManagement:violationInfo:export") + @Log(title = "违规信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出违规信息列表") + public void export(HotViolationInfoBo bo, HttpServletResponse response) { + List list = hotViolationInfoService.queryList(bo); + ExcelUtil.exportExcel(list, "违规信息", HotViolationInfoVo.class, response); + } + + /** + * 审核并完成违规处理(司机或企业均可办理) + */ + //@SaCheckPermission("securityManagement:violationInfo:edit") + @PostMapping("/audit") + @Operation(summary = "审核并完成违规处理") + public R audit(@Validated @RequestBody HotViolationAuditBo bo) { + return R.ok(hotViolationInfoService.auditAndComplete(bo.getTaskId(), bo.getProcessStatus(), bo.getDisposalResult(), bo.getInterviewedSignatureImage(), bo.getPartySignatureImage())); + } + + /** + * 获取违规信息详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:violationInfo:query") + @GetMapping("/{id}") + @Operation(summary = "获取违规信息详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "id", description = "主键", required = true, example = "1") + @PathVariable Long id) { + return R.ok(hotViolationInfoService.queryById(id)); + } + + /** + * 新增违规信息 + */ + //@SaCheckPermission("securityManagement:violationInfo:add") + @Log(title = "违规信息", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增违规信息") + public R add(@Validated(AddGroup.class) @RequestBody HotViolationInfoBo bo) { + return toAjax(hotViolationInfoService.insertByBo(bo)); + } + + /** + * 修改违规信息 + */ + //@SaCheckPermission("securityManagement:violationInfo:edit") + @Log(title = "违规信息", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改违规信息") + public R edit(@Validated(EditGroup.class) @RequestBody HotViolationInfoBo bo) { + return toAjax(hotViolationInfoService.updateByBo(bo)); + } + + /** + * 删除违规信息 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:violationInfo:remove") + @Log(title = "违规信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除违规信息") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3") + @PathVariable Long[] ids) { + return toAjax(hotViolationInfoService.deleteWithValidByIds(List.of(ids), true)); + } + + /** + * 驾驶员查询违规列表(待办/已办) + */ + @GetMapping("/driver/list") + @Operation(summary = "驾驶员查询违规列表") + public TableDataInfo driverList(HotViolationInfoBo bo, PageQuery pageQuery) { + return hotViolationInfoService.queryDriverList(bo, pageQuery); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInfo/domain/HotViolationInfo.java b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/domain/HotViolationInfo.java new file mode 100644 index 0000000..fc8044f --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/domain/HotViolationInfo.java @@ -0,0 +1,221 @@ +package com.hotwj.platform.securityManagement.violationInfo.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 违规信息对象 hot_violation_info + * + * @author shihongwei + * @date 2026-01-06 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_violation_info") +public class HotViolationInfo extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 车辆ID + */ + private Long vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 驾驶员ID + */ + private String driverId; + + /** + * 驾驶员姓名 + */ + private String driverName; + + /** + * 违规类型 + */ + private Long violationType; + + /** + * 开单部门 + */ + private Long billingDepartment; + + /** + * 是否酒驾醉驾 0=否 1=是 + */ + private Long isDrunkDriving; + + /** + * 发生时间 + */ + private Date occurTime; + + /** + * 违章编号 + */ + private String violationNo; + + /** + * 罚款金额 + */ + private BigDecimal fineAmount; + + /** + * 扣分数 + */ + private Long deductPoints; + + /** + * 扣分类型 + */ + private Long deductionType; + + /** + * 记录人ID + */ + private Long recorderId; + + /** + * 记录人姓名 + */ + private String recorderName; + + /** + * 是否需要约谈 0=否 1=是 + */ + private Long needInterview; + + /** + * 违规地点 + */ + private String violationPlace; + + /** + * 违规内容 + */ + private String violationContent; + + /** + * 处理情况 + */ + private String disposalResult; + + /** + * 备注 + */ + private String remark; + + /** + * 违规附件URL + */ + private String attachmentUrls; + + /** + * 负责人签名 + */ + private String responsibleSignatureImage; + + /** + * 当事人签名 + */ + private String partySignatureImage; + + /** + * 约谈时间 + */ + private Date interviewTime; + + /** + * 约谈地点 + */ + private String interviewPlace; + + /** + * 约谈记录人ID + */ + private Long interviewRecorderId; + + /** + * 约谈记录人姓名 + */ + private String interviewRecorderName; + + /** + * 约谈人ID + */ + private Long interviewPersonId; + + /** + * 约谈人姓名 + */ + private String interviewPersonName; + + /** + * 约谈原因 + */ + private String interviewReason; + + /** + * 谈话内容 + */ + private String interviewContent; + + /** + * 承诺书url列表 + */ + private String promiseUrls; + + /** + * 约谈照片url列表 + */ + private String interviewPhotoUrls; + + /** + * 约谈人签名图片 + */ + private String interviewerSignatureImage; + + /** + * 被约谈人签名图片 + */ + private String interviewedSignatureImage; + + /** + * 处理状态 + */ + private Long processStatus; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInfo/domain/bo/HotViolationAuditBo.java b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/domain/bo/HotViolationAuditBo.java new file mode 100644 index 0000000..3ff1d25 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/domain/bo/HotViolationAuditBo.java @@ -0,0 +1,26 @@ +package com.hotwj.platform.securityManagement.violationInfo.domain.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +@Data +@Schema(description = "违规处理审核业务对象") +public class HotViolationAuditBo { + + @Schema(description = "任务ID") + @NotBlank(message = "任务ID不能为空") + private String taskId; + + @Schema(description = "处理状态(传2表示处理完成)") + private Long processStatus; + + @Schema(description = "处理情况") + private String disposalResult; + + @Schema(description = "被约谈人签名图片") + private String interviewedSignatureImage; + + @Schema(description = "当事人签名图片") + private String partySignatureImage; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInfo/domain/bo/HotViolationInfoBo.java b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/domain/bo/HotViolationInfoBo.java new file mode 100644 index 0000000..d0a6c20 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/domain/bo/HotViolationInfoBo.java @@ -0,0 +1,231 @@ +package com.hotwj.platform.securityManagement.violationInfo.domain.bo; + +import com.hotwj.platform.securityManagement.violationInfo.domain.HotViolationInfo; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 违规信息业务对象 hot_violation_info + * + * @author shihongwei + * @date 2026-01-06 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotViolationInfo.class, reverseConvertGenerate = false) +public class HotViolationInfoBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 公司ID + */ + @NotNull(message = "公司ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long companyId; + + /** + * 车辆ID + */ + @NotNull(message = "车辆ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long vehicleId; + + /** + * 车牌号 + */ + private String plateNumber; + + /** + * 驾驶员ID + */ + @NotNull(message = "驾驶员ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private String driverId; + + /** + * 驾驶员姓名 + */ + private String driverName; + + /** + * 违规类型 + */ + @NotNull(message = "违规类型不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long violationType; + + /** + * 开单部门 + */ + private Long billingDepartment; + + /** + * 是否酒驾醉驾 0=否 1=是 + */ + @NotNull(message = "是否酒驾醉驾 0=否 1=是不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDrunkDriving; + + /** + * 发生时间 + */ + @NotNull(message = "发生时间不能为空", groups = {AddGroup.class, EditGroup.class}) + private Date occurTime; + + /** + * 违章编号 + */ + private String violationNo; + + /** + * 罚款金额 + */ + private BigDecimal fineAmount; + + /** + * 扣分数 + */ + private Long deductPoints; + + /** + * 扣分类型 + */ + private Long deductionType; + + /** + * 记录人ID + */ + @NotNull(message = "记录人ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long recorderId; + + /** + * 记录人姓名 + */ + private String recorderName; + + /** + * 是否需要约谈 0=否 1=是 + */ + @NotNull(message = "是否需要约谈 0=否 1=是不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long needInterview; + + /** + * 违规地点 + */ + @NotBlank(message = "违规地点不能为空", groups = {AddGroup.class, EditGroup.class}) + private String violationPlace; + + /** + * 违规内容 + */ + @NotBlank(message = "违规内容不能为空", groups = {AddGroup.class, EditGroup.class}) + private String violationContent; + + /** + * 处理情况 + */ + private String disposalResult; + + /** + * 备注 + */ + private String remark; + + /** + * 违规附件URL + */ + private String attachmentUrls; + + /** + * 负责人签名 + */ + @NotBlank(message = "负责人签名不能为空", groups = {AddGroup.class, EditGroup.class}) + private String responsibleSignatureImage; + + /** + * 当事人签名 + */ + private String partySignatureImage; + + /** + * 约谈时间 + */ + private Date interviewTime; + + /** + * 约谈地点 + */ + private String interviewPlace; + + /** + * 约谈记录人ID + */ + private Long interviewRecorderId; + + /** + * 约谈记录人姓名 + */ + private String interviewRecorderName; + + /** + * 约谈人ID + */ + private Long interviewPersonId; + + /** + * 约谈人姓名 + */ + private String interviewPersonName; + + /** + * 约谈原因 + */ + private String interviewReason; + + /** + * 谈话内容 + */ + private String interviewContent; + + /** + * 承诺书url列表 + */ + private String promiseUrls; + + /** + * 约谈照片url列表 + */ + private String interviewPhotoUrls; + + /** + * 约谈人签名图片 + */ + private String interviewerSignatureImage; + + /** + * 被约谈人签名图片 + */ + private String interviewedSignatureImage; + + /** + * 处理状态 + */ + private Long processStatus; + + /** + * 0=正常, 1=已删除 + */ +// @NotNull(message = "0=正常, 1=已删除不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInfo/domain/vo/HotViolationInfoVo.java b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/domain/vo/HotViolationInfoVo.java new file mode 100644 index 0000000..bbe285f --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/domain/vo/HotViolationInfoVo.java @@ -0,0 +1,304 @@ +package com.hotwj.platform.securityManagement.violationInfo.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.violationInfo.domain.HotViolationInfo; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + + +/** + * 违规信息视图对象 hot_violation_info + * + * @author shihongwei + * @date 2026-01-06 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotViolationInfo.class) +public class HotViolationInfoVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 车辆ID + */ + @ExcelProperty(value = "车辆ID") + private Long vehicleId; + + /** + * 车牌号 + */ + @ExcelProperty(value = "车牌号") + private String plateNumber; + + /** + * 驾驶员ID + */ + @ExcelProperty(value = "驾驶员ID") + private String driverId; + + /** + * 驾驶员姓名 + */ + @ExcelProperty(value = "驾驶员姓名") + private String driverName; + + /** + * 违规类型 + */ + @ExcelProperty(value = "违规类型") + private Long violationType; + + /** + * 开单部门 + */ + @ExcelProperty(value = "开单部门") + private Long billingDepartment; + + /** + * 是否酒驾醉驾 0=否 1=是 + */ + @ExcelProperty(value = "是否酒驾醉驾 0=否 1=是") + private Long isDrunkDriving; + + /** + * 发生时间 + */ + @ExcelProperty(value = "发生时间") + private Date occurTime; + + /** + * 登记时间 + */ + @ExcelProperty(value = "登记时间") + private Date createTime; + + /** + * 违章编号 + */ + @ExcelProperty(value = "违章编号") + private String violationNo; + + /** + * 罚款金额 + */ + @ExcelProperty(value = "罚款金额") + private BigDecimal fineAmount; + + /** + * 扣分数 + */ + @ExcelProperty(value = "扣分数") + private Long deductPoints; + + /** + * 扣分类型 + */ + @ExcelProperty(value = "扣分类型") + private Long deductionType; + + /** + * 记录人ID + */ + @ExcelProperty(value = "记录人ID") + private Long recorderId; + + /** + * 记录人姓名 + */ + @ExcelProperty(value = "记录人姓名") + private String recorderName; + + /** + * 是否需要约谈 0=否 1=是 + */ + @ExcelProperty(value = "是否需要约谈 0=否 1=是") + private Long needInterview; + + /** + * 违规地点 + */ + @ExcelProperty(value = "违规地点") + private String violationPlace; + + /** + * 违规内容 + */ + @ExcelProperty(value = "违规内容") + private String violationContent; + + /** + * 处理情况 + */ + @ExcelProperty(value = "处理情况") + private String disposalResult; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 违规附件URL + */ + @ExcelProperty(value = "违规附件URL") + private String attachmentUrls; + + /** + * 违规附件URLUrl + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "attachmentUrls") + private String attachmentUrlsUrl; + /** + * 负责人签名 + */ + @ExcelProperty(value = "负责人签名") + private String responsibleSignatureImage; + + /** + * 负责人签名Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "responsibleSignatureImage") + private String responsibleSignatureImageUrl; + /** + * 当事人签名 + */ + @ExcelProperty(value = "当事人签名") + private String partySignatureImage; + + /** + * 当事人签名Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "partySignatureImage") + private String partySignatureImageUrl; + /** + * 约谈时间 + */ + @ExcelProperty(value = "约谈时间") + private Date interviewTime; + + /** + * 约谈地点 + */ + @ExcelProperty(value = "约谈地点") + private String interviewPlace; + + /** + * 约谈记录人ID + */ + @ExcelProperty(value = "约谈记录人ID") + private Long interviewRecorderId; + + /** + * 约谈记录人姓名 + */ + @ExcelProperty(value = "约谈记录人姓名") + private String interviewRecorderName; + + /** + * 约谈人ID + */ + @ExcelProperty(value = "约谈人ID") + private Long interviewPersonId; + + /** + * 约谈人姓名 + */ + @ExcelProperty(value = "约谈人姓名") + private String interviewPersonName; + + /** + * 约谈原因 + */ + @ExcelProperty(value = "约谈原因") + private String interviewReason; + + /** + * 谈话内容 + */ + @ExcelProperty(value = "谈话内容") + private String interviewContent; + + /** + * 承诺书url列表 + */ + @ExcelProperty(value = "承诺书url列表") + private String promiseUrls; + + /** + * 承诺书url列表Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "promiseUrls") + private String promiseUrlsUrl; + /** + * 约谈照片url列表 + */ + @ExcelProperty(value = "约谈照片url列表") + private String interviewPhotoUrls; + + /** + * 约谈照片url列表Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "interviewPhotoUrls") + private String interviewPhotoUrlsUrl; + /** + * 约谈人签名图片 + */ + @ExcelProperty(value = "约谈人签名图片") + private String interviewerSignatureImage; + + /** + * 约谈人签名图片Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "interviewerSignatureImage") + private String interviewerSignatureImageUrl; + /** + * 被约谈人签名图片 + */ + @ExcelProperty(value = "被约谈人签名图片") + private String interviewedSignatureImage; + + /** + * 被约谈人签名图片Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "interviewedSignatureImage") + private String interviewedSignatureImageUrl; + /** + * 处理状态 + */ + @ExcelProperty(value = "处理状态") + private Long processStatus; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + /** + * 待办任务ID + */ + private String taskId; +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInfo/mapper/HotViolationInfoMapper.java b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/mapper/HotViolationInfoMapper.java new file mode 100644 index 0000000..443e4cd --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/mapper/HotViolationInfoMapper.java @@ -0,0 +1,28 @@ +package com.hotwj.platform.securityManagement.violationInfo.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.securityManagement.violationInfo.domain.HotViolationInfo; +import com.hotwj.platform.securityManagement.violationInfo.domain.bo.HotViolationInfoBo; +import com.hotwj.platform.securityManagement.violationInfo.domain.vo.HotViolationInfoVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 违规信息Mapper接口 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Mapper +public interface HotViolationInfoMapper extends BaseMapperPlus { + + /** + * 查询驾驶员违规列表 + * + * @param page 分页参数 + * @param bo 查询条件 + * @return 列表 + */ + Page queryDriverViolationList(Page page, @Param("bo") HotViolationInfoBo bo); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInfo/service/IHotViolationInfoService.java b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/service/IHotViolationInfoService.java new file mode 100644 index 0000000..128c186 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/service/IHotViolationInfoService.java @@ -0,0 +1,89 @@ +package com.hotwj.platform.securityManagement.violationInfo.service; + +import com.hotwj.platform.securityManagement.violationInfo.domain.bo.HotViolationInfoBo; +import com.hotwj.platform.securityManagement.violationInfo.domain.vo.HotViolationInfoVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 违规信息Service接口 + * + * @author shihongwei + * @date 2026-01-06 + */ +public interface IHotViolationInfoService { + + /** + * 查询违规信息 + * + * @param id 主键 + * @return 违规信息 + */ + HotViolationInfoVo queryById(Long id); + + /** + * 分页查询违规信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 违规信息分页列表 + */ + TableDataInfo queryPageList(HotViolationInfoBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的违规信息列表 + * + * @param bo 查询条件 + * @return 违规信息列表 + */ + List queryList(HotViolationInfoBo bo); + + /** + * 查询驾驶员违规信息列表(待办/已办) + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 违规信息列表 + */ + TableDataInfo queryDriverList(HotViolationInfoBo bo, PageQuery pageQuery); + + /** + * 审核并完成违规处理(可修改业务字段并结束流程) + * + * @param taskId 任务ID + * @param processStatus 处理状态 + * @param disposalResult 处理情况 + * @param interviewedSignatureImage 被约谈人签名图片 + * @param partySignatureImage 当事人签名图片 + * @return 结果 + */ + Boolean auditAndComplete(String taskId, Long processStatus, String disposalResult, String interviewedSignatureImage, String partySignatureImage); + + /** + * 新增违规信息 + * + * @param bo 违规信息 + * @return 是否新增成功 + */ + Boolean insertByBo(HotViolationInfoBo bo); + + /** + * 修改违规信息 + * + * @param bo 违规信息 + * @return 是否修改成功 + */ + Boolean updateByBo(HotViolationInfoBo bo); + + /** + * 校验并批量删除违规信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInfo/service/impl/HotViolationInfoServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/service/impl/HotViolationInfoServiceImpl.java new file mode 100644 index 0000000..fa445c5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/service/impl/HotViolationInfoServiceImpl.java @@ -0,0 +1,521 @@ +package com.hotwj.platform.securityManagement.violationInfo.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.common.helper.DriverLoginContextHelper; +import com.hotwj.platform.flow.domain.SysFlowInstance; +import com.hotwj.platform.flow.domain.SysFlowTask; +import com.hotwj.platform.flow.domain.SysFlowTaskHis; +import com.hotwj.platform.flow.mapper.SysFlowInstanceMapper; +import com.hotwj.platform.flow.mapper.SysFlowTaskHisMapper; +import com.hotwj.platform.flow.service.ISysFlowService; +import com.hotwj.platform.integration.ocr.SignatureVerifyService; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.securityManagement.violationInfo.domain.HotViolationInfo; +import com.hotwj.platform.securityManagement.violationInfo.domain.bo.HotViolationInfoBo; +import com.hotwj.platform.securityManagement.violationInfo.domain.vo.HotViolationInfoVo; +import com.hotwj.platform.securityManagement.violationInfo.mapper.HotViolationInfoMapper; +import com.hotwj.platform.securityManagement.violationInfo.service.IHotViolationInfoService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 违规信息Service业务层处理 + * + * @author shihongwei + * @date 2026-01-06 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotViolationInfoServiceImpl implements IHotViolationInfoService { + + private static final String FLOW_CODE_VIOLATION_HANDLE = "VIOLATION_HANDLE"; + private static final String NODE_PARTY_SIGN = "PARTY_SIGN_NODE"; + private static final String NODE_WAIT_INTERVIEW_INFO = "WAIT_INTERVIEW_INFO_NODE"; + private static final String NODE_INTERVIEWED_SIGN = "INTERVIEW_SIGN_NODE"; + private static final String NODE_AUTO_DONE = "AUTO_DONE_NODE"; + private static final long PROCESS_STATUS_TODO = 0L; + private static final long PROCESS_STATUS_DONE = 2L; + + private final HotViolationInfoMapper baseMapper; + private final IHotSystemNotificationService notificationService; + private final ISysFlowService flowService; + private final SysFlowInstanceMapper instanceMapper; + private final SysFlowTaskHisMapper taskHisMapper; + private final SignatureVerifyService signatureVerifyService; + + private static boolean isNeedInterview(Long needInterview) { + return Long.valueOf(1L).equals(needInterview); + } + + private static boolean isInterviewInfoComplete(HotViolationInfo info) { + return info != null + && info.getInterviewTime() != null + && StringUtils.isNotBlank(info.getInterviewPlace()) + && StringUtils.isNotBlank(info.getInterviewReason()) + && StringUtils.isNotBlank(info.getInterviewContent()) + && StringUtils.isNotBlank(info.getInterviewerSignatureImage()); + } + + /** + * 处理违规信息流程 + */ + private void reconcileViolationFlow(Long violationId) { + if (violationId == null) { + return; + } + HotViolationInfo info = baseMapper.selectById(violationId); + if (info == null || info.getCompanyId() == null || StringUtils.isBlank(info.getDriverId())) { + return; + } + String businessId = String.valueOf(violationId); + if (info.getProcessStatus() != null + && info.getProcessStatus() == PROCESS_STATUS_DONE + && hasViolationDoneHistory(info.getDriverId(), businessId)) { + return; + } + + long runningCount = instanceMapper.selectCount( + Wrappers.lambdaQuery() + .eq(SysFlowInstance::getFlowCode, FLOW_CODE_VIOLATION_HANDLE) + .eq(SysFlowInstance::getBusinessId, businessId) + .eq(SysFlowInstance::getStatus, 0) + ); + if (runningCount == 0) { + LoginUser loginUser = LoginHelper.getLoginUser(); + flowService.startFlow(FLOW_CODE_VIOLATION_HANDLE, businessId, info.getCompanyId(), loginUser.getBusinessUserId(), info.getDriverId()); + } + autoAdvanceViolationFlow(violationId); + } + + /** + * 自动推进违规处理流程 + */ + private void autoAdvanceViolationFlow(Long violationId) { + if (violationId == null) { + return; + } + for (int i = 0; i < 6; i++) { + HotViolationInfo info = baseMapper.selectById(violationId); + if (info == null || StringUtils.isBlank(info.getDriverId()) || info.getCompanyId() == null) { + return; + } + String businessId = String.valueOf(violationId); + + String auditorId = info.getDriverId(); + + String taskId = flowService.getTaskIdByBusinessId(FLOW_CODE_VIOLATION_HANDLE, businessId, auditorId); + + if (StringUtils.isBlank(taskId)) { + auditorId = "SYSTEM"; + taskId = flowService.getTaskIdByBusinessId(FLOW_CODE_VIOLATION_HANDLE, businessId, auditorId); + } + if (StringUtils.isBlank(taskId)) { + return; + } + SysFlowTask task = flowService.getTaskById(taskId); + if (task == null) { + return; + } + String nodeCode = task.getNodeCode(); + + boolean shouldAudit = false; + if (NODE_PARTY_SIGN.equals(nodeCode)) { + shouldAudit = StringUtils.isNotBlank(info.getPartySignatureImage()); + } else if (NODE_INTERVIEWED_SIGN.equals(nodeCode)) { + shouldAudit = StringUtils.isNotBlank(info.getInterviewedSignatureImage()); + } else if (NODE_WAIT_INTERVIEW_INFO.equals(nodeCode)) { + shouldAudit = isInterviewInfoComplete(info); + auditorId = "SYSTEM"; + } else if (NODE_AUTO_DONE.equals(nodeCode)) { + shouldAudit = true; + } + + if (!shouldAudit) { + return; + } + flowService.audit(taskId, true, "自动流转", auditorId); + } + } + + private void validateAuditForNode(SysFlowTask task, HotViolationInfo info, String interviewedSignatureImage, String partySignatureImage) { + if (task == null || info == null) { + throw new ServiceException("业务数据异常"); + } + String nodeCode = task.getNodeCode(); + if (NODE_PARTY_SIGN.equals(nodeCode)) { + if (StringUtils.isBlank(partySignatureImage) && StringUtils.isBlank(info.getPartySignatureImage())) { + throw new ServiceException("请提供当事人签名"); + } + validateMobileSignatureIfNeeded(info.getDriverName(), partySignatureImage); + return; + } + if (NODE_INTERVIEWED_SIGN.equals(nodeCode)) { + if (!isNeedInterview(info.getNeedInterview())) { + throw new ServiceException("当前违规不需要约谈"); + } + if (!isInterviewInfoComplete(info)) { + throw new ServiceException("约谈信息未填写,无法签字提交"); + } + if (StringUtils.isBlank(info.getPartySignatureImage()) && StringUtils.isBlank(partySignatureImage)) { + throw new ServiceException("请先完成当事人签名"); + } + if (StringUtils.isBlank(interviewedSignatureImage) && StringUtils.isBlank(info.getInterviewedSignatureImage())) { + throw new ServiceException("请提供被约谈人签名"); + } + validateMobileSignatureIfNeeded(info.getDriverName(), interviewedSignatureImage); + return; + } + + // TODO 移动端验证签名 + throw new ServiceException("当前节点不支持签字提交"); + } + + private void validateMobileSignatureIfNeeded(String userName, String signatureImage) { + if (!DriverLoginContextHelper.isDriverPort() || StringUtils.isBlank(signatureImage)) { + return; + } + if (StringUtils.isBlank(userName)) { + throw new ServiceException("签署人姓名不能为空"); + } + String first = signatureImage.split(",")[0].trim(); + if (!first.matches("^\\d+$")) { + throw new ServiceException("签名文件格式错误"); + } + signatureVerifyService.validateSelfSignature(Long.parseLong(first), userName); + } + + /** + * 查询违规信息 + * + * @param id 主键 + * @return 违规信息 + */ + @Override + public HotViolationInfoVo queryById(Long id) { + HotViolationInfoVo vo = baseMapper.selectVoById(id); + if (vo != null) { + // 填充待办taskId,方便前端详情页直接办理 + // 这里查询该违规记录对应驾驶员的待办 + String driverId = vo.getDriverId(); + if (StringUtils.isNotBlank(driverId)) { + // 只有未完成的才可能有待办 + if (vo.getProcessStatus() != null) { + String taskId = flowService.getTaskIdByBusinessId(FLOW_CODE_VIOLATION_HANDLE, String.valueOf(vo.getId()), driverId); + vo.setTaskId(taskId); + } + } + } + return vo; + } + + /** + * 分页查询违规信息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 违规信息分页列表 + */ + @Override + public TableDataInfo queryPageList(HotViolationInfoBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + + // 填充taskId + fillTaskId(result.getRecords(), null); + + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的违规信息列表 + * + * @param bo 查询条件 + * @return 违规信息列表 + */ + @Override + public List queryList(HotViolationInfoBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + @Override + public TableDataInfo queryDriverList(HotViolationInfoBo bo, PageQuery pageQuery) { + // 强制设置 driverId 为当前登录人,防止查询他人数据 + LoginUser loginUser = LoginHelper.getLoginUser(); + String driverId; + if (loginUser != null) { + driverId = String.valueOf(loginUser.getBusinessUserId()); + bo.setDriverId(driverId); + } else { + throw new ServiceException("未登录或无驾驶员身份"); + } + Page result = baseMapper.queryDriverViolationList(pageQuery.build(), bo); + + // 填充taskId,只查询当前驾驶员的待办 + fillTaskId(result.getRecords(), driverId); + + return TableDataInfo.build(result); + } + + /** + * 填充待办任务ID + * + * @param list 违规列表 + * @param approverId 审批人ID(如果为null则不限制审批人,通常用于管理员查看) + */ + private void fillTaskId(List list, String approverId) { + if (list == null || list.isEmpty()) { + return; + } + + LoginUser loginUser = LoginHelper.getLoginUser(); + String currentUserId = (approverId != null) ? approverId : (loginUser != null ? String.valueOf(loginUser.getBusinessUserId()) : null); + + if (currentUserId == null) { + return; + } + + for (HotViolationInfoVo vo : list) { + // 只有未完成的才可能有待办 + if (vo.getProcessStatus() != null) { + String taskId = flowService.getTaskIdByBusinessId(FLOW_CODE_VIOLATION_HANDLE, String.valueOf(vo.getId()), currentUserId); + vo.setTaskId(taskId); + } + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean auditAndComplete(String taskId, Long processStatus, String disposalResult, String interviewedSignatureImage, String partySignatureImage) { + SysFlowTask task = flowService.getTaskById(taskId); + if (task == null) { + throw new ServiceException("任务不存在"); + } + SysFlowInstance instance = flowService.getInstanceByTaskId(taskId); + if (instance == null) { + throw new ServiceException("流程实例不存在"); + } + Long violationId; + try { + violationId = Long.valueOf(instance.getBusinessId()); + } catch (NumberFormatException e) { + throw new ServiceException("业务数据异常"); + } + HotViolationInfo info = baseMapper.selectById(violationId); + if (info == null) { + throw new ServiceException("违规信息不存在"); + } + + validateAuditForNode(task, info, interviewedSignatureImage, partySignatureImage); + + if (StringUtils.isNotBlank(disposalResult)) { + info.setDisposalResult(disposalResult); + } + if (StringUtils.isNotBlank(interviewedSignatureImage)) { + info.setInterviewedSignatureImage(interviewedSignatureImage); + } + if (StringUtils.isNotBlank(partySignatureImage)) { + info.setPartySignatureImage(partySignatureImage); + } + baseMapper.updateById(info); + + LoginUser loginUser = LoginHelper.getLoginUser(); + String approverId = task.getApproverId(); + if (loginUser == null || !StringUtils.equals(loginUser.getBusinessUserId(), approverId)) { + throw new ServiceException("您不是当前节点的审批人"); + } + flowService.audit(taskId, true, "违规处理完成", approverId); + autoAdvanceViolationFlow(violationId); + + return true; + } + + private LambdaQueryWrapper buildQueryWrapper(HotViolationInfoBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HotViolationInfo::getOccurTime); + if (bo.getOccurTime() != null) { + java.util.Calendar calendarStart = java.util.Calendar.getInstance(); + calendarStart.setTime(bo.getOccurTime()); + calendarStart.set(java.util.Calendar.HOUR_OF_DAY, 0); + calendarStart.set(java.util.Calendar.MINUTE, 0); + calendarStart.set(java.util.Calendar.SECOND, 0); + calendarStart.set(java.util.Calendar.MILLISECOND, 0); + java.util.Calendar calendarEnd = (java.util.Calendar) calendarStart.clone(); + calendarEnd.add(java.util.Calendar.DAY_OF_MONTH, 1); + lqw.ge(HotViolationInfo::getOccurTime, calendarStart.getTime()); + lqw.lt(HotViolationInfo::getOccurTime, calendarEnd.getTime()); + } + lqw.eq(bo.getCompanyId() != null, HotViolationInfo::getCompanyId, bo.getCompanyId()); + lqw.eq(bo.getVehicleId() != null, HotViolationInfo::getVehicleId, bo.getVehicleId()); + lqw.eq(StringUtils.isNotBlank(bo.getPlateNumber()), HotViolationInfo::getPlateNumber, bo.getPlateNumber()); + lqw.eq(bo.getDriverId() != null, HotViolationInfo::getDriverId, bo.getDriverId()); + lqw.like(StringUtils.isNotBlank(bo.getDriverName()), HotViolationInfo::getDriverName, bo.getDriverName()); + lqw.eq(bo.getViolationType() != null, HotViolationInfo::getViolationType, bo.getViolationType()); + lqw.eq(bo.getBillingDepartment() != null, HotViolationInfo::getBillingDepartment, bo.getBillingDepartment()); + lqw.eq(bo.getIsDrunkDriving() != null, HotViolationInfo::getIsDrunkDriving, bo.getIsDrunkDriving()); + lqw.eq(StringUtils.isNotBlank(bo.getViolationNo()), HotViolationInfo::getViolationNo, bo.getViolationNo()); + lqw.eq(bo.getFineAmount() != null, HotViolationInfo::getFineAmount, bo.getFineAmount()); + lqw.eq(bo.getDeductPoints() != null, HotViolationInfo::getDeductPoints, bo.getDeductPoints()); + lqw.eq(bo.getDeductionType() != null, HotViolationInfo::getDeductionType, bo.getDeductionType()); + lqw.eq(bo.getRecorderId() != null, HotViolationInfo::getRecorderId, bo.getRecorderId()); + lqw.like(StringUtils.isNotBlank(bo.getRecorderName()), HotViolationInfo::getRecorderName, bo.getRecorderName()); + lqw.eq(bo.getNeedInterview() != null, HotViolationInfo::getNeedInterview, bo.getNeedInterview()); + lqw.eq(StringUtils.isNotBlank(bo.getViolationPlace()), HotViolationInfo::getViolationPlace, bo.getViolationPlace()); + lqw.eq(StringUtils.isNotBlank(bo.getViolationContent()), HotViolationInfo::getViolationContent, bo.getViolationContent()); + lqw.eq(StringUtils.isNotBlank(bo.getDisposalResult()), HotViolationInfo::getDisposalResult, bo.getDisposalResult()); + lqw.eq(StringUtils.isNotBlank(bo.getAttachmentUrls()), HotViolationInfo::getAttachmentUrls, bo.getAttachmentUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getResponsibleSignatureImage()), HotViolationInfo::getResponsibleSignatureImage, bo.getResponsibleSignatureImage()); + lqw.eq(StringUtils.isNotBlank(bo.getPartySignatureImage()), HotViolationInfo::getPartySignatureImage, bo.getPartySignatureImage()); + lqw.eq(bo.getInterviewTime() != null, HotViolationInfo::getInterviewTime, bo.getInterviewTime()); + lqw.eq(StringUtils.isNotBlank(bo.getInterviewPlace()), HotViolationInfo::getInterviewPlace, bo.getInterviewPlace()); + lqw.eq(bo.getInterviewRecorderId() != null, HotViolationInfo::getInterviewRecorderId, bo.getInterviewRecorderId()); + lqw.like(StringUtils.isNotBlank(bo.getInterviewRecorderName()), HotViolationInfo::getInterviewRecorderName, bo.getInterviewRecorderName()); + lqw.eq(bo.getInterviewPersonId() != null, HotViolationInfo::getInterviewPersonId, bo.getInterviewPersonId()); + lqw.like(StringUtils.isNotBlank(bo.getInterviewPersonName()), HotViolationInfo::getInterviewPersonName, bo.getInterviewPersonName()); + lqw.eq(StringUtils.isNotBlank(bo.getInterviewReason()), HotViolationInfo::getInterviewReason, bo.getInterviewReason()); + lqw.eq(StringUtils.isNotBlank(bo.getInterviewContent()), HotViolationInfo::getInterviewContent, bo.getInterviewContent()); + lqw.eq(StringUtils.isNotBlank(bo.getPromiseUrls()), HotViolationInfo::getPromiseUrls, bo.getPromiseUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getInterviewPhotoUrls()), HotViolationInfo::getInterviewPhotoUrls, bo.getInterviewPhotoUrls()); + lqw.eq(StringUtils.isNotBlank(bo.getInterviewerSignatureImage()), HotViolationInfo::getInterviewerSignatureImage, bo.getInterviewerSignatureImage()); + lqw.eq(StringUtils.isNotBlank(bo.getInterviewedSignatureImage()), HotViolationInfo::getInterviewedSignatureImage, bo.getInterviewedSignatureImage()); + lqw.eq(bo.getProcessStatus() != null, HotViolationInfo::getProcessStatus, bo.getProcessStatus()); + lqw.eq(bo.getIsDeleted() != null, HotViolationInfo::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增违规信息 + * + * @param bo 违规信息 + * @return 是否新增成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(HotViolationInfoBo bo) { + HotViolationInfo add = MapstructUtils.convert(bo, HotViolationInfo.class); + if (add.getProcessStatus() == null) { + add.setProcessStatus(PROCESS_STATUS_TODO); + } + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + reconcileViolationFlow(add.getId()); + if (StringUtils.isNotBlank(add.getDriverId())) { + sendViolationNotification(add.getDriverId(), "您的违规信息已记录,请及时处理。", add.getId()); + } + } + return flag; + } + + /** + * 修改违规信息 + * + * @param bo 违规信息 + * @return 是否修改成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(HotViolationInfoBo bo) { + HotViolationInfo before = null; + if (bo.getId() != null) { + before = baseMapper.selectById(bo.getId()); + } + HotViolationInfo update = MapstructUtils.convert(bo, HotViolationInfo.class); + validEntityBeforeSave(update); + boolean ok = baseMapper.updateById(update) > 0; + if (ok) { + reconcileViolationFlow(update.getId()); + String driverId = update.getDriverId(); + if (StringUtils.isBlank(driverId) && before != null) { + driverId = before.getDriverId(); + } + if (StringUtils.isNotBlank(driverId) && update.getId() != null) { + sendViolationNotification(driverId, "您的违规信息已更新,请及时处理。", update.getId()); + } + } + return ok; + } + + private boolean hasViolationDoneHistory(String driverId, String businessId) { + if (StringUtils.isBlank(driverId) || StringUtils.isBlank(businessId)) { + return false; + } + List instances = instanceMapper.selectList( + Wrappers.lambdaQuery() + .eq(SysFlowInstance::getFlowCode, FLOW_CODE_VIOLATION_HANDLE) + .eq(SysFlowInstance::getBusinessId, businessId) + ); + if (instances == null || instances.isEmpty()) { + return false; + } + List instanceIds = instances.stream().map(SysFlowInstance::getInstanceId).toList(); + return taskHisMapper.selectCount( + Wrappers.lambdaQuery() + .in(SysFlowTaskHis::getInstanceId, instanceIds) + .eq(SysFlowTaskHis::getApproverId, driverId) + ) > 0; + } + + /** + * 发送违规系统通知 + */ + private void sendViolationNotification(String driverId, String content, Long violationId) { + if (StringUtils.isBlank(driverId)) { + return; + } + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent(content); + bos.setSourceType("违规信息"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("驾驶员"); + bos.setReceiverIds(java.util.Collections.singletonList(driverId)); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送违规信息通知失败 driverId={} violationId={}", driverId, violationId, e); + } + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotViolationInfo entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除违规信息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInfo/strategy/ViolationHandleStrategy.java b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/strategy/ViolationHandleStrategy.java new file mode 100644 index 0000000..0d4535a --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInfo/strategy/ViolationHandleStrategy.java @@ -0,0 +1,116 @@ +package com.hotwj.platform.securityManagement.violationInfo.strategy; + +import com.hotwj.platform.flow.domain.SysFlowInstance; +import com.hotwj.platform.flow.domain.dto.FlowNextDto; +import com.hotwj.platform.flow.strategy.IFlowStrategy; +import com.hotwj.platform.securityManagement.violationInfo.domain.HotViolationInfo; +import com.hotwj.platform.securityManagement.violationInfo.mapper.HotViolationInfoMapper; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.service.DictService; +import org.dromara.common.core.utils.StringUtils; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class ViolationHandleStrategy implements IFlowStrategy { + + private static final String FLOW_CODE = "VIOLATION_HANDLE"; + private static final String NODE_PARTY_SIGN = "PARTY_SIGN_NODE"; + private static final String NODE_WAIT_INTERVIEW_INFO = "WAIT_INTERVIEW_INFO_NODE"; + private static final String NODE_INTERVIEWED_SIGN = "INTERVIEW_SIGN_NODE"; + private static final String NODE_AUTO_DONE = "AUTO_DONE_NODE"; + private static final String SYSTEM_USER = "SYSTEM"; + + private final HotViolationInfoMapper violationInfoMapper; + private final DictService dictService; + + @Override + public String getFlowCode() { + return FLOW_CODE; + } + + @Override + public String getFlowName() { + return "违规处理"; + } + + @Override + public FlowNextDto next(SysFlowInstance instance, String currentNodeCode) { + if (instance == null || StringUtils.isBlank(instance.getBusinessId())) { + return null; + } + HotViolationInfo info = violationInfoMapper.selectById(instance.getBusinessId()); + if (info == null) { + return null; + } + boolean needInterview = Long.valueOf(1L).equals(info.getNeedInterview()); + + if (NODE_PARTY_SIGN.equals(currentNodeCode)) { + if (!needInterview) { + return null; + } + + if (!isInterviewInfoComplete(info)) { + return new FlowNextDto(NODE_WAIT_INTERVIEW_INFO, buildNodeName(info, "等待约谈信息"), SYSTEM_USER); + } + + if (StringUtils.isBlank(info.getInterviewedSignatureImage())) { + return new FlowNextDto(NODE_INTERVIEWED_SIGN, buildNodeName(info, "被约谈人签名"), info.getDriverId()); + } + + return new FlowNextDto(NODE_AUTO_DONE, buildNodeName(info, "已处理"), SYSTEM_USER); + } + + if (NODE_WAIT_INTERVIEW_INFO.equals(currentNodeCode)) { + if (isInterviewInfoComplete(info) && StringUtils.isBlank(info.getInterviewedSignatureImage())) { + return new FlowNextDto(NODE_INTERVIEWED_SIGN, buildNodeName(info, "被约谈人签名"), info.getDriverId()); + } + return new FlowNextDto(NODE_AUTO_DONE, buildNodeName(info, "已处理"), info.getDriverId()); + } + + if (NODE_INTERVIEWED_SIGN.equals(currentNodeCode)) { + return null; + } + + if (NODE_AUTO_DONE.equals(currentNodeCode)) { + return null; + } + + return new FlowNextDto(NODE_AUTO_DONE, buildNodeName(info, "已处理"), info.getDriverId()); + } + + @Override + public FlowNextDto getInitialNode(String businessId) { + HotViolationInfo info = null; + if (StringUtils.isNotBlank(businessId)) { + info = violationInfoMapper.selectById(businessId); + } + if (info == null) { + return new FlowNextDto(NODE_PARTY_SIGN, "违规通知:车辆 违规,请及时处理。(当事人签名)", null); + } + + return new FlowNextDto(NODE_PARTY_SIGN, buildNodeName(info, "当事人签名"), info.getDriverId()); + } + + private boolean isInterviewInfoComplete(HotViolationInfo info) { + return info != null + && info.getInterviewTime() != null + && StringUtils.isNotBlank(info.getInterviewPlace()) + && StringUtils.isNotBlank(info.getInterviewReason()) + && StringUtils.isNotBlank(info.getInterviewContent()) + && StringUtils.isNotBlank(info.getInterviewerSignatureImage()); + } + + private String buildNodeName(HotViolationInfo info, String statusText) { + String plate = info != null && StringUtils.isNotBlank(info.getPlateNumber()) ? info.getPlateNumber() : "车辆"; + String typeLabel = "违规"; + if (info != null && info.getViolationType() != null) { + String label = dictService.getDictLabel("hot_violation_type", String.valueOf(info.getViolationType())); + if (StringUtils.isNotBlank(label)) { + typeLabel = label; + } + } + String status = StringUtils.isNotBlank(statusText) ? statusText : "待处理"; + return "违规通知:" + plate + " " + typeLabel + ",请及时处理。(" + status + ")"; + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInterview/controller/HotViolationInterviewController.java b/src/main/java/com/hotwj/platform/securityManagement/violationInterview/controller/HotViolationInterviewController.java new file mode 100644 index 0000000..87c5d23 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInterview/controller/HotViolationInterviewController.java @@ -0,0 +1,115 @@ +package com.hotwj.platform.securityManagement.violationInterview.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.hotwj.platform.securityManagement.violationInterview.domain.bo.HotViolationInterviewBo; +import com.hotwj.platform.securityManagement.violationInterview.domain.vo.HotViolationInterviewVo; +import com.hotwj.platform.securityManagement.violationInterview.service.IHotViolationInterviewService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 违规约谈记录 + * + * @author shihongwei + * @date 2025-12-17 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/securityManagement/violationInterview") +@Tag(name = "违规约谈记录", description = "违规约谈记录管理") +public class HotViolationInterviewController extends BaseController { + + private final IHotViolationInterviewService hotViolationInterviewService; + + /** + * 查询违规约谈记录列表 + */ + //@SaCheckPermission("securityManagement:violationInterview:list") + @GetMapping("/list") + @Operation(summary = "查询违规约谈记录列表") + public TableDataInfo list(HotViolationInterviewBo bo, PageQuery pageQuery) { + return hotViolationInterviewService.queryPageList(bo, pageQuery); + } + + /** + * 导出违规约谈记录列表 + */ + //@SaCheckPermission("securityManagement:violationInterview:export") + @Log(title = "违规约谈记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出违规约谈记录列表") + public void export(HotViolationInterviewBo bo, HttpServletResponse response) { + List list = hotViolationInterviewService.queryList(bo); + ExcelUtil.exportExcel(list, "违规约谈记录", HotViolationInterviewVo.class, response); + } + + /** + * 获取违规约谈记录详细信息 + * + * @param id 主键 + */ + //@SaCheckPermission("securityManagement:violationInterview:query") + @GetMapping("/{id}") + @Operation(summary = "获取违规约谈记录详细信息") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(hotViolationInterviewService.queryById(id)); + } + + /** + * 新增违规约谈记录 + */ + //@SaCheckPermission("securityManagement:violationInterview:add") + @Log(title = "违规约谈记录", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增违规约谈记录") + public R add(@Validated(AddGroup.class) @RequestBody HotViolationInterviewBo bo) { + return toAjax(hotViolationInterviewService.insertByBo(bo)); + } + + /** + * 修改违规约谈记录 + */ + //@SaCheckPermission("securityManagement:violationInterview:edit") + @Log(title = "违规约谈记录", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改违规约谈记录") + public R edit(@Validated(EditGroup.class) @RequestBody HotViolationInterviewBo bo) { + return toAjax(hotViolationInterviewService.updateByBo(bo)); + } + + /** + * 删除违规约谈记录 + * + * @param ids 主键串 + */ + //@SaCheckPermission("securityManagement:violationInterview:remove") + @Log(title = "违规约谈记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + @Operation(summary = "删除违规约谈记录") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(hotViolationInterviewService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInterview/domain/HotViolationInterview.java b/src/main/java/com/hotwj/platform/securityManagement/violationInterview/domain/HotViolationInterview.java new file mode 100644 index 0000000..2ab39a5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInterview/domain/HotViolationInterview.java @@ -0,0 +1,105 @@ +package com.hotwj.platform.securityManagement.violationInterview.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 违规约谈记录对象 hot_violation_interview + * + * @author shihongwei + * @date 2025-12-17 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_violation_interview") +public class HotViolationInterview extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 违规记录ID + */ + private Long violationId; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 被约谈人 + */ + private String interviewee; + + /** + * 约谈时间 + */ + private Date interviewTime; + + /** + * 约谈地点 + */ + private String interviewLocation; + + /** + * 记录人 + */ + private String recorder; + + /** + * 约谈人 + */ + private String interviewer; + + /** + * 约谈原因 + */ + private String interviewReason; + + /** + * 谈话内容 + */ + private String conversationContent; + + /** + * 承诺书(图片/PDF) + */ + private String commitmentLetter; + + /** + * 约谈照片(图片/PDF) + */ + private String interviewPhotos; + + /** + * 约谈人签名 + */ + private String interviewerSignature; + + /** + * 被约谈人签名 + */ + private String intervieweeSignature; + + /** + * 0=正常, 1=已删除 + */ + @TableLogic + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInterview/domain/bo/HotViolationInterviewBo.java b/src/main/java/com/hotwj/platform/securityManagement/violationInterview/domain/bo/HotViolationInterviewBo.java new file mode 100644 index 0000000..85aedd5 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInterview/domain/bo/HotViolationInterviewBo.java @@ -0,0 +1,103 @@ +package com.hotwj.platform.securityManagement.violationInterview.domain.bo; + +import com.hotwj.platform.securityManagement.violationInterview.domain.HotViolationInterview; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 违规约谈记录业务对象 hot_violation_interview + * + * @author shihongwei + * @date 2025-12-17 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HotViolationInterview.class, reverseConvertGenerate = false) +public class HotViolationInterviewBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 违规记录ID + */ + @NotNull(message = "违规记录ID不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long violationId; + + /** + * 公司ID + */ + private Long companyId; + + /** + * 被约谈人 + */ + private String interviewee; + + /** + * 约谈时间 + */ + private Date interviewTime; + + /** + * 约谈地点 + */ + private String interviewLocation; + + /** + * 记录人 + */ + private String recorder; + + /** + * 约谈人 + */ + private String interviewer; + + /** + * 约谈原因 + */ + private String interviewReason; + + /** + * 谈话内容 + */ + private String conversationContent; + + /** + * 承诺书(图片/PDF) + */ + private String commitmentLetter; + + /** + * 约谈照片(图片/PDF) + */ + private String interviewPhotos; + + /** + * 约谈人签名 + */ + private String interviewerSignature; + + /** + * 被约谈人签名 + */ + private String intervieweeSignature; + + /** + * 0=正常, 1=已删除 + */ + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInterview/domain/vo/HotViolationInterviewVo.java b/src/main/java/com/hotwj/platform/securityManagement/violationInterview/domain/vo/HotViolationInterviewVo.java new file mode 100644 index 0000000..3b4a466 --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInterview/domain/vo/HotViolationInterviewVo.java @@ -0,0 +1,123 @@ +package com.hotwj.platform.securityManagement.violationInterview.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import com.hotwj.platform.securityManagement.violationInterview.domain.HotViolationInterview; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 违规约谈记录视图对象 hot_violation_interview + * + * @author shihongwei + * @date 2025-12-17 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HotViolationInterview.class) +public class HotViolationInterviewVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 违规记录ID + */ + @ExcelProperty(value = "违规记录ID") + private Long violationId; + + /** + * 公司ID + */ + @ExcelProperty(value = "公司ID") + private Long companyId; + + /** + * 被约谈人 + */ + @ExcelProperty(value = "被约谈人") + private String interviewee; + + /** + * 约谈时间 + */ + @ExcelProperty(value = "约谈时间") + private Date interviewTime; + + /** + * 约谈地点 + */ + @ExcelProperty(value = "约谈地点") + private String interviewLocation; + + /** + * 记录人 + */ + @ExcelProperty(value = "记录人") + private String recorder; + + /** + * 约谈人 + */ + @ExcelProperty(value = "约谈人") + private String interviewer; + + /** + * 约谈原因 + */ + @ExcelProperty(value = "约谈原因") + private String interviewReason; + + /** + * 谈话内容 + */ + @ExcelProperty(value = "谈话内容") + private String conversationContent; + + /** + * 承诺书(图片/PDF) + */ + @ExcelProperty(value = "承诺书", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "图=片/PDF") + private String commitmentLetter; + + /** + * 约谈照片(图片/PDF) + */ + @ExcelProperty(value = "约谈照片", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "图=片/PDF") + private String interviewPhotos; + + /** + * 约谈人签名 + */ + @ExcelProperty(value = "约谈人签名") + private String interviewerSignature; + + /** + * 被约谈人签名 + */ + @ExcelProperty(value = "被约谈人签名") + private String intervieweeSignature; + + /** + * 0=正常, 1=已删除 + */ + @ExcelProperty(value = "0=正常, 1=已删除") + private Long isDeleted; + + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInterview/mapper/HotViolationInterviewMapper.java b/src/main/java/com/hotwj/platform/securityManagement/violationInterview/mapper/HotViolationInterviewMapper.java new file mode 100644 index 0000000..498412f --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInterview/mapper/HotViolationInterviewMapper.java @@ -0,0 +1,17 @@ +package com.hotwj.platform.securityManagement.violationInterview.mapper; + +import com.hotwj.platform.securityManagement.violationInterview.domain.HotViolationInterview; +import com.hotwj.platform.securityManagement.violationInterview.domain.vo.HotViolationInterviewVo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 违规约谈记录Mapper接口 + * + * @author shihongwei + * @date 2025-12-17 + */ +@Mapper +public interface HotViolationInterviewMapper extends BaseMapperPlus { + +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInterview/service/IHotViolationInterviewService.java b/src/main/java/com/hotwj/platform/securityManagement/violationInterview/service/IHotViolationInterviewService.java new file mode 100644 index 0000000..02309bf --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInterview/service/IHotViolationInterviewService.java @@ -0,0 +1,68 @@ +package com.hotwj.platform.securityManagement.violationInterview.service; + +import com.hotwj.platform.securityManagement.violationInterview.domain.bo.HotViolationInterviewBo; +import com.hotwj.platform.securityManagement.violationInterview.domain.vo.HotViolationInterviewVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.Collection; +import java.util.List; + +/** + * 违规约谈记录Service接口 + * + * @author shihongwei + * @date 2025-12-17 + */ +public interface IHotViolationInterviewService { + + /** + * 查询违规约谈记录 + * + * @param id 主键 + * @return 违规约谈记录 + */ + HotViolationInterviewVo queryById(Long id); + + /** + * 分页查询违规约谈记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 违规约谈记录分页列表 + */ + TableDataInfo queryPageList(HotViolationInterviewBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的违规约谈记录列表 + * + * @param bo 查询条件 + * @return 违规约谈记录列表 + */ + List queryList(HotViolationInterviewBo bo); + + /** + * 新增违规约谈记录 + * + * @param bo 违规约谈记录 + * @return 是否新增成功 + */ + Boolean insertByBo(HotViolationInterviewBo bo); + + /** + * 修改违规约谈记录 + * + * @param bo 违规约谈记录 + * @return 是否修改成功 + */ + Boolean updateByBo(HotViolationInterviewBo bo); + + /** + * 校验并批量删除违规约谈记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/src/main/java/com/hotwj/platform/securityManagement/violationInterview/service/impl/HotViolationInterviewServiceImpl.java b/src/main/java/com/hotwj/platform/securityManagement/violationInterview/service/impl/HotViolationInterviewServiceImpl.java new file mode 100644 index 0000000..978527c --- /dev/null +++ b/src/main/java/com/hotwj/platform/securityManagement/violationInterview/service/impl/HotViolationInterviewServiceImpl.java @@ -0,0 +1,197 @@ +package com.hotwj.platform.securityManagement.violationInterview.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.noticeManagerment.systemNotification.domain.bo.HotSystemNotificationGroupBo; +import com.hotwj.platform.noticeManagerment.systemNotification.service.IHotSystemNotificationService; +import com.hotwj.platform.securityManagement.violationInfo.domain.HotViolationInfo; +import com.hotwj.platform.securityManagement.violationInfo.mapper.HotViolationInfoMapper; +import com.hotwj.platform.securityManagement.violationInterview.domain.HotViolationInterview; +import com.hotwj.platform.securityManagement.violationInterview.domain.bo.HotViolationInterviewBo; +import com.hotwj.platform.securityManagement.violationInterview.domain.vo.HotViolationInterviewVo; +import com.hotwj.platform.securityManagement.violationInterview.mapper.HotViolationInterviewMapper; +import com.hotwj.platform.securityManagement.violationInterview.service.IHotViolationInterviewService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 违规约谈记录Service业务层处理 + * + * @author shihongwei + * @date 2025-12-17 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotViolationInterviewServiceImpl implements IHotViolationInterviewService { + + private final HotViolationInterviewMapper baseMapper; + private final HotViolationInfoMapper violationInfoMapper; + private final IHotSystemNotificationService notificationService; + + /** + * 查询违规约谈记录 + * + * @param id 主键 + * @return 违规约谈记录 + */ + @Override + public HotViolationInterviewVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 分页查询违规约谈记录列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 违规约谈记录分页列表 + */ + @Override + public TableDataInfo queryPageList(HotViolationInterviewBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的违规约谈记录列表 + * + * @param bo 查询条件 + * @return 违规约谈记录列表 + */ + @Override + public List queryList(HotViolationInterviewBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HotViolationInterviewBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByAsc(HotViolationInterview::getId); + lqw.eq(bo.getViolationId() != null, HotViolationInterview::getViolationId, bo.getViolationId()); + lqw.eq(bo.getCompanyId() != null, HotViolationInterview::getCompanyId, bo.getCompanyId()); + lqw.eq(StringUtils.isNotBlank(bo.getInterviewee()), HotViolationInterview::getInterviewee, bo.getInterviewee()); + lqw.eq(bo.getInterviewTime() != null, HotViolationInterview::getInterviewTime, bo.getInterviewTime()); + lqw.eq(StringUtils.isNotBlank(bo.getInterviewLocation()), HotViolationInterview::getInterviewLocation, bo.getInterviewLocation()); + lqw.eq(StringUtils.isNotBlank(bo.getRecorder()), HotViolationInterview::getRecorder, bo.getRecorder()); + lqw.eq(StringUtils.isNotBlank(bo.getInterviewer()), HotViolationInterview::getInterviewer, bo.getInterviewer()); + lqw.eq(StringUtils.isNotBlank(bo.getInterviewReason()), HotViolationInterview::getInterviewReason, bo.getInterviewReason()); + lqw.eq(StringUtils.isNotBlank(bo.getConversationContent()), HotViolationInterview::getConversationContent, bo.getConversationContent()); + lqw.eq(StringUtils.isNotBlank(bo.getCommitmentLetter()), HotViolationInterview::getCommitmentLetter, bo.getCommitmentLetter()); + lqw.eq(StringUtils.isNotBlank(bo.getInterviewPhotos()), HotViolationInterview::getInterviewPhotos, bo.getInterviewPhotos()); + lqw.eq(StringUtils.isNotBlank(bo.getInterviewerSignature()), HotViolationInterview::getInterviewerSignature, bo.getInterviewerSignature()); + lqw.eq(StringUtils.isNotBlank(bo.getIntervieweeSignature()), HotViolationInterview::getIntervieweeSignature, bo.getIntervieweeSignature()); + lqw.eq(bo.getIsDeleted() != null, HotViolationInterview::getIsDeleted, bo.getIsDeleted()); + return lqw; + } + + /** + * 新增违规约谈记录 + * + * @param bo 违规约谈记录 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HotViolationInterviewBo bo) { + HotViolationInterview add = MapstructUtils.convert(bo, HotViolationInterview.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + if (add.getViolationId() != null) { + HotViolationInfo violation = violationInfoMapper.selectById(add.getViolationId()); + String driverId = violation != null ? violation.getDriverId() : null; + if (StringUtils.isNotBlank(driverId)) { + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent("您有一条约谈记录,请及时查看。"); + bos.setSourceType("约谈"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("驾驶员"); + bos.setReceiverIds(java.util.Collections.singletonList(driverId)); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送约谈新增通知失败 violationId={} driverId={}", add.getViolationId(), driverId, e); + } + } + } + } + return flag; + } + + /** + * 修改违规约谈记录 + * + * @param bo 违规约谈记录 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HotViolationInterviewBo bo) { + HotViolationInterview before = null; + if (bo.getId() != null) { + before = baseMapper.selectById(bo.getId()); + } + HotViolationInterview update = MapstructUtils.convert(bo, HotViolationInterview.class); + validEntityBeforeSave(update); + boolean ok = baseMapper.updateById(update) > 0; + if (ok) { + Long violationId = update.getViolationId() != null ? update.getViolationId() : (before != null ? before.getViolationId() : null); + if (violationId != null) { + HotViolationInfo violation = violationInfoMapper.selectById(violationId); + String driverId = violation != null ? violation.getDriverId() : null; + if (org.dromara.common.core.utils.StringUtils.isNotBlank(driverId)) { + HotSystemNotificationGroupBo bos = new HotSystemNotificationGroupBo(); + bos.setLevel("普通"); + bos.setContent("您的约谈记录已更新,请及时查看。"); + bos.setSourceType("约谈"); + bos.setSenderType("SYSTEM"); + bos.setReceiverType("驾驶员"); + bos.setReceiverIds(java.util.Collections.singletonList(driverId)); + bos.setIsDeleted(0L); + try { + notificationService.insertByBo(bos); + } catch (Exception e) { + log.warn("发送约谈修改通知失败 violationId={} driverId={}", violationId, driverId, e); + } + } + } + } + return ok; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HotViolationInterview entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除违规约谈记录信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/controller/DailyStatsManualController.java b/src/main/java/com/hotwj/platform/workbench/controller/DailyStatsManualController.java new file mode 100644 index 0000000..e41b5b4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/controller/DailyStatsManualController.java @@ -0,0 +1,32 @@ +package com.hotwj.platform.workbench.controller; + +import cn.dev33.satoken.annotation.SaIgnore; +import com.hotwj.platform.workbench.service.IHotHiddenDangerDailyStatsService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workbench/task") +@Tag(name = "日统计任务", description = "手动触发日统计任务") +public class DailyStatsManualController extends BaseController { + + private final IHotHiddenDangerDailyStatsService dailyStatsService; + + @SaIgnore + @PostMapping("/dailyStats/run") + @Operation(summary = "手动触发日统计任务(隐患、培训)") + public R runDailyStats() { + dailyStatsService.generateDailyStats(); + return R.ok(); + } +} + diff --git a/src/main/java/com/hotwj/platform/workbench/controller/GovWorkbenchController.java b/src/main/java/com/hotwj/platform/workbench/controller/GovWorkbenchController.java new file mode 100644 index 0000000..4690451 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/controller/GovWorkbenchController.java @@ -0,0 +1,152 @@ +package com.hotwj.platform.workbench.controller; + +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import com.hotwj.platform.workbench.domain.vo.*; +import com.hotwj.platform.workbench.service.IGovWorkbenchService; +import com.hotwj.platform.workbench.service.IHotHiddenDangerDailyStatsService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 政府端首页 + * + * @author shihongwei + * @date 2026-02-25 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workbench/gov") +@Tag(name = "政府端首页", description = "政府端首页统计模块") +public class GovWorkbenchController extends BaseController { + + private final IGovWorkbenchService govWorkbenchService; + private final IHotHiddenDangerDailyStatsService dailyStatsService; + + /** + * 获取政府端首页统计 + */ + @GetMapping("/statistics") + @Operation(summary = "获取政府端首页统计") + public R getHomeStatistics() { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return R.fail("非政府端用户无权访问"); + } + return R.ok(govWorkbenchService.getHomeStatistics()); + } + + /** + * 获取隐患统计总览 + */ + @GetMapping("/statistics/hidden-danger/summary") + @Operation(summary = "获取隐患统计总览") + public R getHiddenDangerSummary( + @RequestParam(required = false) Long companyId, + @RequestParam(required = false) String regionCode) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return R.fail("非政府端用户无权访问"); + } + if (companyId != null) { + return R.ok(govWorkbenchService.getCompanyHiddenDangerSummary(companyId)); + } + return R.ok(govWorkbenchService.getHiddenDangerSummary(regionCode)); + } + + /** + * 获取隐患明细列表 + */ + @GetMapping("/statistics/hidden-danger/list") + @Operation(summary = "获取隐患明细列表") + public TableDataInfo getHiddenDangerList( + @RequestParam("type") String type, + @RequestParam(required = false) Long companyId, + PageQuery pageQuery) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return TableDataInfo.build(); + } + return govWorkbenchService.getHiddenDangerList(type, companyId, pageQuery); + } + + @GetMapping("/statistics/hidden-danger/count-by-month") + @Operation(summary = "获取隐患按日统计") + public R> getHiddenDangerCountByMonth( + @RequestParam(required = false) Long companyId, + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return R.fail("非政府端用户无权访问"); + } + return R.ok(govWorkbenchService.getHiddenDangerCountByMonth(companyId, startDate, endDate)); + } + + @GetMapping("/statistics/company/top") + @Operation(summary = "获取隐患总数最高的企业TOP N (实时计算)") + public R> getTopRiskCompanies( + @RequestParam(defaultValue = "10") int limit) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return R.fail("非政府端用户无权访问"); + } + return R.ok(govWorkbenchService.getTopRiskCompanies(limit)); + } + + @GetMapping("/statistics/company/list") + @Operation(summary = "分页获取企业隐患统计列表 (实时计算)") + public GovCompanyRiskTableDataInfo getCompanyRiskList( + @RequestParam(required = false) String companyName, + PageQuery pageQuery) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return new GovCompanyRiskTableDataInfo(); + } + return govWorkbenchService.getCompanyRiskList(companyName, pageQuery); + } + + @GetMapping("/statistics/training/stats") + @Operation(summary = "获取学习折线图数据") + public R> getTrainingStats( + @RequestParam(required = false) String type, + @RequestParam(required = false) String beginTime, + @RequestParam(required = false) String endTime) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return R.fail("非政府端用户无权访问"); + } + return R.ok(govWorkbenchService.getTrainingStats(type, beginTime, endTime)); + } + + @GetMapping("/statistics/accident/level-stats") + @Operation(summary = "获取事故等级统计") + public R> getAccidentLevelStats() { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null || !ISysUserLoginPortService.GOVERNMENT_PORT.equals(loginUser.getLoginPort())) { + return R.fail("非政府端用户无权访问"); + } + return R.ok(govWorkbenchService.getAccidentLevelStats()); + } + + @GetMapping("/daily-stats/trigger") + @Operation(summary = "手动触发隐患日统计(内部使用)") + public R triggerDailyStats() { + dailyStatsService.generateDailyStats(); + return R.ok(); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/controller/HeadquartersWorkbenchController.java b/src/main/java/com/hotwj/platform/workbench/controller/HeadquartersWorkbenchController.java new file mode 100644 index 0000000..0e493bb --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/controller/HeadquartersWorkbenchController.java @@ -0,0 +1,229 @@ +package com.hotwj.platform.workbench.controller; + +import com.hotwj.platform.resourceManagement.company.service.ISysUserLoginPortService; +import com.hotwj.platform.workbench.domain.vo.HeadquartersTrainingStatsVo; +import com.hotwj.platform.workbench.domain.vo.MonthlyStudyTrendVo; +import com.hotwj.platform.workbench.domain.vo.RegionCompanyStatsVo; +import com.hotwj.platform.workbench.service.IHeadquartersWorkbenchService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 总部端首页 + * + * @author shihongwei + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workbench/headquarters") +@Tag(name = "总部端首页", description = "总部端首页统计模块") +public class HeadquartersWorkbenchController extends BaseController { + + private final IHeadquartersWorkbenchService headquartersWorkbenchService; + + /** + * 获取总部端培训计划统计 + */ + @GetMapping("/trainingStats") + @Operation(summary = "获取总部端培训计划统计") + public R> getTrainingStats() { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHeadquarters = loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isHeadquarters && !isAdmin) { + return R.fail("非总部端用户无权访问"); + } + return R.ok(headquartersWorkbenchService.getTrainingStats()); + } + + /** + * 获取事故年度对比统计 + */ + @GetMapping("/accidentYearlyComparison") + @Operation(summary = "获取事故年度对比统计") + public R getAccidentYearlyComparison() { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHeadquarters = loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isHeadquarters && !isAdmin) { + return R.fail("非总部端用户无权访问"); + } + return R.ok(headquartersWorkbenchService.getAccidentYearlyComparison()); + } + + /** + * 获取隐患年度对比统计 + */ + @GetMapping("/hiddenDangerYearlyComparison") + @Operation(summary = "获取隐患年度对比统计") + public R getHiddenDangerYearlyComparison() { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHeadquarters = loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isHeadquarters && !isAdmin) { + return R.fail("非总部端用户无权访问"); + } + return R.ok(headquartersWorkbenchService.getHiddenDangerYearlyComparison()); + } + + /** + * 获取企业列表(含管理人员数) + */ + @GetMapping("/companyList") + @Operation(summary = "获取企业列表(含管理人员数)") + public R getCompanyList() { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHeadquarters = loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isHeadquarters && !isAdmin) { + return R.fail("非总部端用户无权访问"); + } + return R.ok(headquartersWorkbenchService.getCompanyList()); + } + + /** + * 月度学习折线图 + */ + @GetMapping("/monthlyStudyTrend") + @Operation(summary = "获取月度学习折线图数据") + public R getMonthlyStudyTrend(@RequestParam int year, @RequestParam int month) { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHeadquarters = loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isHeadquarters && !isAdmin) { + return R.fail("非总部端用户无权访问"); + } + return R.ok(headquartersWorkbenchService.getMonthlyStudyTrend(year, month)); + } + + /** + * 违规统计柱状图(按月,默认统计本年) + */ + @GetMapping("/violationMonthlyStats") + @Operation(summary = "获取本年违规统计柱状图(按月)") + public R getViolationMonthlyStats( + @RequestParam(required = false) Integer year + ) { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHeadquarters = loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isHeadquarters && !isAdmin) { + return R.fail("非总部端用户无权访问"); + } + return R.ok(headquartersWorkbenchService.getViolationMonthlyStats(year)); + } + + /** + * 隐患统计概览(总部端) + */ + @GetMapping("/hiddenDanger/summary") + @Operation(summary = "获取总部端隐患统计概览") + public R getHiddenDangerSummary( + @RequestParam(required = false) String regionCode + ) { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHeadquarters = loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isHeadquarters && !isAdmin) { + return R.fail("非总部端用户无权访问"); + } + return R.ok(headquartersWorkbenchService.getHeadquartersHiddenDangerSummary(regionCode)); + } + + /** + * 区域企业统计(按行业分类) + */ + @GetMapping("/regionCompanyStats") + @Operation(summary = "获取区域企业统计") + public R getRegionCompanyStats(@RequestParam String regionCode) { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHeadquarters = loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isHeadquarters && !isAdmin) { + return R.fail("非总部端用户无权访问"); + } + return R.ok(headquartersWorkbenchService.getRegionCompanyStats(regionCode)); + } + + /** + * 获取企业发展趋势统计 + */ + @GetMapping("/enterpriseTrendStats") + @Operation(summary = "获取企业发展趋势统计") + public R getEnterpriseTrendStats() { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHeadquarters = loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isHeadquarters && !isAdmin) { + return R.fail("非总部端用户无权访问"); + } + return R.ok(headquartersWorkbenchService.getEnterpriseTrendStats()); + } + + /** + * 获取车辆发展趋势统计 + */ + @GetMapping("/vehicleTrendStats") + @Operation(summary = "获取车辆发展趋势统计") + public R getVehicleTrendStats() { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHeadquarters = loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isHeadquarters && !isAdmin) { + return R.fail("非总部端用户无权访问"); + } + return R.ok(headquartersWorkbenchService.getVehicleTrendStats()); + } + + /** + * 获取从业人员发展趋势统计 + */ + @GetMapping("/personnelTrendStats") + @Operation(summary = "获取从业人员发展趋势统计") + public R getPersonnelTrendStats() { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHeadquarters = loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isHeadquarters && !isAdmin) { + return R.fail("非总部端用户无权访问"); + } + return R.ok(headquartersWorkbenchService.getPersonnelTrendStats()); + } + + @GetMapping("/squareInfo/pendingAuditCount") + @Operation(summary = "获取广场信息审核中数量") + public R getSquareInfoPendingAuditCount() { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHeadquarters = loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isHeadquarters && !isAdmin) { + return R.fail("非总部端用户无权访问"); + } + return R.ok(headquartersWorkbenchService.getSquareInfoPendingAuditCount()); + } + + @GetMapping("/complaintAdvice/pendingReplyCount") + @Operation(summary = "获取投诉建议未回复数量") + public R getComplaintAdvicePendingReplyCount() { + LoginUser loginUser = LoginHelper.getLoginUser(); + boolean isHeadquarters = loginUser != null && ISysUserLoginPortService.HEADQUARTERS_PORT.equals(loginUser.getLoginPort()); + boolean isAdmin = LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin(); + if (!isHeadquarters && !isAdmin) { + return R.fail("非总部端用户无权访问"); + } + return R.ok(headquartersWorkbenchService.getComplaintAdvicePendingReplyCount()); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/controller/WorkbenchController.java b/src/main/java/com/hotwj/platform/workbench/controller/WorkbenchController.java new file mode 100644 index 0000000..6b97466 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/controller/WorkbenchController.java @@ -0,0 +1,97 @@ +package com.hotwj.platform.workbench.controller; + +import com.hotwj.platform.workbench.domain.vo.*; +import com.hotwj.platform.workbench.service.IWorkbenchService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 企业端首页 + * + * @author shihongwei + * @date 2026-02-24 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workbench") +@Tag(name = "企业端首页", description = "企业端首页通知统计模块") +public class WorkbenchController extends BaseController { + + private final IWorkbenchService workbenchService; + + /** + * 获取公司基本信息 + */ + @GetMapping("/company/basicInfo") + @Operation(summary = "获取公司基本信息") + public R getCompanyBasicInfo() { + return R.ok(workbenchService.getCompanyBasicInfo()); + } + + + // 风险提醒模块 + @GetMapping("/riskReminder") + @Operation(summary = "获取风险提醒信息") + public R getRiskReminder() { + return R.ok(workbenchService.getRiskReminder()); + } + + /** + * 获取快捷入口统计 + */ + @GetMapping("/shortcutStatistics") + @Operation(summary = "获取快捷入口统计") + public R getShortcutStatistics() { + return R.ok(workbenchService.getShortcutStatistics()); + } + + /** + * 获取隐患排查统计 + */ + @GetMapping("/hiddenDangerStats") + @Operation(summary = "获取隐患排查统计") + public R getHiddenDangerStats() { + return R.ok(workbenchService.getHiddenDangerStats()); + } + + /** + * 获取学习折线图数据 + */ + @GetMapping("/learningStats") + @Operation(summary = "获取学习折线图数据") + public R> getLearningStats( + @RequestParam(required = false) String type, + @RequestParam(required = false) String beginTime, + @RequestParam(required = false) String endTime) { + return R.ok(workbenchService.getLearningStats(type, beginTime, endTime)); + } + + /** + * 获取培训使用情况 + */ + @GetMapping("/trainingUsage") + @Operation(summary = "获取培训使用情况") + public R getTrainingUsage() { + return R.ok(workbenchService.getTrainingUsage()); + } + + /** + * 获取车辆/驾驶员总数比例 + */ + @GetMapping("/vehicleDriverRatio") + @Operation(summary = "获取车辆/驾驶员总数比例") + public R getVehicleDriverRatio() { + return R.ok(workbenchService.getVehicleDriverRatio()); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/entity/HotHiddenDangerDailyStats.java b/src/main/java/com/hotwj/platform/workbench/domain/entity/HotHiddenDangerDailyStats.java new file mode 100644 index 0000000..2c725c6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/entity/HotHiddenDangerDailyStats.java @@ -0,0 +1,72 @@ +package com.hotwj.platform.workbench.domain.entity; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.time.LocalDate; + +/** + * 隐患日统计对象 hot_hidden_danger_daily_stats + * + * @author shihongwei + * @date 2026-02-25 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hot_hidden_danger_daily_stats") +public class HotHiddenDangerDailyStats extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId + private Long id; + + /** + * 企业ID + */ + private Long companyId; + + /** + * 行政区划代码 + */ + private String regionCode; + + /** + * 统计日期 + */ + private LocalDate statsDate; + + /** + * 企业隐患数 + */ + private Integer enterpriseRisk; + + /** + * 车辆隐患数 + */ + private Integer vehicleRisk; + + /** + * 人员隐患数 + */ + private Integer personnelRisk; + + /** + * 隐患总数 + */ + private Integer totalRisk; + + /** + * 逻辑删除标志 + */ + @TableLogic + private Long isDeleted; + +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/enums/GovHiddenDangerType.java b/src/main/java/com/hotwj/platform/workbench/domain/enums/GovHiddenDangerType.java new file mode 100644 index 0000000..1474fa7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/enums/GovHiddenDangerType.java @@ -0,0 +1,43 @@ +package com.hotwj.platform.workbench.domain.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 政府端隐患类型枚举 + */ +@Getter +@AllArgsConstructor +public enum GovHiddenDangerType { + // 企业类 (Enterprise) + BUSINESS_LICENSE("business_license", "营业执照", "enterprise", "企业隐患"), + ROAD_PERMIT("road_permit", "道路经营许可证", "enterprise", "企业隐患"), + MANAGER_QUALIFICATION("manager_qualification", "上岗资格证", "enterprise", "企业隐患"), + + // 车辆类 (Vehicle) + VEHICLE_LICENSE("vehicle_license", "行驶证", "vehicle", "车辆隐患"), + VEHICLE_TRANSPORT_PERMIT("vehicle_transport_permit", "运输证", "vehicle", "车辆隐患"), + VEHICLE_ANNUAL_INSPECT("vehicle_annual_inspect", "年审检测", "vehicle", "车辆隐患"), + VEHICLE_MAINTENANCE("vehicle_maintenance", "二级维护", "vehicle", "车辆隐患"), + + // 人员类 (Personnel) + DRIVER_ID_CARD("driver_id_card", "驾驶员身份证", "personnel", "人员隐患"), + DRIVER_LICENSE("driver_license", "驾驶证", "personnel", "人员隐患"), + DRIVER_QUALIFICATION("driver_qualification", "从业资格证", "personnel", "人员隐患"), + DRIVER_PRE_JOB_TRAINING("driver_pre_job_training", "岗前教育培训", "personnel", "人员隐患"), + DRIVER_VIOLATION("driver_violation", "违法违规信息", "personnel", "人员隐患"); + + private final String key; + private final String label; + private final String groupKey; + private final String groupLabel; + + public static GovHiddenDangerType getByKey(String key) { + for (GovHiddenDangerType type : values()) { + if (type.getKey().equals(key)) { + return type; + } + } + return null; + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/CompanyBasicInfoVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/CompanyBasicInfoVo.java new file mode 100644 index 0000000..59be17b --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/CompanyBasicInfoVo.java @@ -0,0 +1,70 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 企业首页基本信息视图对象 + * + * @author shihongwei + * @date 2026-02-24 + */ +@Data +@Schema(description = "企业首页基本信息") +public class CompanyBasicInfoVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 企业名称 + */ + @Schema(description = "企业名称") + private String companyName; + + /** + * 负责人姓名 + */ + @Schema(description = "负责人姓名") + private String responsibleName; + + /** + * 员工总人数 + */ + @Schema(description = "员工总人数") + private Long employeeTotal; + + /** + * 管理人员人数 + */ + @Schema(description = "管理人员人数") + private Long managerCount; + + /** + * 驾驶员人数 + */ + @Schema(description = "驾驶员人数") + private Long driverCount; + + /** + * 总车辆数 + */ + @Schema(description = "总车辆数") + private Long vehicleTotal; + + /** + * 正常车辆数量 + */ + @Schema(description = "正常车辆数量") + private Long normalVehicleCount; + + /** + * 暂停车辆数量 + */ + @Schema(description = "暂停车辆数量") + private Long pausedVehicleCount; + +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/CompanyRiskCountVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/CompanyRiskCountVo.java new file mode 100644 index 0000000..49b49fe --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/CompanyRiskCountVo.java @@ -0,0 +1,33 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; + +/** + * 企业隐患统计VO (用于SQL查询返回结果) + * + * @author shihongwei + * @date 2026-02-25 + */ +@Data +public class CompanyRiskCountVo { + + /** + * 企业ID + */ + private Long companyId; + + /** + * 行政区划代码 + */ + private String regionCode; + + /** + * 隐患分类 (enterprise, vehicle, personnel) + */ + private String category; + + /** + * 数量 + */ + private Integer count; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/DailyCountRow.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/DailyCountRow.java new file mode 100644 index 0000000..60b4a93 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/DailyCountRow.java @@ -0,0 +1,10 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; + +@Data +public class DailyCountRow { + private String day; + private Long cnt; +} + diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/GovAccidentStatsVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovAccidentStatsVo.java new file mode 100644 index 0000000..b65453b --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovAccidentStatsVo.java @@ -0,0 +1,19 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; + +/** + * 事故等级统计 VO + */ +@Data +public class GovAccidentStatsVo { + /** + * 事故等级名称 + */ + private String levelName; + + /** + * 数量 + */ + private Long count; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/GovCompanyRiskStatsVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovCompanyRiskStatsVo.java new file mode 100644 index 0000000..c2b813b --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovCompanyRiskStatsVo.java @@ -0,0 +1,41 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; + +/** + * 政府端-企业隐患统计VO + * + * @author shihongwei + */ +@Data +public class GovCompanyRiskStatsVo { + /** + * 企业ID + */ + private Long companyId; + + /** + * 企业名称 + */ + private String companyName; + + /** + * 隐患总数 + */ + private Integer totalRisk; + + /** + * 企业隐患数 + */ + private Integer enterpriseRisk; + + /** + * 车辆隐患数 + */ + private Integer vehicleRisk; + + /** + * 人员隐患数 + */ + private Integer personnelRisk; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/GovCompanyRiskTableDataInfo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovCompanyRiskTableDataInfo.java new file mode 100644 index 0000000..a19eb23 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovCompanyRiskTableDataInfo.java @@ -0,0 +1,24 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * 企业隐患统计列表分页结果 (包含辖区隐患总数) + * + * @author shihongwei + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class GovCompanyRiskTableDataInfo extends TableDataInfo { + + /** + * 辖区所有企业隐患总数 + */ + private Long totalRiskCount; + + public GovCompanyRiskTableDataInfo() { + super(); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/GovHiddenDangerDetailVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovHiddenDangerDetailVo.java new file mode 100644 index 0000000..5dbee12 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovHiddenDangerDetailVo.java @@ -0,0 +1,41 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; + +import java.util.Date; + +/** + * 隐患明细 VO + */ +@Data +public class GovHiddenDangerDetailVo { + /** + * 关联对象的ID(如企业ID、车辆ID) + */ + private String id; + + /** + * 列表显示的标题(如企业名称、车牌号) + */ + private String title; + + /** + * 描述信息(如“营业执照已过期3天”) + */ + private String subTitle; + + /** + * 状态(如 expired, warning) + */ + private String status; + + /** + * 到期时间/相关时间 + */ + private Date deadline; + + /** + * 关联的具体记录ID(如保险记录ID),可选 + */ + private String relatedId; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/GovHiddenDangerGroupVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovHiddenDangerGroupVo.java new file mode 100644 index 0000000..191bb8f --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovHiddenDangerGroupVo.java @@ -0,0 +1,31 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; + +import java.util.List; + +/** + * 隐患大项 VO + */ +@Data +public class GovHiddenDangerGroupVo { + /** + * 大项Key (enterprise, vehicle, personnel) + */ + private String key; + + /** + * 大项名称 + */ + private String label; + + /** + * 该大项下的总隐患数 + */ + private Long count; + + /** + * 隐患小项列表 + */ + private List items; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/GovHiddenDangerItemVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovHiddenDangerItemVo.java new file mode 100644 index 0000000..17857db --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovHiddenDangerItemVo.java @@ -0,0 +1,24 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; + +/** + * 隐患小项 VO + */ +@Data +public class GovHiddenDangerItemVo { + /** + * 小项Key (business_license, etc.) + */ + private String key; + + /** + * 小项名称 + */ + private String label; + + /** + * 隐患数量 + */ + private Long count; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/GovHiddenDangerSummaryVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovHiddenDangerSummaryVo.java new file mode 100644 index 0000000..b4d6703 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovHiddenDangerSummaryVo.java @@ -0,0 +1,36 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; + +import java.util.List; + +/** + * 政府端隐患统计总览 VO + */ +@Data +public class GovHiddenDangerSummaryVo { + /** + * 总隐患数 + */ + private Long totalCount; + + /** + * 隐患大项列表 (企业隐患、车辆隐患、人员隐患) + */ + private List groups; + + /** + * 企业隐患总数 + */ + private Long enterpriseCount; + + /** + * 车辆隐患总数 + */ + private Long vehicleCount; + + /** + * 人员隐患总数 + */ + private Long personnelCount; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/GovHomeStatisticsVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovHomeStatisticsVo.java new file mode 100644 index 0000000..867613d --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovHomeStatisticsVo.java @@ -0,0 +1,47 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * 政府端首页统计 + * + * @author shihongwei + */ +@Data +@Schema(description = "政府端首页统计") +public class GovHomeStatisticsVo { + + @Schema(description = "企业总数") + private Long enterpriseCount; + + @Schema(description = "运营车辆总数") + private Long vehicleCount; + + @Schema(description = "驾驶员总数") + private Long driverCount; + + @Schema(description = "隐患总数") + private Long hiddenDangerCount; + + @Schema(description = "区划名称") + private String jurisdictionName; + + @Schema(description = "当天时间") + private String currentDate; + + @Schema(description = "辖区企业列表") + private List enterpriseList; + + @Data + @Schema(description = "企业简要信息") + public static class EnterpriseInfo { + @Schema(description = "企业ID") + private Long id; + + @Schema(description = "企业名称") + private String companyName; + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/GovTrainingStatsVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovTrainingStatsVo.java new file mode 100644 index 0000000..a0ba73c --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/GovTrainingStatsVo.java @@ -0,0 +1,29 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; + +/** + * 政府端学习统计 VO + */ +@Data +public class GovTrainingStatsVo { + /** + * 日期 + */ + private String date; + + /** + * X轴标签 (周模式显示星期几,月模式显示MM-dd) + */ + private String label; + + /** + * 已完成数量 + */ + private Long completed; + + /** + * 未完成数量 + */ + private Long incomplete; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersCompanyListVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersCompanyListVo.java new file mode 100644 index 0000000..c7af582 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersCompanyListVo.java @@ -0,0 +1,44 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 总部端企业列表VO + * + * @author shihongwei + */ +@Data +@Schema(description = "总部端企业列表VO") +public class HeadquartersCompanyListVo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "入驻企业总数") + private Long totalCount; + + @Schema(description = "企业列表") + private List list; + + @Data + @Schema(description = "企业行数据") + public static class CompanyRow implements Serializable { + @Schema(description = "企业ID") + private Long id; + + @Schema(description = "企业名称") + private String companyName; + + @Schema(description = "负责人") + private String leaderName; + + @Schema(description = "企业法人") + private String legalPerson; + + @Schema(description = "管理人员总数") + private Long managerCount; + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersEnterpriseTrendVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersEnterpriseTrendVo.java new file mode 100644 index 0000000..113d89f --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersEnterpriseTrendVo.java @@ -0,0 +1,37 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 总部端企业发展趋势统计VO + * + * @author shihongwei + */ +@Data +@Schema(description = "总部端企业发展趋势统计VO") +public class HeadquartersEnterpriseTrendVo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "年份列表") + private List years; + + @Schema(description = "企业总计") + private List totalCounts; + + @Schema(description = "普货企业数量") + private List generalCounts; + + @Schema(description = "危货企业数量") + private List dangerousCounts; + + @Schema(description = "客运企业数量") + private List passengerCounts; + + @Schema(description = "其他企业数量") + private List otherCounts; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersHiddenDangerSummaryVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersHiddenDangerSummaryVo.java new file mode 100644 index 0000000..9e3ad28 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersHiddenDangerSummaryVo.java @@ -0,0 +1,12 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; + +@Data +public class HeadquartersHiddenDangerSummaryVo { + private Long totalCount; + private Long enterpriseCount; + private Long vehicleCount; + private Long personnelCount; +} + diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersPersonnelTrendVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersPersonnelTrendVo.java new file mode 100644 index 0000000..55f265a --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersPersonnelTrendVo.java @@ -0,0 +1,29 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + * 总部端从业人员发展趋势统计VO + * + * @author shihongwei + */ +@Data +@Schema(description = "总部端从业人员发展趋势统计VO") +public class HeadquartersPersonnelTrendVo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "年份列表") + private List years; + + @Schema(description = "图例(人员类型列表)") + private List categories; + + @Schema(description = "各类型对应的数据趋势,Key为类型名称,Value为每年对应的数量列表") + private Map> seriesData; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersTrainingStatsVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersTrainingStatsVo.java new file mode 100644 index 0000000..580392c --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersTrainingStatsVo.java @@ -0,0 +1,29 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 总部端培训统计VO + * + * @author shihongwei + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class HeadquartersTrainingStatsVo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 培训类型名称 + */ + private String name; + + /** + * 数量 + */ + private Long value; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersVehicleTrendVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersVehicleTrendVo.java new file mode 100644 index 0000000..b6cfe60 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersVehicleTrendVo.java @@ -0,0 +1,32 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +@Schema(description = "总部端车辆发展趋势统计VO") +public class HeadquartersVehicleTrendVo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "年份列表") + private List years; + + @Schema(description = "车辆总计") + private List totalCounts; + + @Schema(description = "普货车辆数量") + private List generalCounts; + + @Schema(description = "危货车辆数量") + private List dangerousCounts; + + @Schema(description = "客运车辆数量") + private List passengerCounts; + + @Schema(description = "其他车辆数量") + private List otherCounts; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersYearlyComparisonVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersYearlyComparisonVo.java new file mode 100644 index 0000000..d72e81e --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/HeadquartersYearlyComparisonVo.java @@ -0,0 +1,28 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 总部端年度数据对比VO + * + * @author shihongwei + */ +@Data +@Schema(description = "总部端年度数据对比VO") +public class HeadquartersYearlyComparisonVo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "月份列表 (1月-12月)") + private List months; + + @Schema(description = "去年数据") + private List lastYearValues; + + @Schema(description = "今年数据") + private List currentYearValues; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/HiddenDangerCountVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/HiddenDangerCountVo.java new file mode 100644 index 0000000..98c0436 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/HiddenDangerCountVo.java @@ -0,0 +1,23 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 隐患统计VO + */ +@Data +@Schema(description = "隐患统计VO") +public class HiddenDangerCountVo { + /** + * 日期 (格式: yyyy-MM-dd) + */ + @Schema(description = "日期") + private String date; + + /** + * 隐患数量 + */ + @Schema(description = "隐患数量") + private Long count; +} \ No newline at end of file diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/HiddenDangerItemVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/HiddenDangerItemVo.java new file mode 100644 index 0000000..d0d7300 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/HiddenDangerItemVo.java @@ -0,0 +1,77 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 隐患排查统计项 VO + * + * @author shihongwei + * @date 2026-02-24 + */ +@Data +@Schema(description = "隐患排查统计项") +public class HiddenDangerItemVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 标题 (例如: 2024年第一季度排查计划: 未排查) + */ + @Schema(description = "标题") + private String title; + + /** + * 计划名称 + */ + @Schema(description = "计划名称") + private String planName; + + /** + * 目标名称 (车牌号/驾驶员姓名) + */ + @Schema(description = "目标名称") + private String targetName; + + /** + * 截止时间 (计划结束时间) + */ + @Schema(description = "截止时间") + private Date deadline; + + /** + * 状态: 1=未排查, 2=未审核 + */ + @Schema(description = "状态: 1=未排查, 2=未审核") + private Integer status; + + /** + * 计划ID + */ + @Schema(description = "计划ID") + private Long planId; + + /** + * 排查类型: 1=企业, 2=车辆, 3=人员 + */ + @Schema(description = "排查类型: 1=企业, 2=车辆, 3=人员") + private Long projectType; + + /** + * 目标ID (车辆ID/驾驶员ID) + */ + @Schema(description = "目标ID (车辆ID/驾驶员ID)") + private String projectId; + + /** + * 排查记录ID (仅在未审核状态下有值) + */ + @Schema(description = "排查记录ID (仅在未审核状态下有值)") + private Long inspectionId; + +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/HiddenDangerStatsVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/HiddenDangerStatsVo.java new file mode 100644 index 0000000..817345a --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/HiddenDangerStatsVo.java @@ -0,0 +1,41 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 隐患排查统计 VO + * + * @author shihongwei + * @date 2026-02-24 + */ +@Data +@Schema(description = "隐患排查统计") +public class HiddenDangerStatsVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 企业隐患排查未完成项 + */ + @Schema(description = "企业隐患排查未完成项") + private List enterpriseItems; + + /** + * 车辆隐患排查未完成项 + */ + @Schema(description = "车辆隐患排查未完成项") + private List vehicleItems; + + /** + * 驾驶员隐患排查未完成项 + */ + @Schema(description = "驾驶员隐患排查未完成项") + private List driverItems; + +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/LearningStatsItemVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/LearningStatsItemVo.java new file mode 100644 index 0000000..eb189d6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/LearningStatsItemVo.java @@ -0,0 +1,40 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 学习统计项 VO + * + * @author shihongwei + * @date 2026-02-24 + */ +@Data +@Schema(description = "学习统计项") +public class LearningStatsItemVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 日期 (yyyy-MM-dd) + */ + @Schema(description = "日期") + private String date; + + /** + * 已完成数量 + */ + @Schema(description = "已完成数量") + private Long completed; + + /** + * 未完成数量 + */ + @Schema(description = "未完成数量") + private Long incomplete; + +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/MonthlyStudyTrendVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/MonthlyStudyTrendVo.java new file mode 100644 index 0000000..484eb29 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/MonthlyStudyTrendVo.java @@ -0,0 +1,20 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MonthlyStudyTrendVo implements Serializable { + private static final long serialVersionUID = 1L; + + private List dates; + private List completed; + private List uncompleted; +} + diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/MonthlyTrendRow.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/MonthlyTrendRow.java new file mode 100644 index 0000000..9f650d9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/MonthlyTrendRow.java @@ -0,0 +1,11 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; + +@Data +public class MonthlyTrendRow { + private String day; + private Long completed; + private Long uncompleted; +} + diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/RegionCompanyStatsVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/RegionCompanyStatsVo.java new file mode 100644 index 0000000..4ef9c3d --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/RegionCompanyStatsVo.java @@ -0,0 +1,41 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; + +/** + * 区域企业统计视图对象 + * + * @author shihongwei + */ +@Data +@Schema(description = "区域企业统计视图对象") +public class RegionCompanyStatsVo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "总计企业数") + private Long totalCount; + + @Schema(description = "普货企业数") + private Long generalCargoCount; + + @Schema(description = "危货企业数") + private Long dangerousGoodsCount; + + @Schema(description = "客运企业数") + private Long passengerTransportCount; + + @Schema(description = "其他企业数") + private Long otherCount; + + public RegionCompanyStatsVo() { + this.totalCount = 0L; + this.generalCargoCount = 0L; + this.dangerousGoodsCount = 0L; + this.passengerTransportCount = 0L; + this.otherCount = 0L; + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/RiskCategoryVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/RiskCategoryVo.java new file mode 100644 index 0000000..6c1afad --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/RiskCategoryVo.java @@ -0,0 +1,35 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 风险分类 + * + * @author shihongwei + * @date 2026-02-24 + */ +@Data +@Schema(description = "风险分类") +public class RiskCategoryVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "分类标题") + private String title; + + @Schema(description = "分类类型") + private String type; + + @Schema(description = "风险数量") + private Integer count; + + @Schema(description = "风险项列表") + private List items = new ArrayList<>(); +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/RiskItemVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/RiskItemVo.java new file mode 100644 index 0000000..20eb077 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/RiskItemVo.java @@ -0,0 +1,54 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 风险提醒详情 + * + * @author shihongwei + * @date 2026-02-24 + */ +@Data +@Schema(description = "风险提醒详情") +public class RiskItemVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "风险标题") + private String title; + + @Schema(description = "风险类型 (expired=已过期, warning=即将过期, not_configured=未配置, not_filled=未填写)") + private String type; + + @Schema(description = "风险来源模块名") + private String moduleName; + + @Schema(description = "模块类型") + private String moduleType; + + @Schema(description = "人员类型 (1=驾驶员, 2=企业管理人员)") + private Integer personnelType; + + @Schema(description = "人员ID") + private String personnelId; + + @Schema(description = "业务ID(如车辆ID, 隐患ID等)") + private String businessId; + + @Schema(description = "配置项key(企业配置跳转用)") + private String configKey; + + @Schema(description = "培训ID") + private String trainingId; + + @Schema(description = "培训参与人员记录ID") + private String participantId; + + @Schema(description = "跳转链接") + private String link; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/RiskReminderVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/RiskReminderVo.java new file mode 100644 index 0000000..55cceac --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/RiskReminderVo.java @@ -0,0 +1,34 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 风险提醒信息 + * + * @author shihongwei + * @date 2025-06-24 + */ +@Data +@Schema(description = "风险提醒信息") +public class RiskReminderVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "企业风险") + private List companyRisk; + + @Schema(description = "车辆风险") + private List vehicleRisk; + + @Schema(description = "驾驶员风险") + private List driverRisk; + + @Schema(description = "教育培训风险") + private List trainingRisk; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/ShortcutStatisticsVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/ShortcutStatisticsVo.java new file mode 100644 index 0000000..a563c43 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/ShortcutStatisticsVo.java @@ -0,0 +1,58 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 快捷入口统计 VO + * + * @author shihongwei + * @date 2026-02-24 + */ +@Data +@Schema(description = "快捷入口统计") +public class ShortcutStatisticsVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 事故报告待整改数量 + */ + @Schema(description = "事故报告待整改数量") + private Long accidentReportCount; + + /** + * 违规信息待约谈数量 + */ + @Schema(description = "违规信息待约谈数量") + private Long violationInfoCount; + + /** + * 隐患排查数量统计(状态1=特处理、2=审核中) + */ + @Schema(description = "隐患排查数量统计") + private Long hiddenInspectionCount; + + /** + * 隐患治理数量统计(状态6=评估、7=治理、8=复查) + */ + @Schema(description = "隐患治理数量统计") + private Long hiddenGovernanceCount; + + /** + * 日常培训待审核数量 + */ + @Schema(description = "日常培训待审核数量") + private Long dailyTrainingCount; + + /** + * 行管通知未读数量 + */ + @Schema(description = "行管通知未读数量") + private Long adminNoticeCount; + +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/TrainingDailyStatsVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/TrainingDailyStatsVo.java new file mode 100644 index 0000000..094376e --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/TrainingDailyStatsVo.java @@ -0,0 +1,13 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; + +import java.util.Date; + +@Data +public class TrainingDailyStatsVo { + private Date statsDate; + private Long completedCount; + private Long uncompletedCount; +} + diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/TrainingTypeStatRow.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/TrainingTypeStatRow.java new file mode 100644 index 0000000..c81098c --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/TrainingTypeStatRow.java @@ -0,0 +1,10 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; + +@Data +public class TrainingTypeStatRow { + private String type; + private Long count; +} + diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/TrainingUsageVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/TrainingUsageVo.java new file mode 100644 index 0000000..7600b82 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/TrainingUsageVo.java @@ -0,0 +1,35 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 培训使用情况 VO + */ +@Data +@Schema(description = "培训使用情况") +public class TrainingUsageVo implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 已完成培训数量 + */ + @Schema(description = "已完成培训数量") + private Long completedCount; + + /** + * 未完成培训数量 + */ + @Schema(description = "未完成培训数量") + private Long incompleteCount; + + /** + * 完成比例 + */ + @Schema(description = "完成比例") + private BigDecimal completionRate; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/VehicleDriverRatioVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/VehicleDriverRatioVo.java new file mode 100644 index 0000000..714593c --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/VehicleDriverRatioVo.java @@ -0,0 +1,35 @@ +package com.hotwj.platform.workbench.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 车辆/驾驶员总数比例 VO + */ +@Data +@Schema(description = "车辆/驾驶员总数比例") +public class VehicleDriverRatioVo implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 公司运营车辆数 + */ + @Schema(description = "公司运营车辆数") + private Long vehicleCount; + + /** + * 公司驾驶员人数 + */ + @Schema(description = "公司驾驶员人数") + private Long driverCount; + + /** + * 车辆占比 + */ + @Schema(description = "车辆占比") + private BigDecimal vehicleRatio; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/VehicleRiskCalcVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/VehicleRiskCalcVo.java new file mode 100644 index 0000000..c62d8f1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/VehicleRiskCalcVo.java @@ -0,0 +1,32 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; + +import java.util.Date; + +/** + * 车辆风险计算基础VO + */ +@Data +public class VehicleRiskCalcVo { + /** + * 车辆ID + */ + private String id; + /** + * 车牌号 + */ + private String plateNumber; + /** + * 所属企业ID + */ + private Long companyId; + /** + * 所属企业名称 + */ + private String companyName; + /** + * 行驶证发证日期 + */ + private Date issueDate; +} diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/ViolationMonthlyBarVo.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/ViolationMonthlyBarVo.java new file mode 100644 index 0000000..c687916 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/ViolationMonthlyBarVo.java @@ -0,0 +1,12 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; + +import java.util.List; + +@Data +public class ViolationMonthlyBarVo { + private List months; + private List values; +} + diff --git a/src/main/java/com/hotwj/platform/workbench/domain/vo/ViolationMonthlyRow.java b/src/main/java/com/hotwj/platform/workbench/domain/vo/ViolationMonthlyRow.java new file mode 100644 index 0000000..a8d656d --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/domain/vo/ViolationMonthlyRow.java @@ -0,0 +1,10 @@ +package com.hotwj.platform.workbench.domain.vo; + +import lombok.Data; + +@Data +public class ViolationMonthlyRow { + private String month; // '01' ~ '12' + private Long cnt; +} + diff --git a/src/main/java/com/hotwj/platform/workbench/mapper/GovWorkbenchMapper.java b/src/main/java/com/hotwj/platform/workbench/mapper/GovWorkbenchMapper.java new file mode 100644 index 0000000..2d77f54 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/mapper/GovWorkbenchMapper.java @@ -0,0 +1,203 @@ +package com.hotwj.platform.workbench.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.HotTrainingParticipant; +import com.hotwj.platform.workbench.domain.vo.*; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 政府端工作台Mapper接口 + * + * @author shihongwei + */ +@Mapper +public interface GovWorkbenchMapper { + + /** + * 统计当前辖区内的所有企业总数 + * + * @param districtCode 区划代码 + * @return 企业总数 + */ + Long selectEnterpriseCount(@Param("districtCode") String districtCode); + + /** + * 查询当前辖区内的所有企业列表 + * + * @param districtCode 区划代码 + * @param companyName 企业名称 (可选) + * @return 企业列表 + */ + List selectEnterpriseList(@Param("districtCode") String districtCode, @Param("companyName") String companyName); + + /** + * 统计总运营车辆总数 + * + * @param districtCode 区划代码 + * @return 运营车辆总数 + */ + Long selectVehicleCount(@Param("districtCode") String districtCode); + + /** + * 统计当前总驾驶员总数 + * + * @param districtCode 区划代码 + * @return 驾驶员总数 + */ + Long selectDriverCount(@Param("districtCode") String districtCode); + + // ================= 企业隐患 ================= + + /** + * 统计营业执照隐患数 (过期或未填写期限) + */ + Long countBusinessLicense(@Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 查询营业执照隐患列表 + */ + Page listBusinessLicense(Page page, @Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 统计道路经营许可证隐患数 (过期或未填写期限) + */ + Long countRoadPermit(@Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 查询道路经营许可证隐患列表 + */ + Page listRoadPermit(Page page, @Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 统计上岗资格证隐患数 (在职管理人员过期或未填写) + */ + Long countManagerQualification(@Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 查询上岗资格证隐患列表 + */ + Page listManagerQualification(Page page, @Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + // ================= 车辆隐患 ================= + + /** + * 统计行驶证隐患数 (过期、未填写日期、强制报废) + */ + Long countVehicleLicense(@Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 查询行驶证隐患列表 + */ + Page listVehicleLicense(Page page, @Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 统计运输证隐患数 (过期或未填写) + */ + Long countVehicleTransportPermit(@Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 查询运输证隐患列表 + */ + Page listVehicleTransportPermit(Page page, @Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 获取指定企业用于风险计算的车辆列表 + */ + List selectVehicleForRiskCalc(@Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 查询指定时间范围内的培训参与记录 + */ + List selectTrainingParticipants(@Param("districtCode") String districtCode, @Param("endDate") String endDate); + + // ================= 事故统计 ================= + + /** + * 统计事故等级分布 + */ + List selectAccidentLevelStats(@Param("districtCode") String districtCode); + + // ================= 驾驶员隐患 ================= + + /** + * 统计驾驶员身份证隐患数 + */ + Long countDriverIdCard(@Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 查询驾驶员身份证隐患列表 + */ + Page listDriverIdCard(Page page, @Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 统计驾驶员驾驶证隐患数 + */ + Long countDriverLicense(@Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 查询驾驶员驾驶证隐患列表 + */ + Page listDriverLicense(Page page, @Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 统计驾驶员从业资格证隐患数 + */ + Long countDriverQualification(@Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 查询驾驶员从业资格证隐患列表 + */ + Page listDriverQualification(Page page, @Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 统计驾驶员未参加岗前培训隐患数 + */ + Long countDriverPreJobTraining(@Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 查询驾驶员未参加岗前培训隐患列表 + */ + Page listDriverPreJobTraining(Page page, @Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 统计驾驶员违法违规隐患数 (未处理的违章) + */ + Long countDriverViolation(@Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 查询驾驶员违法违规隐患列表 + */ + Page listDriverViolation(Page page, @Param("districtCode") String districtCode, @Param("companyId") Long companyId); + + /** + * 按日统计隐患数量 + * + * @param districtCode 区划代码 + * @param companyId 公司ID (可选) + * @param startDate 开始日期 (可选) + * @param endDate 结束日期 (可选) + * @return 统计结果列表 + */ + List selectHiddenDangerCountByDate(@Param("districtCode") String districtCode, + @Param("companyId") Long companyId, + @Param("startDate") String startDate, + @Param("endDate") String endDate); + + /** + * 查询简单类型的隐患数量,按企业和分类分组 + * + * @param districtCode 辖区编码 (可选,null查询所有) + * @return 统计结果列表 + */ + List selectSimpleRiskCountsGroupedByCompany(@Param("districtCode") String districtCode); + + /** + * 查询企业行政区划代码 + * @param companyIds 企业ID集合 + * @return 企业区划信息 + */ + List selectCompanyRegionCodes(@Param("companyIds") java.util.Collection companyIds); +} diff --git a/src/main/java/com/hotwj/platform/workbench/mapper/HeadquartersWorkbenchMapper.java b/src/main/java/com/hotwj/platform/workbench/mapper/HeadquartersWorkbenchMapper.java new file mode 100644 index 0000000..397c2ec --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/mapper/HeadquartersWorkbenchMapper.java @@ -0,0 +1,101 @@ +package com.hotwj.platform.workbench.mapper; + +import com.hotwj.platform.workbench.domain.vo.*; +import org.apache.ibatis.annotations.MapKey; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; + +/** + * 总部端工作台Mapper接口 + * + * @author shihongwei + */ +@Mapper +public interface HeadquartersWorkbenchMapper { + + /** + * 统计培训计划各类型数量 + * + * @return 统计结果 + */ + List selectTrainingTypeStats(); + + /** + * 统计某月每日完成数量 + */ + List selectMonthlyCompleted(@Param("startDate") Date startDate, + @Param("endDate") Date endDate); + + /** + * 统计某月每日未完成数量 + */ + List selectMonthlyUncompleted(@Param("startDate") Date startDate, + @Param("endDate") Date endDate); + + List selectMonthlyStudyTrendFromDailyStats(@Param("startDate") Date startDate, + @Param("endDate") Date endDate); + + /** + * 统计指定年份按月的违规数量(occur_time),返回 1-12 月及数量 + */ + List selectViolationMonthlyCounts(@Param("startDate") Date startDate, + @Param("endDate") Date endDate); + + /** + * 汇总最近一天的隐患统计(来自 hot_hidden_danger_daily_stats) + */ + HeadquartersHiddenDangerSummaryVo selectLatestHiddenDangerSummary(@Param("regionCode") String regionCode); + + /** + * 统计指定区域内企业各行业的数量 + * + * @param regionCode 区域代码 + * @return 行业统计列表 + */ + @MapKey("industry") + List> selectCompanyIndustryCounts(@Param("regionCode") String regionCode); + + /** + * 查询所有企业的创建年份和行业信息 + */ + @MapKey("year") + List> selectAllCompanyCreateYears(); + + /** + * 查询所有从业人员的入职年份和人员类型 + */ + @MapKey("year") + List> selectAllPersonnelCreateYears(); + + /** + * 查询所有车辆的创建年份及所属企业行业 + */ + @MapKey("year") + List> selectAllVehicleCreateYears(); + + /** + * 统计指定年份范围内的月度事故数量 + * + * @param startYear 开始年份 + * @param endYear 结束年份 + */ + @MapKey("month") + List> selectMonthlyAccidentCounts(@Param("startYear") int startYear, @Param("endYear") int endYear); + + /** + * 统计指定年份范围内的月度隐患数量 + * + * @param startYear 开始年份 + * @param endYear 结束年份 + */ + @MapKey("month") + List> selectMonthlyHiddenDangerCounts(@Param("startYear") int startYear, @Param("endYear") int endYear); + + /** + * 查询企业列表及其管理人员数量 + */ + List selectCompanyListWithManagerCount(); +} diff --git a/src/main/java/com/hotwj/platform/workbench/mapper/HotHiddenDangerDailyStatsMapper.java b/src/main/java/com/hotwj/platform/workbench/mapper/HotHiddenDangerDailyStatsMapper.java new file mode 100644 index 0000000..0dad2e8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/mapper/HotHiddenDangerDailyStatsMapper.java @@ -0,0 +1,70 @@ +package com.hotwj.platform.workbench.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.workbench.domain.entity.HotHiddenDangerDailyStats; +import com.hotwj.platform.workbench.domain.vo.GovCompanyRiskStatsVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +import java.util.List; + +/** + * 隐患日统计Mapper接口 + * + * @author shihongwei + * @date 2026-02-25 + */ +@Mapper +public interface HotHiddenDangerDailyStatsMapper extends BaseMapperPlus { + + /** + * 查询指定日期区间的每日隐患总数(按日期分组) + * + * @param regionCode 行政区划代码(前缀匹配) + * @param startDate 开始日期 + * @param endDate 结束日期 + * @return 每日统计结果 + */ + List selectStatsByDateRange(@Param("regionCode") String regionCode, + @Param("startDate") String startDate, + @Param("endDate") String endDate); + + /** + * 查询指定企业在日期区间的每日隐患数 + * + * @param companyId 企业ID + * @param startDate 开始日期 + * @param endDate 结束日期 + * @return 每日统计结果 + */ + List selectStatsByCompanyAndDateRange(@Param("companyId") Long companyId, + @Param("startDate") String startDate, + @Param("endDate") String endDate); + + /** + * 查询隐患总数最高的企业TOP N + * + * @param districtCode 辖区编码 + * @param statsDate 统计日期 + * @param limit 数量限制 + * @return 统计列表 + */ + List selectTopRiskCompanies(@Param("districtCode") String districtCode, + @Param("statsDate") String statsDate, + @Param("limit") int limit); + + /** + * 分页查询企业隐患统计列表 + * + * @param page 分页对象 + * @param districtCode 辖区编码 + * @param statsDate 统计日期 + * @param companyName 企业名称模糊搜索 + * @return 分页列表 + */ + Page selectCompanyRiskPage(Page page, + @Param("districtCode") String districtCode, + @Param("statsDate") String statsDate, + @Param("companyName") String companyName); +} diff --git a/src/main/java/com/hotwj/platform/workbench/mapper/HotTrainingDailyStatsMapper.java b/src/main/java/com/hotwj/platform/workbench/mapper/HotTrainingDailyStatsMapper.java new file mode 100644 index 0000000..ea6ea92 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/mapper/HotTrainingDailyStatsMapper.java @@ -0,0 +1,19 @@ +package com.hotwj.platform.workbench.mapper; + +import com.hotwj.platform.workbench.domain.vo.TrainingDailyStatsVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; + +@Mapper +public interface HotTrainingDailyStatsMapper { + + int upsertDailyStats(@Param("statsDate") Date statsDate, + @Param("dayEnd") Date dayEnd); + + List selectByCompanyAndDateRange(@Param("companyId") Long companyId, + @Param("startDate") Date startDate, + @Param("endDate") Date endDate); +} diff --git a/src/main/java/com/hotwj/platform/workbench/service/IGovWorkbenchService.java b/src/main/java/com/hotwj/platform/workbench/service/IGovWorkbenchService.java new file mode 100644 index 0000000..acd3e10 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/service/IGovWorkbenchService.java @@ -0,0 +1,92 @@ +package com.hotwj.platform.workbench.service; + +import com.hotwj.platform.workbench.domain.vo.*; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +import java.util.List; + +/** + * 政府端工作台Service接口 + * + * @author shihongwei + */ +public interface IGovWorkbenchService { + + /** + * 获取政府端首页统计 + * + * @return 统计信息 + */ + GovHomeStatisticsVo getHomeStatistics(); + + /** + * 获取隐患统计总览 + * + * @param regionCode 区域代码 (可选) + * @return 总览信息 + */ + GovHiddenDangerSummaryVo getHiddenDangerSummary(String regionCode); + + /** + * 获取隐患明细列表 + * + * @param type 隐患类型Key + * @param companyId 企业ID (可选) + * @param pageQuery 分页参数 + * @return 分页列表 + */ + TableDataInfo getHiddenDangerList(String type, Long companyId, PageQuery pageQuery); + + /** + * 获取隐患按月统计数据 + * + * @param companyId 公司ID (可选) + * @param startDate 开始日期 (格式: yyyy-MM) + * @param endDate 结束日期 (格式: yyyy-MM) + * @return 统计列表 + */ + List getHiddenDangerCountByMonth(Long companyId, String startDate, String endDate); + + /** + * 获取隐患排名前N的企业 (实时计算) + * + * @param limit 数量 + * @return 企业列表 + */ + List getTopRiskCompanies(int limit); + + /** + * 分页获取企业隐患统计列表 (实时计算) + * + * @param companyName 企业名称搜索 + * @param pageQuery 分页参数 + * @return 分页列表 (包含辖区总隐患数) + */ + GovCompanyRiskTableDataInfo getCompanyRiskList(String companyName, PageQuery pageQuery); + + /** + * 获取指定企业的隐患统计总览 + * + * @param companyId 企业ID + * @return 总览信息 + */ + GovHiddenDangerSummaryVo getCompanyHiddenDangerSummary(Long companyId); + + /** + * 获取学习折线图数据 + * + * @param type 时间周期:week, month + * @param beginTime 开始时间 + * @param endTime 结束时间 + * @return 学习统计数据列表 + */ + List getTrainingStats(String type, String beginTime, String endTime); + + /** + * 获取事故等级统计数据 + * + * @return 事故等级统计列表 + */ + List getAccidentLevelStats(); +} diff --git a/src/main/java/com/hotwj/platform/workbench/service/IHeadquartersWorkbenchService.java b/src/main/java/com/hotwj/platform/workbench/service/IHeadquartersWorkbenchService.java new file mode 100644 index 0000000..bd240a4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/service/IHeadquartersWorkbenchService.java @@ -0,0 +1,107 @@ +package com.hotwj.platform.workbench.service; + +import com.hotwj.platform.workbench.domain.vo.*; + +import java.util.List; + +/** + * 总部端工作台Service接口 + * + * @author shihongwei + */ +public interface IHeadquartersWorkbenchService { + + /** + * 获取总部端培训计划统计 + * + * @return 统计数据列表 + */ + List getTrainingStats(); + + /** + * 获取某年月度学习折线图数据 + * + * @param year 年份,如 2025 + * @param month 月份,1-12 + * @return 折线图数据(按日) + */ + MonthlyStudyTrendVo getMonthlyStudyTrend(int year, int month); + + /** + * 获取本年(或指定年)违规统计柱状图(按月) + * + * @param year 年份,null 则取当前年 + */ + ViolationMonthlyBarVo getViolationMonthlyStats(Integer year); + + /** + * 获取总部端隐患统计概览 + * + * @param regionCode 区域代码(可选) + * @return 隐患统计概览 + */ + HeadquartersHiddenDangerSummaryVo getHeadquartersHiddenDangerSummary(String regionCode); + + /** + * 获取区域企业统计 + * + * @param regionCode 区域代码 + * @return 区域企业统计 + */ + RegionCompanyStatsVo getRegionCompanyStats(String regionCode); + + /** + * 获取企业发展趋势统计 + * + * @return 企业发展趋势统计 + */ + HeadquartersEnterpriseTrendVo getEnterpriseTrendStats(); + + /** + * 获取从业人员发展趋势统计 + * + * @return 从业人员发展趋势统计 + */ + HeadquartersPersonnelTrendVo getPersonnelTrendStats(); + + /** + * 获取车辆发展趋势统计 + * + * @return 车辆发展趋势统计 + */ + HeadquartersVehicleTrendVo getVehicleTrendStats(); + /** + * 获取事故年度对比统计 + * + * @return 年度对比数据 + */ + HeadquartersYearlyComparisonVo getAccidentYearlyComparison(); + + /** + * 获取隐患年度对比统计 + * + * @return 年度对比数据 + */ + HeadquartersYearlyComparisonVo getHiddenDangerYearlyComparison(); + + /** + * 获取企业列表(含管理人员数) + * + * @return 企业列表数据 + */ + HeadquartersCompanyListVo getCompanyList(); + + /** + * 获取广场信息审核中数量(auditStatus=0) + * + * @return 审核中数量 + */ + Long getSquareInfoPendingAuditCount(); + + /** + * 获取投诉建议未回复数量(isReplied=0) + * + * @return 未回复数量 + */ + Long getComplaintAdvicePendingReplyCount(); +} diff --git a/src/main/java/com/hotwj/platform/workbench/service/IHotHiddenDangerDailyStatsService.java b/src/main/java/com/hotwj/platform/workbench/service/IHotHiddenDangerDailyStatsService.java new file mode 100644 index 0000000..823ba26 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/service/IHotHiddenDangerDailyStatsService.java @@ -0,0 +1,37 @@ +package com.hotwj.platform.workbench.service; + +import com.hotwj.platform.workbench.domain.vo.HiddenDangerCountVo; + +import java.util.List; + +/** + * 隐患日统计Service接口 + * + * @author shihongwei + * @date 2026-02-25 + */ +public interface IHotHiddenDangerDailyStatsService { + + /** + * 生成每日隐患统计数据(定时任务调用) + */ + void generateDailyStats(); + + /** + * 获取指定日期区间的隐患日统计(按企业分组) + * @param companyId 企业ID + * @param startDate 开始日期 + * @param endDate 结束日期 + * @return 统计列表 + */ + List getCompanyDailyStats(Long companyId, String startDate, String endDate); + + /** + * 获取指定日期区间的辖区隐患日统计(按日期分组) + * @param regionCode 行政区划 + * @param startDate 开始日期 + * @param endDate 结束日期 + * @return 统计列表 + */ + List getRegionDailyStats(String regionCode, String startDate, String endDate); +} diff --git a/src/main/java/com/hotwj/platform/workbench/service/IWorkbenchService.java b/src/main/java/com/hotwj/platform/workbench/service/IWorkbenchService.java new file mode 100644 index 0000000..e2eaa77 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/service/IWorkbenchService.java @@ -0,0 +1,53 @@ +package com.hotwj.platform.workbench.service; + +import com.hotwj.platform.workbench.domain.vo.*; + +import java.util.List; + +/** + * 企业端首页 Service接口 + * + * @author shihongwei + * @date 2026-02-24 + */ +public interface IWorkbenchService { + + /** + * 获取公司基本信息 + */ + CompanyBasicInfoVo getCompanyBasicInfo(); + + /** + * 获取车辆/驾驶员总数比例 + */ + VehicleDriverRatioVo getVehicleDriverRatio(); + + /** + * 获取培训使用情况 + */ + TrainingUsageVo getTrainingUsage(); + + /** + * 获取风险提醒信息 + */ + RiskReminderVo getRiskReminder(); + + /** + * 获取快捷入口统计 + */ + ShortcutStatisticsVo getShortcutStatistics(); + + /** + * 获取隐患排查统计 + */ + HiddenDangerStatsVo getHiddenDangerStats(); + + /** + * 获取学习折线图数据 + * + * @param type 类型:week=上周, month=上月 + * @param beginTime 开始时间 + * @param endTime 结束时间 + */ + List getLearningStats(String type, String beginTime, String endTime); +} diff --git a/src/main/java/com/hotwj/platform/workbench/service/impl/GovWorkbenchServiceImpl.java b/src/main/java/com/hotwj/platform/workbench/service/impl/GovWorkbenchServiceImpl.java new file mode 100644 index 0000000..c7f4d70 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/service/impl/GovWorkbenchServiceImpl.java @@ -0,0 +1,494 @@ +package com.hotwj.platform.workbench.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.hotwj.platform.common.mapper.PublicMapper; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.domain.vo.HotGovEnterpriseUnitVo; +import com.hotwj.platform.resourceManagement.govEnterpriseUnit.service.IHotGovEnterpriseUnitService; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.HotTrainingParticipant; +import com.hotwj.platform.workbench.domain.entity.HotHiddenDangerDailyStats; +import com.hotwj.platform.workbench.domain.enums.GovHiddenDangerType; +import com.hotwj.platform.workbench.domain.vo.*; +import com.hotwj.platform.workbench.mapper.GovWorkbenchMapper; +import com.hotwj.platform.workbench.mapper.HotHiddenDangerDailyStatsMapper; +import com.hotwj.platform.workbench.service.IGovWorkbenchService; +import com.hotwj.platform.workbench.service.IHotHiddenDangerDailyStatsService; +import com.hotwj.platform.workbench.strategy.IGovRiskHandler; +import com.hotwj.platform.workbench.strategy.impl.VehicleAnnualInspectRiskHandler; +import com.hotwj.platform.workbench.strategy.impl.VehicleMaintenanceRiskHandler; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAdjusters; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 政府端工作台Service业务层处理 + * + * @author shihongwei + */ +@RequiredArgsConstructor +@Service +public class GovWorkbenchServiceImpl implements IGovWorkbenchService { + + private final IHotGovEnterpriseUnitService hotGovEnterpriseUnitService; + private final GovWorkbenchMapper govWorkbenchMapper; + private final HotHiddenDangerDailyStatsMapper dailyStatsMapper; + private final PublicMapper publicMapper; + private final List riskHandlers; + private final IHotHiddenDangerDailyStatsService dailyStatsService; + private final VehicleAnnualInspectRiskHandler vehicleAnnualInspectRiskHandler; + private final VehicleMaintenanceRiskHandler vehicleMaintenanceRiskHandler; + + @Override + public GovHomeStatisticsVo getHomeStatistics() { + String districtCode = getDistrictCode(); + + GovHomeStatisticsVo vo = new GovHomeStatisticsVo(); + vo.setEnterpriseCount(govWorkbenchMapper.selectEnterpriseCount(districtCode)); + vo.setVehicleCount(govWorkbenchMapper.selectVehicleCount(districtCode)); + vo.setDriverCount(govWorkbenchMapper.selectDriverCount(districtCode)); + + GovHiddenDangerSummaryVo hiddenDangerSummary = buildHiddenDangerSummary(districtCode, null); + vo.setHiddenDangerCount(hiddenDangerSummary.getTotalCount()); + + // 翻译区划名称 + String regionName = publicMapper.selectRegionFullNameByCode(districtCode); + vo.setJurisdictionName(regionName); + + // 获取辖区企业列表 + vo.setEnterpriseList(govWorkbenchMapper.selectEnterpriseList(districtCode, null)); + + return vo; + } + + @Override + public GovHiddenDangerSummaryVo getHiddenDangerSummary(String regionCode) { + String districtCode = getDistrictCode(); + if (StringUtils.isNotBlank(regionCode)) { + // 如果传入了区域代码,进行权限校验 (确保传入的区域代码是当前用户辖区的子集或相等) + if (!regionCode.startsWith(districtCode)) { + throw new ServiceException("无权查询该区域数据"); + } + districtCode = regionCode; + } + + // 处理行政区划代码,支持省级和市级查询 (去掉末尾的00) + // 例如:510000 -> 51, 510100 -> 5101 + if (districtCode.endsWith("0000")) { + districtCode = districtCode.substring(0, 2); + } else if (districtCode.endsWith("00")) { + districtCode = districtCode.substring(0, 4); + } + + return buildHiddenDangerSummary(districtCode, null); + } + + @Override + public GovHiddenDangerSummaryVo getCompanyHiddenDangerSummary(Long companyId) { + return buildHiddenDangerSummary(getDistrictCode(), companyId); + } + + private GovHiddenDangerSummaryVo buildHiddenDangerSummary(String districtCode, Long companyId) { + Map handlerMap = riskHandlers.stream() + .collect(Collectors.toMap(IGovRiskHandler::getType, h -> h)); + + GovHiddenDangerSummaryVo summary = new GovHiddenDangerSummaryVo(); + List groups = new ArrayList<>(); + long totalCount = 0; + + // 按大项分组 + Map> groupMap = new HashMap<>(); + for (GovHiddenDangerType type : GovHiddenDangerType.values()) { + groupMap.computeIfAbsent(type.getGroupKey(), k -> new ArrayList<>()).add(type); + } + + // 遍历所有大项 (按固定顺序: enterprise, vehicle, personnel) + String[] groupKeys = {"enterprise", "vehicle", "personnel"}; + for (String groupKey : groupKeys) { + List types = groupMap.get(groupKey); + if (types == null) continue; + + GovHiddenDangerGroupVo groupVo = new GovHiddenDangerGroupVo(); + groupVo.setKey(groupKey); + // 从第一个type获取label + groupVo.setLabel(types.get(0).getGroupLabel()); + + List items = new ArrayList<>(); + long groupTotal = 0; + + for (GovHiddenDangerType type : types) { + GovHiddenDangerItemVo itemVo = new GovHiddenDangerItemVo(); + itemVo.setKey(type.getKey()); + itemVo.setLabel(type.getLabel()); + + IGovRiskHandler handler = handlerMap.get(type.getKey()); + long count = 0; + if (handler != null) { + count = handler.count(districtCode, companyId); + } + itemVo.setCount(count); + items.add(itemVo); + groupTotal += count; + } + groupVo.setItems(items); + groupVo.setCount(groupTotal); + groups.add(groupVo); + totalCount += groupTotal; + + // 设置分类统计总数 + if ("enterprise".equals(groupKey)) { + summary.setEnterpriseCount(groupTotal); + } else if ("vehicle".equals(groupKey)) { + summary.setVehicleCount(groupTotal); + } else if ("personnel".equals(groupKey)) { + summary.setPersonnelCount(groupTotal); + } + } + + summary.setGroups(groups); + summary.setTotalCount(totalCount); + return summary; + } + + @Override + public TableDataInfo getHiddenDangerList(String type, Long companyId, PageQuery pageQuery) { + // 1. 转为 Map + Map handlerMap = riskHandlers.stream() + .collect(Collectors.toMap(IGovRiskHandler::getType, h -> h)); + + // 2. 获取处理器 + IGovRiskHandler handler = handlerMap.get(type); + if (handler == null) { + return TableDataInfo.build(new ArrayList<>()); + } + + // 3. 执行查询 + return handler.list(getDistrictCode(), companyId, pageQuery); + } + + @Override + public List getHiddenDangerCountByMonth(Long companyId, String startDate, String endDate) { + String districtCode = getDistrictCode(); + // 1. 如果没有传入日期,默认查询最近一个月(30天) + LocalDate end = StringUtils.isBlank(endDate) ? LocalDate.now() : LocalDate.parse(endDate); + LocalDate start = StringUtils.isBlank(startDate) ? end.minusDays(29) : LocalDate.parse(startDate); + + // 2. 查询数据库数据 (从日统计表查询) + List dbData; + if (companyId != null) { + dbData = dailyStatsService.getCompanyDailyStats(companyId, start.toString(), end.toString()); + } else { + dbData = dailyStatsService.getRegionDailyStats(districtCode, start.toString(), end.toString()); + } + + Map dataMap = dbData.stream() + .collect(Collectors.toMap(HiddenDangerCountVo::getDate, HiddenDangerCountVo::getCount, (k1, k2) -> k1 + k2)); + + // 3. 补全日期区间 + List result = new ArrayList<>(); + for (LocalDate date = start; !date.isAfter(end); date = date.plusDays(1)) { + String dateStr = date.toString(); + HiddenDangerCountVo vo = new HiddenDangerCountVo(); + vo.setDate(dateStr); + vo.setCount(dataMap.getOrDefault(dateStr, 0L)); + result.add(vo); + } + + return result; + } + + @Override + public GovCompanyRiskTableDataInfo getCompanyRiskList(String companyName, PageQuery pageQuery) { + String districtCode = getDistrictCode(); + + // 1. 获取所有企业风险 (Map) + Map statsMap = calculateAllRisksMap(districtCode); + + // 2. 计算辖区总隐患数 + long totalJurisdictionRisk = statsMap.values().stream().mapToLong(GovCompanyRiskStatsVo::getTotalRisk).sum(); + + // 3. 获取符合条件的企业列表 + List enterprises = govWorkbenchMapper.selectEnterpriseList(districtCode, companyName); + + // 4. 组装结果列表 + List resultList = new ArrayList<>(); + for (GovHomeStatisticsVo.EnterpriseInfo ent : enterprises) { + GovCompanyRiskStatsVo stats = statsMap.get(ent.getId()); + if (stats == null) { + // 企业无隐患记录,补零 + stats = new GovCompanyRiskStatsVo(); + stats.setCompanyId(ent.getId()); + stats.setCompanyName(ent.getCompanyName()); + stats.setEnterpriseRisk(0); + stats.setVehicleRisk(0); + stats.setPersonnelRisk(0); + stats.setTotalRisk(0); + } else { + stats.setCompanyName(ent.getCompanyName()); + } + resultList.add(stats); + } + + // 5. 排序 (按总隐患数倒序) + resultList.sort((a, b) -> b.getTotalRisk().compareTo(a.getTotalRisk())); + + // 6. 分页 + int total = resultList.size(); + int pageNum = pageQuery.getPageNum(); + int pageSize = pageQuery.getPageSize(); + int start = (pageNum - 1) * pageSize; + int end = Math.min(start + pageSize, total); + + List pageList; + if (start > total) { + pageList = new ArrayList<>(); + } else { + pageList = resultList.subList(start, end); + } + + GovCompanyRiskTableDataInfo result = new GovCompanyRiskTableDataInfo(); + result.setRows(pageList); + result.setTotal(total); + result.setCode(200); + result.setMsg("查询成功"); + result.setTotalRiskCount(totalJurisdictionRisk); + + return result; + } + + @Override + public List getTopRiskCompanies(int limit) { + String districtCode = getDistrictCode(); + + // 1. 获取所有企业风险 + Map statsMap = calculateAllRisksMap(districtCode); + + // 2. 获取辖区所有企业名称 (不传名称过滤) + List enterprises = govWorkbenchMapper.selectEnterpriseList(districtCode, null); + Map companyNames = enterprises.stream() + .collect(Collectors.toMap(GovHomeStatisticsVo.EnterpriseInfo::getId, GovHomeStatisticsVo.EnterpriseInfo::getCompanyName)); + + List resultList = new ArrayList<>(); + for (GovCompanyRiskStatsVo stats : statsMap.values()) { + String name = companyNames.get(stats.getCompanyId()); + if (name != null) { + stats.setCompanyName(name); + resultList.add(stats); + } + } + + // 3. 排序取TOP + return resultList.stream() + .sorted((a, b) -> b.getTotalRisk().compareTo(a.getTotalRisk())) + .limit(limit) + .collect(Collectors.toList()); + } + + private Map calculateAllRisksMap(String districtCode) { + // 1. 获取简单隐患 + List simpleRisks = govWorkbenchMapper.selectSimpleRiskCountsGroupedByCompany(districtCode); + + Map statsMap = new HashMap<>(); + + // 2. 聚合简单隐患 + for (CompanyRiskCountVo vo : simpleRisks) { + GovCompanyRiskStatsVo stats = getOrCreateStats(statsMap, vo.getCompanyId()); + + int count = vo.getCount() == null ? 0 : vo.getCount(); + if ("enterprise".equals(vo.getCategory())) { + stats.setEnterpriseRisk(stats.getEnterpriseRisk() + count); + } else if ("vehicle".equals(vo.getCategory())) { + stats.setVehicleRisk(stats.getVehicleRisk() + count); + } else if ("personnel".equals(vo.getCategory())) { + stats.setPersonnelRisk(stats.getPersonnelRisk() + count); + } + } + + // 3. 聚合复杂车辆隐患 + Map annualInspectRisks = vehicleAnnualInspectRiskHandler.countByCompany(districtCode); + mergeRealTimeVehicleRisks(statsMap, annualInspectRisks); + + Map maintenanceRisks = vehicleMaintenanceRiskHandler.countByCompany(districtCode); + mergeRealTimeVehicleRisks(statsMap, maintenanceRisks); + + // 4. 计算总数 + for (GovCompanyRiskStatsVo stats : statsMap.values()) { + stats.setTotalRisk(stats.getEnterpriseRisk() + stats.getVehicleRisk() + stats.getPersonnelRisk()); + } + + return statsMap; + } + + private GovCompanyRiskStatsVo getOrCreateStats(Map statsMap, Long companyId) { + return statsMap.computeIfAbsent(companyId, k -> { + GovCompanyRiskStatsVo s = new GovCompanyRiskStatsVo(); + s.setCompanyId(k); + s.setEnterpriseRisk(0); + s.setVehicleRisk(0); + s.setPersonnelRisk(0); + s.setTotalRisk(0); + return s; + }); + } + + private void mergeRealTimeVehicleRisks(Map statsMap, Map risks) { + for (Map.Entry entry : risks.entrySet()) { + Long companyId = entry.getKey(); + Integer count = entry.getValue(); + if (count == null || count == 0) continue; + + GovCompanyRiskStatsVo stats = getOrCreateStats(statsMap, companyId); + stats.setVehicleRisk(stats.getVehicleRisk() + count); + } + } + + @Override + public List getTrainingStats(String type, String beginTime, String endTime) { + String districtCode = getDistrictCode(); + List result = new ArrayList<>(); + + LocalDate today = LocalDate.now(); + LocalDate start; + LocalDate end; + + if (StringUtils.isNotBlank(beginTime) && StringUtils.isNotBlank(endTime)) { + start = LocalDate.parse(beginTime); + end = LocalDate.parse(endTime); + } else if ("month".equals(type)) { + // 上个自然月 + start = today.minusMonths(1).with(TemporalAdjusters.firstDayOfMonth()); + end = today.minusMonths(1).with(TemporalAdjusters.lastDayOfMonth()); + } else { + // 上周一到周日 + LocalDate oneWeekAgo = today.minusWeeks(1); + start = oneWeekAgo.with(DayOfWeek.MONDAY); + end = oneWeekAgo.with(DayOfWeek.SUNDAY); + } + + DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + String endDateStr = end.format(fmt) + " 23:59:59"; + + List participants = govWorkbenchMapper.selectTrainingParticipants(districtCode, endDateStr); + + long days = ChronoUnit.DAYS.between(start, end) + 1; + for (int i = 0; i < days; i++) { + LocalDate currentDate = start.plusDays(i); + String date = currentDate.format(fmt); + GovTrainingStatsVo vo = new GovTrainingStatsVo(); + vo.setDate(date); + + if ("month".equals(type)) { + vo.setLabel(currentDate.format(DateTimeFormatter.ofPattern("MM-dd"))); + } else { +// vo.setLabel(currentDate.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.CHINA)); + vo.setLabel(currentDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + } + + // 当天的结束时间 + Date dayEnd = Date.from(currentDate.atTime(LocalTime.MAX).atZone(ZoneId.systemDefault()).toInstant()); + + long total = 0; + long completed = 0; + + for (HotTrainingParticipant p : participants) { + // 1. 检查是否在当天或之前已创建/分配 + if (p.getCreateTime() != null && !p.getCreateTime().after(dayEnd)) { + total++; + + // 2. 检查是否在当天或之前已完成 + if (p.getIsCompleted() != null && p.getIsCompleted() == 1L && + p.getCompleteTime() != null && !p.getCompleteTime().after(dayEnd)) { + completed++; + } + } + } + + vo.setCompleted(completed); + vo.setIncomplete(total - completed); + result.add(vo); + } + + return result; + } + + @Override + public List getAccidentLevelStats() { + String districtCode = getDistrictCode(); + List dbList = govWorkbenchMapper.selectAccidentLevelStats(districtCode); + + // Define the fixed levels and their order + List fixedLevels = Arrays.asList("轻微事故", "一般事故", "重大事故", "特大事故"); + + // Convert DB results to a map for easy lookup + Map countMap = dbList.stream() + .collect(Collectors.toMap(GovAccidentStatsVo::getLevelName, GovAccidentStatsVo::getCount)); + + // Build the final result list with all 4 levels, preserving order + List result = new ArrayList<>(); + for (String level : fixedLevels) { + GovAccidentStatsVo vo = new GovAccidentStatsVo(); + vo.setLevelName(level); + vo.setCount(countMap.getOrDefault(level, 0L)); + result.add(vo); + } + + return result; + } + + /** + * 获取最近有数据的统计日期(今天或昨天) + */ + private String getLatestStatsDate(String districtCode) { + LocalDate today = LocalDate.now(); + // 检查今日是否有数据 + Long count = dailyStatsMapper.selectCount(new LambdaQueryWrapper() + .eq(HotHiddenDangerDailyStats::getStatsDate, today) + .apply("region_code LIKE CONCAT({0}, '%')", districtCode)); + + if (count != null && count > 0) { + return today.toString(); + } + // 如果今日无数据,尝试昨天 + return today.minusDays(1).toString(); + } + + /** + * 获取当前登录用户的行政区划代码 + */ + private String getDistrictCode() { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null) { + throw new ServiceException("未登录"); + } + // 假设 gov_enterprise_unit 表存储了政府用户的区划信息,或者直接从 user 表获取 + // 这里简化处理,直接从关联的 gov_enterprise_unit 获取 + Long unitId = loginUser.getCompanyId(); // 假设 deptId 对应 unitId + if (unitId == null) { + throw new ServiceException("未关联政府单位"); + } + HotGovEnterpriseUnitVo unit = hotGovEnterpriseUnitService.queryById(unitId); + if (unit == null || StringUtils.isBlank(unit.getDistrictCode())) { + throw new ServiceException("未配置行政区划"); + } + String code = unit.getDistrictCode(); + if (code.endsWith("0000")) { + return code.substring(0, 2); + } else if (code.endsWith("00")) { + return code.substring(0, 4); + } + return code; + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/service/impl/HeadquartersWorkbenchServiceImpl.java b/src/main/java/com/hotwj/platform/workbench/service/impl/HeadquartersWorkbenchServiceImpl.java new file mode 100644 index 0000000..697ca2e --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/service/impl/HeadquartersWorkbenchServiceImpl.java @@ -0,0 +1,696 @@ +package com.hotwj.platform.workbench.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.hotwj.platform.config.squareInfo.domain.HotSquareInfo; +import com.hotwj.platform.config.squareInfo.mapper.HotSquareInfoMapper; +import com.hotwj.platform.noticeManagerment.complaintAdvice.domain.HotComplaintAdvice; +import com.hotwj.platform.noticeManagerment.complaintAdvice.mapper.HotComplaintAdviceMapper; +import com.hotwj.platform.workbench.domain.vo.*; +import com.hotwj.platform.workbench.mapper.HeadquartersWorkbenchMapper; +import com.hotwj.platform.workbench.service.IHeadquartersWorkbenchService; +import lombok.RequiredArgsConstructor; +import org.dromara.system.domain.vo.SysDictDataVo; +import org.dromara.system.service.ISysDictTypeService; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 总部端工作台Service业务层处理 + * + * @author shihongwei + */ +@RequiredArgsConstructor +@Service +public class HeadquartersWorkbenchServiceImpl implements IHeadquartersWorkbenchService { + + private final HeadquartersWorkbenchMapper headquartersWorkbenchMapper; + private final ISysDictTypeService sysDictTypeService; + private final HotSquareInfoMapper hotSquareInfoMapper; + private final HotComplaintAdviceMapper hotComplaintAdviceMapper; + + @Override + public List getTrainingStats() { + // 1. 获取所有启用的培训类型字典 + List dictDataList = sysDictTypeService.selectDictDataByType("hot_training_type"); + if (dictDataList != null) { + dictDataList = dictDataList.stream() + .filter(item -> !"offline".equals(item.getDictValue())) + .collect(Collectors.toList()); + } + + // 2. 统计各类型培训计划数量 + List statsList = headquartersWorkbenchMapper.selectTrainingTypeStats(); + + // 将统计结果转为 Map + Map countMap = statsList.stream() + .collect(Collectors.toMap( + r -> r.getType(), + r -> r.getCount(), + (v1, v2) -> v1 + )); + + // 3. 组装结果 + List result = new ArrayList<>(); + if (dictDataList != null) { + for (SysDictDataVo dict : dictDataList) { + Long count = countMap.getOrDefault(dict.getDictValue(), 0L); + result.add(new HeadquartersTrainingStatsVo(dict.getDictLabel(), count)); + } + } + + return result; + } + + /** + * 获取总部端某年月度学习折线图数据

+ *

+ * 口径说明:

+ * 1) 数据来源:hot_training_daily_stats(日统计表),按天汇总;仅统计已启用且未删除的企业(sys_company.status=1 且 is_deleted=0)。

+ * 2) 统计区间:当月1日 00:00:00 至 当月最后1日 23:59:59(startDate、endDate 均为包含边界)。

+ * 3) 指标定义:completed 来源于 daily_stats.completed_count,uncompleted 来源于 daily_stats.uncompleted_count。

+ * 4) 缺失补零:若某天无数据,返回 0,确保前端 X 轴为整月完整日历。

+ * + * @param year 年份(如 2025) + * @param month 月份(1-12) + * @return 月度折线图数据(dates、completed、uncompleted 一一对应) + */ + @Override + public MonthlyStudyTrendVo getMonthlyStudyTrend(int year, int month) { + Calendar cal = new GregorianCalendar(year, month - 1, 1, 0, 0, 0); + cal.set(Calendar.MILLISECOND, 0); + Date startDate = cal.getTime(); + cal.add(Calendar.MONTH, 1); + cal.add(Calendar.DAY_OF_MONTH, -1); + Date endDate = cal.getTime(); + + List rows = headquartersWorkbenchMapper.selectMonthlyStudyTrendFromDailyStats(startDate, endDate); + + // 根据日期分组 + Map map = rows == null ? new HashMap<>() : rows.stream() + .collect(Collectors.toMap(MonthlyTrendRow::getDay, r -> r, (a, b) -> a)); + + List dates = new ArrayList<>(); + List completed = new ArrayList<>(); + List uncompleted = new ArrayList<>(); + + // 填充缺失日期 + Calendar iter = new GregorianCalendar(year, month - 1, 1, 0, 0, 0); + iter.set(Calendar.MILLISECOND, 0); + Calendar endExclusive = new GregorianCalendar(year, month - 1, 1, 0, 0, 0); + endExclusive.set(Calendar.MILLISECOND, 0); + endExclusive.add(Calendar.MONTH, 1); + while (iter.getTime().before(endExclusive.getTime())) { + int y = iter.get(Calendar.YEAR); + int m = iter.get(Calendar.MONTH) + 1; + int d = iter.get(Calendar.DAY_OF_MONTH); + String dayStr = String.format("%04d-%02d-%02d", y, m, d); + dates.add(dayStr); + MonthlyTrendRow r = map.get(dayStr); + completed.add(r != null && r.getCompleted() != null ? r.getCompleted() : 0L); + uncompleted.add(r != null && r.getUncompleted() != null ? r.getUncompleted() : 0L); + iter.add(Calendar.DAY_OF_MONTH, 1); + } + + // 3. 组装结果 + return new MonthlyStudyTrendVo(dates, completed, uncompleted); + } + + @Override + public ViolationMonthlyBarVo getViolationMonthlyStats(Integer year) { + Calendar cal = new GregorianCalendar(); + int y = (year == null || year <= 0) ? cal.get(Calendar.YEAR) : year; + cal.set(y, Calendar.JANUARY, 1, 0, 0, 0); + cal.set(Calendar.MILLISECOND, 0); + Date startDate = cal.getTime(); + cal.add(Calendar.YEAR, 1); + cal.add(Calendar.DAY_OF_MONTH, -1); + Date endDate = cal.getTime(); + + List rows = headquartersWorkbenchMapper.selectViolationMonthlyCounts(startDate, endDate); + Map monthCount = new HashMap<>(); + if (rows != null) { + for (ViolationMonthlyRow r : rows) { + try { + int m = Integer.parseInt(r.getMonth()); + monthCount.put(m, r.getCnt() == null ? 0L : r.getCnt()); + } catch (Exception ignored) { + } + } + } + + List months = new ArrayList<>(); + List values = new ArrayList<>(); + for (int i = 1; i <= 12; i++) { + months.add(i + "月"); + values.add(monthCount.getOrDefault(i, 0L)); + } + ViolationMonthlyBarVo vo = new ViolationMonthlyBarVo(); + vo.setMonths(months); + vo.setValues(values); + return vo; + } + + @Override + public HeadquartersHiddenDangerSummaryVo getHeadquartersHiddenDangerSummary(String regionCode) { + // 处理区域代码:如果是省/市级别(以00结尾),截取有效前缀 + if (regionCode != null && regionCode.length() == 6) { + if (regionCode.endsWith("0000")) { + regionCode = regionCode.substring(0, 2); + } else if (regionCode.endsWith("00")) { + regionCode = regionCode.substring(0, 4); + } + } + HeadquartersHiddenDangerSummaryVo vo = headquartersWorkbenchMapper.selectLatestHiddenDangerSummary(regionCode); + if (vo == null) { + vo = new HeadquartersHiddenDangerSummaryVo(); + vo.setEnterpriseCount(0L); + vo.setVehicleCount(0L); + vo.setPersonnelCount(0L); + vo.setTotalCount(0L); + } else { + long ent = vo.getEnterpriseCount() == null ? 0L : vo.getEnterpriseCount(); + long veh = vo.getVehicleCount() == null ? 0L : vo.getVehicleCount(); + long per = vo.getPersonnelCount() == null ? 0L : vo.getPersonnelCount(); + vo.setEnterpriseCount(ent); + vo.setVehicleCount(veh); + vo.setPersonnelCount(per); + vo.setTotalCount(ent + veh + per); + } + return vo; + } + + private static final Set PASSENGER_LABELS = new HashSet<>(Arrays.asList( + "城市公共汽电客运", "道路旅客运输", "网约车", "巡游出租汽车", "水路旅客运输", "港口客运码头" + )); + + private static final Set GENERAL_LABELS = new HashSet<>(Arrays.asList( + "道路普通货物运输", "道路货运场站", "水路普通货物运输", "港口普货码头", "渣土(普货)", "商砼(普货)" + )); + + private static final Set DANGEROUS_LABELS = new HashSet<>(Arrays.asList( + "道路危险货物运输", "水路危险货物运输", "港口危货码头" + )); + + @Override + public RegionCompanyStatsVo getRegionCompanyStats(String regionCode) { + // 处理区域代码:如果是省/市级别(以00结尾),截取有效前缀 + if (regionCode != null && regionCode.length() == 6) { + if (regionCode.endsWith("0000")) { + regionCode = regionCode.substring(0, 2); + } else if (regionCode.endsWith("00")) { + regionCode = regionCode.substring(0, 4); + } + } + + // 1. 获取所有行业字典数据 + List industryDicts = sysDictTypeService.selectDictDataByType("hot_industry"); + if (industryDicts == null) { + industryDicts = new ArrayList<>(); + } + + // 2. 查询数据库统计数据 + List> dbStats = headquartersWorkbenchMapper.selectCompanyIndustryCounts(regionCode); + + // 3. 构建映射关系:行业Code -> 分类(普货/危货/客运/其他) + Map categoryMap = new HashMap<>(); + for (SysDictDataVo dict : industryDicts) { + String label = dict.getDictLabel(); + String code = dict.getDictValue(); + String category = "其他"; + if (label != null) { + // 去除可能存在的首尾空格 + label = label.trim(); + if (PASSENGER_LABELS.contains(label)) { + category = "客运"; + } else if (GENERAL_LABELS.contains(label)) { + category = "普货"; + } else if (DANGEROUS_LABELS.contains(label)) { + category = "危货"; + } + } + categoryMap.put(code, category); + } + + // 4. 聚合统计 + RegionCompanyStatsVo vo = new RegionCompanyStatsVo(); + long total = 0; + + if (dbStats != null) { + for (Map stat : dbStats) { + String industryStr = (String) stat.get("industry"); + Object countObj = stat.get("count"); + long count = countObj == null ? 0L : ((Number) countObj).longValue(); + total += count; + + if (industryStr == null || industryStr.isEmpty()) { + vo.setOtherCount(vo.getOtherCount() + count); + continue; + } + + // 处理可能存在的多个行业(中英文逗号分隔) + String[] industries = industryStr.split("[,,]"); + Set categoriesFound = new HashSet<>(); + + for (String industry : industries) { + if (industry == null || industry.trim().isEmpty()) { + continue; + } + String category = categoryMap.getOrDefault(industry.trim(), "其他"); + categoriesFound.add(category); + } + + // 如果没有找到有效分类(例如全是空字符串),归为其他 + if (categoriesFound.isEmpty()) { + vo.setOtherCount(vo.getOtherCount() + count); + } else { + for (String category : categoriesFound) { + switch (category) { + case "普货": + vo.setGeneralCargoCount(vo.getGeneralCargoCount() + count); + break; + case "危货": + vo.setDangerousGoodsCount(vo.getDangerousGoodsCount() + count); + break; + case "客运": + vo.setPassengerTransportCount(vo.getPassengerTransportCount() + count); + break; + default: + vo.setOtherCount(vo.getOtherCount() + count); + } + } + } + } + } + vo.setTotalCount(total); + return vo; + } + + @Override + public HeadquartersEnterpriseTrendVo getEnterpriseTrendStats() { + // 1. 获取所有行业字典数据 + List industryDicts = sysDictTypeService.selectDictDataByType("hot_industry"); + if (industryDicts == null) { + industryDicts = new ArrayList<>(); + } + + // 构建行业代码到分类的映射 + Map categoryMap = new HashMap<>(); + for (SysDictDataVo dict : industryDicts) { + String label = dict.getDictLabel(); + String code = dict.getDictValue(); + String category = "其他"; + if (label != null) { + label = label.trim(); + if (PASSENGER_LABELS.contains(label)) { + category = "客运"; + } else if (GENERAL_LABELS.contains(label)) { + category = "普货"; + } else if (DANGEROUS_LABELS.contains(label)) { + category = "危货"; + } + } + categoryMap.put(code, category); + } + + // 2. 获取所有企业的创建年份和行业 + List> companyList = headquartersWorkbenchMapper.selectAllCompanyCreateYears(); + + int currentYear = java.time.LocalDate.now().getYear(); + int startYear = currentYear; + if (companyList != null && !companyList.isEmpty()) { + Object yearObj = companyList.get(0).get("year"); + if (yearObj != null) { + startYear = ((Number) yearObj).intValue(); + } + } + + // 3. 按年份分组 + Map> yearToIndustries = new HashMap<>(); + if (companyList != null) { + for (Map row : companyList) { + Object yearObj = row.get("year"); + String industry = (String) row.get("industry"); + if (yearObj != null) { + int y = ((Number) yearObj).intValue(); + yearToIndustries.computeIfAbsent(y, k -> new ArrayList<>()).add(industry); + } + } + } + + // 4. 计算累积趋势 + List years = new ArrayList<>(); + List totalCounts = new ArrayList<>(); + List generalCounts = new ArrayList<>(); + List dangerousCounts = new ArrayList<>(); + List passengerCounts = new ArrayList<>(); + List otherCounts = new ArrayList<>(); + + long currentTotal = 0; + long currentGeneral = 0; + long currentDangerous = 0; + long currentPassenger = 0; + long currentOther = 0; + + for (int y = startYear; y <= currentYear; y++) { + List industriesInYear = yearToIndustries.get(y); + if (industriesInYear != null) { + for (String industryStr : industriesInYear) { + currentTotal++; // 每一个企业ID都是唯一的,所以总数直接+1 + + if (industryStr == null || industryStr.isEmpty()) { + currentOther++; + continue; + } + + String[] industries = industryStr.split("[,,]"); + Set categoriesFound = new HashSet<>(); + for (String ind : industries) { + if (ind == null || ind.trim().isEmpty()) continue; + String category = categoryMap.getOrDefault(ind.trim(), "其他"); + categoriesFound.add(category); + } + + if (categoriesFound.isEmpty()) { + currentOther++; + } else { + for (String category : categoriesFound) { + switch (category) { + case "普货": + currentGeneral++; + break; + case "危货": + currentDangerous++; + break; + case "客运": + currentPassenger++; + break; + default: + currentOther++; + } + } + } + } + } + + years.add(y); + totalCounts.add(currentTotal); + generalCounts.add(currentGeneral); + dangerousCounts.add(currentDangerous); + passengerCounts.add(currentPassenger); + otherCounts.add(currentOther); + } + + HeadquartersEnterpriseTrendVo vo = new HeadquartersEnterpriseTrendVo(); + vo.setYears(years); + vo.setTotalCounts(totalCounts); + vo.setGeneralCounts(generalCounts); + vo.setDangerousCounts(dangerousCounts); + vo.setPassengerCounts(passengerCounts); + vo.setOtherCounts(otherCounts); + return vo; + } + + @Override + public HeadquartersPersonnelTrendVo getPersonnelTrendStats() { + // 1. 获取人员类型字典 + List typeDicts = sysDictTypeService.selectDictDataByType("hot_person_type"); + if (typeDicts == null) { + typeDicts = new ArrayList<>(); + } + // 按字典顺序排序(通常数据库查出来已经是按sort_order排序的) + List categories = typeDicts.stream() + .map(SysDictDataVo::getDictLabel) + .collect(Collectors.toList()); + Map valueToLabelMap = typeDicts.stream() + .collect(Collectors.toMap(SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel)); + + // 2. 获取所有从业人员入职年份和类型 + List> personnelList = headquartersWorkbenchMapper.selectAllPersonnelCreateYears(); + + int currentYear = java.time.LocalDate.now().getYear(); + int startYear = currentYear; + if (personnelList != null && !personnelList.isEmpty()) { + Object yearObj = personnelList.get(0).get("year"); + if (yearObj != null) { + startYear = ((Number) yearObj).intValue(); + } + } + + // 3. 按年份分组 + Map> yearToTypes = new HashMap<>(); + if (personnelList != null) { + for (Map row : personnelList) { + Object yearObj = row.get("year"); + String typeVal = (String) row.get("personType"); + if (yearObj != null && typeVal != null) { + int y = ((Number) yearObj).intValue(); + yearToTypes.computeIfAbsent(y, k -> new ArrayList<>()).add(typeVal); + } + } + } + + // 4. 初始化累积计数器 + Map cumulativeCounts = new HashMap<>(); + for (String label : categories) { + cumulativeCounts.put(label, 0L); + } + + // 5. 计算趋势 + List years = new ArrayList<>(); + Map> seriesData = new HashMap<>(); + for (String label : categories) { + seriesData.put(label, new ArrayList<>()); + } + + for (int y = startYear; y <= currentYear; y++) { + List typesInYear = yearToTypes.get(y); + if (typesInYear != null) { + for (String typeVal : typesInYear) { + String label = valueToLabelMap.get(typeVal); + if (label != null && cumulativeCounts.containsKey(label)) { + cumulativeCounts.put(label, cumulativeCounts.get(label) + 1); + } + } + } + + years.add(y); + for (String label : categories) { + seriesData.get(label).add(cumulativeCounts.get(label)); + } + } + + HeadquartersPersonnelTrendVo vo = new HeadquartersPersonnelTrendVo(); + vo.setYears(years); + vo.setCategories(categories); + vo.setSeriesData(seriesData); + return vo; + } + + @Override + public HeadquartersVehicleTrendVo getVehicleTrendStats() { + List industryDicts = sysDictTypeService.selectDictDataByType("hot_industry"); + if (industryDicts == null) { + industryDicts = new ArrayList<>(); + } + Map categoryMap = new HashMap<>(); + for (SysDictDataVo dict : industryDicts) { + String label = dict.getDictLabel(); + String code = dict.getDictValue(); + String category = "其他"; + if (label != null) { + label = label.trim(); + if (PASSENGER_LABELS.contains(label)) { + category = "客运"; + } else if (GENERAL_LABELS.contains(label)) { + category = "普货"; + } else if (DANGEROUS_LABELS.contains(label)) { + category = "危货"; + } + } + categoryMap.put(code, category); + } + + List> vehicleList = headquartersWorkbenchMapper.selectAllVehicleCreateYears(); + int currentYear = java.time.LocalDate.now().getYear(); + int startYear = currentYear; + if (vehicleList != null && !vehicleList.isEmpty()) { + Object yearObj = vehicleList.get(0).get("year"); + if (yearObj != null) { + startYear = ((Number) yearObj).intValue(); + } + } + + Map> yearToIndustries = new HashMap<>(); + if (vehicleList != null) { + for (Map row : vehicleList) { + Object yearObj = row.get("year"); + String industry = row.get("industry") == null ? null : row.get("industry").toString(); + if (yearObj != null) { + int y = ((Number) yearObj).intValue(); + if (industry != null && !industry.isEmpty()) { + String[] codes = industry.split("[,,]"); + for (String code : codes) { + String trimmed = code.trim(); + if (!trimmed.isEmpty()) { + yearToIndustries.computeIfAbsent(y, k -> new ArrayList<>()).add(trimmed); + } + } + } else { + yearToIndustries.computeIfAbsent(y, k -> new ArrayList<>()).add("__NONE__"); + } + } + } + } + + long cumulativeTotal = 0; + long cumulativeGeneral = 0; + long cumulativeDangerous = 0; + long cumulativePassenger = 0; + long cumulativeOther = 0; + + List years = new ArrayList<>(); + List totalCounts = new ArrayList<>(); + List generalCounts = new ArrayList<>(); + List dangerousCounts = new ArrayList<>(); + List passengerCounts = new ArrayList<>(); + List otherCounts = new ArrayList<>(); + + for (int y = startYear; y <= currentYear; y++) { + List industriesInYear = yearToIndustries.get(y); + if (industriesInYear != null) { + for (String code : industriesInYear) { + cumulativeTotal += 1; + String category = "其他"; + if (!"__NONE__".equals(code)) { + category = categoryMap.getOrDefault(code, "其他"); + } + switch (category) { + case "普货" -> cumulativeGeneral += 1; + case "危货" -> cumulativeDangerous += 1; + case "客运" -> cumulativePassenger += 1; + default -> cumulativeOther += 1; + } + } + } + years.add(y); + totalCounts.add(cumulativeTotal); + generalCounts.add(cumulativeGeneral); + dangerousCounts.add(cumulativeDangerous); + passengerCounts.add(cumulativePassenger); + otherCounts.add(cumulativeOther); + } + + HeadquartersVehicleTrendVo vo = new HeadquartersVehicleTrendVo(); + vo.setYears(years); + vo.setTotalCounts(totalCounts); + vo.setGeneralCounts(generalCounts); + vo.setDangerousCounts(dangerousCounts); + vo.setPassengerCounts(passengerCounts); + vo.setOtherCounts(otherCounts); + return vo; + } + + @Override + public HeadquartersYearlyComparisonVo getAccidentYearlyComparison() { + int currentYear = java.time.LocalDate.now().getYear(); + int lastYear = currentYear - 1; + + List> rows = headquartersWorkbenchMapper.selectMonthlyAccidentCounts(lastYear, currentYear); + return buildYearlyComparisonVo(rows, currentYear, lastYear); + } + + @Override + public HeadquartersYearlyComparisonVo getHiddenDangerYearlyComparison() { + int currentYear = java.time.LocalDate.now().getYear(); + int lastYear = currentYear - 1; + + List> rows = headquartersWorkbenchMapper.selectMonthlyHiddenDangerCounts(lastYear, currentYear); + return buildYearlyComparisonVo(rows, currentYear, lastYear); + } + + private HeadquartersYearlyComparisonVo buildYearlyComparisonVo(List> rows, int currentYear, int lastYear) { + // 初始化数据容器 + Map currentYearMap = new HashMap<>(); + Map lastYearMap = new HashMap<>(); + + if (rows != null) { + for (Map row : rows) { + Object yearObj = row.get("year"); + Object monthObj = row.get("month"); + Object countObj = row.get("count"); + + if (yearObj != null && monthObj != null) { + try { + int year = Integer.parseInt(yearObj.toString()); + int month = Integer.parseInt(monthObj.toString()); + long count = 0L; + if (countObj != null) { + if (countObj instanceof Number) { + count = ((Number) countObj).longValue(); + } else { + count = Long.parseLong(countObj.toString()); + } + } + + if (year == currentYear) { + currentYearMap.put(month, count); + } else if (year == lastYear) { + lastYearMap.put(month, count); + } + } catch (Exception ignored) { + } + } + } + } + + List months = new ArrayList<>(); + List currentValues = new ArrayList<>(); + List lastValues = new ArrayList<>(); + + for (int i = 1; i <= 12; i++) { + months.add(i + "月"); + currentValues.add(currentYearMap.getOrDefault(i, 0L)); + lastValues.add(lastYearMap.getOrDefault(i, 0L)); + } + + HeadquartersYearlyComparisonVo vo = new HeadquartersYearlyComparisonVo(); + vo.setMonths(months); + vo.setCurrentYearValues(currentValues); + vo.setLastYearValues(lastValues); + return vo; + } + + @Override + public HeadquartersCompanyListVo getCompanyList() { + List rows = headquartersWorkbenchMapper.selectCompanyListWithManagerCount(); + HeadquartersCompanyListVo vo = new HeadquartersCompanyListVo(); + if (rows != null) { + vo.setTotalCount((long) rows.size()); + vo.setList(rows); + } else { + vo.setTotalCount(0L); + vo.setList(new ArrayList<>()); + } + return vo; + } + + @Override + public Long getSquareInfoPendingAuditCount() { + return hotSquareInfoMapper.selectCount( + Wrappers.lambdaQuery() + .eq(HotSquareInfo::getIsDeleted, 0L) + .eq(HotSquareInfo::getAuditStatus, 0L) + ); + } + + @Override + public Long getComplaintAdvicePendingReplyCount() { + return hotComplaintAdviceMapper.selectCount( + Wrappers.lambdaQuery() + .eq(HotComplaintAdvice::getIsDeleted, 0L) + .eq(HotComplaintAdvice::getIsReplied, 0L) + ); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/service/impl/HotHiddenDangerDailyStatsServiceImpl.java b/src/main/java/com/hotwj/platform/workbench/service/impl/HotHiddenDangerDailyStatsServiceImpl.java new file mode 100644 index 0000000..4c43c10 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/service/impl/HotHiddenDangerDailyStatsServiceImpl.java @@ -0,0 +1,232 @@ +package com.hotwj.platform.workbench.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.hotwj.platform.workbench.domain.entity.HotHiddenDangerDailyStats; +import com.hotwj.platform.workbench.domain.vo.CompanyRiskCountVo; +import com.hotwj.platform.workbench.domain.vo.HiddenDangerCountVo; +import com.hotwj.platform.workbench.mapper.GovWorkbenchMapper; +import com.hotwj.platform.workbench.mapper.HotHiddenDangerDailyStatsMapper; +import com.hotwj.platform.workbench.mapper.HotTrainingDailyStatsMapper; +import com.hotwj.platform.workbench.service.IHotHiddenDangerDailyStatsService; +import com.hotwj.platform.workbench.strategy.impl.VehicleAnnualInspectRiskHandler; +import com.hotwj.platform.workbench.strategy.impl.VehicleMaintenanceRiskHandler; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 政府端隐患日统计Service业务层处理 + * + * @author shihongwei + * @date 2026-02-25 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class HotHiddenDangerDailyStatsServiceImpl extends ServiceImpl implements IHotHiddenDangerDailyStatsService { + + private final HotHiddenDangerDailyStatsMapper dailyStatsMapper; + private final GovWorkbenchMapper govWorkbenchMapper; + private final VehicleAnnualInspectRiskHandler vehicleAnnualInspectRiskHandler; + private final VehicleMaintenanceRiskHandler vehicleMaintenanceRiskHandler; + private final HotTrainingDailyStatsMapper trainingDailyStatsMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public void generateDailyStats() { + // 统计“昨天”的数据:定时任务在每日凌晨执行,口径为上一日00:00:00~23:59:59 + LocalDate statDate = LocalDate.now().minusDays(1); + log.info("开始生成日统计数据(隐患、培训),统计日期: {}", statDate); + + int hiddenCompanyCount = generateHiddenDangerDailyStats(statDate); + int trainingAffectedRows = generateTrainingDailyStats(statDate); + log.info("日统计数据生成完成(隐患、培训),隐患统计企业数: {}, 培训统计写入行数: {}", hiddenCompanyCount, trainingAffectedRows); + } + + private int generateHiddenDangerDailyStats(LocalDate statDate) { + // 1. 获取所有简单隐患统计 (SQL直接聚合) + // 包含: 营业执照, 道路经营许可证, 上岗资格证, 行驶证, 运输证, 驾驶员身份证, 驾驶证, 从业资格证, 违规 + List simpleRisks = govWorkbenchMapper.selectSimpleRiskCountsGroupedByCompany(null); + + // 获取当天已存在的统计数据,用于更新 + List existingStats = dailyStatsMapper.selectList( + new LambdaQueryWrapper() + .eq(HotHiddenDangerDailyStats::getStatsDate, statDate) + ); + Map existingStatsMap = existingStats.stream() + .collect(Collectors.toMap(HotHiddenDangerDailyStats::getCompanyId, s -> s)); + + Map statsMap = new HashMap<>(); + + // 2. 处理简单隐患 + for (CompanyRiskCountVo vo : simpleRisks) { + HotHiddenDangerDailyStats stats = statsMap.computeIfAbsent(vo.getCompanyId(), k -> { + // 如果已存在,则使用现有对象(保留ID),否则新建 + HotHiddenDangerDailyStats s = existingStatsMap.getOrDefault(k, new HotHiddenDangerDailyStats()); + s.setCompanyId(k); + // 重置统计数据 + s.setEnterpriseRisk(0); + s.setVehicleRisk(0); + s.setPersonnelRisk(0); + s.setStatsDate(statDate); + if (s.getRegionCode() == null) { + s.setRegionCode(vo.getRegionCode()); + } + return s; + }); + + // 补充RegionCode (如果之前是null,现在有了) + if (stats.getRegionCode() == null && vo.getRegionCode() != null) { + stats.setRegionCode(vo.getRegionCode()); + } + + int count = vo.getCount() == null ? 0 : vo.getCount(); + if ("enterprise".equals(vo.getCategory())) { + stats.setEnterpriseRisk(stats.getEnterpriseRisk() + count); + } else if ("vehicle".equals(vo.getCategory())) { + stats.setVehicleRisk(stats.getVehicleRisk() + count); + } else if ("personnel".equals(vo.getCategory())) { + stats.setPersonnelRisk(stats.getPersonnelRisk() + count); + } + } + + // 3. 处理复杂车辆隐患 (年审检测) - Java内存计算 + // 传入null表示查询所有辖区 + Map annualInspectRisks = vehicleAnnualInspectRiskHandler.countByCompany(null); + mergeVehicleRisks(statsMap, annualInspectRisks, statDate, existingStatsMap); + + // 4. 处理复杂车辆隐患 (二级维护) - Java内存计算 + Map maintenanceRisks = vehicleMaintenanceRiskHandler.countByCompany(null); + mergeVehicleRisks(statsMap, maintenanceRisks, statDate, existingStatsMap); + + // 5. 补充缺失的RegionCode (针对只有复杂隐患的企业) + Set missingRegionCompanies = statsMap.values().stream() + .filter(s -> s.getRegionCode() == null) + .map(HotHiddenDangerDailyStats::getCompanyId) + .collect(Collectors.toSet()); + + if (!missingRegionCompanies.isEmpty()) { + // 分批查询防止ID过多 + List allIds = new ArrayList<>(missingRegionCompanies); + int batchSize = 1000; + for (int i = 0; i < allIds.size(); i += batchSize) { + List batchIds = allIds.subList(i, Math.min(i + batchSize, allIds.size())); + List regions = govWorkbenchMapper.selectCompanyRegionCodes(batchIds); + for (CompanyRiskCountVo r : regions) { + HotHiddenDangerDailyStats stats = statsMap.get(r.getCompanyId()); + if (stats != null) { + stats.setRegionCode(r.getRegionCode()); + } + } + } + } + + // 6. 计算总数并保存 + if (!statsMap.isEmpty()) { + List list = new ArrayList<>(statsMap.values()); + for (HotHiddenDangerDailyStats stats : list) { + stats.setTotalRisk(stats.getEnterpriseRisk() + stats.getVehicleRisk() + stats.getPersonnelRisk()); + } + + // 批量保存或更新 + this.saveOrUpdateBatch(list); + + // 删除不再存在的企业统计 (即今天之前有隐患但现在没有隐患的企业) + // 从 existingStatsMap 中移除 statsMap 中有的 key,剩下的就是需要删除的 + Set currentCompanyIds = statsMap.keySet(); + List idsToDelete = existingStatsMap.values().stream() + .filter(s -> !currentCompanyIds.contains(s.getCompanyId())) + .map(HotHiddenDangerDailyStats::getId) + .collect(Collectors.toList()); + + if (!idsToDelete.isEmpty()) { + dailyStatsMapper.deleteBatchIds(idsToDelete); + } + } else { + // 如果昨天(指定天)没有任何隐患数据,但数据库里有记录,则全部删除 + dailyStatsMapper.delete(new LambdaQueryWrapper() + .eq(HotHiddenDangerDailyStats::getStatsDate, statDate)); + } + + log.info("隐患日统计数据生成完成,共处理 {} 家企业的数据", statsMap.size()); + return statsMap.size(); + } + + private int generateTrainingDailyStats(LocalDate statDate) { + // 统计口径:statDate 的 00:00:00 ~ 23:59:59 + Date dayEnd = java.util.Date.from(statDate.atTime(java.time.LocalTime.MAX).atZone(java.time.ZoneId.systemDefault()).toInstant()); + Date statsDate = java.util.Date.from(statDate.atStartOfDay(java.time.ZoneId.systemDefault()).toInstant()); + int affected = trainingDailyStatsMapper.upsertDailyStats(statsDate, dayEnd); + return affected; + } + + private void mergeVehicleRisks(Map statsMap, Map risks, LocalDate today, Map existingStatsMap) { + for (Map.Entry entry : risks.entrySet()) { + Long companyId = entry.getKey(); + Integer count = entry.getValue(); + if (count == null || count == 0) continue; + + HotHiddenDangerDailyStats stats = statsMap.computeIfAbsent(companyId, k -> { + HotHiddenDangerDailyStats s = existingStatsMap.getOrDefault(k, new HotHiddenDangerDailyStats()); + s.setCompanyId(k); + // RegionCode暂时为空,如果只有复杂隐患而没有简单隐患,可能拿不到RegionCode + // 这种情况下,可能需要单独查询Company信息。但概率较低且不影响核心统计(按ID查) + // 可以在这里TODO: 查询Company获取RegionCode + if (s.getId() == null) { // 如果是新对象,才重置 + s.setEnterpriseRisk(0); + s.setVehicleRisk(0); + s.setPersonnelRisk(0); + } + // 注意:如果s来自existingStatsMap且已经在statsMap中,说明在上面循环中已经重置过了 + // 如果s来自existingStatsMap但不在statsMap中(即只有复杂隐患),则需要在这里重置 + // 简单起见,这里再次确保初始化,但要小心不要覆盖上面循环累加的值 + // 由于computeIfAbsent只在key不存在时执行,所以进入这里的都是statsMap中没有的 + // 因此如果是从existingStatsMap拿出来的,说明它之前没有被简单隐患循环处理过 + // 所以可以安全地重置为0 + if (existingStatsMap.containsKey(k)) { + s.setEnterpriseRisk(0); + s.setVehicleRisk(0); + s.setPersonnelRisk(0); + } + + s.setStatsDate(today); + return s; + }); + + stats.setVehicleRisk(stats.getVehicleRisk() + count); + } + } + + @Override + public List getCompanyDailyStats(Long companyId, String startDate, String endDate) { + List statsList = dailyStatsMapper.selectStatsByCompanyAndDateRange(companyId, startDate, endDate); + return statsList.stream().map(s -> { + HiddenDangerCountVo vo = new HiddenDangerCountVo(); + vo.setDate(s.getStatsDate().toString()); + vo.setCount(s.getTotalRisk().longValue()); + // 如果需要分类详情,HiddenDangerCountVo目前只有count。 + // 如果前端需要分类,可能需要修改HiddenDangerCountVo或使用新的VO。 + // 根据需求描述:"返回给前端的就是柱状图数据... 隐患数量",似乎只需要总数。 + // 但用户也提到 "每日的隐患数量(企业、车辆、人员)",可能需要展示详情? + // 暂时只返回总数,如果需要详情可以扩展。 + return vo; + }).collect(Collectors.toList()); + } + + @Override + public List getRegionDailyStats(String regionCode, String startDate, String endDate) { + List statsList = dailyStatsMapper.selectStatsByDateRange(regionCode, startDate, endDate); + return statsList.stream().map(s -> { + HiddenDangerCountVo vo = new HiddenDangerCountVo(); + vo.setDate(s.getStatsDate().toString()); + vo.setCount(s.getTotalRisk().longValue()); + return vo; + }).collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/service/impl/WorkbenchServiceImpl.java b/src/main/java/com/hotwj/platform/workbench/service/impl/WorkbenchServiceImpl.java new file mode 100644 index 0000000..7f77b02 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/service/impl/WorkbenchServiceImpl.java @@ -0,0 +1,1415 @@ +package com.hotwj.platform.workbench.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.hotwj.platform.config.companyBasicConfig.domain.bo.HotCompanyBasicConfigBo; +import com.hotwj.platform.config.companyBasicConfig.domain.vo.HotCompanyBasicConfigVo; +import com.hotwj.platform.config.companyBasicConfig.service.IHotCompanyBasicConfigService; +import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.bo.HotHiddenDangerCheckConfigBo; +import com.hotwj.platform.config.hiddenDangerCheckConfig.domain.vo.HotHiddenDangerCheckConfigVo; +import com.hotwj.platform.config.hiddenDangerCheckConfig.service.IHotHiddenDangerCheckConfigService; +import com.hotwj.platform.config.hiddenDangerInspectStore.domain.bo.HotHiddenDangerInspectStoreBo; +import com.hotwj.platform.config.hiddenDangerInspectStore.domain.vo.HotHiddenDangerInspectStoreVo; +import com.hotwj.platform.config.hiddenDangerInspectStore.service.IHotHiddenDangerInspectStoreService; +import com.hotwj.platform.config.vehicleInspectionConfig.domain.bo.HotVehicleInspectionConfigBo; +import com.hotwj.platform.config.vehicleInspectionConfig.domain.vo.HotVehicleInspectionConfigVo; +import com.hotwj.platform.config.vehicleInspectionConfig.service.IHotVehicleInspectionConfigService; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.bo.HotVehicleSecondaryMaintenanceConfigBo; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.vo.HotVehicleSecondaryMaintenanceConfigVo; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.service.IHotVehicleSecondaryMaintenanceConfigService; +import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.bo.HotVehicleThreeInspectConfigBo; +import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.vo.HotVehicleThreeInspectConfigVo; +import com.hotwj.platform.config.vehicleThreeInspectConfig.service.IHotVehicleThreeInspectConfigService; +import com.hotwj.platform.driverManagement.driver.domain.HotDriver; +import com.hotwj.platform.driverManagement.driver.domain.bo.HotDriverBo; +import com.hotwj.platform.driverManagement.driver.domain.vo.HotDriverVo; +import com.hotwj.platform.driverManagement.driver.mapper.HotDriverMapper; +import com.hotwj.platform.driverManagement.driver.service.IHotDriverService; +import com.hotwj.platform.driverManagement.driverLaborContract.domain.bo.HotDriverLaborContractBo; +import com.hotwj.platform.driverManagement.driverLaborContract.domain.vo.HotDriverLaborContractVo; +import com.hotwj.platform.driverManagement.driverLaborContract.service.IHotDriverLaborContractService; +import com.hotwj.platform.noticeManagerment.adminNotice.domain.HotAdminNotice; +import com.hotwj.platform.noticeManagerment.adminNotice.mapper.HotAdminNoticeMapper; +import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo; +import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService; +import com.hotwj.platform.resourceManagement.companyInsurance.domain.bo.HotCompanyInsuranceBo; +import com.hotwj.platform.resourceManagement.companyInsurance.domain.vo.HotCompanyInsuranceVo; +import com.hotwj.platform.resourceManagement.companyInsurance.service.IHotCompanyInsuranceService; +import com.hotwj.platform.resourceManagement.companyLaborContract.domain.bo.HotCompanyLaborContractBo; +import com.hotwj.platform.resourceManagement.companyLaborContract.domain.vo.HotCompanyLaborContractVo; +import com.hotwj.platform.resourceManagement.companyLaborContract.service.IHotCompanyLaborContractService; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.bo.HotCompanySafetyManagerBo; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.vo.HotCompanySafetyManagerVo; +import com.hotwj.platform.resourceManagement.companySafetyManager.service.IHotCompanySafetyManagerService; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.vo.HotVehicleAnnualReviewVo; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.service.IHotVehicleAnnualReviewService; +import com.hotwj.platform.resourceManagement.vehicleContract.domain.bo.HotVehicleContractBo; +import com.hotwj.platform.resourceManagement.vehicleContract.domain.vo.HotVehicleContractVo; +import com.hotwj.platform.resourceManagement.vehicleContract.service.IHotVehicleContractService; +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.bo.HotVehicleInsuranceBo; +import com.hotwj.platform.resourceManagement.vehicleInsurance.domain.vo.HotVehicleInsuranceVo; +import com.hotwj.platform.resourceManagement.vehicleInsurance.service.IHotVehicleInsuranceService; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.vo.HotVehicleMaintenanceVo; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.service.IHotVehicleMaintenanceService; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.bo.HotVehicleBo; +import com.hotwj.platform.resourceManagement.vehicleManagement.domain.vo.HotVehicleVo; +import com.hotwj.platform.resourceManagement.vehicleManagement.mapper.HotVehicleMapper; +import com.hotwj.platform.resourceManagement.vehicleManagement.service.IHotVehicleService; +import com.hotwj.platform.resourceManagement.vehicleManagement.utils.VehicleUtils; +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.bo.HotVehicleTerminalInstallBo; +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.domain.vo.HotVehicleTerminalInstallVo; +import com.hotwj.platform.resourceManagement.vehicleTerminalInstall.service.IHotVehicleTerminalInstallService; +import com.hotwj.platform.securityManagement.accidentArchive.domain.HotAccidentArchive; +import com.hotwj.platform.securityManagement.accidentArchive.mapper.HotAccidentArchiveMapper; +import com.hotwj.platform.securityManagement.hiddenDanger.domain.HotHiddenDanger; +import com.hotwj.platform.securityManagement.hiddenDanger.mapper.HotHiddenDangerMapper; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.domain.HotHiddenDangerInspection; +import com.hotwj.platform.securityManagement.hiddenDangerInspection.mapper.HotHiddenDangerInspectionMapper; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.domain.HotHiddenDangerPlan; +import com.hotwj.platform.securityManagement.hiddenDangerPlan.mapper.HotHiddenDangerPlanMapper; +import com.hotwj.platform.securityManagement.training.domain.bo.HotTrainingBo; +import com.hotwj.platform.securityManagement.training.domain.vo.HotTrainingVo; +import com.hotwj.platform.securityManagement.training.service.IHotTrainingService; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.HotTrainingParticipant; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.bo.HotTrainingParticipantBo; +import com.hotwj.platform.securityManagement.trainingParticipant.domain.vo.HotTrainingParticipantVo; +import com.hotwj.platform.securityManagement.trainingParticipant.mapper.HotTrainingParticipantMapper; +import com.hotwj.platform.securityManagement.trainingParticipant.service.IHotTrainingParticipantService; +import com.hotwj.platform.securityManagement.violationInfo.domain.HotViolationInfo; +import com.hotwj.platform.securityManagement.violationInfo.mapper.HotViolationInfoMapper; +import com.hotwj.platform.workbench.domain.vo.*; +import com.hotwj.platform.workbench.mapper.HotTrainingDailyStatsMapper; +import com.hotwj.platform.workbench.service.IWorkbenchService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 工作台服务实现 + */ +@Service +@RequiredArgsConstructor +public class WorkbenchServiceImpl implements IWorkbenchService { + + private final ISysCompanyService sysCompanyService; + private final IHotCompanySafetyManagerService hotCompanySafetyManagerService; + private final IHotDriverService hotDriverService; + private final IHotVehicleService hotVehicleService; + private final IHotVehicleThreeInspectConfigService hotVehicleThreeInspectConfigService; + private final IHotHiddenDangerInspectStoreService hotHiddenDangerInspectStoreService; + private final IHotHiddenDangerCheckConfigService hotHiddenDangerCheckConfigService; + private final IHotCompanyBasicConfigService hotCompanyBasicConfigService; + private final IHotCompanyLaborContractService hotCompanyLaborContractService; + private final IHotCompanyInsuranceService hotCompanyInsuranceService; + private final IHotVehicleInsuranceService hotVehicleInsuranceService; + private final IHotDriverLaborContractService hotDriverLaborContractService; + private final IHotTrainingService hotTrainingService; + private final IHotVehicleInspectionConfigService hotVehicleInspectionConfigService; + private final IHotVehicleSecondaryMaintenanceConfigService hotVehicleSecondaryMaintenanceConfigService; + private final IHotVehicleContractService hotVehicleContractService; + private final IHotVehicleTerminalInstallService hotVehicleTerminalInstallService; + private final IHotVehicleAnnualReviewService hotVehicleAnnualReviewService; + private final IHotVehicleMaintenanceService hotVehicleMaintenanceService; + + @Override + public CompanyBasicInfoVo getCompanyBasicInfo() { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null) { + throw new ServiceException("未登录"); + } + Long companyId = loginUser.getCompanyId(); + boolean superAdmin = LoginHelper.isSuperAdmin(loginUser.getUserId()); + if (companyId == null) { + if (superAdmin) { + return buildFallbackCompanyBasicInfo(); + } + throw new ServiceException("企业信息不存在"); + } + SysCompanyVo companyVo = sysCompanyService.queryById(companyId); + if (companyVo == null) { + if (superAdmin) { + return buildFallbackCompanyBasicInfo(); + } + throw new ServiceException("企业信息不存在"); + } + + CompanyBasicInfoVo result = new CompanyBasicInfoVo(); + result.setCompanyName(companyVo.getCompanyName()); + result.setResponsibleName(companyVo.getResponsibleName()); + + HotCompanySafetyManagerBo managerBo = new HotCompanySafetyManagerBo(); + managerBo.setCompanyId(companyId); + managerBo.setIsResigned(0L); + managerBo.setStatus(1L); + List safetyManagers = hotCompanySafetyManagerService.queryList(managerBo); + + long managerCount = safetyManagers.stream() +// .filter(m -> (m.getIsDepartmentHead() != null && m.getIsDepartmentHead() == 1L) +// || (m.getCompanyHead() != null && m.getCompanyHead() == 1L)) + .count(); + result.setManagerCount(managerCount); + + + Long driverCount = driverMapper.selectCount(new LambdaQueryWrapper() + .eq(HotDriver::getCompanyId, companyId) + .eq(HotDriver::getStatus, 1L) + .eq(HotDriver::getAuditStatus, 1) + .eq(HotDriver::getIsDeleted, 0L)); + + result.setDriverCount(driverCount); + + result.setEmployeeTotal(managerCount + driverCount); + + + HotVehicleBo vehicleBo = new HotVehicleBo(); + vehicleBo.setCompanyId(companyId); + List vehicles = hotVehicleService.queryList(vehicleBo); + long vehicleCount = vehicles.stream() + .filter(v -> v.getOperationStatus() != null && (v.getOperationStatus() == 1L || v.getOperationStatus() == 2L)) + .count(); + long normalVehicleCount = vehicles.stream() + .filter(v -> v.getOperationStatus() != null && v.getOperationStatus() == 1L) + .count(); + long pausedVehicleCount = vehicles.stream() + .filter(v -> v.getOperationStatus() != null && v.getOperationStatus() == 2L) + .count(); + + result.setVehicleTotal(vehicleCount); + result.setNormalVehicleCount(normalVehicleCount); + result.setPausedVehicleCount(pausedVehicleCount); + + return result; + } + + private CompanyBasicInfoVo buildFallbackCompanyBasicInfo() { + CompanyBasicInfoVo result = new CompanyBasicInfoVo(); + result.setCompanyName("平台管理员"); + result.setResponsibleName("平台管理员"); + result.setEmployeeTotal(0L); + result.setManagerCount(0L); + result.setDriverCount(0L); + result.setVehicleTotal(0L); + result.setNormalVehicleCount(0L); + result.setPausedVehicleCount(0L); + return result; + } + + private final IHotTrainingParticipantService hotTrainingParticipantService; + private final HotAccidentArchiveMapper accidentArchiveMapper; + private final HotViolationInfoMapper violationInfoMapper; + private final HotHiddenDangerInspectionMapper hiddenDangerInspectionMapper; + private final HotHiddenDangerMapper hiddenDangerMapper; + private final HotTrainingParticipantMapper trainingParticipantMapper; + private final HotAdminNoticeMapper adminNoticeMapper; + private final HotHiddenDangerPlanMapper hiddenDangerPlanMapper; + private final HotVehicleMapper vehicleMapper; + private final HotDriverMapper driverMapper; + private final HotTrainingDailyStatsMapper trainingDailyStatsMapper; + + @Override + public VehicleDriverRatioVo getVehicleDriverRatio() { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null) { + throw new ServiceException("未登录"); + } + Long companyId = loginUser.getCompanyId(); + + List operationStatusList = Arrays.asList(1L, 2L); + // 统计运营车辆数 (OperationStatus = 1) + Long vehicleCount = vehicleMapper.selectCount(new LambdaQueryWrapper() + .eq(HotVehicle::getCompanyId, companyId) + .in(HotVehicle::getOperationStatus, operationStatusList) + .eq(HotVehicle::getIsDeleted, 0L)); + + // 统计驾驶员数 (Status = 1, AuditStatus = 1) + Long driverCount = driverMapper.selectCount(new LambdaQueryWrapper() + .eq(HotDriver::getCompanyId, companyId) + .eq(HotDriver::getStatus, 1L) + .eq(HotDriver::getAuditStatus, 1) + .eq(HotDriver::getIsDeleted, 0L)); + + VehicleDriverRatioVo result = new VehicleDriverRatioVo(); + result.setVehicleCount(vehicleCount); + result.setDriverCount(driverCount); + + long total = vehicleCount + driverCount; + if (total > 0) { + // 计算车辆占比: 车辆 / (车辆 + 驾驶员) + BigDecimal ratio = BigDecimal.valueOf(vehicleCount) + .divide(BigDecimal.valueOf(total), 3, BigDecimal.ROUND_HALF_UP) + .multiply(BigDecimal.valueOf(100)) + .setScale(1, BigDecimal.ROUND_HALF_UP); + result.setVehicleRatio(ratio); + } else { + result.setVehicleRatio(BigDecimal.ZERO); + } + + return result; + } + + @Override + public TrainingUsageVo getTrainingUsage() { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null) { + throw new ServiceException("未登录"); + } + Long companyId = loginUser.getCompanyId(); + + // 查询该企业下所有未删除的培训参与记录 + List participants = trainingParticipantMapper.selectList(new LambdaQueryWrapper() + .select(HotTrainingParticipant::getIsCompleted) + .eq(HotTrainingParticipant::getCompanyId, companyId) + .eq(HotTrainingParticipant::getIsDeleted, 0L)); + + long total = participants.size(); + long completedCount = participants.stream() + .filter(p -> p.getIsCompleted() != null && p.getIsCompleted() == 1L) + .count(); + long incompleteCount = total - completedCount; + + TrainingUsageVo result = new TrainingUsageVo(); + result.setCompletedCount(completedCount); + result.setIncompleteCount(incompleteCount); + + if (total > 0) { + // 计算百分比,保留整数 + BigDecimal rate = BigDecimal.valueOf(completedCount) + .divide(BigDecimal.valueOf(total), 2, BigDecimal.ROUND_HALF_UP) + .multiply(BigDecimal.valueOf(100)); + result.setCompletionRate(rate); + } else { + result.setCompletionRate(BigDecimal.ZERO); + } + + return result; + } + + @Override + public RiskReminderVo getRiskReminder() { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null) { + return new RiskReminderVo(); + } + Long companyId = loginUser.getCompanyId(); + RiskReminderVo result = new RiskReminderVo(); + + // 1. 企业风险 + result.setCompanyRisk(getCompanyRisk(companyId)); + // 2. 车辆风险 + result.setVehicleRisk(getVehicleRisk(companyId)); + // 3. 驾驶员风险 + result.setDriverRisk(getDriverRisk(companyId)); + // 4. 教育培训风险 + result.setTrainingRisk(getTrainingRisk(companyId)); + + return result; + } + + @Override + public ShortcutStatisticsVo getShortcutStatistics() { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null) { + return new ShortcutStatisticsVo(); + } + Long companyId = loginUser.getCompanyId(); + ShortcutStatisticsVo result = new ShortcutStatisticsVo(); + + // 1. 事故报告:需要整改就要有消息提醒 + Long accidentCount = accidentArchiveMapper.selectCount(new LambdaQueryWrapper() + .eq(HotAccidentArchive::getCompanyId, companyId) + .and(w -> w.eq(HotAccidentArchive::getIsRectified, 0L).or().isNull(HotAccidentArchive::getIsRectified)) + .eq(HotAccidentArchive::getIsDeleted, 0L)); + result.setAccidentReportCount(accidentCount); + + // 2. 违规信息:需要约谈可编辑的有消息提醒 + Long violationCount = violationInfoMapper.selectCount(new LambdaQueryWrapper() + .eq(HotViolationInfo::getCompanyId, companyId) + .eq(HotViolationInfo::getNeedInterview, 1L) + .isNull(HotViolationInfo::getInterviewTime) + .eq(HotViolationInfo::getIsDeleted, 0L)); + result.setViolationInfoCount(violationCount); + + // 3. 隐患排查:隐患排查项HotHiddenDangerInspection处理待处理或者审核中的 + // 状态: 1=待处理, 2=审核中, 4=待处理(同义), 5=审核中(同义) + Long hiddenInspectionCount = hiddenDangerInspectionMapper.selectCount(new LambdaQueryWrapper() + .eq(HotHiddenDangerInspection::getCompanyId, companyId) + .in(HotHiddenDangerInspection::getStatus, Arrays.asList(1L, 2L, 4L, 5L)) + .eq(HotHiddenDangerInspection::getIsDeleted, 0L)); + result.setHiddenInspectionCount(hiddenInspectionCount); + + // 状态: 0=评估、1=治理、2=复查 + Long hiddenGovernanceCount = hiddenDangerMapper.selectCount(new LambdaQueryWrapper() + .eq(HotHiddenDanger::getCompanyId, companyId) + .in(HotHiddenDanger::getStatus, Arrays.asList(0L, 1L, 2L)) + .eq(HotHiddenDanger::getIsDeleted, 0L)); + result.setHiddenGovernanceCount(hiddenGovernanceCount); + + // 4. 日常培训:审核需要消息提醒,isCompleted=1,并且isPass=null + Long trainingCount = trainingParticipantMapper.selectCount(new LambdaQueryWrapper() + .eq(HotTrainingParticipant::getCompanyId, companyId) + .eq(HotTrainingParticipant::getIsCompleted, 1L) + .isNull(HotTrainingParticipant::getIsPass) + .eq(HotTrainingParticipant::getIsDeleted, 0L)); + result.setDailyTrainingCount(trainingCount); + + // 5. 行管通知:需要提醒 + Long noticeCount = adminNoticeMapper.selectCount(new LambdaQueryWrapper() + .eq(HotAdminNotice::getCompanyId, companyId) + .and(w -> w.eq(HotAdminNotice::getIsReceived, 0L) + .or() + .eq(HotAdminNotice::getStatus, 2)) + .eq(HotAdminNotice::getIsDeleted, 0L)); + result.setAdminNoticeCount(noticeCount); + + return result; + } + + @Override + public HiddenDangerStatsVo getHiddenDangerStats() { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null) { + return new HiddenDangerStatsVo(); + } + Long companyId = loginUser.getCompanyId(); + HiddenDangerStatsVo result = new HiddenDangerStatsVo(); + List enterpriseItems = new ArrayList<>(); + List vehicleItems = new ArrayList<>(); + List driverItems = new ArrayList<>(); + + // 1. 获取所有有效的排查计划 + List plans = hiddenDangerPlanMapper.selectList(new LambdaQueryWrapper() + .eq(HotHiddenDangerPlan::getCompanyId, companyId) + .eq(HotHiddenDangerPlan::getIsDeleted, 0L)); + + if (plans.isEmpty()) { + result.setEnterpriseItems(enterpriseItems); + result.setVehicleItems(vehicleItems); + result.setDriverItems(driverItems); + return result; + } + + // 2. 遍历计划,查询对应的排查项目 + for (HotHiddenDangerPlan plan : plans) { + List inspections = hiddenDangerInspectionMapper.selectList(new LambdaQueryWrapper() + .eq(HotHiddenDangerInspection::getPlanId, plan.getId()) + .eq(HotHiddenDangerInspection::getCompanyId, companyId) + .in(HotHiddenDangerInspection::getStatus, Arrays.asList(1L, 2L)) + .eq(HotHiddenDangerInspection::getIsDeleted, 0L)); + + for (HotHiddenDangerInspection inspection : inspections) { + Long type = inspection.getProjectType(); + Long status = inspection.getStatus(); + if (type == null || status == null) { + continue; + } + + // 状态判断:1=特处理/待处理, 4=特处理 -> 待处理 + // 2=审核中, 5=审核中 -> 待审核 + boolean isPending = (status == 1L || status == 4L); + boolean isAuditing = (status == 2L || status == 5L); + + if (!isPending && !isAuditing) { + continue; + } + + HiddenDangerItemVo item = new HiddenDangerItemVo(); + item.setPlanName(plan.getPlanName()); + item.setDeadline(plan.getEndDate()); + item.setPlanId(plan.getId()); + item.setProjectType(type); + item.setProjectId(inspection.getProjectId()); + item.setInspectionId(inspection.getId()); + item.setTargetName(inspection.getProjectName()); + + String statusText = isPending ? "待处理" : "待审核"; + item.setStatus(isPending ? 1 : 2); + + String title = plan.getPlanName() + " " + inspection.getProjectName() + " " + statusText; + item.setTitle(title); + + if (type == 1L) { + enterpriseItems.add(item); + } else if (type == 2L) { + vehicleItems.add(item); + } else if (type == 3L) { + driverItems.add(item); + } + } + } + + result.setEnterpriseItems(enterpriseItems); + result.setVehicleItems(vehicleItems); + result.setDriverItems(driverItems); + return result; + } + + @Override + public List getLearningStats(String type, String beginTime, String endTime) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser == null) { + return new ArrayList<>(); + } + Long companyId = loginUser.getCompanyId(); + List result = new ArrayList<>(); + + LocalDate today = LocalDate.now(); + LocalDate startDate; + LocalDate endDate = today; + + if (StringUtils.isNotBlank(beginTime) && StringUtils.isNotBlank(endTime)) { + startDate = LocalDate.parse(beginTime); + endDate = LocalDate.parse(endTime); + } else if ("week".equals(type)) { + // 上周 (上周一到上周日) + LocalDate lastWeek = today.minusWeeks(1); + startDate = lastWeek.with(java.time.temporal.TemporalAdjusters.previousOrSame(java.time.DayOfWeek.MONDAY)); + endDate = lastWeek.with(java.time.temporal.TemporalAdjusters.nextOrSame(java.time.DayOfWeek.SUNDAY)); + } else if ("month".equals(type)) { + // 上月 (上个月1号到上个月最后一天) + LocalDate lastMonth = today.minusMonths(1); + startDate = lastMonth.with(java.time.temporal.TemporalAdjusters.firstDayOfMonth()); + endDate = lastMonth.with(java.time.temporal.TemporalAdjusters.lastDayOfMonth()); + } else { + // 默认返回上周 + LocalDate lastWeek = today.minusWeeks(1); + startDate = lastWeek.with(java.time.temporal.TemporalAdjusters.previousOrSame(java.time.DayOfWeek.MONDAY)); + endDate = lastWeek.with(java.time.temporal.TemporalAdjusters.nextOrSame(java.time.DayOfWeek.SUNDAY)); + } + + Date start = Date.from(startDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); + Date end = Date.from(endDate.atTime(LocalTime.MAX).atZone(ZoneId.systemDefault()).toInstant()); + List rows = trainingDailyStatsMapper.selectByCompanyAndDateRange(companyId, start, end); + Map rowMap = new HashMap<>(); + if (rows != null) { + for (TrainingDailyStatsVo r : rows) { + if (r.getStatsDate() != null) { + LocalDate d = r.getStatsDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + rowMap.put(d, r); + } + } + } + + // 遍历每一天生成数据 + for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) { + LearningStatsItemVo item = new LearningStatsItemVo(); +// if ("week".equals(type)) { +// // 如果是周统计,返回星期几(如:星期一) +// item.setDate(date.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, Locale.CHINA)); +// } else { +// item.setDate(date.toString()); +// } + item.setDate(date.toString()); + TrainingDailyStatsVo r = rowMap.get(date); + long completed = r != null && r.getCompletedCount() != null ? r.getCompletedCount() : 0L; + long uncompleted = r != null && r.getUncompletedCount() != null ? r.getUncompletedCount() : 0L; + item.setCompleted(completed); + item.setIncomplete(uncompleted); + result.add(item); + } + + return result; + } + + + /** + * 获取企业风险 + */ + private List getCompanyRisk(Long companyId) { + List categories = new ArrayList<>(); + + // 初始化分类 + RiskCategoryVo configCat = new RiskCategoryVo(); + configCat.setTitle("企业配置"); + configCat.setType("configuration"); + categories.add(configCat); + + RiskCategoryVo licenseCat = new RiskCategoryVo(); + licenseCat.setTitle("营业执照"); + licenseCat.setType("business_license"); + categories.add(licenseCat); + + RiskCategoryVo roadCat = new RiskCategoryVo(); + roadCat.setTitle("道路经营许可证"); + roadCat.setType("road_permit"); + categories.add(roadCat); + + RiskCategoryVo insuranceCat = new RiskCategoryVo(); + insuranceCat.setTitle("企业保险"); + insuranceCat.setType("company_insurance"); + categories.add(insuranceCat); + + RiskCategoryVo qualCat = new RiskCategoryVo(); + qualCat.setTitle("上岗资格证"); + qualCat.setType("qualification_certificate"); + categories.add(qualCat); + + RiskCategoryVo laborCat = new RiskCategoryVo(); + laborCat.setTitle("管理人员劳动合同"); + laborCat.setType("manager_labor_contract"); + categories.add(laborCat); + + // 1.1 企业配置检查 + List configRisks = new ArrayList<>(); + + // 车辆三检检查库 + HotVehicleThreeInspectConfigBo inspectConfigBo = new HotVehicleThreeInspectConfigBo(); + inspectConfigBo.setCompanyId(companyId); + List inspectConfigs = hotVehicleThreeInspectConfigService.queryList(inspectConfigBo); + if (inspectConfigs.isEmpty()) { + addRisk(configRisks, "车辆三检检查库: 未初始化", "not_configured", "企业配置", "configuration", "vehicleThreeInspect", "/config/vehicleThreeInspectConfig"); + } + + // 车辆三检检查项(检查是否配置了类型 1、2、3) + // 由于配置结构是扁平的(每种类型一条记录),我们需要检查类型 1、2、3 是否存在且有内容。 + // 需求:“无记录 或 前/中/后任意内容为空” + boolean hasFront = false, hasMiddle = false, hasBack = false; + boolean contentComplete = true; + for (HotVehicleThreeInspectConfigVo c : inspectConfigs) { + if (c.getInspectType() != null) { + if (c.getInspectType() == 1L) hasFront = true; + if (c.getInspectType() == 2L) hasMiddle = true; + if (c.getInspectType() == 3L) hasBack = true; + if (StringUtils.isAnyBlank(c.getTitle(), c.getNormalText(), c.getAbnormalText())) { + contentComplete = false; + } + } + } + if (!hasFront || !hasMiddle || !hasBack || !contentComplete) { + addRisk(configRisks, "车辆三检检查项: 未配置或内容缺失", "not_configured", "企业配置", "configuration", "InspectItemDialog", "/config/vehicleThreeInspectConfig"); + } + + // 隐患排查库 + HotHiddenDangerInspectStoreBo storeBo = new HotHiddenDangerInspectStoreBo(); + storeBo.setCompanyId(companyId); + List storeItems = hotHiddenDangerInspectStoreService.queryList(storeBo); + if (storeItems.isEmpty()) { + addRisk(configRisks, "隐患排查库: 未初始化", "not_configured", "企业配置", "configuration", "hiddenDangerLib", "/config/hiddenDangerInspectStore"); + } + + // 隐患排查检查项 + HotHiddenDangerCheckConfigBo checkConfigBo = new HotHiddenDangerCheckConfigBo(); + checkConfigBo.setCompanyId(companyId); + List checkConfigs = hotHiddenDangerCheckConfigService.queryList(checkConfigBo); + if (checkConfigs.isEmpty()) { + addRisk(configRisks, "隐患排查检查项: 未配置", "not_configured", "企业配置", "configuration", "hiddenDanger", "/config/hiddenDangerCheckConfig"); + } else { + // 检查是否存在默认项且内容不为空 + // 需求:“无默认项记录 或 企业/车辆/人员检查内容任意一项为空” + boolean hasDefault = false; + boolean contentValid = false; + for (HotHiddenDangerCheckConfigVo c : checkConfigs) { + if (c.getIsDefault() != null && c.getIsDefault() == 1L) { + hasDefault = true; + if (StringUtils.isNotBlank(c.getCompanyInspectJson()) && + StringUtils.isNotBlank(c.getVehicleInspectJson()) && + StringUtils.isNotBlank(c.getPersonInspectJson())) { + contentValid = true; + } + break; + } + } + + if (!hasDefault) { + addRisk(configRisks, "隐患排查检查项: 默认项未配置", "not_configured", "企业配置", "configuration", "hiddenDanger", "/config/hiddenDangerCheckConfig"); + } else if (!contentValid) { + addRisk(configRisks, "隐患排查检查项: 检查内容缺失", "not_configured", "企业配置", "configuration", "hiddenDanger", "/config/hiddenDangerCheckConfig"); + } + } + + // 车辆年审提醒配置 + HotVehicleInspectionConfigBo viBo = new HotVehicleInspectionConfigBo(); + viBo.setCompanyId(companyId); + List viConfigs = hotVehicleInspectionConfigService.queryList(viBo); + boolean viEnabledExists = viConfigs != null && viConfigs.stream().anyMatch(c -> c.getIsEnabled() == null || c.getIsEnabled() == 1L); + if (viConfigs == null || viConfigs.isEmpty() || !viEnabledExists) { + addRisk(configRisks, "车辆年审提醒: 未设置", "not_configured", "企业配置", "configuration", "annualInspection", "/config/vehicleInspectionConfig"); + } + + // 车辆二级维护提醒配置 + HotVehicleSecondaryMaintenanceConfigBo smBo = new HotVehicleSecondaryMaintenanceConfigBo(); + smBo.setCompanyId(companyId); + List smConfigs = hotVehicleSecondaryMaintenanceConfigService.queryList(smBo); + boolean smEnabledExists = smConfigs != null && smConfigs.stream().anyMatch(c -> c.getIsEnabled() == null || c.getIsEnabled() == 1L); + if (smConfigs == null || smConfigs.isEmpty() || !smEnabledExists) { + addRisk(configRisks, "车辆二级维护提醒: 未设置", "not_configured", "企业配置", "configuration", "secondaryMaintenance", "/config/vehicleSecondaryMaintenanceConfig"); + } + // 隐患排查期限 + HotCompanyBasicConfigVo basicConfig = null; + try { + HotCompanyBasicConfigBo basicConfigBo = new HotCompanyBasicConfigBo(); + basicConfigBo.setCompanyId(companyId); + List basicConfigs = hotCompanyBasicConfigService.queryList(basicConfigBo); + if (!basicConfigs.isEmpty()) { + basicConfig = basicConfigs.get(0); + } + } catch (Exception e) { + // 忽略 + } + + if (basicConfig == null || basicConfig.getHiddenDangerRole() == null) { + addRisk(configRisks, "隐患排查期限: 未配置", "not_configured", "企业配置", "configuration", "hiddenDangerRole", "/config/companyBasicConfig"); + } + configCat.setItems(configRisks); + configCat.setCount(configRisks.size()); + + // 1.2 营业执照(预警阈值 60 天) + List licenseRisks = new ArrayList<>(); + List roadRisks = new ArrayList<>(); + SysCompanyVo company = sysCompanyService.queryById(companyId); + long licenseWarningDays = 60; + + if (company != null) { + // 营业执照 + boolean isLongTerm = company.getBusinessLongTerm() != null && company.getBusinessLongTerm(); + if (!isLongTerm) { + checkDateRisk(licenseRisks, company.getBusinessTerm(), "营业执照", "营业执照", "business_license", licenseWarningDays, null, null, null, "/resourceManagement/company"); + } + + // 道路经营许可证(预警阈值 60 天) + checkDateRisk(roadRisks, company.getRoadExpireDate(), "道路经营许可证", "道路经营许可证", "road_permit", licenseWarningDays, null, null, null, "/resourceManagement/company"); + } + licenseCat.setItems(licenseRisks); + licenseCat.setCount(licenseRisks.size()); + roadCat.setItems(roadRisks); + roadCat.setCount(roadRisks.size()); + + // 1.3 企业保险(预警阈值 60 天) + List insuranceRisks = new ArrayList<>(); + HotCompanyInsuranceBo insuranceBo = new HotCompanyInsuranceBo(); + insuranceBo.setCompanyId(companyId); + // 只查询未归档的保险 + insuranceBo.setIsArchived(0L); + List insurances = hotCompanyInsuranceService.queryList(insuranceBo); + + for (HotCompanyInsuranceVo ins : insurances) { + String insuranceName = StringUtils.isNotBlank(ins.getInsuranceType()) ? ins.getInsuranceType() : "未知"; + String dateStr = ins.getEndDate() != null ? convertDateToLocalDate(ins.getEndDate()).toString() : ""; + checkDateRisk(insuranceRisks, convertDateToLocalDate(ins.getEndDate()), "企业保险: (" + insuranceName + ") " + dateStr, "企业保险", "company_insurance", licenseWarningDays, null, null, String.valueOf(ins.getId()), "/resourceManagement/companyInsurance"); + } + + insuranceCat.setItems(insuranceRisks); + insuranceCat.setCount(insuranceRisks.size()); + + // 1.4 管理人员(预警阈值 60 天) + List qualRisks = new ArrayList<>(); + List laborRisks = new ArrayList<>(); + long contractWarningDays = 30; // 劳动合同预警阈值 + + HotCompanySafetyManagerBo managerBo = new HotCompanySafetyManagerBo(); + managerBo.setCompanyId(companyId); + managerBo.setStatus(1L); // 在职 + managerBo.setIsResigned(0L); // 未离职 + List managers = hotCompanySafetyManagerService.queryList(managerBo); + for (HotCompanySafetyManagerVo m : managers) { + boolean isCorpHead = m.getCompanyHead() != null && m.getCompanyHead() == 1L; + if (isCorpHead) { + continue; + } + // 上岗资格证(预警阈值 60 天) + if (StringUtils.isBlank(m.getCertificateNo())) { + checkDateRisk(qualRisks, convertDateToLocalDate(m.getCertificateExpireDate()), "上岗资格证: (" + m.getName() + ")", "上岗资格证", "qualification_certificate", licenseWarningDays, 2, String.valueOf(m.getId()), null, "/resourceManagement/companySafetyManager"); + } else { + checkDateRisk(qualRisks, convertDateToLocalDate(m.getCertificateExpireDate()), "上岗资格证: (" + m.getName() + ")", "上岗资格证", "qualification_certificate", licenseWarningDays, 2, String.valueOf(m.getId()), null, "/resourceManagement/companySafetyManager"); + } + + // 劳动合同(预警阈值 30 天) + // 目标:在职且合同未归档 + HotCompanyLaborContractBo contractBo = new HotCompanyLaborContractBo(); + contractBo.setCompanyId(companyId); + contractBo.setSafeUserId(m.getId()); + // 过滤掉已归档的劳动合同 + contractBo.setIsArchived(0L); + List contracts = hotCompanyLaborContractService.queryList(contractBo); + + boolean hasValidContract = false; + for (HotCompanyLaborContractVo c : contracts) { + hasValidContract = true; + checkDateRisk(laborRisks, convertDateToLocalDate(c.getExpireDate()), "劳动合同: (" + m.getName() + ")", "管理人员劳动合同", "manager_labor_contract", contractWarningDays, 2, String.valueOf(m.getId()), String.valueOf(c.getId()), "/resourceManagement/companyLaborContract"); + } + if (!hasValidContract) { + addRisk(laborRisks, "劳动合同: (" + m.getName() + ") 未签署或未归档", "not_configured", "管理人员劳动合同", "manager_labor_contract", 2, String.valueOf(m.getId()), null, "/resourceManagement/companyLaborContract"); + } + } + + qualCat.setItems(qualRisks); + qualCat.setCount(qualRisks.size()); + laborCat.setItems(laborRisks); + laborCat.setCount(laborRisks.size()); + + return categories; + } + + private List getVehicleRisk(Long companyId) { + List categories = new ArrayList<>(); + long warningDays = 30; + + RiskCategoryVo licenseInspectionCat = new RiskCategoryVo(); + licenseInspectionCat.setTitle("行驶证检验有效期"); + licenseInspectionCat.setType("vehicle_license_inspection_validity"); + categories.add(licenseInspectionCat); + + RiskCategoryVo scrapCat = new RiskCategoryVo(); + scrapCat.setTitle("行驶证强制报废期"); + scrapCat.setType("vehicle_mandatory_scrap"); + categories.add(scrapCat); + + RiskCategoryVo transportCat = new RiskCategoryVo(); + transportCat.setTitle("运输证"); + transportCat.setType("transport_permit"); + categories.add(transportCat); + + RiskCategoryVo contractCat = new RiskCategoryVo(); + contractCat.setTitle("车辆合同"); + contractCat.setType("vehicle_contract"); + categories.add(contractCat); + + RiskCategoryVo insuranceCat = new RiskCategoryVo(); + insuranceCat.setTitle("车辆保险"); + insuranceCat.setType("vehicle_insurance"); + categories.add(insuranceCat); + + RiskCategoryVo terminalCat = new RiskCategoryVo(); + terminalCat.setTitle("定位终端安装"); + terminalCat.setType("vehicle_terminal_install"); + categories.add(terminalCat); + + RiskCategoryVo annualCheckCat = new RiskCategoryVo(); + annualCheckCat.setTitle("年审检查"); + annualCheckCat.setType("vehicle_annual_check"); + categories.add(annualCheckCat); + + RiskCategoryVo secondaryMaintenanceCat = new RiskCategoryVo(); + secondaryMaintenanceCat.setTitle("二级维护"); + secondaryMaintenanceCat.setType("vehicle_secondary_maintenance"); + categories.add(secondaryMaintenanceCat); + + List licenseInspectionRisks = new ArrayList<>(); + List scrapRisks = new ArrayList<>(); + List transportRisks = new ArrayList<>(); + List contractRisks = new ArrayList<>(); + List insuranceRisks = new ArrayList<>(); + List terminalRisks = new ArrayList<>(); + List annualCheckRisks = new ArrayList<>(); + List secondaryMaintenanceRisks = new ArrayList<>(); + + HotVehicleBo vehicleBo = new HotVehicleBo(); + vehicleBo.setCompanyId(companyId); + // 仅检查正常运营车辆 + vehicleBo.setVehicleStatus(1L); + vehicleBo.setOperationStatus(1L); // 1=正常 + List vehicles = hotVehicleService.queryList(vehicleBo); + + HotVehicleContractBo contractBo = new HotVehicleContractBo(); + contractBo.setCompanyId(companyId); + List allContracts = hotVehicleContractService.queryList(contractBo); + Map contractMap = new HashMap<>(); + if (allContracts != null) { + for (HotVehicleContractVo c : allContracts) { + if (c == null || StringUtils.isBlank(c.getVehicleId()) || (c.getIsDeleted() != null && c.getIsDeleted() == 1L)) { + continue; + } + HotVehicleContractVo prev = contractMap.get(c.getVehicleId()); + if (prev == null) { + contractMap.put(c.getVehicleId(), c); + } else { + Date prevDate = prev.getExpireDate(); + Date curDate = c.getExpireDate(); + if (prevDate == null) { + if (curDate != null) { + contractMap.put(c.getVehicleId(), c); + } + } else if (curDate != null && curDate.after(prevDate)) { + contractMap.put(c.getVehicleId(), c); + } + } + } + } + + HotVehicleTerminalInstallBo terminalBo = new HotVehicleTerminalInstallBo(); + terminalBo.setCompanyId(companyId); + List allTerminalInstalls = hotVehicleTerminalInstallService.queryList(terminalBo); + Map terminalMap = new HashMap<>(); + if (allTerminalInstalls != null) { + for (HotVehicleTerminalInstallVo t : allTerminalInstalls) { + if (t == null || StringUtils.isBlank(t.getVehicleId()) || (t.getIsDeleted() != null && t.getIsDeleted() == 1L)) { + continue; + } + HotVehicleTerminalInstallVo prev = terminalMap.get(t.getVehicleId()); + if (prev == null) { + terminalMap.put(t.getVehicleId(), t); + } else { + Date prevDate = prev.getServiceEndDate(); + Date curDate = t.getServiceEndDate(); + if (prevDate == null) { + if (curDate != null) { + terminalMap.put(t.getVehicleId(), t); + } + } else if (curDate != null && curDate.after(prevDate)) { + terminalMap.put(t.getVehicleId(), t); + } + } + } + } + + HotVehicleInspectionConfigBo annualConfigBo = new HotVehicleInspectionConfigBo(); + annualConfigBo.setCompanyId(companyId); + annualConfigBo.setIsEnabled(1L); + annualConfigBo.setIsDeleted(0L); + List annualConfigs = hotVehicleInspectionConfigService.queryList(annualConfigBo); + List annualRules = new ArrayList<>(); + if (annualConfigs != null) { + annualRules = annualConfigs.stream() + .filter(c -> c != null && (c.getIsEnabled() == null || c.getIsEnabled() == 1L)) + .filter(c -> c.getIntervalYears() != null && c.getInspectionTimes() != null) + .sorted(Comparator.comparing(c -> c.getIntervalYears() == null ? Long.MAX_VALUE : c.getIntervalYears())) + .map(c -> new VehicleUtils.AnnualInspectionRule(c.getIntervalYears().intValue(), c.getInspectionTimes().intValue())) + .collect(Collectors.toList()); + } + + HotVehicleSecondaryMaintenanceConfigBo secondaryConfigBo = new HotVehicleSecondaryMaintenanceConfigBo(); + secondaryConfigBo.setCompanyId(companyId); + secondaryConfigBo.setIsEnabled(1L); + secondaryConfigBo.setIsDeleted(0L); + List secondaryConfigs = hotVehicleSecondaryMaintenanceConfigService.queryList(secondaryConfigBo); + List secondaryRules = new ArrayList<>(); + if (secondaryConfigs != null) { + secondaryRules = secondaryConfigs.stream() + .filter(c -> c != null && (c.getIsEnabled() == null || c.getIsEnabled() == 1L)) + .filter(c -> c.getIntervalYears() != null && c.getInspectionTimes() != null) + .sorted(Comparator.comparing(c -> c.getIntervalYears() == null ? Long.MAX_VALUE : c.getIntervalYears())) + .map(c -> new VehicleUtils.AnnualInspectionRule(c.getIntervalYears().intValue(), c.getInspectionTimes().intValue())) + .collect(Collectors.toList()); + } + + for (HotVehicleVo v : vehicles) { + String link = "/resourceManagement/vehicleManagement/detail?id=" + v.getId(); + LocalDate registrationDate = convertDateToLocalDate(v.getCertificateIssueDate()); + + LocalDate licenseInspectionDate = parseYearMonthEnd(v.getInspectionValidMonth()); + if (licenseInspectionDate == null) { + addRisk(licenseInspectionRisks, "行驶证检验有效期: (" + v.getPlateNumber() + ") 未填写", "not_filled", "行驶证检验有效期", "vehicle_license_inspection_validity", null, null, String.valueOf(v.getId()), link); + } else { + checkDateRisk(licenseInspectionRisks, licenseInspectionDate, "行驶证检验有效期: (" + v.getPlateNumber() + ")", "行驶证检验有效期", "vehicle_license_inspection_validity", warningDays, null, null, String.valueOf(v.getId()), link); + } + + LocalDate scrapDate = convertDateToLocalDate(v.getMandatoryScrapEndDate()); + if (scrapDate == null) { + addRisk(scrapRisks, "行驶证强制报废期: (" + v.getPlateNumber() + ") 未填写", "not_filled", "行驶证强制报废期", "vehicle_mandatory_scrap", null, null, String.valueOf(v.getId()), link); + } else { + checkDateRisk(scrapRisks, scrapDate, "行驶证强制报废期: (" + v.getPlateNumber() + ")", "行驶证强制报废期", "vehicle_mandatory_scrap", warningDays, null, null, String.valueOf(v.getId()), link); + } + + LocalDate transportDate = parseYearMonthEnd(v.getTransportValidMonth()); + if (transportDate == null) { + addRisk(transportRisks, "运输证: (" + v.getPlateNumber() + ") 未填写", "not_filled", "运输证", "transport_permit", null, null, String.valueOf(v.getId()), link); + } else { + checkDateRisk(transportRisks, transportDate, "运输证: (" + v.getPlateNumber() + ")", "运输证", "transport_permit", warningDays, null, null, String.valueOf(v.getId()), link); + } + + HotVehicleContractVo contract = contractMap.get(v.getId()); + LocalDate contractDate = contract == null ? null : convertDateToLocalDate(contract.getExpireDate()); + if (contractDate == null) { + addRisk(contractRisks, "车辆合同: (" + v.getPlateNumber() + ") 未填写", "not_filled", "车辆合同", "vehicle_contract", null, null, String.valueOf(v.getId()), link); + } else { + checkDateRisk(contractRisks, contractDate, "车辆合同: (" + v.getPlateNumber() + ")", "车辆合同", "vehicle_contract", warningDays, null, null, String.valueOf(v.getId()), link); + } + + HotVehicleInsuranceBo insuranceBo = new HotVehicleInsuranceBo(); + insuranceBo.setVehicleId(v.getId()); + // 过滤掉已归档的保险 + insuranceBo.setIsArchived(0); + List insurances = hotVehicleInsuranceService.queryList(insuranceBo); + if (insurances.isEmpty()) { + addRisk(insuranceRisks, "车辆保险: (" + v.getPlateNumber() + ") 未配置", "not_configured", "车辆保险", "vehicle_insurance", null, null, String.valueOf(v.getId()), link); + } else { + boolean hasNullEndDate = false; + for (HotVehicleInsuranceVo ins : insurances) { + LocalDate endDate = convertDateToLocalDate(ins == null ? null : ins.getEndDate()); + if (endDate == null) { + hasNullEndDate = true; + continue; + } + checkDateRisk(insuranceRisks, endDate, "车辆保险: (" + v.getPlateNumber() + ")", "车辆保险", "vehicle_insurance", warningDays, null, null, String.valueOf(v.getId()), link); + } + if (hasNullEndDate) { + addRisk(insuranceRisks, "车辆保险: (" + v.getPlateNumber() + ") 未填写", "not_filled", "车辆保险", "vehicle_insurance", null, null, String.valueOf(v.getId()), link); + } + } + + // 从预处理的 terminalMap 中获取该车辆“定位终端安装”的最新记录(按 serviceEndDate 取最近的一条) + HotVehicleTerminalInstallVo terminal = terminalMap.get(v.getId()); + // 将服务截止日期转换为 LocalDate,作为到期/过期判断的基准日期 + LocalDate terminalDate = terminal == null ? null : convertDateToLocalDate(terminal.getServiceEndDate()); + if (terminalDate == null) { + // 无记录或截止日期未填写,提示信息不全 + addRisk(terminalRisks, "定位终端安装: (" + v.getPlateNumber() + ") 未填写", "not_filled", "定位终端安装", "vehicle_terminal_install", null, null, String.valueOf(v.getId()), link); + } else { + // 有截止日期:按 warningDays 做到期/过期预警 + checkDateRisk(terminalRisks, terminalDate, "定位终端安装: (" + v.getPlateNumber() + ")", "定位终端安装", "vehicle_terminal_install", warningDays, null, null, String.valueOf(v.getId()), link); + } + + HotVehicleAnnualReviewVo annual = hotVehicleAnnualReviewService.queryLatestByVehicleId(v.getId()); + LocalDate lastInspectionDate = annual == null ? null : convertDateToLocalDate(annual.getReviewDate()); + VehicleUtils.InspectionResult result1 = VehicleUtils.calculateInspectionStatus(annualRules, registrationDate, lastInspectionDate); + if (result1 != null) { + if (result1.getOverdueDays() > 0 || (result1.getRemainingDays() == 0 && result1.getWarningLevel() == 1)) { + addRisk(annualCheckRisks, "年审检测:(" + v.getPlateNumber() + ") 已到期(" + result1.getOverdueDays() + ")天", "expired", "年审检测", "vehicle_annual_check", null, null, String.valueOf(v.getId()), link); + } else if (result1.getRemainingDays() > 0 && result1.getRemainingDays() <= 30) { + addRisk(annualCheckRisks, "年审检测:(" + v.getPlateNumber() + ") 剩余(" + result1.getRemainingDays() + ")天到期", "warning", "年审检测", "vehicle_annual_check", null, null, String.valueOf(v.getId()), link); + } + } + + HotVehicleMaintenanceVo maintenance = hotVehicleMaintenanceService.queryLatestByVehicleId(v.getId()); + LocalDate maintenanceBaseDate = maintenance == null ? null : convertDateToLocalDate(maintenance.getFinishTime() != null ? maintenance.getFinishTime() : maintenance.getMaintainDate()); + VehicleUtils.InspectionResult result2 = VehicleUtils.calculateMaintenanceStatus(secondaryRules, registrationDate, maintenanceBaseDate); + if (result2 != null) { + if (result2.getOverdueDays() > 0 || (result2.getRemainingDays() == 0 && result2.getWarningLevel() == 1)) { + addRisk(secondaryMaintenanceRisks, "二级维护:(" + v.getPlateNumber() + ") 已到期(" + result2.getOverdueDays() + ")天", "expired", "二级维护", "vehicle_secondary_maintenance", null, null, String.valueOf(v.getId()), link); + } else if (result2.getRemainingDays() > 0 && result2.getRemainingDays() <= 30) { + addRisk(secondaryMaintenanceRisks, "二级维护:(" + v.getPlateNumber() + ") 剩余(" + result2.getRemainingDays() + ")天到期", "warning", "二级维护", "vehicle_secondary_maintenance", null, null, String.valueOf(v.getId()), link); + } + } + } + + licenseInspectionCat.setItems(licenseInspectionRisks); + licenseInspectionCat.setCount(licenseInspectionRisks.size()); + scrapCat.setItems(scrapRisks); + scrapCat.setCount(scrapRisks.size()); + transportCat.setItems(transportRisks); + transportCat.setCount(transportRisks.size()); + contractCat.setItems(contractRisks); + contractCat.setCount(contractRisks.size()); + insuranceCat.setItems(insuranceRisks); + insuranceCat.setCount(insuranceRisks.size()); + terminalCat.setItems(terminalRisks); + terminalCat.setCount(terminalRisks.size()); + annualCheckCat.setItems(annualCheckRisks); + annualCheckCat.setCount(annualCheckRisks.size()); + secondaryMaintenanceCat.setItems(secondaryMaintenanceRisks); + secondaryMaintenanceCat.setCount(secondaryMaintenanceRisks.size()); + + return categories; + } + + private List getDriverRisk(Long companyId) { + List categories = new ArrayList<>(); + // 初始化分类 + RiskCategoryVo idCat = new RiskCategoryVo(); + idCat.setTitle("身份证"); + idCat.setType("id_card"); + categories.add(idCat); + + RiskCategoryVo promiseCat = new RiskCategoryVo(); + promiseCat.setTitle("承诺书"); + promiseCat.setType("commitment_letter"); + categories.add(promiseCat); + + RiskCategoryVo dlCat = new RiskCategoryVo(); + dlCat.setTitle("驾驶证"); + dlCat.setType("driver_license"); + categories.add(dlCat); + + RiskCategoryVo qualCat = new RiskCategoryVo(); + qualCat.setTitle("从业资格证"); + qualCat.setType("qualification_certificate"); + categories.add(qualCat); + + RiskCategoryVo laborCat = new RiskCategoryVo(); + laborCat.setTitle("劳动合同"); + laborCat.setType("labor_contract"); + categories.add(laborCat); + + // 风险项列表 + List idRisks = new ArrayList<>(); + List promiseRisks = new ArrayList<>(); + List dlRisks = new ArrayList<>(); + List qualRisks = new ArrayList<>(); + List laborRisks = new ArrayList<>(); + + // 获取驾驶员列表 + HotDriverBo driverBo = new HotDriverBo(); + driverBo.setCompanyId(companyId); + driverBo.setStatus(1L); // 正常状态 + driverBo.setAuditStatus(1); + List drivers = hotDriverService.queryList(driverBo); + + // 获取合同列表 + HotDriverLaborContractBo contractBo = new HotDriverLaborContractBo(); + contractBo.setCompanyId(companyId); + List allContracts = hotDriverLaborContractService.queryList(contractBo); + Map> contractMap = allContracts.stream() + .collect(Collectors.groupingBy(HotDriverLaborContractVo::getDriverId)); + + for (HotDriverVo d : drivers) { + if (d.getId() == null) continue; + String driverId = d.getId(); + String name = d.getName(); + String linkBase = "/driver/info?id=" + driverId + "&type="; + boolean isDriver = "1".equals(d.getPersonType()) || "driver".equalsIgnoreCase(d.getPersonType()); // 假设 "1" 或 "driver" 代表驾驶员 + + // 1. 身份证 + boolean idCardLongTerm = d.getIdCardLongTerm() != null && d.getIdCardLongTerm() == 1L; + if (!idCardLongTerm) { + if (d.getIdCardExpireDate() == null) { + addRisk(idRisks, "身份证: (" + name + ") 到期时间未填写", "not_filled", "身份证", "id_card", 1, driverId, null, linkBase + "1"); + } else { + checkDateRisk(idRisks, convertDateToLocalDate(d.getIdCardExpireDate()), "身份证: (" + name + ")", "身份证", "id_card", 90, 1, driverId, null, linkBase + "1"); + } + } + + // 2. 承诺书 + if (d.getIntegrityPromiseExpireDate() == null || StringUtils.isBlank(d.getIntegrityLevel()) || StringUtils.isBlank(d.getIntegrityPromiseAttachmentUrl())) { + addRisk(promiseRisks, "承诺书: (" + name + ") 信息不全", "not_filled", "承诺书", "commitment_letter", 1, driverId, null, linkBase + "1"); + } else { + checkDateRisk(promiseRisks, convertDateToLocalDate(d.getIntegrityPromiseExpireDate()), "承诺书: (" + name + ")", "承诺书", "commitment_letter", 30, 1, driverId, null, linkBase + "1"); + } + + // 3. 驾驶证 +// if (StringUtils.isBlank(d.getDriverLicenseNo())) { +// // 仅在人员类型为驾驶员时预警 +// if (isDriver) { +// addRisk(dlRisks, "驾驶证: (" + name + ") 到期时间未填写", "not_filled", "驾驶证", "driver_license", 1, driverId, null, linkBase + "1"); +// } +// } else { + boolean isLongTerm = d.getDriverLicenseLongTerm() != null && d.getDriverLicenseLongTerm() == 1L; + if (!isLongTerm && isDriver) { + checkDateRisk(dlRisks, convertDateToLocalDate(d.getDriverLicenseExpireDate()), "驾驶证: (" + name + ")", "驾驶证", "driver_license", 90, 1, driverId, null, linkBase + "1"); + } +// } + + // 4. 从业资格证 + if (d.getQualificationValidEndDate() != null) { + checkDateRisk(qualRisks, convertDateToLocalDate(d.getQualificationValidEndDate()), "从业资格证: (" + name + ")", "从业资格证", "qualification_certificate", 90, 1, driverId, null, linkBase + "1"); + } else { + // 如果不是驾驶员(如押运员)且缺失,则预警 + if (!isDriver) { + addRisk(qualRisks, "从业资格证: (" + name + ") 有效期未填写", "not_filled", "从业资格证", "qualification_certificate", 1, driverId, null, linkBase + "1"); + } + } + + // 5. 劳动合同 + List contracts = contractMap.getOrDefault(d.getId(), Collections.emptyList()); + boolean hasContract = false; + for (HotDriverLaborContractVo c : contracts) { + if ((c.getIsDeleted() != null && c.getIsDeleted() == 1L) || (c.getIsArchived() != null && c.getIsArchived() == 1L)) + continue; + hasContract = true; + if (c.getExpireDate() == null) { + addRisk(laborRisks, "劳动合同: (" + name + ") 截至时间未填写", "not_filled", "劳动合同", "labor_contract", 1, driverId, String.valueOf(c.getId()), linkBase + "5"); + } else { + checkDateRisk(laborRisks, convertDateToLocalDate(c.getExpireDate()), "劳动合同: (" + name + ")", "劳动合同", "labor_contract", 30, 1, driverId, String.valueOf(c.getId()), linkBase + "5"); + } + } + if (!hasContract) { + addRisk(laborRisks, "劳动合同: (" + name + ") 未签署", "not_signed", "劳动合同", "labor_contract", 1, driverId, null, linkBase + "5"); + } + } + + idCat.setItems(idRisks); + idCat.setCount(idRisks.size()); + promiseCat.setItems(promiseRisks); + promiseCat.setCount(promiseRisks.size()); + dlCat.setItems(dlRisks); + dlCat.setCount(dlRisks.size()); + qualCat.setItems(qualRisks); + qualCat.setCount(qualRisks.size()); + laborCat.setItems(laborRisks); + laborCat.setCount(laborRisks.size()); + + return categories; + } + + private List getTrainingRisk(Long companyId) { + List categories = new ArrayList<>(); + + // 1. 获取有效计划 + HotTrainingBo trainingBo = new HotTrainingBo(); + trainingBo.setCompanyId(companyId); + List allTrainings = hotTrainingService.queryList(trainingBo); + + List validTrainings = allTrainings.stream() + .filter(t -> { + if (t == null) { + return false; + } + if ("offline".equals(t.getTrainingType())) { + return false; + } + return t.getIsEnabled() != null && t.getIsEnabled() == 1L; + }) + .collect(Collectors.toList()); + + // 按类型分组 + Map> trainingMap = validTrainings.stream() + .collect(Collectors.groupingBy(t -> t.getTrainingType() != null ? t.getTrainingType() : "other")); + + // 2. 获取在职驾驶员以检查覆盖率 + HotDriverBo driverBo = new HotDriverBo(); + driverBo.setCompanyId(companyId); + driverBo.setStatus(1L); + driverBo.setAuditStatus(1); + List drivers = hotDriverService.queryList(driverBo); + + // 3. 处理每种类型 + // 关注的预定义类型 + + // 单独处理岗前培训 + processPreJobRisk(companyId, trainingMap, drivers, categories); + + List orderedTypes = List.of( + "daily", + "accident", + "violation", + "emergency", + "prevention", + "education" + ); + + for (String type : orderedTypes) { + List typeList = new ArrayList<>(trainingMap.getOrDefault(type, List.of())); + + List risks = new ArrayList<>(); + RiskCategoryVo cat = new RiskCategoryVo(); + cat.setTitle(getTrainingTypeLabel(type)); + cat.setType(type); + + for (HotTrainingVo t : typeList) { + if (t == null) { + continue; + } + if (t.getEndTime() != null) { + checkDateRisk(risks, convertDateToLocalDate(t.getEndTime()), "培训计划: (" + t.getPlanName() + ")", cat.getTitle(), type, 30, null, null, String.valueOf(t.getId()), "/securityManagement/training", String.valueOf(t.getId()), null); + } + + HotTrainingParticipantBo partBo = new HotTrainingParticipantBo(); + partBo.setTrainingId(t.getId()); + List participants = hotTrainingParticipantService.queryList(partBo); + + for (HotTrainingParticipantVo p : participants) { + boolean completed = (p.getIsCompleted() != null && p.getIsCompleted() == 1L) + || (p.getLearnProgress() != null && p.getLearnProgress().compareTo(BigDecimal.valueOf(100)) >= 0); + if (!completed) { + addRisk(risks, "培训未完成: (" + p.getUserName() + ") - " + t.getPlanName(), "not_completed", cat.getTitle(), type, null, p.getUserId(), String.valueOf(t.getId()), "/securityManagement/training", String.valueOf(t.getId()), String.valueOf(p.getId())); + } + } + } + cat.setItems(risks); + cat.setCount(risks.size()); + categories.add(cat); + } + + return categories; + } + + private void processPreJobRisk(Long companyId, Map> trainingMap, List drivers, List categories) { + RiskCategoryVo cat = new RiskCategoryVo(); + cat.setTitle("岗前培训"); + cat.setType("pre_job"); + List risks = new ArrayList<>(); + + List preJobPlans = trainingMap.getOrDefault("pre-job", new ArrayList<>()); + for (HotTrainingVo t : preJobPlans) { + if (t == null) { + continue; + } + if (t.getEndTime() != null) { + checkDateRisk(risks, convertDateToLocalDate(t.getEndTime()), "培训计划: (" + t.getPlanName() + ")", "岗前培训", "pre_job", 30, null, null, String.valueOf(t.getId()), "/securityManagement/training", String.valueOf(t.getId()), null); + } + } + + // 收集线上培训参与者 + List onlinePlanIds = new ArrayList<>(); + preJobPlans.forEach(p -> onlinePlanIds.add(p.getId())); + + List onlineParticipants = new ArrayList<>(); + if (!onlinePlanIds.isEmpty()) { + for (Long pid : onlinePlanIds) { + HotTrainingParticipantBo bo = new HotTrainingParticipantBo(); + bo.setTrainingId(pid); + onlineParticipants.addAll(hotTrainingParticipantService.queryList(bo)); + } + } + + // 手机号 -> 线上参与者映射 + Map> onlinePartMap = onlineParticipants.stream() + .filter(p -> StringUtils.isNotBlank(p.getPhone())) + .collect(Collectors.groupingBy(HotTrainingParticipantVo::getPhone)); + + Set offlinePreJobUserIds = new HashSet<>(); + if (companyId != null) { + HotTrainingBo offlinePreJobBo = new HotTrainingBo(); + offlinePreJobBo.setCompanyId(companyId); + offlinePreJobBo.setTrainingType("offline"); + offlinePreJobBo.setMeetingType("pre-job"); + offlinePreJobBo.setIsDeleted(0L); + List offlinePreJobPlans = hotTrainingService.queryList(offlinePreJobBo); + if (offlinePreJobPlans != null && !offlinePreJobPlans.isEmpty()) { + for (HotTrainingVo t : offlinePreJobPlans) { + if (t == null || t.getId() == null) { + continue; + } + String participantIds = t.getParticipantIds(); + if (StringUtils.isNotBlank(participantIds)) { + String[] userIds = participantIds.split(","); + offlinePreJobUserIds.addAll(Arrays.asList(userIds)); + } + } + } + } + + // 1. 覆盖率检查 + for (HotDriverVo d : drivers) { + if (StringUtils.isBlank(d.getPhone())) continue; + + boolean isCompliant = false; + String riskMsg = "未培训"; + String riskType = "not_trained"; + String link = null; + String tId = null; + String pId = null; + + if (StringUtils.isNotBlank(d.getId()) && offlinePreJobUserIds.contains(d.getId())) { + isCompliant = true; + } + + List myOnlineParts = onlinePartMap.get(d.getPhone()); + if (myOnlineParts != null && !myOnlineParts.isEmpty()) { + for (HotTrainingParticipantVo p : myOnlineParts) { + boolean completed = (p.getIsCompleted() != null && p.getIsCompleted() == 1L) + || (p.getLearnProgress() != null && p.getLearnProgress().compareTo(BigDecimal.valueOf(100)) >= 0); + if (completed && p.getCompleteTime() != null) { + isCompliant = true; + break; + } + if (riskType.equals("not_trained")) { + riskMsg = "未完成"; + riskType = "not_completed"; + link = "/securityManagement/training"; + tId = String.valueOf(p.getTrainingId()); + pId = String.valueOf(p.getId()); + } + } + } + + if (!isCompliant) { + addRisk(risks, "岗前培训: 驾驶员 (" + d.getName() + ") " + riskMsg, riskType, "岗前培训", "pre_job", 1, d.getId(), tId, link, tId, pId); + } + } + + cat.setItems(risks); + cat.setCount(risks.size()); + categories.add(cat); + } + + private String getTrainingTypeLabel(String type) { + if ("pre-job".equals(type)) return "岗前培训"; + if ("daily".equals(type)) return "日常培训"; + // if ("three_level".equals(type)) return "三级教育"; + if ("accident".equals(type)) return "事故培训"; + if ("special".equals(type)) return "专题培训"; + if ("violation".equals(type)) return "违章培训"; + if ("emergency".equals(type)) return "应急培训"; + if ("prevention".equals(type)) return "双重预防学习"; + if ("education".equals(type)) return "再教育培训"; + if ("offline".equals(type)) return "线下培训"; + return "其他培训"; + } + + private void checkDateRisk(List list, LocalDate date, String titlePrefix, String moduleName, String moduleType, long warningDays, Integer personnelType, String personnelId, Object businessId, String link) { + checkDateRisk(list, date, titlePrefix, moduleName, moduleType, warningDays, personnelType, personnelId, businessId != null ? String.valueOf(businessId) : null, link); + } + + private void checkDateRisk(List list, LocalDate date, String titlePrefix, String moduleName, String moduleType, long warningDays, Integer personnelType, String personnelId, String businessId, String link) { + checkDateRisk(list, date, titlePrefix, moduleName, moduleType, warningDays, personnelType, personnelId, businessId, link, null, null); + } + + private void checkDateRisk(List list, LocalDate date, String titlePrefix, String moduleName, String moduleType, long warningDays, Integer personnelType, String personnelId, String businessId, String link, String trainingId, String participantId) { + if (date == null) { + addRisk(list, titlePrefix + "有效期为空", "expired", moduleName, moduleType, personnelType, personnelId, businessId, null, link, trainingId, participantId); + return; + } + LocalDate now = LocalDate.now(); + long days = ChronoUnit.DAYS.between(now, date); + + if (days < 0) { + addRisk(list, titlePrefix + " 已过期 (" + Math.abs(days) + ") 天", "expired", moduleName, moduleType, personnelType, personnelId, businessId, null, link, trainingId, participantId); + } else if (days <= warningDays) { + addRisk(list, titlePrefix + " 剩余 (" + days + ") 天到期", "warning", moduleName, moduleType, personnelType, personnelId, businessId, null, link, trainingId, participantId); + } + } + + private void addRisk(List list, String title, String type, String moduleName, String moduleType, String link) { + addRisk(list, title, type, moduleName, moduleType, null, null, null, null, link, null, null); + } + + private void addRisk(List list, String title, String type, String moduleName, String moduleType, String configKey, String link) { + addRisk(list, title, type, moduleName, moduleType, null, null, null, configKey, link, null, null); + } + + private void addRisk(List list, String title, String type, String moduleName, String moduleType, Integer personnelType, String personnelId, String link) { + addRisk(list, title, type, moduleName, moduleType, personnelType, personnelId, null, null, link, null, null); + } + + private void addRisk(List list, String title, String type, String moduleName, String moduleType, Integer personnelType, String personnelId, String businessId, String link) { + addRisk(list, title, type, moduleName, moduleType, personnelType, personnelId, businessId, null, link, null, null); + } + + private void addRisk(List list, String title, String type, String moduleName, String moduleType, Integer personnelType, String personnelId, String businessId, String link, String trainingId, String participantId) { + addRisk(list, title, type, moduleName, moduleType, personnelType, personnelId, businessId, null, link, trainingId, participantId); + } + + private void addRisk(List list, String title, String type, String moduleName, String moduleType, Integer personnelType, String personnelId, String businessId, String configKey, String link, String trainingId, String participantId) { + RiskItemVo item = new RiskItemVo(); + item.setTitle(title); + item.setType(type); + item.setModuleName(moduleName); + item.setModuleType(moduleType); + item.setPersonnelType(personnelType); + item.setPersonnelId(personnelId); + item.setBusinessId(businessId); + item.setConfigKey(configKey); + item.setLink(link); + item.setTrainingId(trainingId); + item.setParticipantId(participantId); + list.add(item); + } + + private LocalDate convertDateToLocalDate(Date date) { + if (date == null) return null; + return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } + + private LocalDate parseYearMonth(String yearMonth) { + try { + // 假设格式为 "yyyy-MM" + if (yearMonth.length() == 7) { + return LocalDate.parse(yearMonth + "-01"); + } + } catch (Exception e) { + // 忽略 + } + return null; + } + + private LocalDate parseYearMonthEnd(String yearMonth) { + if (StringUtils.isBlank(yearMonth)) { + return null; + } + LocalDate date = parseYearMonth(yearMonth); + if (date == null) { + return null; + } + return date.plusMonths(1).minusDays(1); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/strategy/IGovRiskHandler.java b/src/main/java/com/hotwj/platform/workbench/strategy/IGovRiskHandler.java new file mode 100644 index 0000000..b40d0c7 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/strategy/IGovRiskHandler.java @@ -0,0 +1,32 @@ +package com.hotwj.platform.workbench.strategy; + +import com.hotwj.platform.workbench.domain.vo.GovHiddenDangerDetailVo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * 政府端隐患处理器策略接口 + */ +public interface IGovRiskHandler { + /** + * 获取支持的隐患类型Key + */ + String getType(); + + /** + * 统计该辖区下的隐患数量 + * @param districtCode 区划代码 + * @param companyId 企业ID (可选) + * @return 数量 + */ + long count(String districtCode, Long companyId); + + /** + * 查询该辖区下的具体隐患列表 + * @param districtCode 区划代码 + * @param companyId 企业ID (可选) + * @param pageQuery 分页参数 + * @return 分页列表 + */ + TableDataInfo list(String districtCode, Long companyId, PageQuery pageQuery); +} diff --git a/src/main/java/com/hotwj/platform/workbench/strategy/impl/BusinessLicenseRiskHandler.java b/src/main/java/com/hotwj/platform/workbench/strategy/impl/BusinessLicenseRiskHandler.java new file mode 100644 index 0000000..3d7e6f3 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/strategy/impl/BusinessLicenseRiskHandler.java @@ -0,0 +1,38 @@ +package com.hotwj.platform.workbench.strategy.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.workbench.domain.enums.GovHiddenDangerType; +import com.hotwj.platform.workbench.domain.vo.GovHiddenDangerDetailVo; +import com.hotwj.platform.workbench.mapper.GovWorkbenchMapper; +import com.hotwj.platform.workbench.strategy.IGovRiskHandler; +import lombok.RequiredArgsConstructor; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Component; + +/** + * 营业执照隐患处理器 + */ +@Component +@RequiredArgsConstructor +public class BusinessLicenseRiskHandler implements IGovRiskHandler { + + private final GovWorkbenchMapper govWorkbenchMapper; + + @Override + public String getType() { + return GovHiddenDangerType.BUSINESS_LICENSE.getKey(); + } + + @Override + public long count(String districtCode, Long companyId) { + return govWorkbenchMapper.countBusinessLicense(districtCode, companyId); + } + + @Override + public TableDataInfo list(String districtCode, Long companyId, PageQuery pageQuery) { + Page page = pageQuery.build(); + Page result = govWorkbenchMapper.listBusinessLicense(page, districtCode, companyId); + return TableDataInfo.build(result); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/strategy/impl/DriverIdCardRiskHandler.java b/src/main/java/com/hotwj/platform/workbench/strategy/impl/DriverIdCardRiskHandler.java new file mode 100644 index 0000000..21e49f4 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/strategy/impl/DriverIdCardRiskHandler.java @@ -0,0 +1,38 @@ +package com.hotwj.platform.workbench.strategy.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.workbench.domain.enums.GovHiddenDangerType; +import com.hotwj.platform.workbench.domain.vo.GovHiddenDangerDetailVo; +import com.hotwj.platform.workbench.mapper.GovWorkbenchMapper; +import com.hotwj.platform.workbench.strategy.IGovRiskHandler; +import lombok.RequiredArgsConstructor; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Component; + +/** + * 驾驶员身份证隐患处理器 + */ +@Component +@RequiredArgsConstructor +public class DriverIdCardRiskHandler implements IGovRiskHandler { + + private final GovWorkbenchMapper govWorkbenchMapper; + + @Override + public String getType() { + return GovHiddenDangerType.DRIVER_ID_CARD.getKey(); + } + + @Override + public long count(String districtCode, Long companyId) { + return govWorkbenchMapper.countDriverIdCard(districtCode, companyId); + } + + @Override + public TableDataInfo list(String districtCode, Long companyId, PageQuery pageQuery) { + Page page = pageQuery.build(); + Page result = govWorkbenchMapper.listDriverIdCard(page, districtCode, companyId); + return TableDataInfo.build(result); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/strategy/impl/DriverLicenseRiskHandler.java b/src/main/java/com/hotwj/platform/workbench/strategy/impl/DriverLicenseRiskHandler.java new file mode 100644 index 0000000..883f196 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/strategy/impl/DriverLicenseRiskHandler.java @@ -0,0 +1,38 @@ +package com.hotwj.platform.workbench.strategy.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.workbench.domain.enums.GovHiddenDangerType; +import com.hotwj.platform.workbench.domain.vo.GovHiddenDangerDetailVo; +import com.hotwj.platform.workbench.mapper.GovWorkbenchMapper; +import com.hotwj.platform.workbench.strategy.IGovRiskHandler; +import lombok.RequiredArgsConstructor; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Component; + +/** + * 驾驶员驾驶证隐患处理器 + */ +@Component +@RequiredArgsConstructor +public class DriverLicenseRiskHandler implements IGovRiskHandler { + + private final GovWorkbenchMapper govWorkbenchMapper; + + @Override + public String getType() { + return GovHiddenDangerType.DRIVER_LICENSE.getKey(); + } + + @Override + public long count(String districtCode, Long companyId) { + return govWorkbenchMapper.countDriverLicense(districtCode, companyId); + } + + @Override + public TableDataInfo list(String districtCode, Long companyId, PageQuery pageQuery) { + Page page = pageQuery.build(); + Page result = govWorkbenchMapper.listDriverLicense(page, districtCode, companyId); + return TableDataInfo.build(result); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/strategy/impl/DriverPreJobTrainingRiskHandler.java b/src/main/java/com/hotwj/platform/workbench/strategy/impl/DriverPreJobTrainingRiskHandler.java new file mode 100644 index 0000000..dd8fb75 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/strategy/impl/DriverPreJobTrainingRiskHandler.java @@ -0,0 +1,38 @@ +package com.hotwj.platform.workbench.strategy.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.workbench.domain.enums.GovHiddenDangerType; +import com.hotwj.platform.workbench.domain.vo.GovHiddenDangerDetailVo; +import com.hotwj.platform.workbench.mapper.GovWorkbenchMapper; +import com.hotwj.platform.workbench.strategy.IGovRiskHandler; +import lombok.RequiredArgsConstructor; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Component; + +/** + * 驾驶员岗前培训隐患处理器 + */ +@Component +@RequiredArgsConstructor +public class DriverPreJobTrainingRiskHandler implements IGovRiskHandler { + + private final GovWorkbenchMapper govWorkbenchMapper; + + @Override + public String getType() { + return GovHiddenDangerType.DRIVER_PRE_JOB_TRAINING.getKey(); + } + + @Override + public long count(String districtCode, Long companyId) { + return govWorkbenchMapper.countDriverPreJobTraining(districtCode, companyId); + } + + @Override + public TableDataInfo list(String districtCode, Long companyId, PageQuery pageQuery) { + Page page = pageQuery.build(); + Page result = govWorkbenchMapper.listDriverPreJobTraining(page, districtCode, companyId); + return TableDataInfo.build(result); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/strategy/impl/DriverQualificationRiskHandler.java b/src/main/java/com/hotwj/platform/workbench/strategy/impl/DriverQualificationRiskHandler.java new file mode 100644 index 0000000..db1b7e9 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/strategy/impl/DriverQualificationRiskHandler.java @@ -0,0 +1,38 @@ +package com.hotwj.platform.workbench.strategy.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.workbench.domain.enums.GovHiddenDangerType; +import com.hotwj.platform.workbench.domain.vo.GovHiddenDangerDetailVo; +import com.hotwj.platform.workbench.mapper.GovWorkbenchMapper; +import com.hotwj.platform.workbench.strategy.IGovRiskHandler; +import lombok.RequiredArgsConstructor; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Component; + +/** + * 驾驶员从业资格证隐患处理器 + */ +@Component +@RequiredArgsConstructor +public class DriverQualificationRiskHandler implements IGovRiskHandler { + + private final GovWorkbenchMapper govWorkbenchMapper; + + @Override + public String getType() { + return GovHiddenDangerType.DRIVER_QUALIFICATION.getKey(); + } + + @Override + public long count(String districtCode, Long companyId) { + return govWorkbenchMapper.countDriverQualification(districtCode, companyId); + } + + @Override + public TableDataInfo list(String districtCode, Long companyId, PageQuery pageQuery) { + Page page = pageQuery.build(); + Page result = govWorkbenchMapper.listDriverQualification(page, districtCode, companyId); + return TableDataInfo.build(result); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/strategy/impl/DriverViolationRiskHandler.java b/src/main/java/com/hotwj/platform/workbench/strategy/impl/DriverViolationRiskHandler.java new file mode 100644 index 0000000..e473c69 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/strategy/impl/DriverViolationRiskHandler.java @@ -0,0 +1,38 @@ +package com.hotwj.platform.workbench.strategy.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.workbench.domain.enums.GovHiddenDangerType; +import com.hotwj.platform.workbench.domain.vo.GovHiddenDangerDetailVo; +import com.hotwj.platform.workbench.mapper.GovWorkbenchMapper; +import com.hotwj.platform.workbench.strategy.IGovRiskHandler; +import lombok.RequiredArgsConstructor; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Component; + +/** + * 驾驶员违法违规隐患处理器 + */ +@Component +@RequiredArgsConstructor +public class DriverViolationRiskHandler implements IGovRiskHandler { + + private final GovWorkbenchMapper govWorkbenchMapper; + + @Override + public String getType() { + return GovHiddenDangerType.DRIVER_VIOLATION.getKey(); + } + + @Override + public long count(String districtCode, Long companyId) { + return govWorkbenchMapper.countDriverViolation(districtCode, companyId); + } + + @Override + public TableDataInfo list(String districtCode, Long companyId, PageQuery pageQuery) { + Page page = pageQuery.build(); + Page result = govWorkbenchMapper.listDriverViolation(page, districtCode, companyId); + return TableDataInfo.build(result); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/strategy/impl/ManagerQualificationRiskHandler.java b/src/main/java/com/hotwj/platform/workbench/strategy/impl/ManagerQualificationRiskHandler.java new file mode 100644 index 0000000..9bb8eb6 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/strategy/impl/ManagerQualificationRiskHandler.java @@ -0,0 +1,38 @@ +package com.hotwj.platform.workbench.strategy.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.workbench.domain.enums.GovHiddenDangerType; +import com.hotwj.platform.workbench.domain.vo.GovHiddenDangerDetailVo; +import com.hotwj.platform.workbench.mapper.GovWorkbenchMapper; +import com.hotwj.platform.workbench.strategy.IGovRiskHandler; +import lombok.RequiredArgsConstructor; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Component; + +/** + * 上岗资格证隐患处理器 + */ +@Component +@RequiredArgsConstructor +public class ManagerQualificationRiskHandler implements IGovRiskHandler { + + private final GovWorkbenchMapper govWorkbenchMapper; + + @Override + public String getType() { + return GovHiddenDangerType.MANAGER_QUALIFICATION.getKey(); + } + + @Override + public long count(String districtCode, Long companyId) { + return govWorkbenchMapper.countManagerQualification(districtCode, companyId); + } + + @Override + public TableDataInfo list(String districtCode, Long companyId, PageQuery pageQuery) { + Page page = pageQuery.build(); + Page result = govWorkbenchMapper.listManagerQualification(page, districtCode, companyId); + return TableDataInfo.build(result); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/strategy/impl/RoadPermitRiskHandler.java b/src/main/java/com/hotwj/platform/workbench/strategy/impl/RoadPermitRiskHandler.java new file mode 100644 index 0000000..d002d21 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/strategy/impl/RoadPermitRiskHandler.java @@ -0,0 +1,38 @@ +package com.hotwj.platform.workbench.strategy.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.workbench.domain.enums.GovHiddenDangerType; +import com.hotwj.platform.workbench.domain.vo.GovHiddenDangerDetailVo; +import com.hotwj.platform.workbench.mapper.GovWorkbenchMapper; +import com.hotwj.platform.workbench.strategy.IGovRiskHandler; +import lombok.RequiredArgsConstructor; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Component; + +/** + * 道路经营许可证隐患处理器 + */ +@Component +@RequiredArgsConstructor +public class RoadPermitRiskHandler implements IGovRiskHandler { + + private final GovWorkbenchMapper govWorkbenchMapper; + + @Override + public String getType() { + return GovHiddenDangerType.ROAD_PERMIT.getKey(); + } + + @Override + public long count(String districtCode, Long companyId) { + return govWorkbenchMapper.countRoadPermit(districtCode, companyId); + } + + @Override + public TableDataInfo list(String districtCode, Long companyId, PageQuery pageQuery) { + Page page = pageQuery.build(); + Page result = govWorkbenchMapper.listRoadPermit(page, districtCode, companyId); + return TableDataInfo.build(result); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/strategy/impl/VehicleAnnualInspectRiskHandler.java b/src/main/java/com/hotwj/platform/workbench/strategy/impl/VehicleAnnualInspectRiskHandler.java new file mode 100644 index 0000000..0443394 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/strategy/impl/VehicleAnnualInspectRiskHandler.java @@ -0,0 +1,209 @@ +package com.hotwj.platform.workbench.strategy.impl; + +import com.hotwj.platform.config.vehicleInspectionConfig.domain.bo.HotVehicleInspectionConfigBo; +import com.hotwj.platform.config.vehicleInspectionConfig.domain.vo.HotVehicleInspectionConfigVo; +import com.hotwj.platform.config.vehicleInspectionConfig.service.IHotVehicleInspectionConfigService; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.domain.vo.HotVehicleAnnualReviewVo; +import com.hotwj.platform.resourceManagement.vehicleAnnualReview.service.IHotVehicleAnnualReviewService; +import com.hotwj.platform.resourceManagement.vehicleManagement.utils.VehicleUtils; +import com.hotwj.platform.workbench.domain.enums.GovHiddenDangerType; +import com.hotwj.platform.workbench.domain.vo.GovHiddenDangerDetailVo; +import com.hotwj.platform.workbench.domain.vo.VehicleRiskCalcVo; +import com.hotwj.platform.workbench.mapper.GovWorkbenchMapper; +import com.hotwj.platform.workbench.strategy.IGovRiskHandler; +import lombok.RequiredArgsConstructor; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 车辆年审检测隐患处理器 + */ +@Component +@RequiredArgsConstructor +public class VehicleAnnualInspectRiskHandler implements IGovRiskHandler { + + private final GovWorkbenchMapper govWorkbenchMapper; + private final IHotVehicleInspectionConfigService hotVehicleInspectionConfigService; + private final IHotVehicleAnnualReviewService hotVehicleAnnualReviewService; + + @Override + public String getType() { + return GovHiddenDangerType.VEHICLE_ANNUAL_INSPECT.getKey(); + } + + @Override + public long count(String districtCode, Long companyId) { + return getRisks(districtCode, companyId).size(); + } + + @Override + public TableDataInfo list(String districtCode, Long companyId, PageQuery pageQuery) { + List risks = getRisks(districtCode, companyId); + + // 内存分页 + int total = risks.size(); + Integer pageNum = pageQuery.getPageNum(); + Integer pageSize = pageQuery.getPageSize(); + + if (pageNum == null) pageNum = 1; + if (pageSize == null) pageSize = 10; + + int start = (pageNum - 1) * pageSize; + int end = Math.min(start + pageSize, total); + + if (start >= total) { + TableDataInfo empty = TableDataInfo.build(new ArrayList<>()); + empty.setTotal(total); + return empty; + } + + List pageList = risks.subList(start, end); + TableDataInfo tableDataInfo = TableDataInfo.build(pageList); + tableDataInfo.setTotal(total); + return tableDataInfo; + } + + public Map countByCompany(String districtCode) { + Map result = new HashMap<>(); + // 处理 districtCode 为 null 的情况,转为空字符串以匹配所有 + String code = districtCode == null ? "" : districtCode; + + // 1. 获取辖区内所有活跃车辆 + List vehicles = govWorkbenchMapper.selectVehicleForRiskCalc(code, null); + if (vehicles.isEmpty()) { + return result; + } + + // 2. 按企业分组 + Map> companyVehicles = vehicles.stream() + .collect(Collectors.groupingBy(VehicleRiskCalcVo::getCompanyId)); + + // 3. 遍历企业处理 + for (Map.Entry> entry : companyVehicles.entrySet()) { + Long companyId = entry.getKey(); + List cvs = entry.getValue(); + int riskCount = 0; + + // 3.1 获取该企业的年审配置 + try { + HotVehicleInspectionConfigBo configBo = new HotVehicleInspectionConfigBo(); + configBo.setCompanyId(companyId); + List allConfigs = hotVehicleInspectionConfigService.queryList(configBo); + + List rules = allConfigs == null ? new ArrayList<>() : allConfigs.stream() + .filter(c -> c != null && (c.getIsEnabled() == null || c.getIsEnabled() == 1L)) + .filter(c -> c.getIntervalYears() != null && c.getInspectionTimes() != null) + .sorted(Comparator.comparing(c -> c.getIntervalYears() == null ? Long.MAX_VALUE : c.getIntervalYears())) + .map(c -> new VehicleUtils.AnnualInspectionRule(c.getIntervalYears().intValue(), c.getInspectionTimes().intValue())) + .collect(Collectors.toList()); + + if (rules.isEmpty()) continue; + + // 3.2 遍历车辆 + for (VehicleRiskCalcVo v : cvs) { + if (v.getIssueDate() == null) continue; + + HotVehicleAnnualReviewVo lastReview = hotVehicleAnnualReviewService.queryLatestByVehicleId(v.getId()); + LocalDate lastReviewDate = lastReview != null && lastReview.getReviewDate() != null + ? lastReview.getReviewDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate() + : null; + + LocalDate issueDate = v.getIssueDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + VehicleUtils.InspectionResult calc = VehicleUtils.calculateInspectionStatus(rules, issueDate, lastReviewDate); + if (calc != null && calc.getOverdueDays() > 0) { + riskCount++; + } + } + + if (riskCount > 0) { + result.put(companyId, riskCount); + } + + } catch (Exception e) { + // 忽略异常,继续处理下一个企业 + e.printStackTrace(); + } + } + + return result; + } + + private List getRisks(String districtCode, Long targetCompanyId) { + List result = new ArrayList<>(); + + // 1. 获取辖区内所有活跃车辆 + List vehicles = govWorkbenchMapper.selectVehicleForRiskCalc(districtCode, targetCompanyId); + if (vehicles.isEmpty()) { + return result; + } + + // 2. 按企业分组 + Map> companyVehicles = vehicles.stream() + .collect(Collectors.groupingBy(VehicleRiskCalcVo::getCompanyId)); + + // 3. 遍历企业处理 + for (Map.Entry> entry : companyVehicles.entrySet()) { + Long companyId = entry.getKey(); + if (targetCompanyId != null && !targetCompanyId.equals(companyId)) { + continue; + } + List cvs = entry.getValue(); + + // 3.1 获取该企业的年审配置 + try { + HotVehicleInspectionConfigBo configBo = new HotVehicleInspectionConfigBo(); + configBo.setCompanyId(companyId); + List allConfigs = hotVehicleInspectionConfigService.queryList(configBo); + + List rules = allConfigs == null ? new ArrayList<>() : allConfigs.stream() + .filter(c -> c != null && (c.getIsEnabled() == null || c.getIsEnabled() == 1L)) + .filter(c -> c.getIntervalYears() != null && c.getInspectionTimes() != null) + .sorted(Comparator.comparing(c -> c.getIntervalYears() == null ? Long.MAX_VALUE : c.getIntervalYears())) + .map(c -> new VehicleUtils.AnnualInspectionRule(c.getIntervalYears().intValue(), c.getInspectionTimes().intValue())) + .collect(Collectors.toList()); + + if (rules.isEmpty()) continue; + + // 3.2 遍历车辆 + for (VehicleRiskCalcVo v : cvs) { + if (v.getIssueDate() == null) continue; + + LocalDate issueDate = v.getIssueDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + + // 获取最近一次年审日期 + HotVehicleAnnualReviewVo lastReview = hotVehicleAnnualReviewService.queryLatestByVehicleId(v.getId()); + LocalDate lastReviewDate = lastReview != null && lastReview.getReviewDate() != null + ? lastReview.getReviewDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate() + : null; + + VehicleUtils.InspectionResult calc = VehicleUtils.calculateInspectionStatus(rules, issueDate, lastReviewDate); + if (calc != null && calc.getOverdueDays() > 0) { + GovHiddenDangerDetailVo vo = new GovHiddenDangerDetailVo(); + vo.setId(v.getId()); + vo.setTitle(v.getPlateNumber()); + vo.setSubTitle("(" + v.getPlateNumber() + ")年审检测:已到期(" + calc.getOverdueDays() + ")天"); + + vo.setStatus("expired"); + if (calc.getNextInspectionDate() != null) { + vo.setDeadline(Date.from(calc.getNextInspectionDate().atStartOfDay(ZoneId.systemDefault()).toInstant())); + } + vo.setRelatedId(v.getCompanyName()); + result.add(vo); + } + } + + } catch (Exception e) { + // 忽略异常,继续处理下一个企业 + e.printStackTrace(); + } + } + + return result; + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/strategy/impl/VehicleLicenseRiskHandler.java b/src/main/java/com/hotwj/platform/workbench/strategy/impl/VehicleLicenseRiskHandler.java new file mode 100644 index 0000000..4fb241e --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/strategy/impl/VehicleLicenseRiskHandler.java @@ -0,0 +1,38 @@ +package com.hotwj.platform.workbench.strategy.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.workbench.domain.enums.GovHiddenDangerType; +import com.hotwj.platform.workbench.domain.vo.GovHiddenDangerDetailVo; +import com.hotwj.platform.workbench.mapper.GovWorkbenchMapper; +import com.hotwj.platform.workbench.strategy.IGovRiskHandler; +import lombok.RequiredArgsConstructor; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Component; + +/** + * 车辆行驶证隐患处理器 + */ +@Component +@RequiredArgsConstructor +public class VehicleLicenseRiskHandler implements IGovRiskHandler { + + private final GovWorkbenchMapper govWorkbenchMapper; + + @Override + public String getType() { + return GovHiddenDangerType.VEHICLE_LICENSE.getKey(); + } + + @Override + public long count(String districtCode, Long companyId) { + return govWorkbenchMapper.countVehicleLicense(districtCode, companyId); + } + + @Override + public TableDataInfo list(String districtCode, Long companyId, PageQuery pageQuery) { + Page page = pageQuery.build(); + Page result = govWorkbenchMapper.listVehicleLicense(page, districtCode, companyId); + return TableDataInfo.build(result); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/strategy/impl/VehicleMaintenanceRiskHandler.java b/src/main/java/com/hotwj/platform/workbench/strategy/impl/VehicleMaintenanceRiskHandler.java new file mode 100644 index 0000000..c429dd1 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/strategy/impl/VehicleMaintenanceRiskHandler.java @@ -0,0 +1,219 @@ +package com.hotwj.platform.workbench.strategy.impl; + +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.bo.HotVehicleSecondaryMaintenanceConfigBo; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.domain.vo.HotVehicleSecondaryMaintenanceConfigVo; +import com.hotwj.platform.config.vehicleSecondaryMaintenanceConfig.service.IHotVehicleSecondaryMaintenanceConfigService; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.domain.vo.HotVehicleMaintenanceVo; +import com.hotwj.platform.resourceManagement.vehicleMaintenance.service.IHotVehicleMaintenanceService; +import com.hotwj.platform.resourceManagement.vehicleManagement.utils.VehicleUtils; +import com.hotwj.platform.workbench.domain.enums.GovHiddenDangerType; +import com.hotwj.platform.workbench.domain.vo.GovHiddenDangerDetailVo; +import com.hotwj.platform.workbench.domain.vo.VehicleRiskCalcVo; +import com.hotwj.platform.workbench.mapper.GovWorkbenchMapper; +import com.hotwj.platform.workbench.strategy.IGovRiskHandler; +import lombok.RequiredArgsConstructor; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 车辆二级维护隐患处理器 + */ +@Component +@RequiredArgsConstructor +public class VehicleMaintenanceRiskHandler implements IGovRiskHandler { + + private final GovWorkbenchMapper govWorkbenchMapper; + private final IHotVehicleSecondaryMaintenanceConfigService hotVehicleSecondaryMaintenanceConfigService; + private final IHotVehicleMaintenanceService hotVehicleMaintenanceService; + + @Override + public String getType() { + return GovHiddenDangerType.VEHICLE_MAINTENANCE.getKey(); + } + + @Override + public long count(String districtCode, Long companyId) { + return getRisks(districtCode, companyId).size(); + } + + @Override + public TableDataInfo list(String districtCode, Long companyId, PageQuery pageQuery) { + List risks = getRisks(districtCode, companyId); + + // 内存分页 + int total = risks.size(); + Integer pageNum = pageQuery.getPageNum(); + Integer pageSize = pageQuery.getPageSize(); + + if (pageNum == null) pageNum = 1; + if (pageSize == null) pageSize = 10; + + int start = (pageNum - 1) * pageSize; + int end = Math.min(start + pageSize, total); + + if (start >= total) { + TableDataInfo empty = TableDataInfo.build(new ArrayList<>()); + empty.setTotal(total); + return empty; + } + + List pageList = risks.subList(start, end); + TableDataInfo tableDataInfo = TableDataInfo.build(pageList); + tableDataInfo.setTotal(total); + return tableDataInfo; + } + + public Map countByCompany(String districtCode) { + Map result = new HashMap<>(); + // 处理 districtCode 为 null 的情况,转为空字符串以匹配所有 + String code = districtCode == null ? "" : districtCode; + + // 1. 获取辖区内所有活跃车辆 + List vehicles = govWorkbenchMapper.selectVehicleForRiskCalc(code, null); + if (vehicles.isEmpty()) { + return result; + } + + // 2. 按企业分组 + Map> companyVehicles = vehicles.stream() + .collect(Collectors.groupingBy(VehicleRiskCalcVo::getCompanyId)); + + // 3. 遍历企业处理 + for (Map.Entry> entry : companyVehicles.entrySet()) { + Long companyId = entry.getKey(); + List cvs = entry.getValue(); + int riskCount = 0; + + // 3.1 获取该企业的二级维护配置 + try { + HotVehicleSecondaryMaintenanceConfigBo configBo = new HotVehicleSecondaryMaintenanceConfigBo(); + configBo.setCompanyId(companyId); + List allConfigs = hotVehicleSecondaryMaintenanceConfigService.queryList(configBo); + + List rules = allConfigs == null ? new ArrayList<>() : allConfigs.stream() + .filter(c -> c != null && (c.getIsEnabled() == null || c.getIsEnabled() == 1L)) + .filter(c -> c.getIntervalYears() != null && c.getInspectionTimes() != null) + .sorted(Comparator.comparing(c -> c.getIntervalYears() == null ? Long.MAX_VALUE : c.getIntervalYears())) + .map(c -> new VehicleUtils.AnnualInspectionRule(c.getIntervalYears().intValue(), c.getInspectionTimes().intValue())) + .collect(Collectors.toList()); + + if (rules.isEmpty()) continue; + + // 3.2 遍历车辆 + for (VehicleRiskCalcVo v : cvs) { + if (v.getIssueDate() == null) continue; + + LocalDate issueDate = v.getIssueDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + + // 获取最近一次二级维护日期 + HotVehicleMaintenanceVo lastMaintenance = hotVehicleMaintenanceService.queryLatestByVehicleId(v.getId()); + LocalDate lastMaintenanceDate = null; + if (lastMaintenance != null) { + Date t = lastMaintenance.getFinishTime() != null ? lastMaintenance.getFinishTime() : lastMaintenance.getMaintainDate(); + if (t != null) { + lastMaintenanceDate = t.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } + } + + VehicleUtils.InspectionResult calc = VehicleUtils.calculateMaintenanceStatus(rules, issueDate, lastMaintenanceDate); + if (calc != null && calc.getOverdueDays() > 0) { + riskCount++; + } + } + + if (riskCount > 0) { + result.put(companyId, riskCount); + } + + } catch (Exception e) { + // 忽略异常,继续处理下一个企业 + e.printStackTrace(); + } + } + + return result; + } + + private List getRisks(String districtCode, Long targetCompanyId) { + List result = new ArrayList<>(); + + // 1. 获取辖区内所有活跃车辆 + List vehicles = govWorkbenchMapper.selectVehicleForRiskCalc(districtCode, targetCompanyId); + if (vehicles.isEmpty()) { + return result; + } + + // 2. 按企业分组 + Map> companyVehicles = vehicles.stream() + .collect(Collectors.groupingBy(VehicleRiskCalcVo::getCompanyId)); + + // 3. 遍历企业处理 + for (Map.Entry> entry : companyVehicles.entrySet()) { + Long companyId = entry.getKey(); + if (targetCompanyId != null && !targetCompanyId.equals(companyId)) { + continue; + } + List cvs = entry.getValue(); + + // 3.1 获取该企业的二级维护配置 + try { + HotVehicleSecondaryMaintenanceConfigBo configBo = new HotVehicleSecondaryMaintenanceConfigBo(); + configBo.setCompanyId(companyId); + List allConfigs = hotVehicleSecondaryMaintenanceConfigService.queryList(configBo); + + List rules = allConfigs == null ? new ArrayList<>() : allConfigs.stream() + .filter(c -> c != null && (c.getIsEnabled() == null || c.getIsEnabled() == 1L)) + .filter(c -> c.getIntervalYears() != null && c.getInspectionTimes() != null) + .sorted(Comparator.comparing(c -> c.getIntervalYears() == null ? Long.MAX_VALUE : c.getIntervalYears())) + .map(c -> new VehicleUtils.AnnualInspectionRule(c.getIntervalYears().intValue(), c.getInspectionTimes().intValue())) + .collect(Collectors.toList()); + + if (rules.isEmpty()) continue; + + // 3.2 遍历车辆 + for (VehicleRiskCalcVo v : cvs) { + if (v.getIssueDate() == null) continue; + + LocalDate issueDate = v.getIssueDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + + // 获取最近一次二级维护日期 + HotVehicleMaintenanceVo lastMaintenance = hotVehicleMaintenanceService.queryLatestByVehicleId(v.getId()); + LocalDate lastMaintenanceDate = null; + if (lastMaintenance != null) { + Date t = lastMaintenance.getFinishTime() != null ? lastMaintenance.getFinishTime() : lastMaintenance.getMaintainDate(); + if (t != null) { + lastMaintenanceDate = t.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } + } + + VehicleUtils.InspectionResult calc = VehicleUtils.calculateMaintenanceStatus(rules, issueDate, lastMaintenanceDate); + if (calc != null && calc.getOverdueDays() > 0) { + GovHiddenDangerDetailVo vo = new GovHiddenDangerDetailVo(); + vo.setId(v.getId()); + vo.setTitle("二级维护:(" + v.getPlateNumber() + ") 已到期(" + calc.getOverdueDays() + ")天"); + vo.setSubTitle(null); + + vo.setStatus("expired"); + if (calc.getNextInspectionDate() != null) { + vo.setDeadline(Date.from(calc.getNextInspectionDate().atStartOfDay(ZoneId.systemDefault()).toInstant())); + } + vo.setRelatedId(v.getCompanyName()); + result.add(vo); + } + } + + } catch (Exception e) { + // 忽略异常,继续处理下一个企业 + e.printStackTrace(); + } + } + + return result; + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/strategy/impl/VehicleTransportPermitRiskHandler.java b/src/main/java/com/hotwj/platform/workbench/strategy/impl/VehicleTransportPermitRiskHandler.java new file mode 100644 index 0000000..50baee8 --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/strategy/impl/VehicleTransportPermitRiskHandler.java @@ -0,0 +1,38 @@ +package com.hotwj.platform.workbench.strategy.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hotwj.platform.workbench.domain.enums.GovHiddenDangerType; +import com.hotwj.platform.workbench.domain.vo.GovHiddenDangerDetailVo; +import com.hotwj.platform.workbench.mapper.GovWorkbenchMapper; +import com.hotwj.platform.workbench.strategy.IGovRiskHandler; +import lombok.RequiredArgsConstructor; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.stereotype.Component; + +/** + * 车辆运输证隐患处理器 + */ +@Component +@RequiredArgsConstructor +public class VehicleTransportPermitRiskHandler implements IGovRiskHandler { + + private final GovWorkbenchMapper govWorkbenchMapper; + + @Override + public String getType() { + return GovHiddenDangerType.VEHICLE_TRANSPORT_PERMIT.getKey(); + } + + @Override + public long count(String districtCode, Long companyId) { + return govWorkbenchMapper.countVehicleTransportPermit(districtCode, companyId); + } + + @Override + public TableDataInfo list(String districtCode, Long companyId, PageQuery pageQuery) { + Page page = pageQuery.build(); + Page result = govWorkbenchMapper.listVehicleTransportPermit(page, districtCode, companyId); + return TableDataInfo.build(result); + } +} diff --git a/src/main/java/com/hotwj/platform/workbench/task/GovHiddenDangerDailyStatsTask.java b/src/main/java/com/hotwj/platform/workbench/task/GovHiddenDangerDailyStatsTask.java new file mode 100644 index 0000000..946c7ad --- /dev/null +++ b/src/main/java/com/hotwj/platform/workbench/task/GovHiddenDangerDailyStatsTask.java @@ -0,0 +1,35 @@ +package com.hotwj.platform.workbench.task; + +import com.hotwj.platform.workbench.service.IHotHiddenDangerDailyStatsService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +/** + * 日统计定时任务(隐患、培训) + * + * @author shihongwei + * @date 2026-02-25 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class GovHiddenDangerDailyStatsTask { + + private final IHotHiddenDangerDailyStatsService dailyStatsService; + + /** + * 每天凌晨1点执行统计 + */ + @Scheduled(cron = "0 0 1 * * ?") + public void generateDailyStats() { + log.info("开始执行日统计定时任务(隐患、培训)"); + try { + dailyStatsService.generateDailyStats(); + } catch (Exception e) { + log.error("日统计定时任务(隐患、培训)执行失败", e); + } + log.info("日统计定时任务(隐患、培训)执行结束"); + } +} diff --git a/src/main/java/me/zhyd/oauth/request/AbstractAuthWeChatEnterpriseRequest.java b/src/main/java/me/zhyd/oauth/request/AbstractAuthWeChatEnterpriseRequest.java new file mode 100644 index 0000000..33da8b4 --- /dev/null +++ b/src/main/java/me/zhyd/oauth/request/AbstractAuthWeChatEnterpriseRequest.java @@ -0,0 +1,155 @@ +package me.zhyd.oauth.request; + +import com.alibaba.fastjson.JSONObject; +import me.zhyd.oauth.cache.AuthStateCache; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.config.AuthSource; +import me.zhyd.oauth.enums.AuthResponseStatus; +import me.zhyd.oauth.enums.AuthUserGender; +import me.zhyd.oauth.exception.AuthException; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthToken; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.request.AuthDefaultRequest; +import me.zhyd.oauth.utils.HttpUtils; +import me.zhyd.oauth.utils.StringUtils; +import me.zhyd.oauth.utils.UrlBuilder; + +/** + *

+ * 企业微信登录父类 + *

+ * + * @author shihongwei + * @since 1.15.9 + */ +public abstract class AbstractAuthWeChatEnterpriseRequest extends AuthDefaultRequest { + + public AbstractAuthWeChatEnterpriseRequest(AuthConfig config, AuthSource source) { + super(config,source); + } + + + public AbstractAuthWeChatEnterpriseRequest(AuthConfig config, AuthSource source, AuthStateCache authStateCache) { + super(config, source, authStateCache); + } + + @Override + public AuthToken getAccessToken(AuthCallback authCallback) { + String response = doGetAuthorizationCode(accessTokenUrl(null)); + + JSONObject object = this.checkResponse(response); + + return AuthToken.builder() + .accessToken(object.getString("access_token")) + .expireIn(object.getIntValue("expires_in")) + .code(authCallback.getCode()) + .build(); + } + + @Override + public AuthUser getUserInfo(AuthToken authToken) { + String response = doGetUserInfo(authToken); + JSONObject object = this.checkResponse(response); + + // 返回 OpenId 或其他,均代表非当前企业用户,不支持 + // https://github.com/justauth/JustAuth/issues/227 修复bug + if (!object.containsKey("userid")) { + throw new AuthException(AuthResponseStatus.UNIDENTIFIED_PLATFORM, source); + } + String userId = object.getString("userid"); + String userTicket = object.getString("user_ticket"); + JSONObject userDetail = getUserDetail(authToken.getAccessToken(), userId, userTicket); + + return AuthUser.builder() + .rawUserInfo(userDetail) + .username(userDetail.getString("name")) + .nickname(userDetail.getString("alias")) + .avatar(userDetail.getString("avatar")) + .location(userDetail.getString("address")) + .email(userDetail.getString("email")) + .uuid(userId) + .gender(AuthUserGender.getWechatRealGender(userDetail.getString("gender"))) + .token(authToken) + .source(source.toString()) + .build(); + } + + /** + * 校验请求结果 + * + * @param response 请求结果 + * @return 如果请求结果正常,则返回JSONObject + */ + private JSONObject checkResponse(String response) { + JSONObject object = JSONObject.parseObject(response); + + if (object.containsKey("errcode") && object.getIntValue("errcode") != 0) { + throw new AuthException(object.getString("errmsg"), source); + } + + return object; + } + + + /** + * 返回获取accessToken的url + * + * @param code 授权码 + * @return 返回获取accessToken的url + */ + @Override + protected String accessTokenUrl(String code) { + return UrlBuilder.fromBaseUrl(source.accessToken()) + .queryParam("corpid", config.getClientId()) + .queryParam("corpsecret", config.getClientSecret()) + .build(); + } + + /** + * 返回获取userInfo的url + * + * @param authToken 用户授权后的token + * @return 返回获取userInfo的url + */ + @Override + protected String userInfoUrl(AuthToken authToken) { + return UrlBuilder.fromBaseUrl(source.userInfo()) + .queryParam("access_token", authToken.getAccessToken()) + .queryParam("code", authToken.getCode()) + .build(); + } + + /** + * 用户详情 + * + * @param accessToken accessToken + * @param userId 企业内用户id + * @param userTicket 成员票据,用于获取用户信息或敏感信息 + * @return 用户详情 + */ + private JSONObject getUserDetail(String accessToken, String userId, String userTicket) { + // 用户基础信息 + String userInfoUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/user/get") + .queryParam("access_token", accessToken) + .queryParam("userid", userId) + .build(); + String userInfoResponse = new HttpUtils(config.getHttpConfig()).get(userInfoUrl).getBody(); + JSONObject userInfo = checkResponse(userInfoResponse); + + // 用户敏感信息 + if (StringUtils.isNotEmpty(userTicket)) { + String userDetailUrl = UrlBuilder.fromBaseUrl("https://qyapi.weixin.qq.com/cgi-bin/auth/getuserdetail") + .queryParam("access_token", accessToken) + .build(); + JSONObject param = new JSONObject(); + param.put("user_ticket", userTicket); + String userDetailResponse = new HttpUtils(config.getHttpConfig()).post(userDetailUrl, param.toJSONString()).getBody(); + JSONObject userDetail = checkResponse(userDetailResponse); + + userInfo.putAll(userDetail); + } + return userInfo; + } + +} diff --git a/src/main/java/me/zhyd/oauth/request/AuthDingTalkV2Request.java b/src/main/java/me/zhyd/oauth/request/AuthDingTalkV2Request.java new file mode 100644 index 0000000..4da1bb0 --- /dev/null +++ b/src/main/java/me/zhyd/oauth/request/AuthDingTalkV2Request.java @@ -0,0 +1,110 @@ +package me.zhyd.oauth.request; + +import com.alibaba.fastjson.JSONObject; +import com.xkcoding.http.support.HttpHeader; +import me.zhyd.oauth.cache.AuthStateCache; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.config.AuthDefaultSource; +import me.zhyd.oauth.enums.scope.AuthDingTalkScope; +import me.zhyd.oauth.exception.AuthException; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthToken; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.request.AuthDefaultRequest; +import me.zhyd.oauth.utils.AuthScopeUtils; +import me.zhyd.oauth.utils.GlobalAuthUtils; +import me.zhyd.oauth.utils.HttpUtils; +import me.zhyd.oauth.utils.UrlBuilder; + +import java.util.HashMap; +import java.util.Map; + +/** + * 新版钉钉二维码登录 + * + * @author shihongwei + * @since 1.16.7 + */ +public class AuthDingTalkV2Request extends AuthDefaultRequest { + + public AuthDingTalkV2Request(AuthConfig config) { + super(config, AuthDefaultSource.DINGTALK_V2); + } + + public AuthDingTalkV2Request(AuthConfig config, AuthStateCache authStateCache) { + super(config, AuthDefaultSource.DINGTALK_V2, authStateCache); + } + + @Override + public String authorize(String state) { + return UrlBuilder.fromBaseUrl(source.authorize()) + .queryParam("response_type", "code") + .queryParam("client_id", config.getClientId()) + .queryParam("scope", this.getScopes(",", true, AuthScopeUtils.getDefaultScopes(AuthDingTalkScope.values()))) + .queryParam("redirect_uri", GlobalAuthUtils.urlEncode(config.getRedirectUri())) + .queryParam("prompt", "consent") + .queryParam("org_type", config.getDingTalkOrgType()) + .queryParam("corpId", config.getDingTalkCorpId()) + .queryParam("exclusiveLogin", config.isDingTalkExclusiveLogin()) + .queryParam("exclusiveCorpId", config.getDingTalkExclusiveCorpId()) + .queryParam("state", getRealState(state)) + .build(); + } + + @Override + public AuthToken getAccessToken(AuthCallback authCallback) { + Map params = new HashMap<>(); + params.put("grantType", "authorization_code"); + params.put("clientId", config.getClientId()); + params.put("clientSecret", config.getClientSecret()); + params.put("code", authCallback.getCode()); + String response = new HttpUtils(config.getHttpConfig()).post(this.source.accessToken(), JSONObject.toJSONString(params)).getBody(); + JSONObject accessTokenObject = JSONObject.parseObject(response); + if (!accessTokenObject.containsKey("accessToken")) { + throw new AuthException(JSONObject.toJSONString(response), source); + } + return AuthToken.builder() + .accessToken(accessTokenObject.getString("accessToken")) + .refreshToken(accessTokenObject.getString("refreshToken")) + .expireIn(accessTokenObject.getIntValue("expireIn")) + .corpId(accessTokenObject.getString("corpId")) + .build(); + } + + @Override + public AuthUser getUserInfo(AuthToken authToken) { + HttpHeader header = new HttpHeader(); + header.add("x-acs-dingtalk-access-token", authToken.getAccessToken()); + + String response = new HttpUtils(config.getHttpConfig()).get(this.source.userInfo(), null, header, false).getBody(); + JSONObject object = JSONObject.parseObject(response); + + authToken.setOpenId(object.getString("openId")); + authToken.setUnionId(object.getString("unionId")); + return AuthUser.builder() + .rawUserInfo(object) + .uuid(object.getString("unionId")) + .username(object.getString("nick")) + .nickname(object.getString("nick")) + .avatar(object.getString("avatarUrl")) + .snapshotUser(object.getBooleanValue("visitor")) + .token(authToken) + .source(source.toString()) + .build(); + } + + /** + * 返回获取accessToken的url + * + * @param code 授权码 + * @return 返回获取accessToken的url + */ + protected String accessTokenUrl(String code) { + return UrlBuilder.fromBaseUrl(source.accessToken()) + .queryParam("code", code) + .queryParam("clientId", config.getClientId()) + .queryParam("clientSecret", config.getClientSecret()) + .queryParam("grantType", "authorization_code") + .build(); + } +} diff --git a/src/main/java/org/dromara/DromaraServletInitializer.java b/src/main/java/org/dromara/DromaraServletInitializer.java new file mode 100644 index 0000000..cad1266 --- /dev/null +++ b/src/main/java/org/dromara/DromaraServletInitializer.java @@ -0,0 +1,18 @@ +package org.dromara; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + * + * @author shihongwei + */ +public class DromaraServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(HotPlatformApplication.class); + } + +} diff --git a/src/main/java/org/dromara/HotPlatformApplication.java b/src/main/java/org/dromara/HotPlatformApplication.java new file mode 100644 index 0000000..9aff688 --- /dev/null +++ b/src/main/java/org/dromara/HotPlatformApplication.java @@ -0,0 +1,24 @@ +package org.dromara; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * 启动程序 + * + * @author shihongwei + */ + +@EnableScheduling +@SpringBootApplication(scanBasePackages = {"org.dromara", "com.hotwj"}) +public class HotPlatformApplication { + + public static void main(String[] args) { + SpringApplication application = new SpringApplication(HotPlatformApplication.class); + application.setApplicationStartup(new BufferingApplicationStartup(2048)); + application.run(args); + } + +} diff --git a/src/main/java/org/dromara/SFrameworkStartup.java b/src/main/java/org/dromara/SFrameworkStartup.java new file mode 100644 index 0000000..cd67f8f --- /dev/null +++ b/src/main/java/org/dromara/SFrameworkStartup.java @@ -0,0 +1,65 @@ +package org.dromara; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.stp.StpUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import java.net.InetAddress; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * springboot启动之后,打印版本信息 + * + * @author shihongwei + */ +@Component +@Slf4j +public class SFrameworkStartup implements CommandLineRunner { + + public static final String VERSION = "v1.0.0"; + + @Value("${spring.application.name:sa-plus}") + private String applicationName; + + @Value("${server.port:8080}") + private String port; + + @Value("${server.servlet.context-path:}") + private String path; + + @Value("${spring.profiles.active:}") + private String active; + + /** + * 返回系统当前时间的YYYY-MM-dd hh:mm:ss 字符串格式 + */ + private static String getNow() { + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); + } + + @Override + public void run(String... args) throws Exception { + String ip = InetAddress.getLocalHost().getHostAddress(); + String str = "\n------------- " + applicationName + " [" + VERSION + "] (" + active + ") 启动成功 --by " + getNow() + " -------------\n" + + "\t- Local: http://localhost:" + port + path + "\n" + + "\t- Local2: http://127.0.0.1:" + port + path; + log.info(str); + // + try { + log.info("\t- Network: http://" + ip + ":" + port + path + "\n"); + log.info("Swagger: http://" + ip + ":" + port + path + "swagger-ui/index.html\n"); + + log.info("启动成功,Sa-Token 配置如下:\n" + SaManager.getConfig()); + + } catch (Exception e) { + log.error("\t- Network: 未能成功获取\n"); + log.error("异常:" + e.getMessage()); + } + } + +} + diff --git a/src/main/java/org/dromara/common/core/config/ApplicationConfig.java b/src/main/java/org/dromara/common/core/config/ApplicationConfig.java new file mode 100644 index 0000000..472a439 --- /dev/null +++ b/src/main/java/org/dromara/common/core/config/ApplicationConfig.java @@ -0,0 +1,17 @@ +package org.dromara.common.core.config; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.scheduling.annotation.EnableAsync; + +/** + * 程序注解配置 + * + * @author shihongwei + */ +@AutoConfiguration +@EnableAspectJAutoProxy +@EnableAsync(proxyTargetClass = true) +public class ApplicationConfig { + +} diff --git a/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java b/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java new file mode 100644 index 0000000..34d56bb --- /dev/null +++ b/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java @@ -0,0 +1,112 @@ +package org.dromara.common.core.config; + +import jakarta.annotation.PreDestroy; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.dromara.common.core.config.properties.ThreadPoolProperties; +import org.dromara.common.core.utils.SpringUtils; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.core.task.VirtualThreadTaskExecutor; + +import java.util.concurrent.*; + +/** + * 线程池配置 + * + * @author shihongwei + **/ +@Slf4j +@AutoConfiguration +@EnableConfigurationProperties(ThreadPoolProperties.class) +public class ThreadPoolConfig { + + /** + * 核心线程数 = cpu 核心数 + 1 + */ + private final int core = Runtime.getRuntime().availableProcessors() + 1; + + private ScheduledExecutorService scheduledExecutorService; + + /** + * 执行周期性或定时任务 + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() { + // daemon 必须为 true + BasicThreadFactory.Builder builder = new BasicThreadFactory.Builder().daemon(true); + if (SpringUtils.isVirtual()) { + builder.namingPattern("virtual-schedule-pool-%d").wrappedFactory(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); + } else { + builder.namingPattern("schedule-pool-%d"); + } + ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(core, + builder.build(), + new ThreadPoolExecutor.CallerRunsPolicy()) { + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + printException(r, t); + } + }; + this.scheduledExecutorService = scheduledThreadPoolExecutor; + return scheduledThreadPoolExecutor; + } + + /** + * 销毁事件 + * 停止线程池 + * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. + * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍然超時,則強制退出. + * 另对在shutdown时线程本身被调用中断做了处理. + */ + @PreDestroy + public void destroy() { + try { + log.info("====关闭后台任务任务线程池===="); + ScheduledExecutorService pool = scheduledExecutorService; + if (pool != null && !pool.isShutdown()) { + pool.shutdown(); + try { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + log.info("Pool did not terminate"); + } + } + } catch (InterruptedException ie) { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) { + if (t == null && r instanceof Future) { + try { + Future future = (Future) r; + if (future.isDone()) { + future.get(); + } + } catch (CancellationException ce) { + t = ce; + } catch (ExecutionException ee) { + t = ee.getCause(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + if (t != null) { + log.error(t.getMessage(), t); + } + } + +} diff --git a/src/main/java/org/dromara/common/core/config/ValidatorConfig.java b/src/main/java/org/dromara/common/core/config/ValidatorConfig.java new file mode 100644 index 0000000..961a9c4 --- /dev/null +++ b/src/main/java/org/dromara/common/core/config/ValidatorConfig.java @@ -0,0 +1,41 @@ +package org.dromara.common.core.config; + +import jakarta.validation.Validator; +import org.hibernate.validator.HibernateValidator; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration; +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Bean; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; + +import java.util.Properties; + +/** + * 校验框架配置类 + * + * @author shihongwei + */ +@AutoConfiguration(before = ValidationAutoConfiguration.class) +public class ValidatorConfig { + + /** + * 配置校验框架 快速失败模式 + */ + @Bean + public Validator validator(MessageSource messageSource) { + try (LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean()) { + // 国际化 + factoryBean.setValidationMessageSource(messageSource); + // 设置使用 HibernateValidator 校验器 + factoryBean.setProviderClass(HibernateValidator.class); + Properties properties = new Properties(); + // 设置快速失败模式(fail-fast),即校验过程中一旦遇到失败,立即停止并返回错误 + properties.setProperty("hibernate.validator.fail_fast", "true"); + factoryBean.setValidationProperties(properties); + // 加载配置 + factoryBean.afterPropertiesSet(); + return factoryBean.getValidator(); + } + } + +} diff --git a/src/main/java/org/dromara/common/core/config/properties/ThreadPoolProperties.java b/src/main/java/org/dromara/common/core/config/properties/ThreadPoolProperties.java new file mode 100644 index 0000000..4ac14d2 --- /dev/null +++ b/src/main/java/org/dromara/common/core/config/properties/ThreadPoolProperties.java @@ -0,0 +1,30 @@ +package org.dromara.common.core.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 线程池 配置属性 + * + * @author shihongwei + */ +@Data +@ConfigurationProperties(prefix = "thread-pool") +public class ThreadPoolProperties { + + /** + * 是否开启线程池 + */ + private boolean enabled; + + /** + * 队列最大长度 + */ + private int queueCapacity; + + /** + * 线程池维护线程所允许的空闲时间 + */ + private int keepAliveSeconds; + +} diff --git a/src/main/java/org/dromara/common/core/constant/CacheConstants.java b/src/main/java/org/dromara/common/core/constant/CacheConstants.java new file mode 100644 index 0000000..7c600e6 --- /dev/null +++ b/src/main/java/org/dromara/common/core/constant/CacheConstants.java @@ -0,0 +1,30 @@ +package org.dromara.common.core.constant; + +/** + * 缓存的key 常量 + * + * @author shihongwei + */ +public interface CacheConstants { + + /** + * 在线用户 redis key + */ + String ONLINE_TOKEN_KEY = "online_tokens:"; + + /** + * 参数管理 cache key + */ + String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 字典管理 cache key + */ + String SYS_DICT_KEY = "sys_dict:"; + + /** + * 登录账户密码错误次数 redis key + */ + String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; + +} diff --git a/src/main/java/org/dromara/common/core/constant/CacheNames.java b/src/main/java/org/dromara/common/core/constant/CacheNames.java new file mode 100644 index 0000000..69ed15d --- /dev/null +++ b/src/main/java/org/dromara/common/core/constant/CacheNames.java @@ -0,0 +1,89 @@ +package org.dromara.common.core.constant; + +/** + * 缓存组名称常量 + *

+ * key 格式为 cacheNames#ttl#maxIdleTime#maxSize#local + *

+ * ttl 过期时间 如果设置为0则不过期 默认为0 + * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0 + * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0 + * local 默认开启本地缓存为1 关闭本地缓存为0 + *

+ * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500、test#1h#0#500#0 + * + * @author shihongwei + */ +public interface CacheNames { + + /** + * 演示案例 + */ + String DEMO_CACHE = "demo:cache#60s#10m#20"; + + /** + * 系统配置 + */ + String SYS_CONFIG = "sys_config"; + + /** + * 数据字典 + */ + String SYS_DICT = "sys_dict"; + + /** + * 数据字典类型 + */ + String SYS_DICT_TYPE = "sys_dict_type"; + + /** + * 租户 + */ + String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d"; + + /** + * 客户端 + */ + String SYS_CLIENT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_client#30d"; + + /** + * 用户账户 + */ + String SYS_USER_NAME = "sys_user_name#30d"; + + /** + * 用户名称 + */ + String SYS_NICKNAME = "sys_nickname#30d"; + + /** + * 部门 + */ + String SYS_DEPT = "sys_dept#30d"; + + /** + * OSS内容 + */ + String SYS_OSS = "sys_oss#30d"; + + /** + * 角色自定义权限 + */ + String SYS_ROLE_CUSTOM = "sys_role_custom#30d"; + + /** + * 部门及以下权限 + */ + String SYS_DEPT_AND_CHILD = "sys_dept_and_child#30d"; + + /** + * OSS配置 + */ + String SYS_OSS_CONFIG = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss_config"; + + /** + * 在线用户 + */ + String ONLINE_TOKEN = "online_tokens"; + +} diff --git a/src/main/java/org/dromara/common/core/constant/Constants.java b/src/main/java/org/dromara/common/core/constant/Constants.java new file mode 100644 index 0000000..3ee90ca --- /dev/null +++ b/src/main/java/org/dromara/common/core/constant/Constants.java @@ -0,0 +1,81 @@ +package org.dromara.common.core.constant; + +/** + * 通用常量信息 + * + * @author shihongwei + */ +public interface Constants { + + /** + * UTF-8 字符集 + */ + String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + String GBK = "GBK"; + + /** + * www主域 + */ + String WWW = "www."; + + /** + * http请求 + */ + String HTTP = "http://"; + + /** + * https请求 + */ + String HTTPS = "https://"; + + /** + * 通用成功标识 + */ + String SUCCESS = "0"; + + /** + * 通用失败标识 + */ + String FAIL = "1"; + + /** + * 登录成功 + */ + String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + String LOGOUT = "Logout"; + + /** + * 注册 + */ + String REGISTER = "Register"; + + /** + * 登录失败 + */ + String LOGIN_FAIL = "Error"; + + /** + * 验证码有效期(分钟) + */ + Integer CAPTCHA_EXPIRATION = 2; + + /** + * 顶级父级id + */ + Long TOP_PARENT_ID = 0L; + + /** + * 加密头 + */ + String ENCRYPT_HEADER = "ENC_"; + +} + diff --git a/src/main/java/org/dromara/common/core/constant/GlobalConstants.java b/src/main/java/org/dromara/common/core/constant/GlobalConstants.java new file mode 100644 index 0000000..376a59c --- /dev/null +++ b/src/main/java/org/dromara/common/core/constant/GlobalConstants.java @@ -0,0 +1,34 @@ +package org.dromara.common.core.constant; + +/** + * 全局的key常量 (业务无关的key) + * + * @author shihongwei + */ +public interface GlobalConstants { + + /** + * 全局 redis key (业务无关的key) + */ + String GLOBAL_REDIS_KEY = "global:"; + + /** + * 验证码 redis key + */ + String CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "captcha_codes:"; + + /** + * 防重提交 redis key + */ + String REPEAT_SUBMIT_KEY = GLOBAL_REDIS_KEY + "repeat_submit:"; + + /** + * 限流 redis key + */ + String RATE_LIMIT_KEY = GLOBAL_REDIS_KEY + "rate_limit:"; + + /** + * 三方认证 redis key + */ + String SOCIAL_AUTH_CODE_KEY = GLOBAL_REDIS_KEY + "social_auth_codes:"; +} diff --git a/src/main/java/org/dromara/common/core/constant/HttpStatus.java b/src/main/java/org/dromara/common/core/constant/HttpStatus.java new file mode 100644 index 0000000..fcda069 --- /dev/null +++ b/src/main/java/org/dromara/common/core/constant/HttpStatus.java @@ -0,0 +1,93 @@ +package org.dromara.common.core.constant; + +/** + * 返回状态码 + * + * @author shihongwei + */ +public interface HttpStatus { + /** + * 操作成功 + */ + int SUCCESS = 200; + + /** + * 对象创建成功 + */ + int CREATED = 201; + + /** + * 请求已经被接受 + */ + int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + int MOVED_PERM = 301; + + /** + * 重定向 + */ + int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + int BAD_REQUEST = 400; + + /** + * 未授权 + */ + int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + int ERROR = 500; + + /** + * 接口未实现 + */ + int NOT_IMPLEMENTED = 501; + + /** + * 系统警告消息 + */ + int WARN = 601; +} diff --git a/src/main/java/org/dromara/common/core/constant/RegexConstants.java b/src/main/java/org/dromara/common/core/constant/RegexConstants.java new file mode 100644 index 0000000..c4bba7d --- /dev/null +++ b/src/main/java/org/dromara/common/core/constant/RegexConstants.java @@ -0,0 +1,59 @@ +package org.dromara.common.core.constant; + +import cn.hutool.core.lang.RegexPool; + +/** + * 常用正则表达式字符串 + *

+ * 常用正则表达式集合,更多正则见: https://any86.github.io/any-rule/ + * + * @author shihongwei + */ +public interface RegexConstants extends RegexPool { + + /** + * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) + */ + String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$"; + + /** + * 权限标识必须符合以下格式: + * 1. 标准格式:xxx:yyy:zzz + * - 第一部分(xxx):只能包含字母、数字和下划线(_),不能使用 `*` + * - 第二部分(yyy):可以包含字母、数字、下划线(_)和 `*` + * - 第三部分(zzz):可以包含字母、数字、下划线(_)和 `*` + * 2. 允许空字符串(""),表示没有权限标识 + */ + String PERMISSION_STRING = "^$|^[a-zA-Z0-9_]+:[a-zA-Z0-9_*]+:[a-zA-Z0-9_*]+$"; + + /** + * 身份证号码(后6位) + */ + String ID_CARD_LAST_6 = "^(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$"; + + /** + * QQ号码 + */ + String QQ_NUMBER = "^[1-9][0-9]\\d{4,9}$"; + + /** + * 邮政编码 + */ + String POSTAL_CODE = "^[1-9]\\d{5}$"; + + /** + * 注册账号 + */ + String ACCOUNT = "^[a-zA-Z][a-zA-Z0-9_]{4,15}$"; + + /** + * 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符 + */ + String PASSWORD = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$"; + + /** + * 通用状态(0表示正常,1表示停用) + */ + String STATUS = "^[01]$"; + +} diff --git a/src/main/java/org/dromara/common/core/constant/SystemConstants.java b/src/main/java/org/dromara/common/core/constant/SystemConstants.java new file mode 100644 index 0000000..d850dc3 --- /dev/null +++ b/src/main/java/org/dromara/common/core/constant/SystemConstants.java @@ -0,0 +1,85 @@ +package org.dromara.common.core.constant; + +/** + * 系统常量信息 + * + * @author shihongwei + */ +public interface SystemConstants { + + /** + * 正常状态 + */ + String NORMAL = "0"; + + /** + * 异常状态 + */ + String DISABLE = "1"; + + /** + * 是否为系统默认(是) + */ + String YES = "Y"; + + /** + * 是否为系统默认(否) + */ + String NO = "N"; + + /** + * 是否菜单外链(是) + */ + String YES_FRAME = "0"; + + /** + * 是否菜单外链(否) + */ + String NO_FRAME = "1"; + + /** + * 菜单类型(目录) + */ + String TYPE_DIR = "M"; + + /** + * 菜单类型(菜单) + */ + String TYPE_MENU = "C"; + + /** + * 菜单类型(按钮) + */ + String TYPE_BUTTON = "F"; + + /** + * Layout组件标识 + */ + String LAYOUT = "Layout"; + + /** + * ParentView组件标识 + */ + String PARENT_VIEW = "ParentView"; + + /** + * InnerLink组件标识 + */ + String INNER_LINK = "InnerLink"; + + /** + * 超级管理员ID + */ + Long SUPER_ADMIN_ID = 1L; + + /** + * 根部门祖级列表 + */ + String ROOT_DEPT_ANCESTORS = "0"; + + /** + * 默认部门 ID + */ + Long DEFAULT_DEPT_ID = 100L; + +} diff --git a/src/main/java/org/dromara/common/core/constant/TenantConstants.java b/src/main/java/org/dromara/common/core/constant/TenantConstants.java new file mode 100644 index 0000000..943d6f1 --- /dev/null +++ b/src/main/java/org/dromara/common/core/constant/TenantConstants.java @@ -0,0 +1,35 @@ +package org.dromara.common.core.constant; + +/** + * 租户常量信息 + * + * @author shihongwei + */ +public interface TenantConstants { + + /** + * 超级管理员ID + */ + Long SUPER_ADMIN_ID = 1L; + + /** + * 超级管理员角色 roleKey + */ + String SUPER_ADMIN_ROLE_KEY = "superadmin"; + + /** + * 租户管理员角色 roleKey + */ + String TENANT_ADMIN_ROLE_KEY = "admin"; + + /** + * 租户管理员角色名称 + */ + String TENANT_ADMIN_ROLE_NAME = "管理员"; + + /** + * 默认租户ID + */ + String DEFAULT_TENANT_ID = "000000"; + +} diff --git a/src/main/java/org/dromara/common/core/domain/R.java b/src/main/java/org/dromara/common/core/domain/R.java new file mode 100644 index 0000000..9b24a64 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/R.java @@ -0,0 +1,110 @@ +package org.dromara.common.core.domain; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.dromara.common.core.constant.HttpStatus; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 响应信息主体 + * + * @author shihongwei + */ +@Data +@NoArgsConstructor +public class R implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 成功 + */ + public static final int SUCCESS = 200; + + /** + * 失败 + */ + public static final int FAIL = 500; + + private int code; + + private String msg; + + private T data; + + public static R ok() { + return restResult(null, SUCCESS, "操作成功"); + } + + public static R ok(T data) { + return restResult(data, SUCCESS, "操作成功"); + } + + public static R ok(String msg) { + return restResult(null, SUCCESS, msg); + } + + public static R ok(String msg, T data) { + return restResult(data, SUCCESS, msg); + } + + public static R fail() { + return restResult(null, FAIL, "操作失败"); + } + + public static R fail(String msg) { + return restResult(null, FAIL, msg); + } + + public static R fail(T data) { + return restResult(data, FAIL, "操作失败"); + } + + public static R fail(String msg, T data) { + return restResult(data, FAIL, msg); + } + + public static R fail(int code, String msg) { + return restResult(null, code, msg); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static R warn(String msg) { + return restResult(null, HttpStatus.WARN, msg); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static R warn(String msg, T data) { + return restResult(data, HttpStatus.WARN, msg); + } + + private static R restResult(T data, int code, String msg) { + R r = new R<>(); + r.setCode(code); + r.setData(data); + r.setMsg(msg); + return r; + } + + public static Boolean isError(R ret) { + return !isSuccess(ret); + } + + public static Boolean isSuccess(R ret) { + return R.SUCCESS == ret.getCode(); + } +} diff --git a/src/main/java/org/dromara/common/core/domain/dto/CompleteTaskDTO.java b/src/main/java/org/dromara/common/core/domain/dto/CompleteTaskDTO.java new file mode 100644 index 0000000..8681ed8 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/dto/CompleteTaskDTO.java @@ -0,0 +1,76 @@ +package org.dromara.common.core.domain.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 办理任务请求对象 + * + * @author shihongwei + */ +@Data +public class CompleteTaskDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务id + */ + private Long taskId; + + /** + * 附件id + */ + private String fileId; + + /** + * 抄送人员 + */ + private List flowCopyList; + + /** + * 消息类型 + */ + private List messageType; + + /** + * 办理意见 + */ + private String message; + + /** + * 消息通知 + */ + private String notice; + + /** + * 办理人(可不填 用于覆盖当前节点办理人) + */ + private String handler; + + /** + * 流程变量 + */ + private Map variables; + + /** + * 扩展变量(此处为逗号分隔的ossId) + */ + private String ext; + + public Map getVariables() { + if (variables == null) { + return new HashMap<>(16); + } + variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); + return variables; + } + +} diff --git a/src/main/java/org/dromara/common/core/domain/dto/DeptDTO.java b/src/main/java/org/dromara/common/core/domain/dto/DeptDTO.java new file mode 100644 index 0000000..14b8309 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/dto/DeptDTO.java @@ -0,0 +1,36 @@ +package org.dromara.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 部门 + * + * @author shihongwei + */ +@Data +@NoArgsConstructor +public class DeptDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 父部门ID + */ + private Long parentId; + + /** + * 部门名称 + */ + private String deptName; + +} diff --git a/src/main/java/org/dromara/common/core/domain/dto/DictDataDTO.java b/src/main/java/org/dromara/common/core/domain/dto/DictDataDTO.java new file mode 100644 index 0000000..433537a --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/dto/DictDataDTO.java @@ -0,0 +1,41 @@ +package org.dromara.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 字典数据DTO + * + * @author shihongwei + */ +@Data +@NoArgsConstructor +public class DictDataDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 字典标签 + */ + private String dictLabel; + + /** + * 字典键值 + */ + private String dictValue; + + /** + * 是否默认(Y是 N否) + */ + private String isDefault; + + /** + * 备注 + */ + private String remark; + +} diff --git a/src/main/java/org/dromara/common/core/domain/dto/DictTypeDTO.java b/src/main/java/org/dromara/common/core/domain/dto/DictTypeDTO.java new file mode 100644 index 0000000..749a80c --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/dto/DictTypeDTO.java @@ -0,0 +1,41 @@ +package org.dromara.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 字典类型DTO + * + * @author shihongwei + */ +@Data +@NoArgsConstructor +public class DictTypeDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 字典主键 + */ + private Long dictId; + + /** + * 字典名称 + */ + private String dictName; + + /** + * 字典类型 + */ + private String dictType; + + /** + * 备注 + */ + private String remark; + +} diff --git a/src/main/java/org/dromara/common/core/domain/dto/FlowCopyDTO.java b/src/main/java/org/dromara/common/core/domain/dto/FlowCopyDTO.java new file mode 100644 index 0000000..fc8eaf2 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/dto/FlowCopyDTO.java @@ -0,0 +1,30 @@ +package org.dromara.common.core.domain.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 抄送 + * + * @author shihongwei + */ +@Data +public class FlowCopyDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private Long userId; + + /** + * 用户名称 + */ + private String userName; + +} diff --git a/src/main/java/org/dromara/common/core/domain/dto/FlowInstanceBizExtDTO.java b/src/main/java/org/dromara/common/core/domain/dto/FlowInstanceBizExtDTO.java new file mode 100644 index 0000000..e0c6c8f --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/dto/FlowInstanceBizExtDTO.java @@ -0,0 +1,45 @@ +package org.dromara.common.core.domain.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 流程实例业务扩展对象 + * + * @author shihongwei + * @date 2025-08-05 + */ +@Data +public class FlowInstanceBizExtDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long id; + + /** + * 流程实例ID + */ + private Long instanceId; + + /** + * 业务ID + */ + private String businessId; + + /** + * 业务编码 + */ + private String businessCode; + + /** + * 业务标题 + */ + private String businessTitle; + +} diff --git a/src/main/java/org/dromara/common/core/domain/dto/OssDTO.java b/src/main/java/org/dromara/common/core/domain/dto/OssDTO.java new file mode 100644 index 0000000..941d185 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/dto/OssDTO.java @@ -0,0 +1,46 @@ +package org.dromara.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * OSS对象 + * + * @author shihongwei + */ +@Data +@NoArgsConstructor +public class OssDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 对象存储主键 + */ + private Long ossId; + + /** + * 文件名 + */ + private String fileName; + + /** + * 原名 + */ + private String originalName; + + /** + * 文件后缀名 + */ + private String fileSuffix; + + /** + * URL地址 + */ + private String url; + +} diff --git a/src/main/java/org/dromara/common/core/domain/dto/PostDTO.java b/src/main/java/org/dromara/common/core/domain/dto/PostDTO.java new file mode 100644 index 0000000..12e50aa --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/dto/PostDTO.java @@ -0,0 +1,46 @@ +package org.dromara.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 岗位 + * + * @author shihongwei + */ +@Data +@NoArgsConstructor +public class PostDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 岗位ID + */ + private Long postId; + + /** + * 部门id + */ + private Long deptId; + + /** + * 岗位编码 + */ + private String postCode; + + /** + * 岗位名称 + */ + private String postName; + + /** + * 岗位类别编码 + */ + private String postCategory; + +} diff --git a/src/main/java/org/dromara/common/core/domain/dto/RoleDTO.java b/src/main/java/org/dromara/common/core/domain/dto/RoleDTO.java new file mode 100644 index 0000000..11cfe92 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/dto/RoleDTO.java @@ -0,0 +1,42 @@ +package org.dromara.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 角色 + * + * @author shihongwei + */ + +@Data +@NoArgsConstructor +public class RoleDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 角色ID + */ + private Long roleId; + + /** + * 角色名称 + */ + private String roleName; + + /** + * 角色权限 + */ + private String roleKey; + + /** + * 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限 6:部门及以下或本人数据权限) + */ + private String dataScope; + +} diff --git a/src/main/java/org/dromara/common/core/domain/dto/StartProcessDTO.java b/src/main/java/org/dromara/common/core/domain/dto/StartProcessDTO.java new file mode 100644 index 0000000..4d8be59 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/dto/StartProcessDTO.java @@ -0,0 +1,63 @@ +package org.dromara.common.core.domain.dto; + + +import cn.hutool.core.util.ObjectUtil; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * 启动流程对象 + * + * @author shihongwei + */ +@Data +public class StartProcessDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 业务唯一值id + */ + private String businessId; + + /** + * 流程定义编码 + */ + private String flowCode; + + /** + * 办理人(可不填 用于覆盖当前节点办理人) + */ + private String handler; + + /** + * 流程变量,前端会提交一个元素{'entity': {业务详情数据对象}} + */ + private Map variables; + + /** + * 流程业务扩展信息 + */ + private FlowInstanceBizExtDTO bizExt; + + public Map getVariables() { + if (variables == null) { + return new HashMap<>(16); + } + variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); + return variables; + } + + public FlowInstanceBizExtDTO getBizExt() { + if (ObjectUtil.isNull(bizExt)) { + bizExt = new FlowInstanceBizExtDTO(); + } + return bizExt; + } +} diff --git a/src/main/java/org/dromara/common/core/domain/dto/StartProcessReturnDTO.java b/src/main/java/org/dromara/common/core/domain/dto/StartProcessReturnDTO.java new file mode 100644 index 0000000..2ee6088 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/dto/StartProcessReturnDTO.java @@ -0,0 +1,30 @@ +package org.dromara.common.core.domain.dto; + + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 启动流程返回对象 + * + * @author shihongwei + */ +@Data +public class StartProcessReturnDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 流程实例id + */ + private Long processInstanceId; + + /** + * 任务id + */ + private Long taskId; + +} diff --git a/src/main/java/org/dromara/common/core/domain/dto/TaskAssigneeDTO.java b/src/main/java/org/dromara/common/core/domain/dto/TaskAssigneeDTO.java new file mode 100644 index 0000000..089a49a --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/dto/TaskAssigneeDTO.java @@ -0,0 +1,101 @@ +package org.dromara.common.core.domain.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 任务受让人 + * + * @author shihongwei + */ +@Data +@NoArgsConstructor +public class TaskAssigneeDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 总大小 + */ + private Long total = 0L; + + /** + * + */ + private List list; + + public TaskAssigneeDTO(Long total, List list) { + this.total = total; + this.list = list; + } + + /** + * 将源列表转换为 TaskHandler 列表 + * + * @param 通用类型 + * @param sourceList 待转换的源列表 + * @param storageId 提取 storageId 的函数 + * @param handlerCode 提取 handlerCode 的函数 + * @param handlerName 提取 handlerName 的函数 + * @param groupName 提取 groupName 的函数 + * @param createTimeMapper 提取 createTime 的函数 + * @return 转换后的 TaskHandler 列表 + */ + public static List convertToHandlerList( + List sourceList, + Function storageId, + Function handlerCode, + Function handlerName, + Function groupName, + Function createTimeMapper) { + return sourceList.stream() + .map(item -> new TaskHandler( + storageId.apply(item), + handlerCode.apply(item), + handlerName.apply(item), + groupName.apply(item), + createTimeMapper.apply(item) + )).collect(Collectors.toList()); + } + + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class TaskHandler { + + /** + * 主键 + */ + private String storageId; + + /** + * 权限编码 + */ + private String handlerCode; + + /** + * 权限名称 + */ + private String handlerName; + + /** + * 权限分组 + */ + private String groupName; + + /** + * 创建时间 + */ + private Date createTime; + } + +} diff --git a/src/main/java/org/dromara/common/core/domain/dto/UserDTO.java b/src/main/java/org/dromara/common/core/domain/dto/UserDTO.java new file mode 100644 index 0000000..e4d4d70 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/dto/UserDTO.java @@ -0,0 +1,73 @@ +package org.dromara.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 用户 + * + * @author shihongwei + */ +@Data +@NoArgsConstructor +public class UserDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 用户昵称 + */ + private String nickName; + + /** + * 用户类型(sys_user系统用户) + */ + private String userType; + + /** + * 用户邮箱 + */ + private String email; + + /** + * 手机号码 + */ + private String phonenumber; + + /** + * 用户性别(0男 1女 2未知) + */ + private String sex; + + /** + * 帐号状态(0正常 1停用) + */ + private String status; + + /** + * 创建时间 + */ + private Date createTime; + +} diff --git a/src/main/java/org/dromara/common/core/domain/dto/UserOnlineDTO.java b/src/main/java/org/dromara/common/core/domain/dto/UserOnlineDTO.java new file mode 100644 index 0000000..5f533d3 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/dto/UserOnlineDTO.java @@ -0,0 +1,72 @@ +package org.dromara.common.core.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 当前在线会话 + * + * @author shihongwei + */ + +@Data +@NoArgsConstructor +public class UserOnlineDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 会话编号 + */ + private String tokenId; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 用户名称 + */ + private String userName; + + /** + * 客户端 + */ + private String clientKey; + + /** + * 设备类型 + */ + private String deviceType; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地址 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录时间 + */ + private Long loginTime; + +} diff --git a/src/main/java/org/dromara/common/core/domain/event/ProcessDeleteEvent.java b/src/main/java/org/dromara/common/core/domain/event/ProcessDeleteEvent.java new file mode 100644 index 0000000..04600c0 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/event/ProcessDeleteEvent.java @@ -0,0 +1,34 @@ +package org.dromara.common.core.domain.event; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 删除流程监听 + * + * @author shihongwei + */ +@Data +public class ProcessDeleteEvent implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 流程定义编码 + */ + private String flowCode; + + /** + * 业务id + */ + private String businessId; + +} diff --git a/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java b/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java new file mode 100644 index 0000000..26214f0 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/event/ProcessEvent.java @@ -0,0 +1,70 @@ +package org.dromara.common.core.domain.event; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Map; + +/** + * 总体流程监听 + * + * @author shihongwei + */ +@Data +public class ProcessEvent implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 流程定义编码 + */ + private String flowCode; + + /** + * 实例id + */ + private Long instanceId; + + /** + * 业务id + */ + private String businessId; + + /** + * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + */ + private Integer nodeType; + + /** + * 流程节点编码 + */ + private String nodeCode; + + /** + * 流程节点名称 + */ + private String nodeName; + + /** + * 流程状态 + */ + private String status; + + /** + * 办理参数 + */ + private Map params; + + /** + * 当为true时为申请人节点办理 + */ + private Boolean submit; + +} diff --git a/src/main/java/org/dromara/common/core/domain/event/ProcessTaskEvent.java b/src/main/java/org/dromara/common/core/domain/event/ProcessTaskEvent.java new file mode 100644 index 0000000..05e17a2 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/event/ProcessTaskEvent.java @@ -0,0 +1,70 @@ +package org.dromara.common.core.domain.event; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Map; + +/** + * 流程任务监听 + * + * @author shihongwei + */ +@Data +public class ProcessTaskEvent implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 流程定义编码 + */ + private String flowCode; + + /** + * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关) + */ + private Integer nodeType; + + /** + * 流程节点编码 + */ + private String nodeCode; + + /** + * 流程节点名称 + */ + private String nodeName; + + /** + * 任务id + */ + private Long taskId; + + /** + * 实例id + */ + private Long instanceId; + + /** + * 业务id + */ + private String businessId; + + /** + * 流程状态 + */ + private String status; + + /** + * 办理参数 + */ + private Map params; + +} diff --git a/src/main/java/org/dromara/common/core/domain/model/EmailLoginBody.java b/src/main/java/org/dromara/common/core/domain/model/EmailLoginBody.java new file mode 100644 index 0000000..0255815 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/model/EmailLoginBody.java @@ -0,0 +1,31 @@ +package org.dromara.common.core.domain.model; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 邮件登录对象 + * + * @author shihongwei + */ + +@Data +@EqualsAndHashCode(callSuper = true) +public class EmailLoginBody extends LoginBody { + + /** + * 邮箱 + */ + @NotBlank(message = "{user.email.not.blank}") + @Email(message = "{user.email.not.valid}") + private String email; + + /** + * 邮箱code + */ + @NotBlank(message = "{email.code.not.blank}") + private String emailCode; + +} diff --git a/src/main/java/org/dromara/common/core/domain/model/LoginBody.java b/src/main/java/org/dromara/common/core/domain/model/LoginBody.java new file mode 100644 index 0000000..2e42d0e --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/model/LoginBody.java @@ -0,0 +1,48 @@ +package org.dromara.common.core.domain.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户登录对象 + * + * @author shihongwei + */ + +@Data +public class LoginBody implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 客户端id + */ + @NotBlank(message = "{auth.clientid.not.blank}") + private String clientId; + + /** + * 授权类型 + */ + @NotBlank(message = "{auth.grant.type.not.blank}") + private String grantType; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 验证码 + */ + private String code; + + /** + * 唯一标识 + */ + private String uuid; + +} diff --git a/src/main/java/org/dromara/common/core/domain/model/LoginUser.java b/src/main/java/org/dromara/common/core/domain/model/LoginUser.java new file mode 100644 index 0000000..0c5acf4 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/model/LoginUser.java @@ -0,0 +1,188 @@ +package org.dromara.common.core.domain.model; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.dromara.common.core.domain.dto.PostDTO; +import org.dromara.common.core.domain.dto.RoleDTO; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; +import java.util.Set; + +/** + * 登录用户身份权限 + * + * @author shihongwei + */ +@Data +@NoArgsConstructor +public class LoginUser implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 业务表的用户id + */ + private String businessUserId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 部门类别编码 + */ + private String deptCategory; + + /** + * 部门名 + */ + private String deptName; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 用户类型 + */ + private String userType; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 菜单权限 + */ + private Set menuPermission; + + /** + * 角色权限 + */ + private Set rolePermission; + + /** + * 角色权限码 + */ + private Set permissionCodes; + + /** + * 用户名 + */ + private String username; + + /** + * 用户昵称 + */ + private String nickname; + + /** + * 角色对象 + */ + private List roles; + + /** + * 岗位对象 + */ + private List posts; + + /** + * 数据权限 当前角色ID + */ + private Long roleId; + + /** + * 客户端 + */ + private String clientKey; + + /** + * 设备类型 + */ + private String deviceType; + + /** + * 是否为管理员 + */ + private Boolean isAdmin = false; + + /** + * 企业角色ID + */ + private Long companyRoleId; + + /** + * 企业ID + */ + private Long companyId; + + /** + * 企业名称 + */ + private String companyName; + + /** + * 登录端口 + */ + private String loginPort; + + /** + * 允许登录的端口ID集合(密码验证通过的端口) + */ + private Set allowedPortIds; + + /** + * 获取登录id + */ + public String getLoginId() { + if (userType == null) { + throw new IllegalArgumentException("用户类型不能为空"); + } + if (userId == null) { + throw new IllegalArgumentException("用户ID不能为空"); + } + return userType + ":" + userId; + } + +} diff --git a/src/main/java/org/dromara/common/core/domain/model/PasswordLoginBody.java b/src/main/java/org/dromara/common/core/domain/model/PasswordLoginBody.java new file mode 100644 index 0000000..ec279cb --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/model/PasswordLoginBody.java @@ -0,0 +1,41 @@ +package org.dromara.common.core.domain.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Length; + +/** + * 密码登录对象 + * + * @author shihongwei + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class PasswordLoginBody extends LoginBody { + + /** + * 账号(手机号、用户名等) + */ + @NotBlank(message = "{user.account.not.blank}") + @Length(min = 2, max = 30, message = "{user.username.length.valid}") + private String account; + + /** + * 用户密码 + */ + @NotBlank(message = "{user.password.not.blank}") + @Length(min = 5, max = 30, message = "{user.password.length.valid}") +// @Pattern(regexp = RegexConstants.PASSWORD, message = "{user.password.format.valid}") + private String password; + + /** + * 企业用户角色ID,在存在多企业角色时使用 + */ + private Long companyRoleId; + + /** + * 登录端口 + */ + private String loginPort; +} diff --git a/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java b/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java new file mode 100644 index 0000000..02aef82 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/model/RegisterBody.java @@ -0,0 +1,37 @@ +package org.dromara.common.core.domain.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Length; + +/** + * 用户注册对象 + * + * @author shihongwei + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class RegisterBody extends LoginBody { + + /** + * 用户名 + */ + @NotBlank(message = "{user.username.not.blank}") + @Length(min = 2, max = 30, message = "{user.username.length.valid}") + private String username; + + /** + * 用户密码 + */ + @NotBlank(message = "{user.password.not.blank}") + @Length(min = 5, max = 30, message = "{user.password.length.valid}") +// @Pattern(regexp = RegexConstants.PASSWORD, message = "{user.password.format.valid}") + private String password; + + /** + * 用户类型 + */ + private String userType; + +} diff --git a/src/main/java/org/dromara/common/core/domain/model/SmsLoginBody.java b/src/main/java/org/dromara/common/core/domain/model/SmsLoginBody.java new file mode 100644 index 0000000..d5ca90d --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/model/SmsLoginBody.java @@ -0,0 +1,38 @@ +package org.dromara.common.core.domain.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 短信登录对象 + * + * @author shihongwei + */ + +@Data +@EqualsAndHashCode(callSuper = true) +public class SmsLoginBody extends LoginBody { + + /** + * 手机号 + */ + @NotBlank(message = "{user.phonenumber.not.blank}") + private String phonenumber; + + /** + * 短信code + */ + @NotBlank(message = "{sms.code.not.blank}") + private String smsCode; + + /** + * 企业用户角色ID,在存在多企业角色时使用 + */ + private Long companyRoleId; + + /** + * 登录端口 + */ + private String loginPort; +} diff --git a/src/main/java/org/dromara/common/core/domain/model/SocialLoginBody.java b/src/main/java/org/dromara/common/core/domain/model/SocialLoginBody.java new file mode 100644 index 0000000..1b16935 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/model/SocialLoginBody.java @@ -0,0 +1,35 @@ +package org.dromara.common.core.domain.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 三方登录对象 + * + * @author shihongwei + */ + +@Data +@EqualsAndHashCode(callSuper = true) +public class SocialLoginBody extends LoginBody { + + /** + * 第三方登录平台 + */ + @NotBlank(message = "{social.source.not.blank}") + private String source; + + /** + * 第三方登录code + */ + @NotBlank(message = "{social.code.not.blank}") + private String socialCode; + + /** + * 第三方登录socialState + */ + @NotBlank(message = "{social.state.not.blank}") + private String socialState; + +} diff --git a/src/main/java/org/dromara/common/core/domain/model/TaskAssigneeBody.java b/src/main/java/org/dromara/common/core/domain/model/TaskAssigneeBody.java new file mode 100644 index 0000000..ba48e57 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/model/TaskAssigneeBody.java @@ -0,0 +1,56 @@ +package org.dromara.common.core.domain.model; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 任务受让人 + * + * @author shihongwei + */ +@Data +@NoArgsConstructor +public class TaskAssigneeBody implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 权限编码 + */ + private String handlerCode; + + /** + * 权限名称 + */ + private String handlerName; + + /** + * 权限分组 + */ + private String groupId; + + /** + * 开始时间 + */ + private String beginTime; + + /** + * 结束时间 + */ + private String endTime; + + /** + * 当前页 + */ + private Integer pageNum = 1; + + /** + * 每页显示条数 + */ + private Integer pageSize = 10; + +} diff --git a/src/main/java/org/dromara/common/core/domain/model/XcxLoginBody.java b/src/main/java/org/dromara/common/core/domain/model/XcxLoginBody.java new file mode 100644 index 0000000..7fcea15 --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/model/XcxLoginBody.java @@ -0,0 +1,28 @@ +package org.dromara.common.core.domain.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 三方登录对象 + * + * @author shihongwei + */ + +@Data +@EqualsAndHashCode(callSuper = true) +public class XcxLoginBody extends LoginBody { + + /** + * 小程序id(多个小程序时使用) + */ + private String appid; + + /** + * 小程序code + */ + @NotBlank(message = "{xcx.code.not.blank}") + private String xcxCode; + +} diff --git a/src/main/java/org/dromara/common/core/domain/model/XcxLoginUser.java b/src/main/java/org/dromara/common/core/domain/model/XcxLoginUser.java new file mode 100644 index 0000000..10bc28a --- /dev/null +++ b/src/main/java/org/dromara/common/core/domain/model/XcxLoginUser.java @@ -0,0 +1,27 @@ +package org.dromara.common.core.domain.model; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serial; + +/** + * 小程序登录用户身份权限 + * + * @author shihongwei + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class XcxLoginUser extends LoginUser { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * openid + */ + private String openid; + +} diff --git a/src/main/java/org/dromara/common/core/enums/BusinessStatusEnum.java b/src/main/java/org/dromara/common/core/enums/BusinessStatusEnum.java new file mode 100644 index 0000000..c046476 --- /dev/null +++ b/src/main/java/org/dromara/common/core/enums/BusinessStatusEnum.java @@ -0,0 +1,215 @@ +package org.dromara.common.core.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 业务状态枚举 + * + * @author shihongwei + */ +@Getter +@AllArgsConstructor +public enum BusinessStatusEnum { + + /** + * 已撤销 + */ + CANCEL("cancel", "已撤销"), + + /** + * 草稿 + */ + DRAFT("draft", "草稿"), + + /** + * 待审核 + */ + WAITING("waiting", "待审核"), + + /** + * 已完成 + */ + FINISH("finish", "已完成"), + + /** + * 已作废 + */ + INVALID("invalid", "已作废"), + + /** + * 已退回 + */ + BACK("back", "已退回"), + + /** + * 已终止 + */ + TERMINATION("termination", "已终止"); + + /** + * 状态 + */ + private final String status; + + /** + * 描述 + */ + private final String desc; + + private static final Map STATUS_MAP = Arrays.stream(BusinessStatusEnum.values()) + .collect(Collectors.toConcurrentMap(BusinessStatusEnum::getStatus, Function.identity())); + + /** + * 根据状态获取对应的 BusinessStatusEnum 枚举 + * + * @param status 业务状态码 + * @return 对应的 BusinessStatusEnum 枚举,如果找不到则返回 null + */ + public static BusinessStatusEnum getByStatus(String status) { + // 使用 STATUS_MAP 获取对应的枚举,若找不到则返回 null + return STATUS_MAP.get(status); + } + + /** + * 根据状态获取对应的业务状态描述信息 + * + * @param status 业务状态码 + * @return 返回业务状态描述,若状态码为空或未找到对应的枚举,返回空字符串 + */ + public static String findByStatus(String status) { + if (StringUtils.isBlank(status)) { + return StrUtil.EMPTY; + } + BusinessStatusEnum statusEnum = STATUS_MAP.get(status); + return (statusEnum != null) ? statusEnum.getDesc() : StrUtil.EMPTY; + } + + /** + * 判断是否为指定的状态之一:草稿、已撤销或已退回 + * + * @param status 要检查的状态 + * @return 如果状态为草稿、已撤销或已退回之一,则返回 true;否则返回 false + */ + public static boolean isDraftOrCancelOrBack(String status) { + return DRAFT.status.equals(status) || CANCEL.status.equals(status) || BACK.status.equals(status); + } + + /** + * 判断是否为撤销,退回,作废,终止 + * + * @param status status + * @return 结果 + */ + public static boolean initialState(String status) { + return CANCEL.status.equals(status) || BACK.status.equals(status) || INVALID.status.equals(status) || TERMINATION.status.equals(status); + } + + /** + * 获取运行中的实例状态列表 + * + * @return 包含运行中实例状态的不可变列表 + * (包含 DRAFT、WAITING、BACK 和 CANCEL 状态) + */ + public static List runningStatus() { + return Arrays.asList(DRAFT.status, WAITING.status, BACK.status, CANCEL.status); + } + + /** + * 获取结束实例的状态列表 + * + * @return 包含结束实例状态的不可变列表 + * (包含 FINISH、INVALID 和 TERMINATION 状态) + */ + public static List finishStatus() { + return Arrays.asList(FINISH.status, INVALID.status, TERMINATION.status); + } + + /** + * 启动流程校验 + * + * @param status 状态 + */ + public static void checkStartStatus(String status) { + if (WAITING.getStatus().equals(status)) { + throw new ServiceException("该单据已提交过申请,正在审批中!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 撤销流程校验 + * + * @param status 状态 + */ + public static void checkCancelStatus(String status) { + if (CANCEL.getStatus().equals(status)) { + throw new ServiceException("该单据已撤销!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (BACK.getStatus().equals(status)) { + throw new ServiceException("该单据已退回!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 驳回流程校验 + * + * @param status 状态 + */ + public static void checkBackStatus(String status) { + if (BACK.getStatus().equals(status)) { + throw new ServiceException("该单据已退回!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (CANCEL.getStatus().equals(status)) { + throw new ServiceException("该单据已撤销!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 作废,终止流程校验 + * + * @param status 状态 + */ + public static void checkInvalidStatus(String status) { + if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + +} diff --git a/src/main/java/org/dromara/common/core/enums/DeviceType.java b/src/main/java/org/dromara/common/core/enums/DeviceType.java new file mode 100644 index 0000000..bf7c47d --- /dev/null +++ b/src/main/java/org/dromara/common/core/enums/DeviceType.java @@ -0,0 +1,39 @@ +package org.dromara.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 设备类型 + * + * @author shihongwei + */ +@Getter +@AllArgsConstructor +public enum DeviceType { + + /** + * pc端 + */ + PC("pc"), + + /** + * app端 + */ + APP("app"), + + /** + * 小程序端 + */ + XCX("xcx"), + + /** + * 第三方社交登录平台 + */ + SOCIAL("social"); + + /** + * 设备标识 + */ + private final String device; +} diff --git a/src/main/java/org/dromara/common/core/enums/FormatsType.java b/src/main/java/org/dromara/common/core/enums/FormatsType.java new file mode 100644 index 0000000..8d4b6d9 --- /dev/null +++ b/src/main/java/org/dromara/common/core/enums/FormatsType.java @@ -0,0 +1,146 @@ +package org.dromara.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.dromara.common.core.utils.StringUtils; + +/* + * 日期格式 + * "yyyy":4位数的年份,例如:2023年表示为"2023"。 + * "yy":2位数的年份,例如:2023年表示为"23"。 + * "MM":2位数的月份,取值范围为01到12,例如:7月表示为"07"。 + * "M":不带前导零的月份,取值范围为1到12,例如:7月表示为"7"。 + * "dd":2位数的日期,取值范围为01到31,例如:22日表示为"22"。 + * "d":不带前导零的日期,取值范围为1到31,例如:22日表示为"22"。 + * "EEEE":星期的全名,例如:星期三表示为"Wednesday"。 + * "E":星期的缩写,例如:星期三表示为"Wed"。 + * "DDD" 或 "D":一年中的第几天,取值范围为001到366,例如:第200天表示为"200"。 + * 时间格式 + * "HH":24小时制的小时数,取值范围为00到23,例如:下午5点表示为"17"。 + * "hh":12小时制的小时数,取值范围为01到12,例如:下午5点表示为"05"。 + * "mm":分钟数,取值范围为00到59,例如:30分钟表示为"30"。 + * "ss":秒数,取值范围为00到59,例如:45秒表示为"45"。 + * "SSS":毫秒数,取值范围为000到999,例如:123毫秒表示为"123"。 + */ + +/** + * 日期格式与时间格式枚举 + */ +@Getter +@AllArgsConstructor +public enum FormatsType { + + /** + * 例如:2023年表示为"23" + */ + YY("yy"), + + /** + * 例如:2023年表示为"2023" + */ + YYYY("yyyy"), + + /** + * 例例如,2023年7月可以表示为 "2023-07" + */ + YYYY_MM("yyyy-MM"), + + /** + * 例如,日期 "2023年7月22日" 可以表示为 "2023-07-22" + */ + YYYY_MM_DD("yyyy-MM-dd"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023-07-22 15:30" + */ + YYYY_MM_DD_HH_MM("yyyy-MM-dd HH:mm"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023-07-22 15:30:45" + */ + YYYY_MM_DD_HH_MM_SS("yyyy-MM-dd HH:mm:ss"), + + /** + * 例如:下午3点30分45秒,表示为 "15:30:45" + */ + HH_MM_SS("HH:mm:ss"), + + /** + * 例例如,2023年7月可以表示为 "2023/07" + */ + YYYY_MM_SLASH("yyyy/MM"), + + /** + * 例如,日期 "2023年7月22日" 可以表示为 "2023/07/22" + */ + YYYY_MM_DD_SLASH("yyyy/MM/dd"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45" + */ + YYYY_MM_DD_HH_MM_SLASH("yyyy/MM/dd HH:mm"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45" + */ + YYYY_MM_DD_HH_MM_SS_SLASH("yyyy/MM/dd HH:mm:ss"), + + /** + * 例例如,2023年7月可以表示为 "2023.07" + */ + YYYY_MM_DOT("yyyy.MM"), + + /** + * 例如,日期 "2023年7月22日" 可以表示为 "2023.07.22" + */ + YYYY_MM_DD_DOT("yyyy.MM.dd"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023.07.22 15:30" + */ + YYYY_MM_DD_HH_MM_DOT("yyyy.MM.dd HH:mm"), + + /** + * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023.07.22 15:30:45" + */ + YYYY_MM_DD_HH_MM_SS_DOT("yyyy.MM.dd HH:mm:ss"), + + /** + * 例如,2023年7月可以表示为 "202307" + */ + YYYYMM("yyyyMM"), + + /** + * 例如,2023年7月22日可以表示为 "20230722" + */ + YYYYMMDD("yyyyMMdd"), + + /** + * 例如,2023年7月22日下午3点可以表示为 "2023072215" + */ + YYYYMMDDHH("yyyyMMddHH"), + + /** + * 例如,2023年7月22日下午3点30分可以表示为 "202307221530" + */ + YYYYMMDDHHMM("yyyyMMddHHmm"), + + /** + * 例如,2023年7月22日下午3点30分45秒可以表示为 "20230722153045" + */ + YYYYMMDDHHMMSS("yyyyMMddHHmmss"); + + /** + * 时间格式 + */ + private final String timeFormat; + + public static FormatsType getFormatsType(String str) { + for (FormatsType value : values()) { + if (StringUtils.contains(str, value.getTimeFormat())) { + return value; + } + } + throw new RuntimeException("'FormatsType' not found By " + str); + } +} diff --git a/src/main/java/org/dromara/common/core/enums/LoginType.java b/src/main/java/org/dromara/common/core/enums/LoginType.java new file mode 100644 index 0000000..aaa125d --- /dev/null +++ b/src/main/java/org/dromara/common/core/enums/LoginType.java @@ -0,0 +1,44 @@ +package org.dromara.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 登录类型 + * + * @author shihongwei + */ +@Getter +@AllArgsConstructor +public enum LoginType { + + /** + * 密码登录 + */ + PASSWORD("user.password.retry.limit.exceed", "user.password.retry.limit.count"), + + /** + * 短信登录 + */ + SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"), + + /** + * 邮箱登录 + */ + EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"), + + /** + * 小程序登录 + */ + XCX("", ""); + + /** + * 登录重试超出限制提示 + */ + final String retryLimitExceed; + + /** + * 登录重试限制计数提示 + */ + final String retryLimitCount; +} diff --git a/src/main/java/org/dromara/common/core/enums/UserStatus.java b/src/main/java/org/dromara/common/core/enums/UserStatus.java new file mode 100644 index 0000000..7fac456 --- /dev/null +++ b/src/main/java/org/dromara/common/core/enums/UserStatus.java @@ -0,0 +1,30 @@ +package org.dromara.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 用户状态 + * + * @author shihongwei + */ +@Getter +@AllArgsConstructor +public enum UserStatus { + /** + * 正常 + */ + OK("0", "正常"), + /** + * 停用 + */ + DISABLE("1", "停用"), + /** + * 删除 + */ + DELETED("2", "删除"); + + private final String code; + private final String info; + +} diff --git a/src/main/java/org/dromara/common/core/enums/UserType.java b/src/main/java/org/dromara/common/core/enums/UserType.java new file mode 100644 index 0000000..8566bd5 --- /dev/null +++ b/src/main/java/org/dromara/common/core/enums/UserType.java @@ -0,0 +1,39 @@ +package org.dromara.common.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.dromara.common.core.utils.StringUtils; + +/** + * 用户类型 + * + * @author shihongwei + */ +@Getter +@AllArgsConstructor +public enum UserType { + + /** + * 后台系统用户 + */ + SYS_USER("sys_user"), + + /** + * 移动客户端用户 + */ + APP_USER("app_user"); + + /** + * 用户类型标识(用于 token、权限识别等) + */ + private final String userType; + + public static UserType getUserType(String str) { + for (UserType value : values()) { + if (StringUtils.contains(str, value.getUserType())) { + return value; + } + } + throw new RuntimeException("'UserType' not found By " + str); + } +} diff --git a/src/main/java/org/dromara/common/core/exception/ServiceException.java b/src/main/java/org/dromara/common/core/exception/ServiceException.java new file mode 100644 index 0000000..5782e64 --- /dev/null +++ b/src/main/java/org/dromara/common/core/exception/ServiceException.java @@ -0,0 +1,67 @@ +package org.dromara.common.core.exception; + +import cn.hutool.core.text.StrFormatter; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serial; + +/** + * 业务异常(支持占位符 {} ) + * + * @author shihongwei + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public final class ServiceException extends RuntimeException { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + */ + private String detailMessage; + + public ServiceException(String message) { + this.message = message; + } + + public ServiceException(String message, Integer code) { + this.message = message; + this.code = code; + } + + public ServiceException(String message, Object... args) { + this.message = StrFormatter.format(message, args); + } + + @Override + public String getMessage() { + return message; + } + + public ServiceException setMessage(String message) { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) { + this.detailMessage = detailMessage; + return this; + } +} diff --git a/src/main/java/org/dromara/common/core/exception/SseException.java b/src/main/java/org/dromara/common/core/exception/SseException.java new file mode 100644 index 0000000..ac911bd --- /dev/null +++ b/src/main/java/org/dromara/common/core/exception/SseException.java @@ -0,0 +1,62 @@ +package org.dromara.common.core.exception; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serial; + +/** + * sse 特制异常 + * + * @author shihongwei + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public final class SseException extends RuntimeException { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + */ + private String detailMessage; + + public SseException(String message) { + this.message = message; + } + + public SseException(String message, Integer code) { + this.message = message; + this.code = code; + } + + @Override + public String getMessage() { + return message; + } + + public SseException setMessage(String message) { + this.message = message; + return this; + } + + public SseException setDetailMessage(String detailMessage) { + this.detailMessage = detailMessage; + return this; + } +} diff --git a/src/main/java/org/dromara/common/core/exception/base/BaseException.java b/src/main/java/org/dromara/common/core/exception/base/BaseException.java new file mode 100644 index 0000000..989add9 --- /dev/null +++ b/src/main/java/org/dromara/common/core/exception/base/BaseException.java @@ -0,0 +1,74 @@ +package org.dromara.common.core.exception.base; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.MessageUtils; +import org.dromara.common.core.utils.StringUtils; + +import java.io.Serial; + +/** + * 基础异常 + * + * @author shihongwei + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class BaseException extends RuntimeException { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args) { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) { + this(null, null, null, defaultMessage); + } + + @Override + public String getMessage() { + String message = null; + if (!StringUtils.isEmpty(code)) { + message = MessageUtils.message(code, args); + } + if (message == null) { + message = defaultMessage; + } + return message; + } + +} diff --git a/src/main/java/org/dromara/common/core/exception/file/FileException.java b/src/main/java/org/dromara/common/core/exception/file/FileException.java new file mode 100644 index 0000000..9a81711 --- /dev/null +++ b/src/main/java/org/dromara/common/core/exception/file/FileException.java @@ -0,0 +1,21 @@ +package org.dromara.common.core.exception.file; + +import org.dromara.common.core.exception.base.BaseException; + +import java.io.Serial; + +/** + * 文件信息异常类 + * + * @author shihongwei + */ +public class FileException extends BaseException { + + @Serial + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) { + super("file", code, args, null); + } + +} diff --git a/src/main/java/org/dromara/common/core/exception/file/FileNameLengthLimitExceededException.java b/src/main/java/org/dromara/common/core/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 0000000..971d8ec --- /dev/null +++ b/src/main/java/org/dromara/common/core/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,18 @@ +package org.dromara.common.core.exception.file; + +import java.io.Serial; + +/** + * 文件名称超长限制异常类 + * + * @author shihongwei + */ +public class FileNameLengthLimitExceededException extends FileException { + + @Serial + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) { + super("upload.filename.exceed.length", new Object[]{defaultFileNameLength}); + } +} diff --git a/src/main/java/org/dromara/common/core/exception/file/FileSizeLimitExceededException.java b/src/main/java/org/dromara/common/core/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 0000000..d004612 --- /dev/null +++ b/src/main/java/org/dromara/common/core/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,18 @@ +package org.dromara.common.core.exception.file; + +import java.io.Serial; + +/** + * 文件名大小限制异常类 + * + * @author shihongwei + */ +public class FileSizeLimitExceededException extends FileException { + + @Serial + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) { + super("upload.exceed.maxSize", new Object[]{defaultMaxSize}); + } +} diff --git a/src/main/java/org/dromara/common/core/exception/user/CaptchaException.java b/src/main/java/org/dromara/common/core/exception/user/CaptchaException.java new file mode 100644 index 0000000..a0f0df2 --- /dev/null +++ b/src/main/java/org/dromara/common/core/exception/user/CaptchaException.java @@ -0,0 +1,18 @@ +package org.dromara.common.core.exception.user; + +import java.io.Serial; + +/** + * 验证码错误异常类 + * + * @author shihongwei + */ +public class CaptchaException extends UserException { + + @Serial + private static final long serialVersionUID = 1L; + + public CaptchaException() { + super("user.jcaptcha.error"); + } +} diff --git a/src/main/java/org/dromara/common/core/exception/user/CaptchaExpireException.java b/src/main/java/org/dromara/common/core/exception/user/CaptchaExpireException.java new file mode 100644 index 0000000..de6298c --- /dev/null +++ b/src/main/java/org/dromara/common/core/exception/user/CaptchaExpireException.java @@ -0,0 +1,18 @@ +package org.dromara.common.core.exception.user; + +import java.io.Serial; + +/** + * 验证码失效异常类 + * + * @author shihongwei + */ +public class CaptchaExpireException extends UserException { + + @Serial + private static final long serialVersionUID = 1L; + + public CaptchaExpireException() { + super("user.jcaptcha.expire"); + } +} diff --git a/src/main/java/org/dromara/common/core/exception/user/UserException.java b/src/main/java/org/dromara/common/core/exception/user/UserException.java new file mode 100644 index 0000000..77e9b3c --- /dev/null +++ b/src/main/java/org/dromara/common/core/exception/user/UserException.java @@ -0,0 +1,20 @@ +package org.dromara.common.core.exception.user; + +import org.dromara.common.core.exception.base.BaseException; + +import java.io.Serial; + +/** + * 用户信息异常类 + * + * @author shihongwei + */ +public class UserException extends BaseException { + + @Serial + private static final long serialVersionUID = 1L; + + public UserException(String code, Object... args) { + super("user", code, args, null); + } +} diff --git a/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java b/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java new file mode 100644 index 0000000..a6ba3d0 --- /dev/null +++ b/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java @@ -0,0 +1,52 @@ +package org.dromara.common.core.factory; + +import cn.hutool.core.lang.PatternPool; +import org.dromara.common.core.constant.RegexConstants; + +import java.util.regex.Pattern; + +/** + * 正则表达式模式池工厂 + *

初始化的时候将正则表达式加入缓存池当中

+ *

提高正则表达式的性能,避免重复编译相同的正则表达式

+ * + * @author shihongwei + */ +public class RegexPatternPoolFactory extends PatternPool { + + /** + * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) + */ + public static final Pattern DICTIONARY_TYPE = get(RegexConstants.DICTIONARY_TYPE); + + /** + * 身份证号码(后6位) + */ + public static final Pattern ID_CARD_LAST_6 = get(RegexConstants.ID_CARD_LAST_6); + + /** + * QQ号码 + */ + public static final Pattern QQ_NUMBER = get(RegexConstants.QQ_NUMBER); + + /** + * 邮政编码 + */ + public static final Pattern POSTAL_CODE = get(RegexConstants.POSTAL_CODE); + + /** + * 注册账号 + */ + public static final Pattern ACCOUNT = get(RegexConstants.ACCOUNT); + + /** + * 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符 + */ + public static final Pattern PASSWORD = get(RegexConstants.PASSWORD); + + /** + * 通用状态(0表示正常,1表示停用) + */ + public static final Pattern STATUS = get(RegexConstants.STATUS); + +} diff --git a/src/main/java/org/dromara/common/core/factory/YmlPropertySourceFactory.java b/src/main/java/org/dromara/common/core/factory/YmlPropertySourceFactory.java new file mode 100644 index 0000000..ad6c8a4 --- /dev/null +++ b/src/main/java/org/dromara/common/core/factory/YmlPropertySourceFactory.java @@ -0,0 +1,31 @@ +package org.dromara.common.core.factory; + +import org.dromara.common.core.utils.StringUtils; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.support.DefaultPropertySourceFactory; +import org.springframework.core.io.support.EncodedResource; + +import java.io.IOException; + +/** + * yml 配置源工厂 + * + * @author shihongwei + */ +public class YmlPropertySourceFactory extends DefaultPropertySourceFactory { + + @Override + public PropertySource createPropertySource(String name, EncodedResource resource) throws IOException { + String sourceName = resource.getResource().getFilename(); + if (StringUtils.isNotBlank(sourceName) && StringUtils.endsWithAny(sourceName, ".yml", ".yaml")) { + YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); + factory.setResources(resource.getResource()); + factory.afterPropertiesSet(); + return new PropertiesPropertySource(sourceName, factory.getObject()); + } + return super.createPropertySource(name, resource); + } + +} diff --git a/src/main/java/org/dromara/common/core/service/ConfigService.java b/src/main/java/org/dromara/common/core/service/ConfigService.java new file mode 100644 index 0000000..b5ea543 --- /dev/null +++ b/src/main/java/org/dromara/common/core/service/ConfigService.java @@ -0,0 +1,18 @@ +package org.dromara.common.core.service; + +/** + * 通用 参数配置服务 + * + * @author shihongwei + */ +public interface ConfigService { + + /** + * 根据参数 key 获取参数值 + * + * @param configKey 参数 key + * @return 参数值 + */ + String getConfigValue(String configKey); + +} diff --git a/src/main/java/org/dromara/common/core/service/DeptService.java b/src/main/java/org/dromara/common/core/service/DeptService.java new file mode 100644 index 0000000..bdf83ef --- /dev/null +++ b/src/main/java/org/dromara/common/core/service/DeptService.java @@ -0,0 +1,46 @@ +package org.dromara.common.core.service; + +import org.dromara.common.core.domain.dto.DeptDTO; + +import java.util.List; +import java.util.Map; + +/** + * 通用 部门服务 + * + * @author shihongwei + */ +public interface DeptService { + + /** + * 通过部门ID查询部门名称 + * + * @param deptIds 部门ID串逗号分隔 + * @return 部门名称串逗号分隔 + */ + String selectDeptNameByIds(String deptIds); + + /** + * 根据部门ID查询部门负责人 + * + * @param deptId 部门ID,用于指定需要查询的部门 + * @return 返回该部门的负责人ID + */ + Long selectDeptLeaderById(Long deptId); + + /** + * 查询部门 + * + * @return 部门列表 + */ + List selectDeptsByList(); + + /** + * 根据部门 ID 列表查询部门名称映射关系 + * + * @param deptIds 部门 ID 列表 + * @return Map,其中 key 为部门 ID,value 为对应的部门名称 + */ + Map selectDeptNamesByIds(List deptIds); + +} diff --git a/src/main/java/org/dromara/common/core/service/DictService.java b/src/main/java/org/dromara/common/core/service/DictService.java new file mode 100644 index 0000000..f9e0137 --- /dev/null +++ b/src/main/java/org/dromara/common/core/service/DictService.java @@ -0,0 +1,87 @@ +package org.dromara.common.core.service; + +import org.dromara.common.core.domain.dto.DictDataDTO; +import org.dromara.common.core.domain.dto.DictTypeDTO; + +import java.util.List; +import java.util.Map; + +/** + * 通用 字典服务 + * + * @author shihongwei + */ +public interface DictService { + + /** + * 分隔符 + */ + String SEPARATOR = ","; + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @return 字典标签 + */ + default String getDictLabel(String dictType, String dictValue) { + return getDictLabel(dictType, dictValue, SEPARATOR); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @return 字典值 + */ + default String getDictValue(String dictType, String dictLabel) { + return getDictValue(dictType, dictLabel, SEPARATOR); + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @param separator 分隔符 + * @return 字典标签 + */ + String getDictLabel(String dictType, String dictValue, String separator); + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @param separator 分隔符 + * @return 字典值 + */ + String getDictValue(String dictType, String dictLabel, String separator); + + /** + * 获取字典下所有的字典值与标签 + * + * @param dictType 字典类型 + * @return dictValue为key,dictLabel为值组成的Map + */ + Map getAllDictByDictType(String dictType); + + /** + * 根据字典类型查询详细信息 + * + * @param dictType 字典类型 + * @return 字典类型详细信息 + */ + DictTypeDTO getDictType(String dictType); + + /** + * 根据字典类型查询字典数据列表 + * + * @param dictType 字典类型 + * @return 字典数据列表 + */ + List getDictData(String dictType); + +} diff --git a/src/main/java/org/dromara/common/core/service/OssService.java b/src/main/java/org/dromara/common/core/service/OssService.java new file mode 100644 index 0000000..8008966 --- /dev/null +++ b/src/main/java/org/dromara/common/core/service/OssService.java @@ -0,0 +1,29 @@ +package org.dromara.common.core.service; + +import org.dromara.common.core.domain.dto.OssDTO; + +import java.util.List; + +/** + * 通用 OSS服务 + * + * @author shihongwei + */ +public interface OssService { + + /** + * 通过ossId查询对应的url + * + * @param ossIds ossId串逗号分隔 + * @return url串逗号分隔 + */ + String selectUrlByIds(String ossIds); + + /** + * 通过ossId查询列表 + * + * @param ossIds ossId串逗号分隔 + * @return 列表 + */ + List selectByIds(String ossIds); +} diff --git a/src/main/java/org/dromara/common/core/service/PermissionService.java b/src/main/java/org/dromara/common/core/service/PermissionService.java new file mode 100644 index 0000000..ee85808 --- /dev/null +++ b/src/main/java/org/dromara/common/core/service/PermissionService.java @@ -0,0 +1,28 @@ +package org.dromara.common.core.service; + +import java.util.Set; + +/** + * 用户权限处理 + * + * @author shihongwei + */ +public interface PermissionService { + + /** + * 获取角色数据权限 + * + * @param userId 用户id + * @return 角色权限信息 + */ + Set getRolePermission(Long userId); + + /** + * 获取菜单数据权限 + * + * @param userId 用户id + * @return 菜单权限信息 + */ + Set getMenuPermission(Long userId); + +} diff --git a/src/main/java/org/dromara/common/core/service/PostService.java b/src/main/java/org/dromara/common/core/service/PostService.java new file mode 100644 index 0000000..e8d0c5c --- /dev/null +++ b/src/main/java/org/dromara/common/core/service/PostService.java @@ -0,0 +1,21 @@ +package org.dromara.common.core.service; + +import java.util.List; +import java.util.Map; + +/** + * 通用 岗位服务 + * + * @author shihongwei + */ +public interface PostService { + + /** + * 根据岗位 ID 列表查询岗位名称映射关系 + * + * @param postIds 岗位 ID 列表 + * @return Map,其中 key 为岗位 ID,value 为对应的岗位名称 + */ + Map selectPostNamesByIds(List postIds); + +} diff --git a/src/main/java/org/dromara/common/core/service/RoleService.java b/src/main/java/org/dromara/common/core/service/RoleService.java new file mode 100644 index 0000000..5ba5c10 --- /dev/null +++ b/src/main/java/org/dromara/common/core/service/RoleService.java @@ -0,0 +1,21 @@ +package org.dromara.common.core.service; + +import java.util.List; +import java.util.Map; + +/** + * 通用 角色服务 + * + * @author shihongwei + */ +public interface RoleService { + + /** + * 根据角色 ID 列表查询角色名称映射关系 + * + * @param roleIds 角色 ID 列表 + * @return Map,其中 key 为角色 ID,value 为对应的角色名称 + */ + Map selectRoleNamesByIds(List roleIds); + +} diff --git a/src/main/java/org/dromara/common/core/service/TaskAssigneeService.java b/src/main/java/org/dromara/common/core/service/TaskAssigneeService.java new file mode 100644 index 0000000..93f4a46 --- /dev/null +++ b/src/main/java/org/dromara/common/core/service/TaskAssigneeService.java @@ -0,0 +1,45 @@ +package org.dromara.common.core.service; + +import org.dromara.common.core.domain.dto.TaskAssigneeDTO; +import org.dromara.common.core.domain.model.TaskAssigneeBody; + +/** + * 工作流设计器获取任务执行人 + * + * @author shihongwei + */ +public interface TaskAssigneeService { + + /** + * 查询角色并返回任务指派的列表,支持分页 + * + * @param taskQuery 查询条件 + * @return 办理人 + */ + TaskAssigneeDTO selectRolesByTaskAssigneeList(TaskAssigneeBody taskQuery); + + /** + * 查询岗位并返回任务指派的列表,支持分页 + * + * @param taskQuery 查询条件 + * @return 办理人 + */ + TaskAssigneeDTO selectPostsByTaskAssigneeList(TaskAssigneeBody taskQuery); + + /** + * 查询部门并返回任务指派的列表,支持分页 + * + * @param taskQuery 查询条件 + * @return 办理人 + */ + TaskAssigneeDTO selectDeptsByTaskAssigneeList(TaskAssigneeBody taskQuery); + + /** + * 查询用户并返回任务指派的列表,支持分页 + * + * @param taskQuery 查询条件 + * @return 办理人 + */ + TaskAssigneeDTO selectUsersByTaskAssigneeList(TaskAssigneeBody taskQuery); + +} diff --git a/src/main/java/org/dromara/common/core/service/UserService.java b/src/main/java/org/dromara/common/core/service/UserService.java new file mode 100644 index 0000000..5c8c0ad --- /dev/null +++ b/src/main/java/org/dromara/common/core/service/UserService.java @@ -0,0 +1,103 @@ +package org.dromara.common.core.service; + +import org.dromara.common.core.domain.dto.UserDTO; + +import java.util.List; +import java.util.Map; + +/** + * 通用 用户服务 + * + * @author shihongwei + */ +public interface UserService { + + /** + * 通过用户ID查询用户账户 + * + * @param userId 用户ID + * @return 用户账户 + */ + String selectUserNameById(Long userId); + + /** + * 通过用户ID查询用户账户 + * + * @param userId 用户ID + * @return 用户名称 + */ + String selectNicknameById(Long userId); + + /** + * 通过用户ID查询用户账户 + * + * @param userIds 用户ID 多个用逗号隔开 + * @return 用户名称 + */ + String selectNicknameByIds(String userIds); + + /** + * 通过用户ID查询用户手机号 + * + * @param userId 用户id + * @return 用户手机号 + */ + String selectPhonenumberById(Long userId); + + /** + * 通过用户ID查询用户邮箱 + * + * @param userId 用户id + * @return 用户邮箱 + */ + String selectEmailById(Long userId); + + /** + * 通过用户ID查询用户列表 + * + * @param userIds 用户ids + * @return 用户列表 + */ + List selectListByIds(List userIds); + + /** + * 通过角色ID查询用户ID + * + * @param roleIds 角色ids + * @return 用户ids + */ + List selectUserIdsByRoleIds(List roleIds); + + /** + * 通过角色ID查询用户 + * + * @param roleIds 角色ids + * @return 用户 + */ + List selectUsersByRoleIds(List roleIds); + + /** + * 通过部门ID查询用户 + * + * @param deptIds 部门ids + * @return 用户 + */ + List selectUsersByDeptIds(List deptIds); + + /** + * 通过岗位ID查询用户 + * + * @param postIds 岗位ids + * @return 用户 + */ + List selectUsersByPostIds(List postIds); + + /** + * 根据用户 ID 列表查询用户名称映射关系 + * + * @param userIds 用户 ID 列表 + * @return Map,其中 key 为用户 ID,value 为对应的用户名称 + */ + Map selectUserNamesByIds(List userIds); + +} diff --git a/src/main/java/org/dromara/common/core/service/WorkflowService.java b/src/main/java/org/dromara/common/core/service/WorkflowService.java new file mode 100644 index 0000000..a82d059 --- /dev/null +++ b/src/main/java/org/dromara/common/core/service/WorkflowService.java @@ -0,0 +1,105 @@ +package org.dromara.common.core.service; + +import org.dromara.common.core.domain.dto.CompleteTaskDTO; +import org.dromara.common.core.domain.dto.StartProcessDTO; +import org.dromara.common.core.domain.dto.StartProcessReturnDTO; + +import java.util.List; +import java.util.Map; + +/** + * 通用 工作流服务 + * + * @author shihongwei + */ +public interface WorkflowService { + + /** + * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param businessIds 业务id + * @return 结果 + */ + boolean deleteInstance(List businessIds); + + /** + * 获取当前流程状态 + * + * @param taskId 任务id + * @return 状态 + */ + String getBusinessStatusByTaskId(Long taskId); + + /** + * 获取当前流程状态 + * + * @param businessId 业务id + * @return 状态 + */ + String getBusinessStatus(String businessId); + + /** + * 设置流程变量 + * + * @param instanceId 流程实例id + * @param variable 流程变量 + */ + void setVariable(Long instanceId, Map variable); + + /** + * 获取流程变量 + * + * @param instanceId 流程实例id + */ + Map instanceVariable(Long instanceId); + + /** + * 按照业务id查询流程实例id + * + * @param businessId 业务id + * @return 结果 + */ + Long getInstanceIdByBusinessId(String businessId); + + /** + * 新增租户流程定义 + * + * @param tenantId 租户id + */ + void syncDef(String tenantId); + + /** + * 启动流程 + * + * @param startProcess 参数 + * @return 结果 + */ + StartProcessReturnDTO startWorkFlow(StartProcessDTO startProcess); + + /** + * 办理任务 + * 系统后台发起审批 无用户信息 需要忽略权限 + * completeTask.getVariables().put("ignore", true); + * + * @param completeTask 参数 + * @return 结果 + */ + boolean completeTask(CompleteTaskDTO completeTask); + + /** + * 办理任务 + * + * @param taskId 任务ID + * @param message 办理意见 + * @return 结果 + */ + boolean completeTask(Long taskId, String message); + + /** + * 启动流程并办理第一个任务 + * + * @param startProcess 参数 + * @return 结果 + */ + boolean startCompleteTask(StartProcessDTO startProcess); +} diff --git a/src/main/java/org/dromara/common/core/utils/DateUtils.java b/src/main/java/org/dromara/common/core/utils/DateUtils.java new file mode 100644 index 0000000..78a21c7 --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/DateUtils.java @@ -0,0 +1,300 @@ +package org.dromara.common.core.utils; + +import org.apache.commons.lang3.time.DateFormatUtils; +import org.dromara.common.core.enums.FormatsType; +import org.dromara.common.core.exception.ServiceException; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.*; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +/** + * 时间工具类 + * + * @author shihongwei + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils { + private static final String[] PARSE_PATTERNS = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + @Deprecated + private DateUtils() { + } + + /** + * 获取当前日期和时间 + * + * @return 当前日期和时间的 Date 对象表示 + */ + public static Date getNowDate() { + return new Date(); + } + + /** + * 获取当前日期的字符串表示,格式为YYYY-MM-DD + * + * @return 当前日期的字符串表示 + */ + public static String getDate() { + return dateTimeNow(FormatsType.YYYY_MM_DD); + } + + /** + * 获取当前日期的字符串表示,格式为yyyyMMdd + * + * @return 当前日期的字符串表示 + */ + public static String getCurrentDate() { + return DateFormatUtils.format(new Date(), FormatsType.YYYYMMDD.getTimeFormat()); + } + + /** + * 获取当前日期的路径格式字符串,格式为"yyyy/MM/dd" + * + * @return 当前日期的路径格式字符串 + */ + public static String datePath() { + Date now = new Date(); + return DateFormatUtils.format(now, FormatsType.YYYY_MM_DD_SLASH.getTimeFormat()); + } + + /** + * 获取当前时间的字符串表示,格式为YYYY-MM-DD HH:MM:SS + * + * @return 当前时间的字符串表示 + */ + public static String getTime() { + return dateTimeNow(FormatsType.YYYY_MM_DD_HH_MM_SS); + } + + /** + * 获取当前时间的字符串表示,格式为 "HH:MM:SS" + * + * @return 当前时间的字符串表示,格式为 "HH:MM:SS" + */ + public static String getTimeWithHourMinuteSecond() { + return dateTimeNow(FormatsType.HH_MM_SS); + } + + /** + * 获取当前日期和时间的字符串表示,格式为YYYYMMDDHHMMSS + * + * @return 当前日期和时间的字符串表示 + */ + public static String dateTimeNow() { + return dateTimeNow(FormatsType.YYYYMMDDHHMMSS); + } + + /** + * 获取当前日期和时间的指定格式的字符串表示 + * + * @param format 日期时间格式,例如"YYYY-MM-DD HH:MM:SS" + * @return 当前日期和时间的字符串表示 + */ + public static String dateTimeNow(final FormatsType format) { + return parseDateToStr(format, new Date()); + } + + /** + * 将指定日期格式化为 YYYY-MM-DD 格式的字符串 + * + * @param date 要格式化的日期对象 + * @return 格式化后的日期字符串 + */ + public static String formatDate(final Date date) { + return parseDateToStr(FormatsType.YYYY_MM_DD, date); + } + + /** + * 将指定日期格式化为 YYYY-MM-DD HH:MM:SS 格式的字符串 + * + * @param date 要格式化的日期对象 + * @return 格式化后的日期时间字符串 + */ + public static String formatDateTime(final Date date) { + return parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, date); + } + + /** + * 将指定日期按照指定格式进行格式化 + * + * @param format 要使用的日期时间格式,例如"YYYY-MM-DD HH:MM:SS" + * @param date 要格式化的日期对象 + * @return 格式化后的日期时间字符串 + */ + public static String parseDateToStr(final FormatsType format, final Date date) { + return new SimpleDateFormat(format.getTimeFormat()).format(date); + } + + /** + * 将指定格式的日期时间字符串转换为 Date 对象 + * + * @param format 要解析的日期时间格式,例如"YYYY-MM-DD HH:MM:SS" + * @param ts 要解析的日期时间字符串 + * @return 解析后的 Date 对象 + * @throws RuntimeException 如果解析过程中发生异常 + */ + public static Date parseDateTime(final FormatsType format, final String ts) { + try { + return new SimpleDateFormat(format.getTimeFormat()).parse(ts); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * 将对象转换为日期对象 + * + * @param str 要转换的对象,通常是字符串 + * @return 转换后的日期对象,如果转换失败或输入为null,则返回null + */ + public static Date parseDate(Object str) { + if (str == null) { + return null; + } + try { + return parseDate(str.toString(), PARSE_PATTERNS); + } catch (ParseException e) { + return null; + } + } + + /** + * 获取服务器启动时间 + * + * @return 服务器启动时间的 Date 对象表示 + */ + public static Date getServerStartDate() { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算两个时间之间的时间差,并以指定单位返回(绝对值) + * + * @param start 起始时间 + * @param end 结束时间 + * @param unit 所需返回的时间单位(DAYS、HOURS、MINUTES、SECONDS、MILLISECONDS、MICROSECONDS、NANOSECONDS) + * @return 时间差的绝对值,以指定单位表示 + */ + public static long difference(Date start, Date end, TimeUnit unit) { + // 计算时间差,单位为毫秒,取绝对值避免负数 + long diffInMillis = Math.abs(end.getTime() - start.getTime()); + + // 根据目标单位转换时间差 + return switch (unit) { + case DAYS -> diffInMillis / TimeUnit.DAYS.toMillis(1); + case HOURS -> diffInMillis / TimeUnit.HOURS.toMillis(1); + case MINUTES -> diffInMillis / TimeUnit.MINUTES.toMillis(1); + case SECONDS -> diffInMillis / TimeUnit.SECONDS.toMillis(1); + case MILLISECONDS -> diffInMillis; + case MICROSECONDS -> TimeUnit.MILLISECONDS.toMicros(diffInMillis); + case NANOSECONDS -> TimeUnit.MILLISECONDS.toNanos(diffInMillis); + }; + } + + /** + * 计算两个日期之间的时间差,并以天、小时和分钟的格式返回 + * + * @param endDate 结束日期 + * @param nowDate 当前日期 + * @return 表示时间差的字符串,格式为"天 小时 分钟" + */ + public static String getDatePoor(Date endDate, Date nowDate) { + long diffInMillis = endDate.getTime() - nowDate.getTime(); + long day = TimeUnit.MILLISECONDS.toDays(diffInMillis); + long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24; + long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60; + return String.format("%d天 %d小时 %d分钟", day, hour, min); + } + + /** + * 计算两个时间点的差值(天、小时、分钟、秒),当值为0时不显示该单位 + * + * @param endDate 结束时间 + * @param nowDate 当前时间 + * @return 时间差字符串,格式为 "x天 x小时 x分钟 x秒",若为 0 则不显示 + */ + public static String getTimeDifference(Date endDate, Date nowDate) { + long diffInMillis = endDate.getTime() - nowDate.getTime(); + long day = TimeUnit.MILLISECONDS.toDays(diffInMillis); + long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24; + long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60; + long sec = TimeUnit.MILLISECONDS.toSeconds(diffInMillis) % 60; + // 构建时间差字符串,条件是值不为0才显示 + StringBuilder result = new StringBuilder(); + if (day > 0) { + result.append(String.format("%d天 ", day)); + } + if (hour > 0) { + result.append(String.format("%d小时 ", hour)); + } + if (min > 0) { + result.append(String.format("%d分钟 ", min)); + } + if (sec > 0) { + result.append(String.format("%d秒", sec)); + } + return result.length() > 0 ? result.toString().trim() : "0秒"; + } + + /** + * 将 LocalDateTime 对象转换为 Date 对象 + * + * @param temporalAccessor 要转换的 LocalDateTime 对象 + * @return 转换后的 Date 对象 + */ + public static Date toDate(LocalDateTime temporalAccessor) { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 将 LocalDate 对象转换为 Date 对象 + * + * @param temporalAccessor 要转换的 LocalDate 对象 + * @return 转换后的 Date 对象 + */ + public static Date toDate(LocalDate temporalAccessor) { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 校验日期范围 + * + * @param startDate 开始日期 + * @param endDate 结束日期 + * @param maxValue 最大时间跨度的限制值 + * @param unit 时间跨度的单位,可选择 "DAYS"、"HOURS" 或 "MINUTES" + */ + public static void validateDateRange(Date startDate, Date endDate, int maxValue, TimeUnit unit) { + // 校验结束日期不能早于开始日期 + if (endDate.before(startDate)) { + throw new ServiceException("结束日期不能早于开始日期"); + } + + // 计算时间跨度 + long diffInMillis = endDate.getTime() - startDate.getTime(); + + // 根据单位转换时间跨度 + long diff = switch (unit) { + case DAYS -> TimeUnit.MILLISECONDS.toDays(diffInMillis); + case HOURS -> TimeUnit.MILLISECONDS.toHours(diffInMillis); + case MINUTES -> TimeUnit.MILLISECONDS.toMinutes(diffInMillis); + default -> throw new IllegalArgumentException("不支持的时间单位"); + }; + + // 校验时间跨度不超过最大限制 + if (diff > maxValue) { + throw new ServiceException("最大时间跨度为 {} {}", maxValue, unit.toString().toLowerCase()); + } + } + +} diff --git a/src/main/java/org/dromara/common/core/utils/MapstructUtils.java b/src/main/java/org/dromara/common/core/utils/MapstructUtils.java new file mode 100644 index 0000000..55b31dd --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/MapstructUtils.java @@ -0,0 +1,93 @@ +package org.dromara.common.core.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import io.github.linpeilie.Converter; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Map; + +/** + * Mapstruct 工具类 + *

参考文档:mapstruct-plus

+ * + * + * @author shihongwei + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class MapstructUtils { + + private final static Converter CONVERTER = SpringUtils.getBean(Converter.class); + + /** + * 将 T 类型对象,转换为 desc 类型的对象并返回 + * + * @param source 数据来源实体 + * @param desc 描述对象 转换后的对象 + * @return desc + */ + public static V convert(T source, Class desc) { + if (ObjectUtil.isNull(source)) { + return null; + } + if (ObjectUtil.isNull(desc)) { + return null; + } + return CONVERTER.convert(source, desc); + } + + /** + * 将 T 类型对象,按照配置的映射字段规则,给 desc 类型的对象赋值并返回 desc 对象 + * + * @param source 数据来源实体 + * @param desc 转换后的对象 + * @return desc + */ + public static V convert(T source, V desc) { + if (ObjectUtil.isNull(source)) { + return null; + } + if (ObjectUtil.isNull(desc)) { + return null; + } + return CONVERTER.convert(source, desc); + } + + /** + * 将 T 类型的集合,转换为 desc 类型的集合并返回 + * + * @param sourceList 数据来源实体列表 + * @param desc 描述对象 转换后的对象 + * @return desc + */ + public static List convert(List sourceList, Class desc) { + if (ObjectUtil.isNull(sourceList)) { + return null; + } + if (CollUtil.isEmpty(sourceList)) { + return CollUtil.newArrayList(); + } + return CONVERTER.convert(sourceList, desc); + } + + /** + * 将 Map 转换为 beanClass 类型的集合并返回 + * + * @param map 数据来源 + * @param beanClass bean类 + * @return bean对象 + */ + public static T convert(Map map, Class beanClass) { + if (MapUtil.isEmpty(map)) { + return null; + } + if (ObjectUtil.isNull(beanClass)) { + return null; + } + return CONVERTER.convert(map, beanClass); + } + +} diff --git a/src/main/java/org/dromara/common/core/utils/MessageUtils.java b/src/main/java/org/dromara/common/core/utils/MessageUtils.java new file mode 100644 index 0000000..d024a39 --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/MessageUtils.java @@ -0,0 +1,33 @@ +package org.dromara.common.core.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.context.MessageSource; +import org.springframework.context.NoSuchMessageException; +import org.springframework.context.i18n.LocaleContextHolder; + +/** + * 获取i18n资源文件 + * + * @author shihongwei + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class MessageUtils { + + private static final MessageSource MESSAGE_SOURCE = SpringUtils.getBean(MessageSource.class); + + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String message(String code, Object... args) { + try { + return MESSAGE_SOURCE.getMessage(code, args, LocaleContextHolder.getLocale()); + } catch (NoSuchMessageException e) { + return code; + } + } +} diff --git a/src/main/java/org/dromara/common/core/utils/NetUtils.java b/src/main/java/org/dromara/common/core/utils/NetUtils.java new file mode 100644 index 0000000..cb1de9f --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/NetUtils.java @@ -0,0 +1,84 @@ +package org.dromara.common.core.utils; + +import cn.hutool.core.lang.PatternPool; +import cn.hutool.core.net.NetUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.regex.RegexUtils; + +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * 增强网络相关工具类 + * + * @author shihongwei + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class NetUtils extends NetUtil { + + /** + * 判断是否为IPv6地址 + * + * @param ip IP地址 + * @return 是否为IPv6地址 + */ + public static boolean isIPv6(String ip) { + try { + // 判断是否为IPv6地址 + return InetAddress.getByName(ip) instanceof Inet6Address; + } catch (UnknownHostException e) { + return false; + } + } + + /** + * 判断IPv6地址是否为内网地址 + *

+ * 以下地址将归类为本地地址,如有业务场景有需要,请根据需求自行处理: + *
+     * 通配符地址 0:0:0:0:0:0:0:0
+     * 链路本地地址 fe80::/10
+     * 唯一本地地址 fec0::/10
+     * 环回地址 ::1
+     * 
+ * + * @param ip IP地址 + * @return 是否为内网地址 + */ + public static boolean isInnerIPv6(String ip) { + try { + // 判断是否为IPv6地址 + if (InetAddress.getByName(ip) instanceof Inet6Address inet6Address) { + // isAnyLocalAddress 判断是否为通配符地址,通常不会将其视为内网地址,根据业务场景自行处理判断 + // isLinkLocalAddress 判断是否为链路本地地址,通常不算内网地址,是否划分归属于内网需要根据业务场景自行处理判断 + // isLoopbackAddress 判断是否为环回地址,与IPv4的 127.0.0.1 同理,用于表示本机 + // isSiteLocalAddress 判断是否为本地站点地址,IPv6唯一本地地址(Unique Local Addresses,简称ULA) + if (inet6Address.isAnyLocalAddress() + || inet6Address.isLinkLocalAddress() + || inet6Address.isLoopbackAddress() + || inet6Address.isSiteLocalAddress()) { + return true; + } + } + } catch (UnknownHostException e) { + // 注意,isInnerIPv6方法和isIPv6方法的适用范围不同,所以此处不能忽略其异常信息。 + throw new IllegalArgumentException("Invalid IPv6 address!", e); + } + return false; + } + + /** + * 判断是否为IPv4地址 + * + * @param ip IP地址 + * @return 是否为IPv4地址 + */ + public static boolean isIPv4(String ip) { + return RegexUtils.isMatch(PatternPool.IPV4, ip); + } + +} diff --git a/src/main/java/org/dromara/common/core/utils/ObjectUtils.java b/src/main/java/org/dromara/common/core/utils/ObjectUtils.java new file mode 100644 index 0000000..5242187 --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/ObjectUtils.java @@ -0,0 +1,60 @@ +package org.dromara.common.core.utils; + +import cn.hutool.core.util.ObjectUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.function.Function; + +/** + * 对象工具类 + * + * @author shihongwei + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ObjectUtils extends ObjectUtil { + + /** + * 如果对象不为空,则获取对象中的某个字段 ObjectUtils.notNullGetter(user, User::getName); + * + * @param obj 对象 + * @param func 获取方法 + * @return 对象字段 + */ + public static E notNullGetter(T obj, Function func) { + if (isNotNull(obj) && isNotNull(func)) { + return func.apply(obj); + } + return null; + } + + /** + * 如果对象不为空,则获取对象中的某个字段,否则返回默认值 + * + * @param obj 对象 + * @param func 获取方法 + * @param defaultValue 默认值 + * @return 对象字段 + */ + public static E notNullGetter(T obj, Function func, E defaultValue) { + if (isNotNull(obj) && isNotNull(func)) { + return func.apply(obj); + } + return defaultValue; + } + + /** + * 如果值不为空,则返回值,否则返回默认值 + * + * @param obj 对象 + * @param defaultValue 默认值 + * @return 对象字段 + */ + public static T notNull(T obj, T defaultValue) { + if (isNotNull(obj)) { + return obj; + } + return defaultValue; + } + +} diff --git a/src/main/java/org/dromara/common/core/utils/ServletUtils.java b/src/main/java/org/dromara/common/core/utils/ServletUtils.java new file mode 100644 index 0000000..bdc39b7 --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/ServletUtils.java @@ -0,0 +1,289 @@ +package org.dromara.common.core.utils; + +import cn.hutool.core.convert.Convert; +import cn.hutool.extra.servlet.JakartaServletUtil; +import cn.hutool.http.HttpStatus; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.util.LinkedCaseInsensitiveMap; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.io.IOException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +/** + * 客户端工具类,提供获取请求参数、响应处理、头部信息等常用操作 + * + * @author shihongwei + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ServletUtils extends JakartaServletUtil { + + /** + * 获取指定名称的 String 类型的请求参数 + * + * @param name 参数名 + * @return 参数值 + */ + public static String getParameter(String name) { + return getRequest().getParameter(name); + } + + /** + * 获取指定名称的 String 类型的请求参数,若参数不存在,则返回默认值 + * + * @param name 参数名 + * @param defaultValue 默认值 + * @return 参数值或默认值 + */ + public static String getParameter(String name, String defaultValue) { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取指定名称的 Integer 类型的请求参数 + * + * @param name 参数名 + * @return 参数值 + */ + public static Integer getParameterToInt(String name) { + return Convert.toInt(getRequest().getParameter(name)); + } + + /** + * 获取指定名称的 Integer 类型的请求参数,若参数不存在,则返回默认值 + * + * @param name 参数名 + * @param defaultValue 默认值 + * @return 参数值或默认值 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取指定名称的 Boolean 类型的请求参数 + * + * @param name 参数名 + * @return 参数值 + */ + public static Boolean getParameterToBool(String name) { + return Convert.toBool(getRequest().getParameter(name)); + } + + /** + * 获取指定名称的 Boolean 类型的请求参数,若参数不存在,则返回默认值 + * + * @param name 参数名 + * @param defaultValue 默认值 + * @return 参数值或默认值 + */ + public static Boolean getParameterToBool(String name, Boolean defaultValue) { + return Convert.toBool(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取所有请求参数(以 Map 的形式返回) + * + * @param request 请求对象{@link ServletRequest} + * @return 请求参数的 Map,键为参数名,值为参数值数组 + */ + public static Map getParams(ServletRequest request) { + final Map map = request.getParameterMap(); + return Collections.unmodifiableMap(map); + } + + /** + * 获取所有请求参数(以 Map 的形式返回,值为字符串形式的拼接) + * + * @param request 请求对象{@link ServletRequest} + * @return 请求参数的 Map,键为参数名,值为拼接后的字符串 + */ + public static Map getParamMap(ServletRequest request) { + Map params = new HashMap<>(); + for (Map.Entry entry : getParams(request).entrySet()) { + params.put(entry.getKey(), StringUtils.joinComma(entry.getValue())); + } + return params; + } + + /** + * 获取当前 HTTP 请求对象 + * + * @return 当前 HTTP 请求对象 + */ + public static HttpServletRequest getRequest() { + try { + return getRequestAttributes().getRequest(); + } catch (Exception e) { + return null; + } + } + + /** + * 获取当前 HTTP 响应对象 + * + * @return 当前 HTTP 响应对象 + */ + public static HttpServletResponse getResponse() { + try { + return getRequestAttributes().getResponse(); + } catch (Exception e) { + return null; + } + } + + /** + * 获取当前请求的 HttpSession 对象 + *

+ * 如果当前请求已经关联了一个会话(即已经存在有效的 session ID), + * 则返回该会话对象;如果没有关联会话,则会创建一个新的会话对象并返回。 + *

+ * HttpSession 用于存储会话级别的数据,如用户登录信息、购物车内容等, + * 可以在多个请求之间共享会话数据 + * + * @return 当前请求的 HttpSession 对象 + */ + public static HttpSession getSession() { + return getRequest().getSession(); + } + + /** + * 获取当前请求的请求属性 + * + * @return {@link ServletRequestAttributes} 请求属性对象 + */ + public static ServletRequestAttributes getRequestAttributes() { + try { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } catch (Exception e) { + return null; + } + } + + /** + * 获取指定请求头的值,如果头部为空则返回空字符串 + * + * @param request 请求对象 + * @param name 头部名称 + * @return 头部值 + */ + public static String getHeader(HttpServletRequest request, String name) { + String value = request.getHeader(name); + if (StringUtils.isEmpty(value)) { + return StringUtils.EMPTY; + } + return urlDecode(value); + } + + /** + * 获取所有请求头的 Map,键为头部名称,值为头部值 + * + * @param request 请求对象 + * @return 请求头的 Map + */ + public static Map getHeaders(HttpServletRequest request) { + Map map = new LinkedCaseInsensitiveMap<>(); + Enumeration enumeration = request.getHeaderNames(); + if (enumeration != null) { + while (enumeration.hasMoreElements()) { + String key = enumeration.nextElement(); + String value = request.getHeader(key); + map.put(key, value); + } + } + return map; + } + + /** + * 将字符串渲染到客户端(以 JSON 格式返回) + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void renderString(HttpServletResponse response, String string) { + try { + response.setStatus(HttpStatus.HTTP_OK); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding(StandardCharsets.UTF_8.toString()); + response.getWriter().print(string); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 判断当前请求是否为 Ajax 异步请求 + * + * @param request 请求对象 + * @return 是否为 Ajax 请求 + */ + public static boolean isAjaxRequest(HttpServletRequest request) { + + // 判断 Accept 头部是否包含 application/json + String accept = request.getHeader("accept"); + if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) { + return true; + } + + // 判断 X-Requested-With 头部是否包含 XMLHttpRequest + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) { + return true; + } + + // 判断 URI 后缀是否为 .json 或 .xml + String uri = request.getRequestURI(); + if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) { + return true; + } + + // 判断请求参数 __ajax 是否为 json 或 xml + String ajax = request.getParameter("__ajax"); + return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml"); + } + + /** + * 获取客户端 IP 地址 + * + * @return 客户端 IP 地址 + */ + public static String getClientIP() { + return getClientIP(getRequest()); + } + + /** + * 对内容进行 URL 编码 + * + * @param str 内容 + * @return 编码后的内容 + */ + public static String urlEncode(String str) { + return URLEncoder.encode(str, StandardCharsets.UTF_8); + } + + /** + * 对内容进行 URL 解码 + * + * @param str 内容 + * @return 解码后的内容 + */ + public static String urlDecode(String str) { + return URLDecoder.decode(str, StandardCharsets.UTF_8); + } + +} diff --git a/src/main/java/org/dromara/common/core/utils/SpringUtils.java b/src/main/java/org/dromara/common/core/utils/SpringUtils.java new file mode 100644 index 0000000..8ff9653 --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/SpringUtils.java @@ -0,0 +1,67 @@ +package org.dromara.common.core.utils; + +import cn.hutool.extra.spring.SpringUtil; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.autoconfigure.thread.Threading; +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +/** + * spring工具类 + * + * @author shihongwei + */ +@Component +public final class SpringUtils extends SpringUtil { + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + */ + public static boolean containsBean(String name) { + return getBeanFactory().containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 + * 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().isSingleton(name); + } + + /** + * @return Class 注册对象的类型 + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { + return getBeanFactory().getAliases(name); + } + + /** + * 获取aop代理对象 + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) { + return (T) getBean(invoker.getClass()); + } + + + /** + * 获取spring上下文 + */ + public static ApplicationContext context() { + return getApplicationContext(); + } + + public static boolean isVirtual() { + return Threading.VIRTUAL.isActive(getBean(Environment.class)); + } + +} diff --git a/src/main/java/org/dromara/common/core/utils/StreamUtils.java b/src/main/java/org/dromara/common/core/utils/StreamUtils.java new file mode 100644 index 0000000..d183912 --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/StreamUtils.java @@ -0,0 +1,328 @@ +package org.dromara.common.core.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * stream 流工具类 + * + * @author shihongwei + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class StreamUtils { + + /** + * 将collection过滤 + * + * @param collection 需要转化的集合 + * @param function 过滤方法 + * @return 过滤后的list + */ + public static List filter(Collection collection, Predicate function) { + if (CollUtil.isEmpty(collection)) { + return CollUtil.newArrayList(); + } + return collection.stream() + .filter(function) + // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题 + .collect(Collectors.toList()); + } + + /** + * 找到流中满足条件的第一个元素 + * + * @param collection 需要查询的集合 + * @param function 过滤方法 + * @return 找到符合条件的第一个元素,没有则返回 Optional.empty() + */ + public static Optional findFirst(Collection collection, Predicate function) { + if (CollUtil.isEmpty(collection)) { + return Optional.empty(); + } + return collection.stream() + .filter(function) + .findFirst(); + } + + /** + * 找到流中满足条件的第一个元素值 + * + * @param collection 需要查询的集合 + * @param function 过滤方法 + * @return 找到符合条件的第一个元素,没有则返回 null + */ + public static E findFirstValue(Collection collection, Predicate function) { + return findFirst(collection,function).orElse(null); + } + + /** + * 找到流中任意一个满足条件的元素 + * + * @param collection 需要查询的集合 + * @param function 过滤方法 + * @return 找到符合条件的任意一个元素,没有则返回 Optional.empty() + */ + public static Optional findAny(Collection collection, Predicate function) { + if (CollUtil.isEmpty(collection)) { + return Optional.empty(); + } + return collection.stream() + .filter(function) + .findAny(); + } + + /** + * 找到流中任意一个满足条件的元素值 + * + * @param collection 需要查询的集合 + * @param function 过滤方法 + * @return 找到符合条件的任意一个元素,没有则返回null + */ + public static E findAnyValue(Collection collection, Predicate function) { + return findAny(collection,function).orElse(null); + } + + /** + * 将collection拼接 + * + * @param collection 需要转化的集合 + * @param function 拼接方法 + * @return 拼接后的list + */ + public static String join(Collection collection, Function function) { + return join(collection, function, StringUtils.SEPARATOR); + } + + /** + * 将collection拼接 + * + * @param collection 需要转化的集合 + * @param function 拼接方法 + * @param delimiter 拼接符 + * @return 拼接后的list + */ + public static String join(Collection collection, Function function, CharSequence delimiter) { + if (CollUtil.isEmpty(collection)) { + return StringUtils.EMPTY; + } + return collection.stream() + .map(function) + .filter(Objects::nonNull) + .collect(Collectors.joining(delimiter)); + } + + /** + * 将collection排序 + * + * @param collection 需要转化的集合 + * @param comparing 排序方法 + * @return 排序后的list + */ + public static List sorted(Collection collection, Comparator comparing) { + if (CollUtil.isEmpty(collection)) { + return CollUtil.newArrayList(); + } + return collection.stream() + .filter(Objects::nonNull) + .sorted(comparing) + // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题 + .collect(Collectors.toList()); + } + + /** + * 将collection转化为类型不变的map
+ * {@code Collection ----> Map} + * + * @param collection 需要转化的集合 + * @param key V类型转化为K类型的lambda方法 + * @param collection中的泛型 + * @param map中的key类型 + * @return 转化后的map + */ + public static Map toIdentityMap(Collection collection, Function key) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection.stream() + .filter(Objects::nonNull) + .collect(Collectors.toMap(key, Function.identity(), (l, r) -> l)); + } + + /** + * 将Collection转化为map(value类型与collection的泛型不同)
+ * {@code Collection -----> Map } + * + * @param collection 需要转化的集合 + * @param key E类型转化为K类型的lambda方法 + * @param value E类型转化为V类型的lambda方法 + * @param collection中的泛型 + * @param map中的key类型 + * @param map中的value类型 + * @return 转化后的map + */ + public static Map toMap(Collection collection, Function key, Function value) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection.stream() + .filter(Objects::nonNull) + .collect(Collectors.toMap(key, value, (l, r) -> l)); + } + + /** + * 获取 map 中的数据作为新 Map 的 value ,key 不变 + * @param map 需要处理的map + * @param take 取值函数 + * @param map中的key类型 + * @param map中的value类型 + * @param 新map中的value类型 + * @return 新的map + */ + public static Map toMap(Map map, BiFunction take) { + if (CollUtil.isEmpty(map)) { + return MapUtil.newHashMap(); + } + return toMap(map.entrySet(), Map.Entry::getKey, entry -> take.apply(entry.getKey(), entry.getValue())); + } + + /** + * 将collection按照规则(比如有相同的班级id)分类成map
+ * {@code Collection -------> Map> } + * + * @param collection 需要分类的集合 + * @param key 分类的规则 + * @param collection中的泛型 + * @param map中的key类型 + * @return 分类后的map + */ + public static Map> groupByKey(Collection collection, Function key) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection.stream() + .filter(Objects::nonNull) + .collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList())); + } + + /** + * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
+ * {@code Collection ---> Map>> } + * + * @param collection 需要分类的集合 + * @param key1 第一个分类的规则 + * @param key2 第二个分类的规则 + * @param 集合元素类型 + * @param 第一个map中的key类型 + * @param 第二个map中的key类型 + * @return 分类后的map + */ + public static Map>> groupBy2Key(Collection collection, Function key1, Function key2) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection.stream() + .filter(Objects::nonNull) + .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2, LinkedHashMap::new, Collectors.toList()))); + } + + /** + * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
+ * {@code Collection ---> Map> } + * + * @param collection 需要分类的集合 + * @param key1 第一个分类的规则 + * @param key2 第二个分类的规则 + * @param 第一个map中的key类型 + * @param 第二个map中的key类型 + * @param collection中的泛型 + * @return 分类后的map + */ + public static Map> group2Map(Collection collection, Function key1, Function key2) { + if (CollUtil.isEmpty(collection)) { + return MapUtil.newHashMap(); + } + return collection.stream() + .filter(Objects::nonNull) + .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2, Function.identity(), (l, r) -> l))); + } + + /** + * 将collection转化为List集合,但是两者的泛型不同
+ * {@code Collection ------> List } + * + * @param collection 需要转化的集合 + * @param function collection中的泛型转化为list泛型的lambda表达式 + * @param collection中的泛型 + * @param List中的泛型 + * @return 转化后的list + */ + public static List toList(Collection collection, Function function) { + if (CollUtil.isEmpty(collection)) { + return CollUtil.newArrayList(); + } + return collection.stream() + .map(function) + .filter(Objects::nonNull) + // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题 + .collect(Collectors.toList()); + } + + /** + * 将collection转化为Set集合,但是两者的泛型不同
+ * {@code Collection ------> Set } + * + * @param collection 需要转化的集合 + * @param function collection中的泛型转化为set泛型的lambda表达式 + * @param collection中的泛型 + * @param Set中的泛型 + * @return 转化后的Set + */ + public static Set toSet(Collection collection, Function function) { + if (CollUtil.isEmpty(collection)) { + return CollUtil.newHashSet(); + } + return collection.stream() + .map(function) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + + + /** + * 合并两个相同key类型的map + * + * @param map1 第一个需要合并的 map + * @param map2 第二个需要合并的 map + * @param merge 合并的lambda,将key value1 value2合并成最终的类型,注意value可能为空的情况 + * @param map中的key类型 + * @param 第一个 map的value类型 + * @param 第二个 map的value类型 + * @param 最终map的value类型 + * @return 合并后的map + */ + public static Map merge(Map map1, Map map2, BiFunction merge) { + if (CollUtil.isEmpty(map1) && CollUtil.isEmpty(map2)) { + // 如果两个 map 都为空,则直接返回空的 map + return MapUtil.newHashMap(); + } else if (CollUtil.isEmpty(map1)) { + // 如果 map1 为空,则直接处理返回 map2 + return toMap(map2.entrySet(), Map.Entry::getKey, entry -> merge.apply(null, entry.getValue())); + } else if (CollUtil.isEmpty(map2)) { + // 如果 map2 为空,则直接处理返回 map1 + return toMap(map1.entrySet(), Map.Entry::getKey, entry -> merge.apply(entry.getValue(), null)); + } + Set keySet = new HashSet<>(); + keySet.addAll(map1.keySet()); + keySet.addAll(map2.keySet()); + return toMap(keySet, key -> key, key -> merge.apply(map1.get(key), map2.get(key))); + } + +} diff --git a/src/main/java/org/dromara/common/core/utils/StringUtils.java b/src/main/java/org/dromara/common/core/utils/StringUtils.java new file mode 100644 index 0000000..18ee42a --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/StringUtils.java @@ -0,0 +1,384 @@ +package org.dromara.common.core.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.StrUtil; +import org.springframework.util.AntPathMatcher; + +import java.nio.charset.Charset; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 字符串工具类 + * + * @author shihongwei + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils { + + public static final String SEPARATOR = ","; + + public static final String SLASH = "/"; + + @Deprecated + private StringUtils() { + } + + /** + * 获取参数不为空值 + * + * @param str defaultValue 要判断的value + * @return value 返回值 + */ + public static String blankToDefault(String str, String defaultValue) { + return StrUtil.blankToDefault(str, defaultValue); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) { + return StrUtil.isEmpty(str); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) { + return !isEmpty(str); + } + + /** + * 去空格 + */ + public static String trim(String str) { + return StrUtil.trim(str); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) { + return substring(str, start, str.length()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) { + return StrUtil.sub(str, start, end); + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is {} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) { + return StrUtil.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * @return 结果 + */ + public static boolean ishttp(String link) { + return Validator.isUrl(link); + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static Set str2Set(String str, String sep) { + return new HashSet<>(str2List(str, sep, true, false)); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @param filterBlank 过滤纯空白 + * @param trim 去掉首尾空白 + * @return list集合 + */ + public static List str2List(String str, String sep, boolean filterBlank, boolean trim) { + List list = new ArrayList<>(); + if (isEmpty(str)) { + return list; + } + + // 过滤空白字符串 + if (filterBlank && isBlank(str)) { + return list; + } + String[] split = str.split(sep); + for (String string : split) { + if (filterBlank && isBlank(string)) { + continue; + } + if (trim) { + string = trim(string); + } + list.add(string); + } + + return list; + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 + * + * @param cs 指定字符串 + * @param searchCharSequences 需要检查的字符串数组 + * @return 是否包含任意一个字符串 + */ + public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) { + return StrUtil.containsAnyIgnoreCase(cs, searchCharSequences); + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) { + return StrUtil.toUnderlineCase(str); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) { + return StrUtil.equalsAnyIgnoreCase(str, strs); + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) { + return StrUtil.upperFirst(StrUtil.toCamelCase(name)); + } + + /** + * 驼峰式命名法 例如:user_name->userName + */ + public static String toCamelCase(String s) { + return StrUtil.toCamelCase(s); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List strs) { + if (isEmpty(str) || CollUtil.isEmpty(strs)) { + return false; + } + for (String pattern : strs) { + if (isMatch(pattern, str)) { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + */ + public static boolean isMatch(String pattern, String url) { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static String padl(final Number num, final int size) { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static String padl(final String s, final int size, final char c) { + final StringBuilder sb = new StringBuilder(size); + if (s != null) { + final int len = s.length(); + if (s.length() <= size) { + sb.append(Convert.toStr(c).repeat(size - len)); + sb.append(s); + } else { + return s.substring(len - size, len); + } + } else { + sb.append(Convert.toStr(c).repeat(Math.max(0, size))); + } + return sb.toString(); + } + + /** + * 切分字符串(分隔符默认逗号) + * + * @param str 被切分的字符串 + * @return 分割后的数据列表 + */ + public static List splitList(String str) { + return splitTo(str, Convert::toStr); + } + + /** + * 切分字符串 + * + * @param str 被切分的字符串 + * @param separator 分隔符 + * @return 分割后的数据列表 + */ + public static List splitList(String str, String separator) { + return splitTo(str, separator, Convert::toStr); + } + + /** + * 切分字符串自定义转换(分隔符默认逗号) + * + * @param str 被切分的字符串 + * @param mapper 自定义转换 + * @return 分割后的数据列表 + */ + public static List splitTo(String str, Function mapper) { + return splitTo(str, SEPARATOR, mapper); + } + + /** + * 切分字符串自定义转换 + * + * @param str 被切分的字符串 + * @param separator 分隔符 + * @param mapper 自定义转换 + * @return 分割后的数据列表 + */ + public static List splitTo(String str, String separator, Function mapper) { + if (isBlank(str)) { + return new ArrayList<>(0); + } + return StrUtil.split(str, separator) + .stream() + .filter(Objects::nonNull) + .map(mapper) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + /** + * 不区分大小写检查 CharSequence 是否以指定的前缀开头。 + * + * @param str 要检查的 CharSequence 可能为 null + * @param prefixs 要查找的前缀可能为 null + * @return 是否包含 + */ + public static boolean startWithAnyIgnoreCase(CharSequence str, CharSequence... prefixs) { + // 判断是否是以指定字符串开头 + for (CharSequence prefix : prefixs) { + if (StringUtils.startsWithIgnoreCase(str, prefix)) { + return true; + } + } + return false; + } + + /** + * 将字符串从源字符集转换为目标字符集 + * + * @param input 原始字符串 + * @param fromCharset 源字符集 + * @param toCharset 目标字符集 + * @return 转换后的字符串 + */ + public static String convert(String input, Charset fromCharset, Charset toCharset) { + if (isBlank(input)) { + return input; + } + try { + // 从源字符集获取字节 + byte[] bytes = input.getBytes(fromCharset); + // 使用目标字符集解码 + return new String(bytes, toCharset); + } catch (Exception e) { + return input; + } + } + /** + * 将可迭代对象中的元素使用逗号拼接成字符串 + * + * @param iterable 可迭代对象,如 List、Set 等 + * @return 拼接后的字符串 + */ + public static String joinComma(Iterable iterable) { + return StringUtils.join(iterable, SEPARATOR); + } + + /** + * 将数组中的元素使用逗号拼接成字符串 + * + * @param array 任意类型的数组 + * @return 拼接后的字符串 + */ + public static String joinComma(Object[] array) { + return StringUtils.join(array, SEPARATOR); + } + +} diff --git a/src/main/java/org/dromara/common/core/utils/TreeBuildUtils.java b/src/main/java/org/dromara/common/core/utils/TreeBuildUtils.java new file mode 100644 index 0000000..62a53d9 --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/TreeBuildUtils.java @@ -0,0 +1,123 @@ +package org.dromara.common.core.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.tree.Tree; +import cn.hutool.core.lang.tree.TreeNodeConfig; +import cn.hutool.core.lang.tree.TreeUtil; +import cn.hutool.core.lang.tree.parser.NodeParser; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.reflect.ReflectUtils; + +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 扩展 hutool TreeUtil 封装系统树构建 + * + * @author shihongwei + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class TreeBuildUtils extends TreeUtil { + + /** + * 根据前端定制差异化字段 + */ + public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label"); + + /** + * 构建树形结构 + * + * @param 输入节点的类型 + * @param 节点ID的类型 + * @param list 节点列表,其中包含了要构建树形结构的所有节点 + * @param nodeParser 解析器,用于将输入节点转换为树节点 + * @return 构建好的树形结构列表 + */ + public static List> build(List list, NodeParser nodeParser) { + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + K k = ReflectUtils.invokeGetter(list.get(0), "parentId"); + return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser); + } + + /** + * 构建树形结构 + * + * @param 输入节点的类型 + * @param 节点ID的类型 + * @param parentId 顶级节点 + * @param list 节点列表,其中包含了要构建树形结构的所有节点 + * @param nodeParser 解析器,用于将输入节点转换为树节点 + * @return 构建好的树形结构列表 + */ + public static List> build(List list, K parentId, NodeParser nodeParser) { + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return TreeUtil.build(list, parentId, DEFAULT_CONFIG, nodeParser); + } + + /** + * 构建多根节点的树结构(支持多个顶级节点) + * + * @param list 原始数据列表 + * @param getId 获取节点 ID 的方法引用,例如:node -> node.getId() + * @param getParentId 获取节点父级 ID 的方法引用,例如:node -> node.getParentId() + * @param parser 树节点属性映射器,用于将原始节点 T 转为 Tree 节点 + * @param 原始数据类型(如实体类、DTO 等) + * @param 节点 ID 类型(如 Long、String) + * @return 构建完成的树形结构(可能包含多个顶级根节点) + */ + public static List> buildMultiRoot(List list, Function getId, Function getParentId, NodeParser parser) { + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + + Set rootParentIds = StreamUtils.toSet(list, getParentId); + rootParentIds.removeAll(StreamUtils.toSet(list, getId)); + + // 构建每一个根 parentId 下的树,并合并成最终结果列表 + return rootParentIds.stream() + .flatMap(rootParentId -> TreeUtil.build(list, rootParentId, parser).stream()) + .collect(Collectors.toList()); + } + + /** + * 获取节点列表中所有节点的叶子节点 + * + * @param 节点ID的类型 + * @param nodes 节点列表 + * @return 包含所有叶子节点的列表 + */ + public static List> getLeafNodes(List> nodes) { + if (CollUtil.isEmpty(nodes)) { + return CollUtil.newArrayList(); + } + return nodes.stream() + .flatMap(TreeBuildUtils::extractLeafNodes) + .collect(Collectors.toList()); + } + + /** + * 获取指定节点下的所有叶子节点 + * + * @param 节点ID的类型 + * @param node 要查找叶子节点的根节点 + * @return 包含所有叶子节点的列表 + */ + private static Stream> extractLeafNodes(Tree node) { + if (!node.hasChild()) { + return Stream.of(node); + } else { + // 递归调用,获取所有子节点的叶子节点 + return node.getChildren().stream() + .flatMap(TreeBuildUtils::extractLeafNodes); + } + } + +} diff --git a/src/main/java/org/dromara/common/core/utils/ValidatorUtils.java b/src/main/java/org/dromara/common/core/utils/ValidatorUtils.java new file mode 100644 index 0000000..c4b5673 --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/ValidatorUtils.java @@ -0,0 +1,35 @@ +package org.dromara.common.core.utils; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.Validator; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.Set; + +/** + * Validator 校验框架工具 + * + * @author shihongwei + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ValidatorUtils { + + private static final Validator VALID = SpringUtils.getBean(Validator.class); + + /** + * 对给定对象进行参数校验,并根据指定的校验组进行校验 + * + * @param object 要进行校验的对象 + * @param groups 校验组 + * @throws ConstraintViolationException 如果校验不通过,则抛出参数校验异常 + */ + public static void validate(T object, Class... groups) { + Set> validate = VALID.validate(object, groups); + if (!validate.isEmpty()) { + throw new ConstraintViolationException("参数校验异常", validate); + } + } + +} diff --git a/src/main/java/org/dromara/common/core/utils/file/FileUtils.java b/src/main/java/org/dromara/common/core/utils/file/FileUtils.java new file mode 100644 index 0000000..495f5be --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/file/FileUtils.java @@ -0,0 +1,43 @@ +package org.dromara.common.core.utils.file; + +import cn.hutool.core.io.FileUtil; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * 文件处理工具类 + * + * @author shihongwei + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class FileUtils extends FileUtil { + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) { + String percentEncodedFileName = percentEncode(realFileName); + String contentDispositionValue = "attachment; filename=%s;filename*=utf-8''%s".formatted(percentEncodedFileName, percentEncodedFileName); + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); + response.setHeader("Content-disposition", contentDispositionValue); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8); + return encode.replaceAll("\\+", "%20"); + } +} diff --git a/src/main/java/org/dromara/common/core/utils/file/MimeTypeUtils.java b/src/main/java/org/dromara/common/core/utils/file/MimeTypeUtils.java new file mode 100644 index 0000000..129a58d --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/file/MimeTypeUtils.java @@ -0,0 +1,40 @@ +package org.dromara.common.core.utils.file; + +/** + * 媒体类型工具类 + * + * @author shihongwei + */ +public class MimeTypeUtils { + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"}; + + public static final String[] FLASH_EXTENSION = {"swf", "flv"}; + + public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb"}; + + public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"}; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf"}; + +} diff --git a/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java b/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java new file mode 100644 index 0000000..2157a35 --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java @@ -0,0 +1,70 @@ +package org.dromara.common.core.utils.ip; + +import cn.hutool.http.HtmlUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.NetUtils; +import org.dromara.common.core.utils.StringUtils; + +/** + * 获取地址类 + * + * @author shihongwei + */ +@Slf4j +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class AddressUtils { + + // 未知IP + public static final String UNKNOWN_IP = "XX XX"; + // 内网地址 + public static final String LOCAL_ADDRESS = "内网IP"; + // 未知地址 + public static final String UNKNOWN_ADDRESS = "未知"; + + public static String getRealAddressByIP(String ip) { + // 处理空串并过滤HTML标签 + ip = HtmlUtil.cleanHtmlTag(StringUtils.blankToDefault(ip,"")); + // 判断是否为IPv4 + if (NetUtils.isIPv4(ip)) { + return resolverIPv4Region(ip); + } + // 判断是否为IPv6 + if (NetUtils.isIPv6(ip)) { + return resolverIPv6Region(ip); + } + // 如果不是IPv4或IPv6,则返回未知IP + return UNKNOWN_IP; + } + + /** + * 根据IPv4地址查询IP归属行政区域 + * @param ip ipv4地址 + * @return 归属行政区域 + */ + private static String resolverIPv4Region(String ip){ + // 内网不查询 + if (NetUtils.isInnerIP(ip)) { + return LOCAL_ADDRESS; + } + return RegionUtils.getCityInfo(ip); + } + + /** + * 根据IPv6地址查询IP归属行政区域 + * @param ip ipv6地址 + * @return 归属行政区域 + */ + private static String resolverIPv6Region(String ip){ + // 内网不查询 + if (NetUtils.isInnerIPv6(ip)) { + return LOCAL_ADDRESS; + } + log.warn("ip2region不支持IPV6地址解析:{}", ip); + // 不支持IPv6,不再进行没有必要的IP地址信息的解析,直接返回 + // 如有需要,可自行实现IPv6地址信息解析逻辑,并在这里返回 + return UNKNOWN_ADDRESS; + } + +} diff --git a/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java b/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java new file mode 100644 index 0000000..5011dc6 --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/ip/RegionUtils.java @@ -0,0 +1,51 @@ +package org.dromara.common.core.utils.ip; + +import cn.hutool.core.io.resource.NoResourceException; +import cn.hutool.core.io.resource.ResourceUtil; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; +import org.lionsoul.ip2region.xdb.Searcher; + +/** + * 根据ip地址定位工具类,离线方式 + * 参考地址:集成 ip2region 实现离线IP地址定位库 + * + * @author shihongwei + */ +@Slf4j +public class RegionUtils { + + // IP地址库文件名称 + public static final String IP_XDB_FILENAME = "ip2region.xdb"; + + private static final Searcher SEARCHER; + + static { + try { + // 1、将 ip2region 数据库文件 xdb 从 ClassPath 加载到内存。 + // 2、基于加载到内存的 xdb 数据创建一个 Searcher 查询对象。 + SEARCHER = Searcher.newWithBuffer(ResourceUtil.readBytes(IP_XDB_FILENAME)); + log.info("RegionUtils初始化成功,加载IP地址库数据成功!"); + } catch (NoResourceException e) { + throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!"); + } catch (Exception e) { + throw new ServiceException("RegionUtils初始化失败,原因:" + e.getMessage()); + } + } + + /** + * 根据IP地址离线获取城市 + */ + public static String getCityInfo(String ip) { + try { + // 3、执行查询 + String region = SEARCHER.search(StringUtils.trim(ip)); + return region.replace("0|", "").replace("|0", ""); + } catch (Exception e) { + log.error("IP地址离线获取城市异常 {}", ip); + return "未知"; + } + } + +} diff --git a/src/main/java/org/dromara/common/core/utils/reflect/ReflectUtils.java b/src/main/java/org/dromara/common/core/utils/reflect/ReflectUtils.java new file mode 100644 index 0000000..ff4d4ea --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/reflect/ReflectUtils.java @@ -0,0 +1,56 @@ +package org.dromara.common.core.utils.reflect; + +import cn.hutool.core.util.ReflectUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.StringUtils; + +import java.lang.reflect.Method; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author shihongwei + */ +@SuppressWarnings("rawtypes") +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ReflectUtils extends ReflectUtil { + + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter(Object obj, String propertyName) { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invoke(object, getterMethodName); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, E value) { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i = 0; i < names.length; i++) { + if (i < names.length - 1) { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); + object = invoke(object, getterMethodName); + } else { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + Method method = getMethodByName(object.getClass(), setterMethodName); + invoke(object, method, value); + } + } + } + +} diff --git a/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java b/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java new file mode 100644 index 0000000..cada2c9 --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java @@ -0,0 +1,31 @@ +package org.dromara.common.core.utils.regex; + + +import cn.hutool.core.util.ReUtil; +import org.dromara.common.core.constant.RegexConstants; + +/** + * 正则相关工具类 + * + * @author shihongwei + */ +public final class RegexUtils extends ReUtil { + + /** + * 从输入字符串中提取匹配的部分,如果没有匹配则返回默认值 + * + * @param input 要提取的输入字符串 + * @param regex 用于匹配的正则表达式,可以使用 {@link RegexConstants} 中定义的常量 + * @param defaultInput 如果没有匹配时返回的默认值 + * @return 如果找到匹配的部分,则返回匹配的部分,否则返回默认值 + */ + public static String extractFromString(String input, String regex, String defaultInput) { + try { + String str = ReUtil.get(regex, input, 1); + return str == null ? defaultInput : str; + } catch (Exception e) { + return defaultInput; + } + } + +} diff --git a/src/main/java/org/dromara/common/core/utils/regex/RegexValidator.java b/src/main/java/org/dromara/common/core/utils/regex/RegexValidator.java new file mode 100644 index 0000000..04d3cdf --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/regex/RegexValidator.java @@ -0,0 +1,105 @@ +package org.dromara.common.core.utils.regex; + +import cn.hutool.core.exceptions.ValidateException; +import cn.hutool.core.lang.Validator; +import org.dromara.common.core.factory.RegexPatternPoolFactory; + +import java.util.regex.Pattern; + +/** + * 正则字段校验器 + * 主要验证字段非空、是否为满足指定格式等 + * + * @author shihongwei + */ +public class RegexValidator extends Validator { + + /** + * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) + */ + public static final Pattern DICTIONARY_TYPE = RegexPatternPoolFactory.DICTIONARY_TYPE; + + /** + * 身份证号码(后6位) + */ + public static final Pattern ID_CARD_LAST_6 = RegexPatternPoolFactory.ID_CARD_LAST_6; + + /** + * QQ号码 + */ + public static final Pattern QQ_NUMBER = RegexPatternPoolFactory.QQ_NUMBER; + + /** + * 邮政编码 + */ + public static final Pattern POSTAL_CODE = RegexPatternPoolFactory.POSTAL_CODE; + + /** + * 注册账号 + */ + public static final Pattern ACCOUNT = RegexPatternPoolFactory.ACCOUNT; + + /** + * 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符 + */ + public static final Pattern PASSWORD = RegexPatternPoolFactory.PASSWORD; + + /** + * 通用状态(0表示正常,1表示停用) + */ + public static final Pattern STATUS = RegexPatternPoolFactory.STATUS; + + + /** + * 检查输入的账号是否匹配预定义的规则 + * + * @param value 要验证的账号 + * @return 如果账号符合规则,返回 true;否则,返回 false。 + */ + public static boolean isAccount(CharSequence value) { + return isMatchRegex(ACCOUNT, value); + } + + /** + * 验证输入的账号是否符合规则,如果不符合,则抛出 ValidateException 异常 + * + * @param value 要验证的账号 + * @param errorMsg 验证失败时抛出的异常消息 + * @param CharSequence 的子类型 + * @return 如果验证通过,返回输入的账号 + * @throws ValidateException 如果验证失败 + */ + public static T validateAccount(T value, String errorMsg) throws ValidateException { + if (!isAccount(value)) { + throw new ValidateException(errorMsg); + } + return value; + } + + /** + * 检查输入的状态是否匹配预定义的规则 + * + * @param value 要验证的状态 + * @return 如果状态符合规则,返回 true;否则,返回 false。 + */ + public static boolean isStatus(CharSequence value) { + return isMatchRegex(STATUS, value); + } + + /** + * 验证输入的状态是否符合规则,如果不符合,则抛出 ValidateException 异常 + * + * @param value 要验证的状态 + * @param errorMsg 验证失败时抛出的异常消息 + * @param CharSequence 的子类型 + * @return 如果验证通过,返回输入的状态 + * @throws ValidateException 如果验证失败 + */ + public static T validateStatus(T value, String errorMsg) throws ValidateException { + if (!isStatus(value)) { + throw new ValidateException(errorMsg); + } + return value; + } + +} diff --git a/src/main/java/org/dromara/common/core/utils/sql/SqlUtil.java b/src/main/java/org/dromara/common/core/utils/sql/SqlUtil.java new file mode 100644 index 0000000..68386d8 --- /dev/null +++ b/src/main/java/org/dromara/common/core/utils/sql/SqlUtil.java @@ -0,0 +1,56 @@ +package org.dromara.common.core.utils.sql; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.StringUtils; + +/** + * sql操作工具类 + * + * @author shihongwei + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class SqlUtil { + + /** + * 定义常用的 sql关键字 + */ + public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()"; + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static final String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) { + throw new IllegalArgumentException("参数不符合规范,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) { + return value.matches(SQL_PATTERN); + } + + /** + * SQL关键字检查 + */ + public static void filterKeyword(String value) { + if (StringUtils.isEmpty(value)) { + return; + } + String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); + for (String sqlKeyword : sqlKeywords) { + if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) { + throw new IllegalArgumentException("参数存在SQL注入风险"); + } + } + } +} diff --git a/src/main/java/org/dromara/common/core/validate/AddGroup.java b/src/main/java/org/dromara/common/core/validate/AddGroup.java new file mode 100644 index 0000000..e6261c5 --- /dev/null +++ b/src/main/java/org/dromara/common/core/validate/AddGroup.java @@ -0,0 +1,9 @@ +package org.dromara.common.core.validate; + +/** + * 校验分组 add + * + * @author shihongwei + */ +public interface AddGroup { +} diff --git a/src/main/java/org/dromara/common/core/validate/EditGroup.java b/src/main/java/org/dromara/common/core/validate/EditGroup.java new file mode 100644 index 0000000..be1bbf2 --- /dev/null +++ b/src/main/java/org/dromara/common/core/validate/EditGroup.java @@ -0,0 +1,9 @@ +package org.dromara.common.core.validate; + +/** + * 校验分组 edit + * + * @author shihongwei + */ +public interface EditGroup { +} diff --git a/src/main/java/org/dromara/common/core/validate/QueryGroup.java b/src/main/java/org/dromara/common/core/validate/QueryGroup.java new file mode 100644 index 0000000..4578a5e --- /dev/null +++ b/src/main/java/org/dromara/common/core/validate/QueryGroup.java @@ -0,0 +1,9 @@ +package org.dromara.common.core.validate; + +/** + * 校验分组 query + * + * @author shihongwei + */ +public interface QueryGroup { +} diff --git a/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java b/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java new file mode 100644 index 0000000..a9717d4 --- /dev/null +++ b/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java @@ -0,0 +1,40 @@ +package org.dromara.common.core.validate.dicts; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 字典项校验注解 + * + * @author shihongwei + */ +@Constraint(validatedBy = DictPatternValidator.class) +@Target({ElementType.FIELD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface DictPattern { + + /** + * 字典类型,如 "sys_user_sex" + */ + String dictType(); + + /** + * 分隔符 + */ + String separator(); + + /** + * 默认校验失败提示信息 + */ + String message() default "字典值无效"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} diff --git a/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java b/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java new file mode 100644 index 0000000..c22eb58 --- /dev/null +++ b/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java @@ -0,0 +1,55 @@ +package org.dromara.common.core.validate.dicts; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import org.dromara.common.core.service.DictService; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; + +/** + * 自定义字典值校验器 + * + * @author shihongwei + */ +public class DictPatternValidator implements ConstraintValidator { + + /** + * 字典类型 + */ + private String dictType; + + /** + * 分隔符 + */ + private String separator = ","; + + /** + * 初始化校验器,提取注解上的字典类型 + * + * @param annotation 注解实例 + */ + @Override + public void initialize(DictPattern annotation) { + this.dictType = annotation.dictType(); + if (StringUtils.isNotBlank(annotation.separator())) { + this.separator = annotation.separator(); + } + } + + /** + * 校验字段值是否为指定字典类型中的合法值 + * + * @param value 被校验的字段值 + * @param context 校验上下文(可用于构建错误信息) + * @return true 表示校验通过(合法字典值),false 表示不通过 + */ + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + if (StringUtils.isBlank(dictType) || StringUtils.isBlank(value)) { + return false; + } + String dictLabel = SpringUtils.getBean(DictService.class).getDictLabel(dictType, value, separator); + return StringUtils.isNotBlank(dictLabel); + } + +} diff --git a/src/main/java/org/dromara/common/core/validate/enumd/EnumPattern.java b/src/main/java/org/dromara/common/core/validate/enumd/EnumPattern.java new file mode 100644 index 0000000..ed04a33 --- /dev/null +++ b/src/main/java/org/dromara/common/core/validate/enumd/EnumPattern.java @@ -0,0 +1,51 @@ +package org.dromara.common.core.validate.enumd; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.Documented; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 自定义枚举校验 + * + * @author shihongwei + * @date 2024-12-09 + */ +@Documented +@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) +@Retention(RUNTIME) +@Repeatable(EnumPattern.List.class) // 允许在同一元素上多次使用该注解 +@Constraint(validatedBy = {EnumPatternValidator.class}) +public @interface EnumPattern { + + /** + * 需要校验的枚举类型 + */ + Class> type(); + + /** + * 枚举类型校验值字段名称 + * 需确保该字段实现了 getter 方法 + */ + String fieldName(); + + String message() default "输入值不在枚举范围内"; + + Class[] groups() default {}; + + Class[] payload() default {}; + + @Documented + @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) + @Retention(RUNTIME) + @interface List { + EnumPattern[] value(); + } + +} diff --git a/src/main/java/org/dromara/common/core/validate/enumd/EnumPatternValidator.java b/src/main/java/org/dromara/common/core/validate/enumd/EnumPatternValidator.java new file mode 100644 index 0000000..9aa4205 --- /dev/null +++ b/src/main/java/org/dromara/common/core/validate/enumd/EnumPatternValidator.java @@ -0,0 +1,37 @@ +package org.dromara.common.core.validate.enumd; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.reflect.ReflectUtils; + +/** + * 自定义枚举校验注解实现 + * + * @author shihongwei + * @date 2024-12-09 + */ +public class EnumPatternValidator implements ConstraintValidator { + + private EnumPattern annotation; + + @Override + public void initialize(EnumPattern annotation) { + ConstraintValidator.super.initialize(annotation); + this.annotation = annotation; + } + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + if (StringUtils.isNotBlank(value)) { + String fieldName = annotation.fieldName(); + for (Object e : annotation.type().getEnumConstants()) { + if (value.equals(ReflectUtils.invokeGetter(e, fieldName))) { + return true; + } + } + } + return false; + } + +} diff --git a/src/main/java/org/dromara/common/core/xss/Xss.java b/src/main/java/org/dromara/common/core/xss/Xss.java new file mode 100644 index 0000000..8e4fcb0 --- /dev/null +++ b/src/main/java/org/dromara/common/core/xss/Xss.java @@ -0,0 +1,27 @@ +package org.dromara.common.core.xss; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author shihongwei + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER}) +@Constraint(validatedBy = {XssValidator.class}) +public @interface Xss { + + String message() default "不允许任何脚本运行"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} diff --git a/src/main/java/org/dromara/common/core/xss/XssValidator.java b/src/main/java/org/dromara/common/core/xss/XssValidator.java new file mode 100644 index 0000000..0a8336c --- /dev/null +++ b/src/main/java/org/dromara/common/core/xss/XssValidator.java @@ -0,0 +1,20 @@ +package org.dromara.common.core.xss; + +import cn.hutool.core.util.ReUtil; +import cn.hutool.http.HtmlUtil; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +/** + * 自定义xss校验注解实现 + * + * @author shihongwei + */ +public class XssValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + return !ReUtil.contains(HtmlUtil.RE_HTML_MARK, value); + } + +} diff --git a/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java b/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java new file mode 100644 index 0000000..ff40465 --- /dev/null +++ b/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java @@ -0,0 +1,70 @@ +package org.dromara.common.doc.config; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import org.dromara.common.doc.config.properties.SpringDocProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 接口文档配置 + * + * @author shihongwei + */ +@EnableConfigurationProperties(SpringDocProperties.class) +@ConditionalOnProperty(name = "springdoc.api-docs.enabled", havingValue = "true", matchIfMissing = true) +@Configuration +public class SpringDocConfig { + + /** + * 创建OpenAPI配置 + */ + @Bean + public OpenAPI customOpenAPI() { + return new OpenAPI() + .info(apiInfo()) + .addSecurityItem(new SecurityRequirement().addList("JWT Authentication").addList("ClientId")) + .components(new Components() + .addSecuritySchemes("JWT Authentication", createAPIKeyScheme()) + .addSecuritySchemes("ClientId", createClientIdScheme())); + } + + /** + * API信息配置 + */ + private Info apiInfo() { + return new Info() + .title("HOT平台API文档") + .description("HOT平台后端接口文档,提供完整的API接口说明") + .version("1.0.0") + .contact(new Contact() + .name("xiaoshi98") + .email("178899525@qq.com") + .url("http://xiaoshi98.top")); + } + + /** + * 创建API Key安全方案 + */ + private SecurityScheme createAPIKeyScheme() { + return new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) + .in(SecurityScheme.In.HEADER) + .name("Authorization") + .description("请在此处输入JWT Token(无需Bearer前缀)"); + } + + private SecurityScheme createClientIdScheme() { + return new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) + .in(SecurityScheme.In.HEADER) + .name("clientid"); + } + +} diff --git a/src/main/java/org/dromara/common/doc/config/properties/SpringDocProperties.java b/src/main/java/org/dromara/common/doc/config/properties/SpringDocProperties.java new file mode 100644 index 0000000..bdffe38 --- /dev/null +++ b/src/main/java/org/dromara/common/doc/config/properties/SpringDocProperties.java @@ -0,0 +1,94 @@ +package org.dromara.common.doc.config.properties; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.ExternalDocumentation; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.tags.Tag; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.NestedConfigurationProperty; + +import java.util.List; + +/** + * swagger 配置属性 + * + * @author shihongwei + */ +@Data +@ConfigurationProperties(prefix = "springdoc") +public class SpringDocProperties { + + /** + * 文档基本信息 + */ + @NestedConfigurationProperty + private InfoProperties info = new InfoProperties(); + + /** + * 扩展文档地址 + */ + @NestedConfigurationProperty + private ExternalDocumentation externalDocs; + + /** + * 标签 + */ + private List tags = null; + + /** + * 路径 + */ + @NestedConfigurationProperty + private Paths paths = null; + + /** + * 组件 + */ + @NestedConfigurationProperty + private Components components = null; + + /** + *

+ * 文档的基础属性信息 + *

+ * + * @see io.swagger.v3.oas.models.info.Info + * + * 为了 springboot 自动生产配置提示信息,所以这里复制一个类出来 + */ + @Data + public static class InfoProperties { + + /** + * 标题 + */ + private String title = null; + + /** + * 描述 + */ + private String description = null; + + /** + * 联系人信息 + */ + @NestedConfigurationProperty + private Contact contact = null; + + /** + * 许可证 + */ + @NestedConfigurationProperty + private License license = null; + + /** + * 版本 + */ + private String version = null; + + } + +} diff --git a/src/main/java/org/dromara/common/encrypt/annotation/ApiEncrypt.java b/src/main/java/org/dromara/common/encrypt/annotation/ApiEncrypt.java new file mode 100644 index 0000000..2a4ade0 --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/annotation/ApiEncrypt.java @@ -0,0 +1,20 @@ +package org.dromara.common.encrypt.annotation; + +import java.lang.annotation.*; + +/** + * 强制加密注解 + * + * @author shihongwei + */ +@Documented +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ApiEncrypt { + + /** + * 响应加密忽略,默认不加密,为 true 时加密 + */ + boolean response() default false; + +} diff --git a/src/main/java/org/dromara/common/encrypt/annotation/EncryptField.java b/src/main/java/org/dromara/common/encrypt/annotation/EncryptField.java new file mode 100644 index 0000000..69a7b55 --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/annotation/EncryptField.java @@ -0,0 +1,44 @@ +package org.dromara.common.encrypt.annotation; + +import org.dromara.common.encrypt.enumd.AlgorithmType; +import org.dromara.common.encrypt.enumd.EncodeType; + +import java.lang.annotation.*; + +/** + * 字段加密注解 + * + * @author shihongwei + */ +@Documented +@Inherited +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface EncryptField { + + /** + * 加密算法 + */ + AlgorithmType algorithm() default AlgorithmType.DEFAULT; + + /** + * 秘钥。AES、SM4需要 + */ + String password() default ""; + + /** + * 公钥。RSA、SM2需要 + */ + String publicKey() default ""; + + /** + * 私钥。RSA、SM2需要 + */ + String privateKey() default ""; + + /** + * 编码方式。对加密算法为BASE64的不起作用 + */ + EncodeType encode() default EncodeType.DEFAULT; + +} diff --git a/src/main/java/org/dromara/common/encrypt/config/ApiDecryptAutoConfiguration.java b/src/main/java/org/dromara/common/encrypt/config/ApiDecryptAutoConfiguration.java new file mode 100644 index 0000000..5f9b64a --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/config/ApiDecryptAutoConfiguration.java @@ -0,0 +1,34 @@ +package org.dromara.common.encrypt.config; + +import jakarta.servlet.DispatcherType; +import org.dromara.common.encrypt.filter.CryptoFilter; +import org.dromara.common.encrypt.properties.ApiDecryptProperties; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistration; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; + +/** + * api 解密自动配置 + * + * @author shihongwei + */ +@AutoConfiguration +@EnableConfigurationProperties(ApiDecryptProperties.class) +@ConditionalOnProperty(value = "api-decrypt.enabled", havingValue = "true") +public class ApiDecryptAutoConfiguration { + + @Bean + @FilterRegistration( + name = "cryptoFilter", + urlPatterns = "/*", + order = FilterRegistrationBean.HIGHEST_PRECEDENCE, + dispatcherTypes = DispatcherType.REQUEST + ) + public CryptoFilter cryptoFilter(ApiDecryptProperties properties) { + return new CryptoFilter(properties); + } + +} diff --git a/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java b/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java new file mode 100644 index 0000000..6311e80 --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java @@ -0,0 +1,49 @@ +package org.dromara.common.encrypt.config; + +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.encrypt.core.EncryptorManager; +import org.dromara.common.encrypt.interceptor.MybatisDecryptInterceptor; +import org.dromara.common.encrypt.interceptor.MybatisEncryptInterceptor; +import org.dromara.common.encrypt.properties.EncryptorProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** + * 加解密配置 + * + * @author shihongwei + * @version 4.6.0 + */ +@AutoConfiguration(after = MybatisPlusAutoConfiguration.class) +@EnableConfigurationProperties(EncryptorProperties.class) +@ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true") +@Slf4j +public class EncryptorAutoConfiguration { + + @Autowired + private EncryptorProperties properties; + + @Bean + public EncryptorManager encryptorManager(MybatisPlusProperties mybatisPlusProperties) { + return new EncryptorManager(mybatisPlusProperties.getTypeAliasesPackage()); + } + + @Bean + public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptorManager encryptorManager) { + return new MybatisEncryptInterceptor(encryptorManager, properties); + } + + @Bean + public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) { + return new MybatisDecryptInterceptor(encryptorManager, properties); + } + +} + + + diff --git a/src/main/java/org/dromara/common/encrypt/core/EncryptContext.java b/src/main/java/org/dromara/common/encrypt/core/EncryptContext.java new file mode 100644 index 0000000..d5b2cd0 --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/core/EncryptContext.java @@ -0,0 +1,41 @@ +package org.dromara.common.encrypt.core; + +import lombok.Data; +import org.dromara.common.encrypt.enumd.AlgorithmType; +import org.dromara.common.encrypt.enumd.EncodeType; + +/** + * 加密上下文 用于encryptor传递必要的参数。 + * + * @author shihongwei + * @version 4.6.0 + */ +@Data +public class EncryptContext { + + /** + * 默认算法 + */ + private AlgorithmType algorithm; + + /** + * 安全秘钥 + */ + private String password; + + /** + * 公钥 + */ + private String publicKey; + + /** + * 私钥 + */ + private String privateKey; + + /** + * 编码方式,base64/hex + */ + private EncodeType encode; + +} diff --git a/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java b/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java new file mode 100644 index 0000000..fa080ec --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java @@ -0,0 +1,168 @@ +package org.dromara.common.encrypt.core; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ReflectUtil; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.io.Resources; +import org.dromara.common.core.constant.Constants; +import org.dromara.common.core.utils.ObjectUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.encrypt.annotation.EncryptField; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.type.ClassMetadata; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +import org.springframework.util.ClassUtils; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * 加密管理类 + * + * @author shihongwei + * @version 4.6.0 + */ +@Slf4j +@NoArgsConstructor +public class EncryptorManager { + + /** + * 缓存加密器 + */ + Map encryptorMap = new ConcurrentHashMap<>(); + + /** + * 类加密字段缓存 + */ + Map, Set> fieldCache = new ConcurrentHashMap<>(); + + /** + * 构造方法传入类加密字段缓存 + * + * @param typeAliasesPackage 实体类包 + */ + public EncryptorManager(String typeAliasesPackage) { + scanEncryptClasses(typeAliasesPackage); + } + + + /** + * 获取类加密字段缓存 + */ + public Set getFieldCache(Class sourceClazz) { + return ObjectUtils.notNullGetter(fieldCache, f -> f.get(sourceClazz)); + } + + /** + * 注册加密执行者到缓存 + * + * @param encryptContext 加密执行者需要的相关配置参数 + */ + public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) { + int key = encryptContext.hashCode(); + if (encryptorMap.containsKey(key)) { + return encryptorMap.get(key); + } + IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext); + encryptorMap.put(key, encryptor); + return encryptor; + } + + /** + * 移除缓存中的加密执行者 + * + * @param encryptContext 加密执行者需要的相关配置参数 + */ + public void removeEncryptor(EncryptContext encryptContext) { + this.encryptorMap.remove(encryptContext.hashCode()); + } + + /** + * 根据配置进行加密。会进行本地缓存对应的算法和对应的秘钥信息。 + * + * @param value 待加密的值 + * @param encryptContext 加密相关的配置信息 + */ + public String encrypt(String value, EncryptContext encryptContext) { + if (StringUtils.startsWith(value, Constants.ENCRYPT_HEADER)) { + return value; + } + IEncryptor encryptor = this.registAndGetEncryptor(encryptContext); + String encrypt = encryptor.encrypt(value, encryptContext.getEncode()); + return Constants.ENCRYPT_HEADER + encrypt; + } + + /** + * 根据配置进行解密 + * + * @param value 待解密的值 + * @param encryptContext 加密相关的配置信息 + */ + public String decrypt(String value, EncryptContext encryptContext) { + if (!StringUtils.startsWith(value, Constants.ENCRYPT_HEADER)) { + return value; + } + IEncryptor encryptor = this.registAndGetEncryptor(encryptContext); + String str = StringUtils.removeStart(value, Constants.ENCRYPT_HEADER); + return encryptor.decrypt(str); + } + + /** + * 通过 typeAliasesPackage 设置的扫描包 扫描缓存实体 + */ + private void scanEncryptClasses(String typeAliasesPackage) { + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); + String[] packagePatternArray = StringUtils.splitPreserveAllTokens(typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); + String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX; + try { + for (String packagePattern : packagePatternArray) { + String path = ClassUtils.convertClassNameToResourcePath(packagePattern); + Resource[] resources = resolver.getResources(classpath + path + "/*.class"); + for (Resource resource : resources) { + ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata(); + Class clazz = Resources.classForName(classMetadata.getClassName()); + Set encryptFieldSet = getEncryptFieldSetFromClazz(clazz); + if (CollUtil.isNotEmpty(encryptFieldSet)) { + fieldCache.put(clazz, encryptFieldSet); + } + } + } + } catch (Exception e) { + log.error("初始化数据安全缓存时出错:{}", e.getMessage()); + } + } + + /** + * 获得一个类的加密字段集合 + */ + private Set getEncryptFieldSetFromClazz(Class clazz) { + Set fieldSet = new HashSet<>(); + // 判断clazz如果是接口,内部类,匿名类就直接返回 + if (clazz.isInterface() || clazz.isMemberClass() || clazz.isAnonymousClass()) { + return fieldSet; + } + while (clazz != null) { + Field[] fields = clazz.getDeclaredFields(); + fieldSet.addAll(Arrays.asList(fields)); + clazz = clazz.getSuperclass(); + } + fieldSet = fieldSet.stream().filter(field -> + field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class) + .collect(Collectors.toSet()); + for (Field field : fieldSet) { + field.setAccessible(true); + } + return fieldSet; + } + +} diff --git a/src/main/java/org/dromara/common/encrypt/core/IEncryptor.java b/src/main/java/org/dromara/common/encrypt/core/IEncryptor.java new file mode 100644 index 0000000..7cae700 --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/core/IEncryptor.java @@ -0,0 +1,35 @@ +package org.dromara.common.encrypt.core; + +import org.dromara.common.encrypt.enumd.AlgorithmType; +import org.dromara.common.encrypt.enumd.EncodeType; + +/** + * 加解者 + * + * @author shihongwei + * @version 4.6.0 + */ +public interface IEncryptor { + + /** + * 获得当前算法 + */ + AlgorithmType algorithm(); + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + * @return 加密后的字符串 + */ + String encrypt(String value, EncodeType encodeType); + + /** + * 解密 + * + * @param value 待加密字符串 + * @return 解密后的字符串 + */ + String decrypt(String value); +} diff --git a/src/main/java/org/dromara/common/encrypt/core/encryptor/AbstractEncryptor.java b/src/main/java/org/dromara/common/encrypt/core/encryptor/AbstractEncryptor.java new file mode 100644 index 0000000..6b0b48c --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/core/encryptor/AbstractEncryptor.java @@ -0,0 +1,18 @@ +package org.dromara.common.encrypt.core.encryptor; + +import org.dromara.common.encrypt.core.EncryptContext; +import org.dromara.common.encrypt.core.IEncryptor; + +/** + * 所有加密执行者的基类 + * + * @author shihongwei + * @version 4.6.0 + */ +public abstract class AbstractEncryptor implements IEncryptor { + + public AbstractEncryptor(EncryptContext context) { + // 用户配置校验与配置注入 + } + +} diff --git a/src/main/java/org/dromara/common/encrypt/core/encryptor/AesEncryptor.java b/src/main/java/org/dromara/common/encrypt/core/encryptor/AesEncryptor.java new file mode 100644 index 0000000..f2cac61 --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/core/encryptor/AesEncryptor.java @@ -0,0 +1,55 @@ +package org.dromara.common.encrypt.core.encryptor; + +import org.dromara.common.encrypt.core.EncryptContext; +import org.dromara.common.encrypt.enumd.AlgorithmType; +import org.dromara.common.encrypt.enumd.EncodeType; +import org.dromara.common.encrypt.utils.EncryptUtils; + +/** + * AES算法实现 + * + * @author shihongwei + * @version 4.6.0 + */ +public class AesEncryptor extends AbstractEncryptor { + + private final EncryptContext context; + + public AesEncryptor(EncryptContext context) { + super(context); + this.context = context; + } + + /** + * 获得当前算法 + */ + @Override + public AlgorithmType algorithm() { + return AlgorithmType.AES; + } + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + */ + @Override + public String encrypt(String value, EncodeType encodeType) { + if (encodeType == EncodeType.HEX) { + return EncryptUtils.encryptByAesHex(value, context.getPassword()); + } else { + return EncryptUtils.encryptByAes(value, context.getPassword()); + } + } + + /** + * 解密 + * + * @param value 待加密字符串 + */ + @Override + public String decrypt(String value) { + return EncryptUtils.decryptByAes(value, context.getPassword()); + } +} diff --git a/src/main/java/org/dromara/common/encrypt/core/encryptor/Base64Encryptor.java b/src/main/java/org/dromara/common/encrypt/core/encryptor/Base64Encryptor.java new file mode 100644 index 0000000..ac85975 --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/core/encryptor/Base64Encryptor.java @@ -0,0 +1,48 @@ +package org.dromara.common.encrypt.core.encryptor; + +import org.dromara.common.encrypt.core.EncryptContext; +import org.dromara.common.encrypt.enumd.AlgorithmType; +import org.dromara.common.encrypt.enumd.EncodeType; +import org.dromara.common.encrypt.utils.EncryptUtils; + +/** + * Base64算法实现 + * + * @author shihongwei + * @version 4.6.0 + */ +public class Base64Encryptor extends AbstractEncryptor { + + public Base64Encryptor(EncryptContext context) { + super(context); + } + + /** + * 获得当前算法 + */ + @Override + public AlgorithmType algorithm() { + return AlgorithmType.BASE64; + } + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + */ + @Override + public String encrypt(String value, EncodeType encodeType) { + return EncryptUtils.encryptByBase64(value); + } + + /** + * 解密 + * + * @param value 待加密字符串 + */ + @Override + public String decrypt(String value) { + return EncryptUtils.decryptByBase64(value); + } +} diff --git a/src/main/java/org/dromara/common/encrypt/core/encryptor/RsaEncryptor.java b/src/main/java/org/dromara/common/encrypt/core/encryptor/RsaEncryptor.java new file mode 100644 index 0000000..ce6d016 --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/core/encryptor/RsaEncryptor.java @@ -0,0 +1,62 @@ +package org.dromara.common.encrypt.core.encryptor; + +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.encrypt.core.EncryptContext; +import org.dromara.common.encrypt.enumd.AlgorithmType; +import org.dromara.common.encrypt.enumd.EncodeType; +import org.dromara.common.encrypt.utils.EncryptUtils; + + +/** + * RSA算法实现 + * + * @author shihongwei + * @version 4.6.0 + */ +public class RsaEncryptor extends AbstractEncryptor { + + private final EncryptContext context; + + public RsaEncryptor(EncryptContext context) { + super(context); + String privateKey = context.getPrivateKey(); + String publicKey = context.getPublicKey(); + if (StringUtils.isAnyEmpty(privateKey, publicKey)) { + throw new IllegalArgumentException("RSA公私钥均需要提供,公钥加密,私钥解密。"); + } + this.context = context; + } + + /** + * 获得当前算法 + */ + @Override + public AlgorithmType algorithm() { + return AlgorithmType.RSA; + } + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + */ + @Override + public String encrypt(String value, EncodeType encodeType) { + if (encodeType == EncodeType.HEX) { + return EncryptUtils.encryptByRsaHex(value, context.getPublicKey()); + } else { + return EncryptUtils.encryptByRsa(value, context.getPublicKey()); + } + } + + /** + * 解密 + * + * @param value 待加密字符串 + */ + @Override + public String decrypt(String value) { + return EncryptUtils.decryptByRsa(value, context.getPrivateKey()); + } +} diff --git a/src/main/java/org/dromara/common/encrypt/core/encryptor/Sm2Encryptor.java b/src/main/java/org/dromara/common/encrypt/core/encryptor/Sm2Encryptor.java new file mode 100644 index 0000000..7fee21c --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/core/encryptor/Sm2Encryptor.java @@ -0,0 +1,61 @@ +package org.dromara.common.encrypt.core.encryptor; + +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.encrypt.core.EncryptContext; +import org.dromara.common.encrypt.enumd.AlgorithmType; +import org.dromara.common.encrypt.enumd.EncodeType; +import org.dromara.common.encrypt.utils.EncryptUtils; + +/** + * sm2算法实现 + * + * @author shihongwei + * @version 4.6.0 + */ +public class Sm2Encryptor extends AbstractEncryptor { + + private final EncryptContext context; + + public Sm2Encryptor(EncryptContext context) { + super(context); + String privateKey = context.getPrivateKey(); + String publicKey = context.getPublicKey(); + if (StringUtils.isAnyEmpty(privateKey, publicKey)) { + throw new IllegalArgumentException("SM2公私钥均需要提供,公钥加密,私钥解密。"); + } + this.context = context; + } + + /** + * 获得当前算法 + */ + @Override + public AlgorithmType algorithm() { + return AlgorithmType.SM2; + } + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + */ + @Override + public String encrypt(String value, EncodeType encodeType) { + if (encodeType == EncodeType.HEX) { + return EncryptUtils.encryptBySm2Hex(value, context.getPublicKey()); + } else { + return EncryptUtils.encryptBySm2(value, context.getPublicKey()); + } + } + + /** + * 解密 + * + * @param value 待加密字符串 + */ + @Override + public String decrypt(String value) { + return EncryptUtils.decryptBySm2(value, context.getPrivateKey()); + } +} diff --git a/src/main/java/org/dromara/common/encrypt/core/encryptor/Sm4Encryptor.java b/src/main/java/org/dromara/common/encrypt/core/encryptor/Sm4Encryptor.java new file mode 100644 index 0000000..dbf053c --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/core/encryptor/Sm4Encryptor.java @@ -0,0 +1,55 @@ +package org.dromara.common.encrypt.core.encryptor; + +import org.dromara.common.encrypt.core.EncryptContext; +import org.dromara.common.encrypt.enumd.AlgorithmType; +import org.dromara.common.encrypt.enumd.EncodeType; +import org.dromara.common.encrypt.utils.EncryptUtils; + +/** + * sm4算法实现 + * + * @author shihongwei + * @version 4.6.0 + */ +public class Sm4Encryptor extends AbstractEncryptor { + + private final EncryptContext context; + + public Sm4Encryptor(EncryptContext context) { + super(context); + this.context = context; + } + + /** + * 获得当前算法 + */ + @Override + public AlgorithmType algorithm() { + return AlgorithmType.SM4; + } + + /** + * 加密 + * + * @param value 待加密字符串 + * @param encodeType 加密后的编码格式 + */ + @Override + public String encrypt(String value, EncodeType encodeType) { + if (encodeType == EncodeType.HEX) { + return EncryptUtils.encryptBySm4Hex(value, context.getPassword()); + } else { + return EncryptUtils.encryptBySm4(value, context.getPassword()); + } + } + + /** + * 解密 + * + * @param value 待加密字符串 + */ + @Override + public String decrypt(String value) { + return EncryptUtils.decryptBySm4(value, context.getPassword()); + } +} diff --git a/src/main/java/org/dromara/common/encrypt/enumd/AlgorithmType.java b/src/main/java/org/dromara/common/encrypt/enumd/AlgorithmType.java new file mode 100644 index 0000000..c1a40e6 --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/enumd/AlgorithmType.java @@ -0,0 +1,48 @@ +package org.dromara.common.encrypt.enumd; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.dromara.common.encrypt.core.encryptor.*; + +/** + * 算法名称 + * + * @author shihongwei + * @version 4.6.0 + */ +@Getter +@AllArgsConstructor +public enum AlgorithmType { + + /** + * 默认走yml配置 + */ + DEFAULT(null), + + /** + * base64 + */ + BASE64(Base64Encryptor.class), + + /** + * aes + */ + AES(AesEncryptor.class), + + /** + * rsa + */ + RSA(RsaEncryptor.class), + + /** + * sm2 + */ + SM2(Sm2Encryptor.class), + + /** + * sm4 + */ + SM4(Sm4Encryptor.class); + + private final Class clazz; +} diff --git a/src/main/java/org/dromara/common/encrypt/enumd/EncodeType.java b/src/main/java/org/dromara/common/encrypt/enumd/EncodeType.java new file mode 100644 index 0000000..751537a --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/enumd/EncodeType.java @@ -0,0 +1,26 @@ +package org.dromara.common.encrypt.enumd; + +/** + * 编码类型 + * + * @author shihongwei + * @version 4.6.0 + */ +public enum EncodeType { + + /** + * 默认使用yml配置 + */ + DEFAULT, + + /** + * base64编码 + */ + BASE64, + + /** + * 16进制编码 + */ + HEX; + +} diff --git a/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java b/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java new file mode 100644 index 0000000..e913e01 --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java @@ -0,0 +1,110 @@ +package org.dromara.common.encrypt.filter; + +import cn.hutool.core.util.ObjectUtil; +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.dromara.common.core.constant.HttpStatus; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.encrypt.annotation.ApiEncrypt; +import org.dromara.common.encrypt.properties.ApiDecryptProperties; +import org.springframework.http.HttpMethod; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerExceptionResolver; +import org.springframework.web.servlet.HandlerExecutionChain; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.io.IOException; + + +/** + * Crypto 过滤器 + * + * @author shihongwei + */ +public class CryptoFilter implements Filter { + private final ApiDecryptProperties properties; + + public CryptoFilter(ApiDecryptProperties properties) { + this.properties = properties; + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest servletRequest = (HttpServletRequest) request; + HttpServletResponse servletResponse = (HttpServletResponse) response; + // 获取加密注解 + ApiEncrypt apiEncrypt = this.getApiEncryptAnnotation(servletRequest); + boolean responseFlag = apiEncrypt != null && apiEncrypt.response(); + ServletRequest requestWrapper = null; + ServletResponse responseWrapper = null; + EncryptResponseBodyWrapper responseBodyWrapper = null; + + // 是否为 put 或者 post 请求 + if (HttpMethod.PUT.matches(servletRequest.getMethod()) || HttpMethod.POST.matches(servletRequest.getMethod())) { + // 是否存在加密标头 + String headerValue = servletRequest.getHeader(properties.getHeaderFlag()); + if (StringUtils.isNotBlank(headerValue)) { + // 请求解密 + requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag()); + } else { + // 是否有注解,有就报错,没有放行 + if (ObjectUtil.isNotNull(apiEncrypt)) { + HandlerExceptionResolver exceptionResolver = SpringUtils.getBean("handlerExceptionResolver", HandlerExceptionResolver.class); + exceptionResolver.resolveException( + servletRequest, servletResponse, null, + new ServiceException("没有访问权限,请联系管理员授权", HttpStatus.FORBIDDEN)); + return; + } + } + } + + // 判断是否响应加密 + if (responseFlag) { + responseBodyWrapper = new EncryptResponseBodyWrapper(servletResponse); + responseWrapper = responseBodyWrapper; + } + + chain.doFilter( + ObjectUtil.defaultIfNull(requestWrapper, request), + ObjectUtil.defaultIfNull(responseWrapper, response)); + + if (responseFlag) { + servletResponse.reset(); + // 对原始内容加密 + String encryptContent = responseBodyWrapper.getEncryptContent( + servletResponse, properties.getPublicKey(), properties.getHeaderFlag()); + // 对加密后的内容写出 + servletResponse.getWriter().write(encryptContent); + } + } + + /** + * 获取 ApiEncrypt 注解 + */ + private ApiEncrypt getApiEncryptAnnotation(HttpServletRequest servletRequest) { + RequestMappingHandlerMapping handlerMapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class); + // 获取注解 + try { + HandlerExecutionChain mappingHandler = handlerMapping.getHandler(servletRequest); + if (ObjectUtil.isNotNull(mappingHandler)) { + Object handler = mappingHandler.getHandler(); + if (ObjectUtil.isNotNull(handler)) { + // 从handler获取注解 + if (handler instanceof HandlerMethod handlerMethod) { + return handlerMethod.getMethodAnnotation(ApiEncrypt.class); + } + } + } + } catch (Exception e) { + return null; + } + return null; + } + + @Override + public void destroy() { + } +} diff --git a/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java b/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java new file mode 100644 index 0000000..8fa2f65 --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java @@ -0,0 +1,94 @@ +package org.dromara.common.encrypt.filter; + +import cn.hutool.core.io.IoUtil; +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import org.dromara.common.core.constant.Constants; +import org.dromara.common.encrypt.utils.EncryptUtils; +import org.springframework.http.MediaType; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +/** + * 解密请求参数工具类 + * + * @author shihongwei + */ +public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper { + + private final byte[] body; + + public DecryptRequestBodyWrapper(HttpServletRequest request, String privateKey, String headerFlag) throws IOException { + super(request); + // 获取 AES 密码 采用 RSA 加密 + String headerRsa = request.getHeader(headerFlag); + String decryptAes = EncryptUtils.decryptByRsa(headerRsa, privateKey); + // 解密 AES 密码 + String aesPassword = EncryptUtils.decryptByBase64(decryptAes); + request.setCharacterEncoding(Constants.UTF8); + byte[] readBytes = IoUtil.readBytes(request.getInputStream(), false); + String requestBody = new String(readBytes, StandardCharsets.UTF_8); + // 解密 body 采用 AES 加密 + String decryptBody = EncryptUtils.decryptByAes(requestBody, aesPassword); + body = decryptBody.getBytes(StandardCharsets.UTF_8); + } + + @Override + public BufferedReader getReader() { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + + @Override + public int getContentLength() { + return body.length; + } + + @Override + public long getContentLengthLong() { + return body.length; + } + + @Override + public String getContentType() { + return MediaType.APPLICATION_JSON_VALUE; + } + + + @Override + public ServletInputStream getInputStream() { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() { + @Override + public int read() { + return bais.read(); + } + + @Override + public int available() { + return body.length; + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + + } + }; + } +} diff --git a/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java b/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java new file mode 100644 index 0000000..9379095 --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java @@ -0,0 +1,126 @@ +package org.dromara.common.encrypt.filter; + +import cn.hutool.core.util.RandomUtil; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.WriteListener; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponseWrapper; +import org.dromara.common.encrypt.utils.EncryptUtils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; + +/** + * 加密响应参数包装类 + * + * @author shihongwei + */ +public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper { + + private final ByteArrayOutputStream byteArrayOutputStream; + private final ServletOutputStream servletOutputStream; + private final PrintWriter printWriter; + + public EncryptResponseBodyWrapper(HttpServletResponse response) throws IOException { + super(response); + this.byteArrayOutputStream = new ByteArrayOutputStream(); + this.servletOutputStream = this.getOutputStream(); + this.printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream)); + } + + @Override + public PrintWriter getWriter() { + return printWriter; + } + + @Override + public void flushBuffer() throws IOException { + if (servletOutputStream != null) { + servletOutputStream.flush(); + } + if (printWriter != null) { + printWriter.flush(); + } + } + + @Override + public void reset() { + byteArrayOutputStream.reset(); + } + + public byte[] getResponseData() throws IOException { + flushBuffer(); + return byteArrayOutputStream.toByteArray(); + } + + public String getContent() throws IOException { + flushBuffer(); + return byteArrayOutputStream.toString(); + } + + /** + * 获取加密内容 + * + * @param servletResponse response + * @param publicKey RSA公钥 (用于加密 AES 秘钥) + * @param headerFlag 请求头标志 + * @return 加密内容 + * @throws IOException + */ + public String getEncryptContent(HttpServletResponse servletResponse, String publicKey, String headerFlag) throws IOException { + // 生成秘钥 + String aesPassword = RandomUtil.randomString(32); + // 秘钥使用 Base64 编码 + String encryptAes = EncryptUtils.encryptByBase64(aesPassword); + // Rsa 公钥加密 Base64 编码 + String encryptPassword = EncryptUtils.encryptByRsa(encryptAes, publicKey); + + // 设置响应头 + // vue版本需要设置 + servletResponse.addHeader("Access-Control-Expose-Headers", headerFlag); + servletResponse.setHeader("Access-Control-Allow-Origin", "*"); + servletResponse.setHeader("Access-Control-Allow-Methods", "*"); + servletResponse.setHeader(headerFlag, encryptPassword); + servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString()); + + + // 获取原始内容 + String originalBody = this.getContent(); + // 对内容进行加密 + return EncryptUtils.encryptByAes(originalBody, aesPassword); + } + + @Override + public ServletOutputStream getOutputStream() throws IOException { + return new ServletOutputStream() { + @Override + public boolean isReady() { + return false; + } + + @Override + public void setWriteListener(WriteListener writeListener) { + + } + + @Override + public void write(int b) throws IOException { + byteArrayOutputStream.write(b); + } + + @Override + public void write(byte[] b) throws IOException { + byteArrayOutputStream.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + byteArrayOutputStream.write(b, off, len); + } + }; + } + +} diff --git a/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java b/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java new file mode 100644 index 0000000..fac5de5 --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java @@ -0,0 +1,132 @@ +package org.dromara.common.encrypt.interceptor; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.executor.parameter.ParameterHandler; +import org.apache.ibatis.executor.resultset.ResultSetHandler; +import org.apache.ibatis.plugin.*; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.encrypt.annotation.EncryptField; +import org.dromara.common.encrypt.core.EncryptContext; +import org.dromara.common.encrypt.core.EncryptorManager; +import org.dromara.common.encrypt.enumd.AlgorithmType; +import org.dromara.common.encrypt.enumd.EncodeType; +import org.dromara.common.encrypt.properties.EncryptorProperties; + +import java.lang.reflect.Field; +import java.sql.Statement; +import java.util.*; + +/** + * 出参解密拦截器 + * + * @author shihongwei + * @version 4.6.0 + */ +@Slf4j +@Intercepts({@Signature( + type = ResultSetHandler.class, + method = "handleResultSets", + args = {Statement.class}) +}) +@AllArgsConstructor +public class MybatisDecryptInterceptor implements Interceptor { + + private final EncryptorManager encryptorManager; + private final EncryptorProperties defaultProperties; + + @Override + public Object intercept(Invocation invocation) throws Throwable { + // 开始进行参数解密 + ResultSetHandler resultSetHandler = (ResultSetHandler) invocation.getTarget(); + Field parameterHandlerField = resultSetHandler.getClass().getDeclaredField("parameterHandler"); + parameterHandlerField.setAccessible(true); + Object target = parameterHandlerField.get(resultSetHandler); + if (target instanceof ParameterHandler parameterHandler) { + Object parameterObject = parameterHandler.getParameterObject(); + if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) { + this.decryptHandler(parameterObject); + } + } + // 获取执行mysql执行结果 + Object result = invocation.proceed(); + if (result == null) { + return null; + } + this.decryptHandler(result); + return result; + } + + /** + * 解密对象 + * + * @param sourceObject 待加密对象 + */ + private void decryptHandler(Object sourceObject) { + if (ObjectUtil.isNull(sourceObject)) { + return; + } + if (sourceObject instanceof Map map) { + new HashSet<>(map.values()).forEach(this::decryptHandler); + return; + } + if (sourceObject instanceof List list) { + if(CollUtil.isEmpty(list)) { + return; + } + // 判断第一个元素是否含有注解。如果没有直接返回,提高效率 + Object firstItem = list.get(0); + if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) { + return; + } + list.forEach(this::decryptHandler); + return; + } + // 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错) + Set fields = encryptorManager.getFieldCache(sourceObject.getClass()); + if(ObjectUtil.isNull(fields)){ + return; + } + try { + for (Field field : fields) { + field.set(sourceObject, this.decryptField(Convert.toStr(field.get(sourceObject)), field)); + } + } catch (Exception e) { + log.error("处理解密字段时出错", e); + } + } + + /** + * 字段值进行加密。通过字段的批注注册新的加密算法 + * + * @param value 待加密的值 + * @param field 待加密字段 + * @return 加密后结果 + */ + private String decryptField(String value, Field field) { + if (ObjectUtil.isNull(value)) { + return null; + } + EncryptField encryptField = field.getAnnotation(EncryptField.class); + EncryptContext encryptContext = new EncryptContext(); + encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm()); + encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode()); + encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password()); + encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey()); + encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey()); + return this.encryptorManager.decrypt(value, encryptContext); + } + + @Override + public Object plugin(Object target) { + return Plugin.wrap(target, this); + } + + @Override + public void setProperties(Properties properties) { + + } +} diff --git a/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java b/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java new file mode 100644 index 0000000..47b2770 --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java @@ -0,0 +1,124 @@ +package org.dromara.common.encrypt.interceptor; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.executor.parameter.ParameterHandler; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.plugin.Intercepts; +import org.apache.ibatis.plugin.Invocation; +import org.apache.ibatis.plugin.Signature; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.encrypt.annotation.EncryptField; +import org.dromara.common.encrypt.core.EncryptContext; +import org.dromara.common.encrypt.core.EncryptorManager; +import org.dromara.common.encrypt.enumd.AlgorithmType; +import org.dromara.common.encrypt.enumd.EncodeType; +import org.dromara.common.encrypt.properties.EncryptorProperties; + +import java.lang.reflect.Field; +import java.sql.PreparedStatement; +import java.util.*; + +/** + * 入参加密拦截器 + * + * @author shihongwei + * @version 4.6.0 + */ +@Slf4j +@Intercepts({@Signature( + type = ParameterHandler.class, + method = "setParameters", + args = {PreparedStatement.class}) +}) +@AllArgsConstructor +public class MybatisEncryptInterceptor implements Interceptor { + + private final EncryptorManager encryptorManager; + private final EncryptorProperties defaultProperties; + + @Override + public Object intercept(Invocation invocation) throws Throwable { + return invocation; + } + + @Override + public Object plugin(Object target) { + if (target instanceof ParameterHandler parameterHandler) { + // 进行加密操作 + Object parameterObject = parameterHandler.getParameterObject(); + if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) { + this.encryptHandler(parameterObject); + } + } + return target; + } + + /** + * 加密对象 + * + * @param sourceObject 待加密对象 + */ + private void encryptHandler(Object sourceObject) { + if (ObjectUtil.isNull(sourceObject)) { + return; + } + if (sourceObject instanceof Map map) { + new HashSet<>(map.values()).forEach(this::encryptHandler); + return; + } + if (sourceObject instanceof List list) { + if(CollUtil.isEmpty(list)) { + return; + } + // 判断第一个元素是否含有注解。如果没有直接返回,提高效率 + Object firstItem = list.get(0); + if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) { + return; + } + list.forEach(this::encryptHandler); + return; + } + // 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错) + Set fields = encryptorManager.getFieldCache(sourceObject.getClass()); + if(ObjectUtil.isNull(fields)){ + return; + } + try { + for (Field field : fields) { + field.set(sourceObject, this.encryptField(Convert.toStr(field.get(sourceObject)), field)); + } + } catch (Exception e) { + log.error("处理加密字段时出错", e); + } + } + + /** + * 字段值进行加密。通过字段的批注注册新的加密算法 + * + * @param value 待加密的值 + * @param field 待加密字段 + * @return 加密后结果 + */ + private String encryptField(String value, Field field) { + if (ObjectUtil.isNull(value)) { + return null; + } + EncryptField encryptField = field.getAnnotation(EncryptField.class); + EncryptContext encryptContext = new EncryptContext(); + encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm()); + encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode()); + encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password()); + encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey()); + encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey()); + return this.encryptorManager.encrypt(value, encryptContext); + } + + + @Override + public void setProperties(Properties properties) { + } +} diff --git a/src/main/java/org/dromara/common/encrypt/properties/ApiDecryptProperties.java b/src/main/java/org/dromara/common/encrypt/properties/ApiDecryptProperties.java new file mode 100644 index 0000000..e97a7fc --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/properties/ApiDecryptProperties.java @@ -0,0 +1,34 @@ +package org.dromara.common.encrypt.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * api解密属性配置类 + * @author shihongwei + */ +@Data +@ConfigurationProperties(prefix = "api-decrypt") +public class ApiDecryptProperties { + + /** + * 加密开关 + */ + private Boolean enabled; + + /** + * 头部标识 + */ + private String headerFlag; + + /** + * 响应加密公钥 + */ + private String publicKey; + + /** + * 请求解密私钥 + */ + private String privateKey; + +} diff --git a/src/main/java/org/dromara/common/encrypt/properties/EncryptorProperties.java b/src/main/java/org/dromara/common/encrypt/properties/EncryptorProperties.java new file mode 100644 index 0000000..7f12073 --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/properties/EncryptorProperties.java @@ -0,0 +1,48 @@ +package org.dromara.common.encrypt.properties; + +import lombok.Data; +import org.dromara.common.encrypt.enumd.AlgorithmType; +import org.dromara.common.encrypt.enumd.EncodeType; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 加解密属性配置类 + * + * @author shihongwei + * @version 4.6.0 + */ +@Data +@ConfigurationProperties(prefix = "mybatis-encryptor") +public class EncryptorProperties { + + /** + * 过滤开关 + */ + private Boolean enable; + + /** + * 默认算法 + */ + private AlgorithmType algorithm; + + /** + * 安全秘钥 + */ + private String password; + + /** + * 公钥 + */ + private String publicKey; + + /** + * 私钥 + */ + private String privateKey; + + /** + * 编码方式,base64/hex + */ + private EncodeType encode; + +} diff --git a/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java b/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java new file mode 100644 index 0000000..69d54a1 --- /dev/null +++ b/src/main/java/org/dromara/common/encrypt/utils/EncryptUtils.java @@ -0,0 +1,313 @@ +package org.dromara.common.encrypt.utils; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.SmUtil; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.RSA; +import cn.hutool.crypto.asymmetric.SM2; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +/** + * 安全相关工具类 + * + * @author shihongwei + */ +public class EncryptUtils { + + /** + * 公钥 + */ + public static final String PUBLIC_KEY = "publicKey"; + + /** + * 私钥 + */ + public static final String PRIVATE_KEY = "privateKey"; + + /** + * Base64加密 + * + * @param data 待加密数据 + * @return 加密后字符串 + */ + public static String encryptByBase64(String data) { + return Base64.encode(data, StandardCharsets.UTF_8); + } + + /** + * Base64解密 + * + * @param data 待解密数据 + * @return 解密后字符串 + */ + public static String decryptByBase64(String data) { + return Base64.decodeStr(data, StandardCharsets.UTF_8); + } + + /** + * AES加密 + * + * @param data 待加密数据 + * @param password 秘钥字符串 + * @return 加密后字符串, 采用Base64编码 + */ + public static String encryptByAes(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("AES需要传入秘钥信息"); + } + // aes算法的秘钥要求是16位、24位、32位 + int[] array = {16, 24, 32}; + if (!ArrayUtil.contains(array, password.length())) { + throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位"); + } + return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8); + } + + /** + * AES加密 + * + * @param data 待加密数据 + * @param password 秘钥字符串 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptByAesHex(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("AES需要传入秘钥信息"); + } + // aes算法的秘钥要求是16位、24位、32位 + int[] array = {16, 24, 32}; + if (!ArrayUtil.contains(array, password.length())) { + throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位"); + } + return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8); + } + + /** + * AES解密 + * + * @param data 待解密数据 + * @param password 秘钥字符串 + * @return 解密后字符串 + */ + public static String decryptByAes(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("AES需要传入秘钥信息"); + } + // aes算法的秘钥要求是16位、24位、32位 + int[] array = {16, 24, 32}; + if (!ArrayUtil.contains(array, password.length())) { + throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位"); + } + return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8); + } + + /** + * SM4加密(Base64编码) + * + * @param data 待加密数据 + * @param password 秘钥字符串 + * @return 加密后字符串, 采用Base64编码 + */ + public static String encryptBySm4(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("SM4需要传入秘钥信息"); + } + // sm4算法的秘钥要求是16位长度 + int sm4PasswordLength = 16; + if (sm4PasswordLength != password.length()) { + throw new IllegalArgumentException("SM4秘钥长度要求为16位"); + } + return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8); + } + + /** + * SM4加密(Hex编码) + * + * @param data 待加密数据 + * @param password 秘钥字符串 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptBySm4Hex(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("SM4需要传入秘钥信息"); + } + // sm4算法的秘钥要求是16位长度 + int sm4PasswordLength = 16; + if (sm4PasswordLength != password.length()) { + throw new IllegalArgumentException("SM4秘钥长度要求为16位"); + } + return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8); + } + + /** + * sm4解密 + * + * @param data 待解密数据(可以是Base64或Hex编码) + * @param password 秘钥字符串 + * @return 解密后字符串 + */ + public static String decryptBySm4(String data, String password) { + if (StrUtil.isBlank(password)) { + throw new IllegalArgumentException("SM4需要传入秘钥信息"); + } + // sm4算法的秘钥要求是16位长度 + int sm4PasswordLength = 16; + if (sm4PasswordLength != password.length()) { + throw new IllegalArgumentException("SM4秘钥长度要求为16位"); + } + return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8); + } + + /** + * 产生sm2加解密需要的公钥和私钥 + * + * @return 公私钥Map + */ + public static Map generateSm2Key() { + Map keyMap = new HashMap<>(2); + SM2 sm2 = SmUtil.sm2(); + keyMap.put(PRIVATE_KEY, sm2.getPrivateKeyBase64()); + keyMap.put(PUBLIC_KEY, sm2.getPublicKeyBase64()); + return keyMap; + } + + /** + * sm2公钥加密 + * + * @param data 待加密数据 + * @param publicKey 公钥 + * @return 加密后字符串, 采用Base64编码 + */ + public static String encryptBySm2(String data, String publicKey) { + if (StrUtil.isBlank(publicKey)) { + throw new IllegalArgumentException("SM2需要传入公钥进行加密"); + } + SM2 sm2 = SmUtil.sm2(null, publicKey); + return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey); + } + + /** + * sm2公钥加密 + * + * @param data 待加密数据 + * @param publicKey 公钥 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptBySm2Hex(String data, String publicKey) { + if (StrUtil.isBlank(publicKey)) { + throw new IllegalArgumentException("SM2需要传入公钥进行加密"); + } + SM2 sm2 = SmUtil.sm2(null, publicKey); + return sm2.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey); + } + + /** + * sm2私钥解密 + * + * @param data 待解密数据 + * @param privateKey 私钥 + * @return 解密后字符串 + */ + public static String decryptBySm2(String data, String privateKey) { + if (StrUtil.isBlank(privateKey)) { + throw new IllegalArgumentException("SM2需要传入私钥进行解密"); + } + SM2 sm2 = SmUtil.sm2(privateKey, null); + return sm2.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8); + } + + /** + * 产生RSA加解密需要的公钥和私钥 + * + * @return 公私钥Map + */ + public static Map generateRsaKey() { + Map keyMap = new HashMap<>(2); + RSA rsa = SecureUtil.rsa(); + keyMap.put(PRIVATE_KEY, rsa.getPrivateKeyBase64()); + keyMap.put(PUBLIC_KEY, rsa.getPublicKeyBase64()); + return keyMap; + } + + /** + * rsa公钥加密 + * + * @param data 待加密数据 + * @param publicKey 公钥 + * @return 加密后字符串, 采用Base64编码 + */ + public static String encryptByRsa(String data, String publicKey) { + if (StrUtil.isBlank(publicKey)) { + throw new IllegalArgumentException("RSA需要传入公钥进行加密"); + } + RSA rsa = SecureUtil.rsa(null, publicKey); + return rsa.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey); + } + + /** + * rsa公钥加密 + * + * @param data 待加密数据 + * @param publicKey 公钥 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptByRsaHex(String data, String publicKey) { + if (StrUtil.isBlank(publicKey)) { + throw new IllegalArgumentException("RSA需要传入公钥进行加密"); + } + RSA rsa = SecureUtil.rsa(null, publicKey); + return rsa.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey); + } + + /** + * rsa私钥解密 + * + * @param data 待解密数据 + * @param privateKey 私钥 + * @return 解密后字符串 + */ + public static String decryptByRsa(String data, String privateKey) { + if (StrUtil.isBlank(privateKey)) { + throw new IllegalArgumentException("RSA需要传入私钥进行解密"); + } + RSA rsa = SecureUtil.rsa(privateKey, null); + return rsa.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8); + } + + /** + * md5加密 + * + * @param data 待加密数据 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptByMd5(String data) { + return SecureUtil.md5(data); + } + + /** + * sha256加密 + * + * @param data 待加密数据 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptBySha256(String data) { + return SecureUtil.sha256(data); + } + + /** + * sm3加密 + * + * @param data 待加密数据 + * @return 加密后字符串, 采用Hex编码 + */ + public static String encryptBySm3(String data) { + return SmUtil.sm3(data); + } + +} diff --git a/src/main/java/org/dromara/common/excel/annotation/CellMerge.java b/src/main/java/org/dromara/common/excel/annotation/CellMerge.java new file mode 100644 index 0000000..48ab67b --- /dev/null +++ b/src/main/java/org/dromara/common/excel/annotation/CellMerge.java @@ -0,0 +1,29 @@ +package org.dromara.common.excel.annotation; + +import org.dromara.common.excel.core.CellMergeStrategy; + +import java.lang.annotation.*; + +/** + * excel 列单元格合并(合并列相同项) + * + * 需搭配 {@link CellMergeStrategy} 策略使用 + * + * @author shihongwei + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface CellMerge { + + /** + * col index + */ + int index() default -1; + + /** + * 合并需要依赖的其他字段名称 + */ + String[] mergeBy() default {}; + +} diff --git a/src/main/java/org/dromara/common/excel/annotation/ExcelDictFormat.java b/src/main/java/org/dromara/common/excel/annotation/ExcelDictFormat.java new file mode 100644 index 0000000..65f84c2 --- /dev/null +++ b/src/main/java/org/dromara/common/excel/annotation/ExcelDictFormat.java @@ -0,0 +1,32 @@ +package org.dromara.common.excel.annotation; + +import org.dromara.common.core.utils.StringUtils; + +import java.lang.annotation.*; + +/** + * 字典格式化 + * + * @author shihongwei + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ExcelDictFormat { + + /** + * 如果是字典类型,请设置字典的type值 (如: sys_user_sex) + */ + String dictType() default ""; + + /** + * 读取内容转表达式 (如: 0=男,1=女,2=未知) + */ + String readConverterExp() default ""; + + /** + * 分隔符,读取字符串组内容 + */ + String separator() default StringUtils.SEPARATOR; + +} diff --git a/src/main/java/org/dromara/common/excel/annotation/ExcelEnumFormat.java b/src/main/java/org/dromara/common/excel/annotation/ExcelEnumFormat.java new file mode 100644 index 0000000..cd82733 --- /dev/null +++ b/src/main/java/org/dromara/common/excel/annotation/ExcelEnumFormat.java @@ -0,0 +1,30 @@ +package org.dromara.common.excel.annotation; + +import java.lang.annotation.*; + +/** + * 枚举格式化 + * + * @author shihongwei + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ExcelEnumFormat { + + /** + * 字典枚举类型 + */ + Class> enumClass(); + + /** + * 字典枚举类中对应的code属性名称,默认为code + */ + String codeField() default "code"; + + /** + * 字典枚举类中对应的text属性名称,默认为text + */ + String textField() default "text"; + +} diff --git a/src/main/java/org/dromara/common/excel/annotation/ExcelNotation.java b/src/main/java/org/dromara/common/excel/annotation/ExcelNotation.java new file mode 100644 index 0000000..f156e72 --- /dev/null +++ b/src/main/java/org/dromara/common/excel/annotation/ExcelNotation.java @@ -0,0 +1,20 @@ +package org.dromara.common.excel.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 批注 此注解仅用于单表头 不支持多层级表头 + * @author shihongwei + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExcelNotation { + + /** + * 批注内容 + */ + String value() default ""; +} diff --git a/src/main/java/org/dromara/common/excel/annotation/ExcelRequired.java b/src/main/java/org/dromara/common/excel/annotation/ExcelRequired.java new file mode 100644 index 0000000..11e8100 --- /dev/null +++ b/src/main/java/org/dromara/common/excel/annotation/ExcelRequired.java @@ -0,0 +1,22 @@ +package org.dromara.common.excel.annotation; + +import org.apache.poi.ss.usermodel.IndexedColors; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 是否必填 此注解仅用于单表头 不支持多层级表头 + * @author shihongwei + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExcelRequired { + + /** + * 字体颜色 + */ + IndexedColors fontColor() default IndexedColors.RED; +} diff --git a/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java b/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java new file mode 100644 index 0000000..b771503 --- /dev/null +++ b/src/main/java/org/dromara/common/excel/convert/ExcelBigNumberConvert.java @@ -0,0 +1,52 @@ +package org.dromara.common.excel.convert; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import cn.idev.excel.converters.Converter; +import cn.idev.excel.enums.CellDataTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; +import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.metadata.property.ExcelContentProperty; +import lombok.extern.slf4j.Slf4j; + +import java.math.BigDecimal; + +/** + * 大数值转换 + * Excel 数值长度位15位 大于15位的数值转换位字符串 + * + * @author shihongwei + */ +@Slf4j +public class ExcelBigNumberConvert implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Long.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return null; + } + + @Override + public Long convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + return Convert.toLong(cellData.getData()); + } + + @Override + public WriteCellData convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (ObjectUtil.isNotNull(object)) { + String str = Convert.toStr(object); + if (str.length() > 15) { + return new WriteCellData<>(str); + } + } + WriteCellData cellData = new WriteCellData<>(new BigDecimal(object)); + cellData.setType(CellDataTypeEnum.NUMBER); + return cellData; + } + +} diff --git a/src/main/java/org/dromara/common/excel/convert/ExcelDateStringConvert.java b/src/main/java/org/dromara/common/excel/convert/ExcelDateStringConvert.java new file mode 100644 index 0000000..cfba66a --- /dev/null +++ b/src/main/java/org/dromara/common/excel/convert/ExcelDateStringConvert.java @@ -0,0 +1,44 @@ +package org.dromara.common.excel.convert; + +import cn.hutool.core.date.DateUtil; +import cn.idev.excel.converters.Converter; +import cn.idev.excel.enums.CellDataTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; +import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.metadata.property.ExcelContentProperty; + +import java.util.Date; + +/** + * 日期导出为字符串(yyyy-MM-dd)转换器 + */ +public class ExcelDateStringConvert implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Date.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return null; + } + + @Override + public Date convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + Object data = cellData.getData(); + if (data instanceof Date) { + return (Date) data; + } + return null; + } + + @Override + public WriteCellData convertToExcelData(Date value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + String str = value == null ? "" : DateUtil.format(value, "yyyy-MM-dd"); + WriteCellData cellData = new WriteCellData<>(str); + cellData.setType(CellDataTypeEnum.STRING); + return cellData; + } +} diff --git a/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java b/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java new file mode 100644 index 0000000..07313c0 --- /dev/null +++ b/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java @@ -0,0 +1,73 @@ +package org.dromara.common.excel.convert; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import cn.idev.excel.converters.Converter; +import cn.idev.excel.enums.CellDataTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; +import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.metadata.property.ExcelContentProperty; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.service.DictService; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.utils.ExcelUtil; + +import java.lang.reflect.Field; + +/** + * 字典格式化转换处理 + * + * @author shihongwei + */ +@Slf4j +public class ExcelDictConvert implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Object.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return null; + } + + @Override + public Object convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + ExcelDictFormat anno = getAnnotation(contentProperty.getField()); + String type = anno.dictType(); + String label = cellData.getStringValue(); + String value; + if (StringUtils.isBlank(type)) { + value = ExcelUtil.reverseByExp(label, anno.readConverterExp(), anno.separator()); + } else { + value = SpringUtils.getBean(DictService.class).getDictValue(type, label, anno.separator()); + } + return Convert.convert(contentProperty.getField().getType(), value); + } + + @Override + public WriteCellData convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (ObjectUtil.isNull(object)) { + return new WriteCellData<>(""); + } + ExcelDictFormat anno = getAnnotation(contentProperty.getField()); + String type = anno.dictType(); + String value = Convert.toStr(object); + String label; + if (StringUtils.isBlank(type)) { + label = ExcelUtil.convertByExp(value, anno.readConverterExp(), anno.separator()); + } else { + label = SpringUtils.getBean(DictService.class).getDictLabel(type, value, anno.separator()); + } + return new WriteCellData<>(label); + } + + private ExcelDictFormat getAnnotation(Field field) { + return AnnotationUtil.getAnnotation(field, ExcelDictFormat.class); + } +} diff --git a/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java b/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java new file mode 100644 index 0000000..1022c51 --- /dev/null +++ b/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java @@ -0,0 +1,87 @@ +package org.dromara.common.excel.convert; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import cn.idev.excel.converters.Converter; +import cn.idev.excel.enums.CellDataTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; +import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.metadata.property.ExcelContentProperty; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.reflect.ReflectUtils; +import org.dromara.common.excel.annotation.ExcelEnumFormat; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +/** + * 枚举格式化转换处理 + * + * @author shihongwei + */ +@Slf4j +public class ExcelEnumConvert implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Object.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return null; + } + + @Override + public Object convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + cellData.checkEmpty(); + // Excel中填入的是枚举中指定的描述 + Object textValue = switch (cellData.getType()) { + case STRING, DIRECT_STRING, RICH_TEXT_STRING -> cellData.getStringValue(); + case NUMBER -> cellData.getNumberValue(); + case BOOLEAN -> cellData.getBooleanValue(); + default -> throw new IllegalArgumentException("单元格类型异常!"); + }; + // 如果是空值 + if (ObjectUtil.isNull(textValue)) { + return null; + } + Map enumCodeToTextMap = beforeConvert(contentProperty); + // 从Java输出至Excel是code转text + // 因此从Excel转Java应该将text与code对调 + Map enumTextToCodeMap = new HashMap<>(); + enumCodeToTextMap.forEach((key, value) -> enumTextToCodeMap.put(value, key)); + // 应该从text -> code中查找 + Object codeValue = enumTextToCodeMap.get(textValue); + return Convert.convert(contentProperty.getField().getType(), codeValue); + } + + @Override + public WriteCellData convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (ObjectUtil.isNull(object)) { + return new WriteCellData<>(""); + } + Map enumValueMap = beforeConvert(contentProperty); + String value = Convert.toStr(enumValueMap.get(object), ""); + return new WriteCellData<>(value); + } + + private Map beforeConvert(ExcelContentProperty contentProperty) { + ExcelEnumFormat anno = getAnnotation(contentProperty.getField()); + Map enumValueMap = new HashMap<>(); + Enum[] enumConstants = anno.enumClass().getEnumConstants(); + for (Enum enumConstant : enumConstants) { + Object codeValue = ReflectUtils.invokeGetter(enumConstant, anno.codeField()); + String textValue = ReflectUtils.invokeGetter(enumConstant, anno.textField()); + enumValueMap.put(codeValue, textValue); + } + return enumValueMap; + } + + private ExcelEnumFormat getAnnotation(Field field) { + return AnnotationUtil.getAnnotation(field, ExcelEnumFormat.class); + } +} diff --git a/src/main/java/org/dromara/common/excel/convert/SafetyManagerNameExcelConvert.java b/src/main/java/org/dromara/common/excel/convert/SafetyManagerNameExcelConvert.java new file mode 100644 index 0000000..5f1dbf5 --- /dev/null +++ b/src/main/java/org/dromara/common/excel/convert/SafetyManagerNameExcelConvert.java @@ -0,0 +1,43 @@ +package org.dromara.common.excel.convert; + +import cn.idev.excel.converters.Converter; +import cn.idev.excel.enums.CellDataTypeEnum; +import cn.idev.excel.metadata.GlobalConfiguration; +import cn.idev.excel.metadata.data.ReadCellData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.metadata.property.ExcelContentProperty; +import com.hotwj.platform.resourceManagement.companySafetyManager.domain.HotCompanySafetyManager; +import com.hotwj.platform.resourceManagement.companySafetyManager.mapper.HotCompanySafetyManagerMapper; +import org.dromara.common.core.utils.SpringUtils; + +/** + * 导出时将 安全管理人员ID 转换为姓名 + */ +public class SafetyManagerNameExcelConvert implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Long.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Long convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + return null; + } + + @Override + public WriteCellData convertToExcelData(Long value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (value == null) { + return new WriteCellData<>(""); + } + HotCompanySafetyManagerMapper mapper = SpringUtils.getBean(HotCompanySafetyManagerMapper.class); + HotCompanySafetyManager entity = mapper.selectById(value); + String name = entity != null ? entity.getName() : ""; + return new WriteCellData<>(name); + } +} diff --git a/src/main/java/org/dromara/common/excel/core/CellMergeHandler.java b/src/main/java/org/dromara/common/excel/core/CellMergeHandler.java new file mode 100644 index 0000000..0614afa --- /dev/null +++ b/src/main/java/org/dromara/common/excel/core/CellMergeHandler.java @@ -0,0 +1,200 @@ +package org.dromara.common.excel.core; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import cn.idev.excel.annotation.ExcelIgnore; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import lombok.SneakyThrows; +import org.apache.poi.ss.util.CellRangeAddress; +import org.dromara.common.core.utils.reflect.ReflectUtils; +import org.dromara.common.excel.annotation.CellMerge; + +import java.lang.reflect.Field; +import java.util.*; + +/** + * 单元格合并处理器 + * + * @author shihongwei + */ +public class CellMergeHandler { + + private final boolean hasTitle; + private int rowIndex; + + private CellMergeHandler(final boolean hasTitle) { + this.hasTitle = hasTitle; + // 行合并开始下标 + this.rowIndex = hasTitle ? 1 : 0; + } + + @SneakyThrows + public List handle(List rows) { + // 如果入参为空集合则返回空集 + if (CollUtil.isEmpty(rows)) { + return Collections.emptyList(); + } + + // 获取有合并注解的字段 + Map mergeFields = getFieldColumnIndexMap(rows.get(0).getClass()); + // 如果没有需要合并的字段则返回空集 + if (CollUtil.isEmpty(mergeFields)) { + return Collections.emptyList(); + } + + // 结果集 + List result = new ArrayList<>(); + + // 生成两两合并单元格 + Map rowRepeatCellMap = new HashMap<>(); + for (Map.Entry item : mergeFields.entrySet()) { + Field field = item.getKey(); + FieldColumnIndex itemValue = item.getValue(); + int colNum = itemValue.colIndex(); + CellMerge cellMerge = itemValue.cellMerge(); + + for (int i = 0; i < rows.size(); i++) { + // 当前行数据 + Object currentRowObj = rows.get(i); + // 当前行数据字段值 + Object currentRowObjFieldVal = ReflectUtils.invokeGetter(currentRowObj, field.getName()); + + // 空值跳过不处理 + if (currentRowObjFieldVal == null || "".equals(currentRowObjFieldVal)) { + continue; + } + + // 单元格合并Map是否存在数据,如果不存在则添加当前行的字段值 + if (!rowRepeatCellMap.containsKey(field)) { + rowRepeatCellMap.put(field, RepeatCell.of(currentRowObjFieldVal, i)); + continue; + } + + // 获取 单元格合并Map 中字段值 + RepeatCell repeatCell = rowRepeatCellMap.get(field); + Object cellValue = repeatCell.value(); + int current = repeatCell.current(); + + // 检查是否满足合并条件 + // currentRowObj 当前行数据 + // rows.get(i - 1) 上一行数据 注:由于 if (!rowRepeatCellMap.containsKey(field)) 条件的存在,所以该 i 必不可能小于1 + // cellMerge 当前行字段合并注解 + boolean merge = isMerge(currentRowObj, rows.get(i - 1), cellMerge); + + // 是否添加到结果集 + boolean isAddResult = false; + // 最新行 + int lastRow = i + rowIndex - 1; + + // 如果当前行字段值和缓存中的字段值不相等,或不满足合并条件,则替换 + if (!currentRowObjFieldVal.equals(cellValue) || !merge) { + rowRepeatCellMap.put(field, RepeatCell.of(currentRowObjFieldVal, i)); + isAddResult = true; + } + + // 如果最后一行不能合并,检查之前的数据是否需要合并;如果最后一行可以合并,则直接合并到最后 + if (i == rows.size() - 1) { + isAddResult = true; + if (i > current) { + lastRow = i + rowIndex; + } + } + + if (isAddResult && i > current) { + result.add(new CellRangeAddress(current + rowIndex, lastRow, colNum, colNum)); + } + } + } + return result; + } + + /** + * 获取带有合并注解的字段列索引和合并注解信息Map集 + */ + private Map getFieldColumnIndexMap(Class clazz) { + boolean annotationPresent = clazz.isAnnotationPresent(ExcelIgnoreUnannotated.class); + Field[] fields = ReflectUtils.getFields(clazz, field -> { + if ("serialVersionUID".equals(field.getName())) { + return false; + } + if (field.isAnnotationPresent(ExcelIgnore.class)) { + return false; + } + return !annotationPresent || field.isAnnotationPresent(ExcelProperty.class); + }); + + // 有注解的字段 + Map mergeFields = new HashMap<>(); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + if (!field.isAnnotationPresent(CellMerge.class)) { + continue; + } + CellMerge cm = field.getAnnotation(CellMerge.class); + int index = cm.index() == -1 ? i : cm.index(); + mergeFields.put(field, FieldColumnIndex.of(index, cm)); + + if (hasTitle) { + ExcelProperty property = field.getAnnotation(ExcelProperty.class); + rowIndex = Math.max(rowIndex, property.value().length); + } + } + return mergeFields; + } + + private boolean isMerge(Object currentRow, Object preRow, CellMerge cellMerge) { + final String[] mergeBy = cellMerge.mergeBy(); + if (StrUtil.isAllNotBlank(mergeBy)) { + //比对当前行和上一行的各个属性值一一比对 如果全为真 则为真 + for (String fieldName : mergeBy) { + final Object valCurrent = ReflectUtil.getFieldValue(currentRow, fieldName); + final Object valPre = ReflectUtil.getFieldValue(preRow, fieldName); + if (!Objects.equals(valPre, valCurrent)) { + //依赖字段如有任一不等值,则标记为不可合并 + return false; + } + } + } + return true; + } + + /** + * 单元格合并 + */ + record RepeatCell(Object value, int current) { + static RepeatCell of(Object value, int current) { + return new RepeatCell(value, current); + } + } + + /** + * 字段列索引和合并注解信息 + */ + record FieldColumnIndex(int colIndex, CellMerge cellMerge) { + static FieldColumnIndex of(int colIndex, CellMerge cellMerge) { + return new FieldColumnIndex(colIndex, cellMerge); + } + } + + /** + * 创建一个单元格合并处理器实例 + * + * @param hasTitle 是否合并标题 + * @return 单元格合并处理器 + */ + public static CellMergeHandler of(final boolean hasTitle) { + return new CellMergeHandler(hasTitle); + } + + /** + * 创建一个单元格合并处理器实例(默认不合并标题) + * + * @return 单元格合并处理器 + */ + public static CellMergeHandler of() { + return new CellMergeHandler(false); + } + +} diff --git a/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java b/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java new file mode 100644 index 0000000..a67069f --- /dev/null +++ b/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java @@ -0,0 +1,59 @@ +package org.dromara.common.excel.core; + +import cn.hutool.core.collection.CollUtil; +import cn.idev.excel.metadata.Head; +import cn.idev.excel.write.handler.WorkbookWriteHandler; +import cn.idev.excel.write.handler.context.WorkbookWriteHandlerContext; +import cn.idev.excel.write.merge.AbstractMergeStrategy; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddress; + +import java.util.List; + +/** + * 列值重复合并策略 + * + * @author shihongwei + */ +@Slf4j +public class CellMergeStrategy extends AbstractMergeStrategy implements WorkbookWriteHandler { + + private final List cellList; + + public CellMergeStrategy(List cellList) { + this.cellList = cellList; + } + + public CellMergeStrategy(List list, boolean hasTitle) { + this.cellList = CellMergeHandler.of(hasTitle).handle(list); + } + + @Override + protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { + if (CollUtil.isEmpty(cellList)){ + return; + } + //单元格写入了,遍历合并区域,如果该Cell在区域内,但非首行,则清空 + final int rowIndex = cell.getRowIndex(); + for (CellRangeAddress cellAddresses : cellList) { + final int firstRow = cellAddresses.getFirstRow(); + if (cellAddresses.isInRange(cell) && rowIndex != firstRow){ + cell.setBlank(); + } + } + } + + @Override + public void afterWorkbookDispose(final WorkbookWriteHandlerContext context) { + if (CollUtil.isEmpty(cellList)){ + return; + } + //当前表格写完后,统一写入 + for (CellRangeAddress item : cellList) { + context.getWriteContext().writeSheetHolder().getSheet().addMergedRegion(item); + } + } + +} diff --git a/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java b/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java new file mode 100644 index 0000000..1039324 --- /dev/null +++ b/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java @@ -0,0 +1,104 @@ +package org.dromara.common.excel.core; + +import cn.hutool.core.util.StrUtil; +import cn.idev.excel.context.AnalysisContext; +import cn.idev.excel.event.AnalysisEventListener; +import cn.idev.excel.exception.ExcelAnalysisException; +import cn.idev.excel.exception.ExcelDataConvertException; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.ValidatorUtils; +import org.dromara.common.json.utils.JsonUtils; + +import java.util.Map; +import java.util.Set; + +/** + * Excel 导入监听 + * + * @author shihongwei + * @author shihongwei + */ +@Slf4j +@NoArgsConstructor +public class DefaultExcelListener extends AnalysisEventListener implements ExcelListener { + + /** + * 是否Validator检验,默认为是 + */ + private Boolean isValidate = Boolean.TRUE; + + /** + * excel 表头数据 + */ + private Map headMap; + + /** + * 导入回执 + */ + private ExcelResult excelResult; + + public DefaultExcelListener(boolean isValidate) { + this.excelResult = new DefaultExcelResult<>(); + this.isValidate = isValidate; + } + + /** + * 处理异常 + * + * @param exception ExcelDataConvertException + * @param context Excel 上下文 + */ + @Override + public void onException(Exception exception, AnalysisContext context) throws Exception { + String errMsg = null; + if (exception instanceof ExcelDataConvertException excelDataConvertException) { + // 如果是某一个单元格的转换异常 能获取到具体行号 + Integer rowIndex = excelDataConvertException.getRowIndex(); + Integer columnIndex = excelDataConvertException.getColumnIndex(); + errMsg = StrUtil.format("第{}行-第{}列-表头{}: 解析异常
", + rowIndex + 1, columnIndex + 1, headMap.get(columnIndex)); + if (log.isDebugEnabled()) { + log.error(errMsg); + } + } + if (exception instanceof ConstraintViolationException constraintViolationException) { + Set> constraintViolations = constraintViolationException.getConstraintViolations(); + String constraintViolationsMsg = StreamUtils.join(constraintViolations, ConstraintViolation::getMessage, ", "); + errMsg = StrUtil.format("第{}行数据校验异常: {}", context.readRowHolder().getRowIndex() + 1, constraintViolationsMsg); + if (log.isDebugEnabled()) { + log.error(errMsg); + } + } + excelResult.getErrorList().add(errMsg); + throw new ExcelAnalysisException(errMsg); + } + + @Override + public void invokeHeadMap(Map headMap, AnalysisContext context) { + this.headMap = headMap; + log.debug("解析到一条表头数据: {}", JsonUtils.toJsonString(headMap)); + } + + @Override + public void invoke(T data, AnalysisContext context) { + if (isValidate) { + ValidatorUtils.validate(data); + } + excelResult.getList().add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + log.debug("所有数据解析完成!"); + } + + @Override + public ExcelResult getExcelResult() { + return excelResult; + } + +} diff --git a/src/main/java/org/dromara/common/excel/core/DefaultExcelResult.java b/src/main/java/org/dromara/common/excel/core/DefaultExcelResult.java new file mode 100644 index 0000000..d5ba370 --- /dev/null +++ b/src/main/java/org/dromara/common/excel/core/DefaultExcelResult.java @@ -0,0 +1,73 @@ +package org.dromara.common.excel.core; + +import cn.hutool.core.util.StrUtil; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +/** + * 默认excel返回对象 + * + * @author shihongwei + * @author shihongwei + */ +public class DefaultExcelResult implements ExcelResult { + + /** + * 数据对象list + */ + @Setter + private List list; + + /** + * 错误信息列表 + */ + @Setter + private List errorList; + + public DefaultExcelResult() { + this.list = new ArrayList<>(); + this.errorList = new ArrayList<>(); + } + + public DefaultExcelResult(List list, List errorList) { + this.list = list; + this.errorList = errorList; + } + + public DefaultExcelResult(ExcelResult excelResult) { + this.list = excelResult.getList(); + this.errorList = excelResult.getErrorList(); + } + + @Override + public List getList() { + return list; + } + + @Override + public List getErrorList() { + return errorList; + } + + /** + * 获取导入回执 + * + * @return 导入回执 + */ + @Override + public String getAnalysis() { + int successCount = list.size(); + int errorCount = errorList.size(); + if (successCount == 0) { + return "读取失败,未解析到数据"; + } else { + if (errorCount == 0) { + return StrUtil.format("恭喜您,全部读取成功!共{}条", successCount); + } else { + return ""; + } + } + } +} diff --git a/src/main/java/org/dromara/common/excel/core/DropDownOptions.java b/src/main/java/org/dromara/common/excel/core/DropDownOptions.java new file mode 100644 index 0000000..ad06cdb --- /dev/null +++ b/src/main/java/org/dromara/common/excel/core/DropDownOptions.java @@ -0,0 +1,150 @@ +package org.dromara.common.excel.core; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.dromara.common.core.exception.ServiceException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + *

Excel下拉可选项

+ * 注意:为确保下拉框解析正确,传值务必使用createOptionValue()做为值的拼接 + * + * @author shihongwei + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@SuppressWarnings("unused") +public class DropDownOptions { + /** + * 一级下拉所在列index,从0开始算 + */ + private int index = 0; + /** + * 二级下拉所在的index,从0开始算,不能与一级相同 + */ + private int nextIndex = 0; + /** + * 一级下拉所包含的数据 + */ + private List options = new ArrayList<>(); + /** + * 二级下拉所包含的数据Map + *

以每一个一级选项值为Key,每个一级选项对应的二级数据为Value

+ */ + private Map> nextOptions = new HashMap<>(); + /** + * 分隔符 + */ + private static final String DELIMITER = "_"; + + /** + * 创建只有一级的下拉选 + */ + public DropDownOptions(int index, List options) { + this.index = index; + this.options = options; + } + + /** + *

创建每个选项可选值

+ *

注意:不能以数字,特殊符号开头,选项中不可以包含任何运算符号

+ * + * @param vars 可选值内包含的参数 + * @return 合规的可选值 + */ + public static String createOptionValue(Object... vars) { + StringBuilder stringBuffer = new StringBuilder(); + String regex = "^[\\S\\d\\u4e00-\\u9fa5]+$"; + for (int i = 0; i < vars.length; i++) { + String var = StrUtil.trimToEmpty(Convert.toStr(vars[i])); + if (!var.matches(regex)) { + throw new ServiceException("选项数据不符合规则,仅允许使用中英文字符以及数字"); + } + stringBuffer.append(var); + if (i < vars.length - 1) { + // 直至最后一个前,都以_作为切割线 + stringBuffer.append(DELIMITER); + } + } + if (stringBuffer.toString().matches("^\\d_*$")) { + throw new ServiceException("禁止以数字开头"); + } + return stringBuffer.toString(); + } + + /** + * 将处理后合理的可选值解析为原始的参数 + * + * @param option 经过处理后的合理的可选项 + * @return 原始的参数 + */ + public static List analyzeOptionValue(String option) { + return StrUtil.split(option, DELIMITER, true, true); + } + + /** + * 创建级联下拉选项 + * + * @param parentList 父实体可选项原始数据 + * @param parentIndex 父下拉选位置 + * @param sonList 子实体可选项原始数据 + * @param sonIndex 子下拉选位置 + * @param parentHowToGetIdFunction 父类如何获取唯一标识 + * @param sonHowToGetParentIdFunction 子类如何获取父类的唯一标识 + * @param howToBuildEveryOption 如何生成下拉选内容 + * @return 级联下拉选项 + */ + public static DropDownOptions buildLinkedOptions(List parentList, + int parentIndex, + List sonList, + int sonIndex, + Function parentHowToGetIdFunction, + Function sonHowToGetParentIdFunction, + Function howToBuildEveryOption) { + DropDownOptions parentLinkSonOptions = new DropDownOptions(); + // 先创建父类的下拉 + parentLinkSonOptions.setIndex(parentIndex); + parentLinkSonOptions.setOptions( + parentList.stream() + .map(howToBuildEveryOption) + .collect(Collectors.toList()) + ); + // 提取父-子级联下拉 + Map> sonOptions = new HashMap<>(); + // 父级依据自己的ID分组 + Map> parentGroupByIdMap = + parentList.stream().collect(Collectors.groupingBy(parentHowToGetIdFunction)); + // 遍历每个子集,提取到Map中 + sonList.forEach(everySon -> { + if (parentGroupByIdMap.containsKey(sonHowToGetParentIdFunction.apply(everySon))) { + // 找到对应的上级 + T parentObj = parentGroupByIdMap.get(sonHowToGetParentIdFunction.apply(everySon)).get(0); + // 提取名称和ID作为Key + String key = howToBuildEveryOption.apply(parentObj); + // Key对应的Value + List thisParentSonOptionList; + if (sonOptions.containsKey(key)) { + thisParentSonOptionList = sonOptions.get(key); + } else { + thisParentSonOptionList = new ArrayList<>(); + sonOptions.put(key, thisParentSonOptionList); + } + // 往Value中添加当前子集选项 + thisParentSonOptionList.add(howToBuildEveryOption.apply(everySon)); + } + }); + parentLinkSonOptions.setNextIndex(sonIndex); + parentLinkSonOptions.setNextOptions(sonOptions); + return parentLinkSonOptions; + } +} diff --git a/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java b/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java new file mode 100644 index 0000000..c024a25 --- /dev/null +++ b/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java @@ -0,0 +1,402 @@ +package org.dromara.common.excel.core; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.EnumUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.idev.excel.metadata.FieldCache; +import cn.idev.excel.metadata.FieldWrapper; +import cn.idev.excel.util.ClassUtils; +import cn.idev.excel.write.handler.SheetWriteHandler; +import cn.idev.excel.write.metadata.holder.WriteSheetHolder; +import cn.idev.excel.write.metadata.holder.WriteWorkbookHolder; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.ss.util.WorkbookUtil; +import org.apache.poi.xssf.usermodel.XSSFDataValidation; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.service.DictService; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.annotation.ExcelEnumFormat; + +import java.lang.reflect.Field; +import java.util.*; + +/** + *

Excel表格下拉选操作

+ * 考虑到下拉选过多可能导致Excel打开缓慢的问题,只校验前1000行 + *

+ * 即只有前1000行的数据可以用下拉框,超出的自行通过限制数据量的形式,第二次输出 + * + * @author shihongwei + */ +@Slf4j +public class ExcelDownHandler implements SheetWriteHandler { + + /** + * Excel表格中的列名英文 + * 仅为了解析列英文,禁止修改 + */ + private static final String EXCEL_COLUMN_NAME = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + /** + * 单选数据Sheet名 + */ + private static final String OPTIONS_SHEET_NAME = "options"; + /** + * 联动选择数据Sheet名的头 + */ + private static final String LINKED_OPTIONS_SHEET_NAME = "linkedOptions"; + /** + * 下拉可选项 + */ + private final List dropDownOptions; + private final DictService dictService; + /** + * 当前单选进度 + */ + private int currentOptionsColumnIndex; + /** + * 当前联动选择进度 + */ + private int currentLinkedOptionsSheetIndex; + + public ExcelDownHandler(List options) { + this.dropDownOptions = options; + this.currentOptionsColumnIndex = 0; + this.currentLinkedOptionsSheetIndex = 0; + this.dictService = SpringUtils.getBean(DictService.class); + } + + /** + *

开始创建下拉数据

+ * 1.通过解析传入的@ExcelProperty同级是否标注有@DropDown选项 + * 如果有且设置了value值,则将其直接置为下拉可选项 + *

+ * 2.或者在调用ExcelUtil时指定了可选项,将依据传入的可选项做下拉 + *

+ * 3.二者并存,注意调用方式 + */ + @Override + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + Sheet sheet = writeSheetHolder.getSheet(); + // 开始设置下拉框 HSSFWorkbook + DataValidationHelper helper = sheet.getDataValidationHelper(); + Workbook workbook = writeWorkbookHolder.getWorkbook(); + FieldCache fieldCache = ClassUtils.declaredFields(writeWorkbookHolder.getClazz(), writeWorkbookHolder); + for (Map.Entry entry : fieldCache.getSortedFieldMap().entrySet()) { + Integer index = entry.getKey(); + FieldWrapper wrapper = entry.getValue(); + Field field = wrapper.getField(); + // 循环实体中的每个属性 + // 可选的下拉值 + List options = new ArrayList<>(); + if (field.isAnnotationPresent(ExcelDictFormat.class)) { + // 如果指定了@ExcelDictFormat,则使用字典的逻辑 + ExcelDictFormat format = field.getDeclaredAnnotation(ExcelDictFormat.class); + String dictType = format.dictType(); + String converterExp = format.readConverterExp(); + if (StringUtils.isNotBlank(dictType)) { + // 如果传递了字典名,则依据字典建立下拉 + Collection values = Optional.ofNullable(dictService.getAllDictByDictType(dictType)) + .orElseThrow(() -> new ServiceException("字典 {} 不存在", dictType)) + .values(); + options = new ArrayList<>(values); + } else if (StringUtils.isNotBlank(converterExp)) { + // 如果指定了确切的值,则直接解析确切的值 + List strList = StringUtils.splitList(converterExp, format.separator()); + options = StreamUtils.toList(strList, s -> StringUtils.split(s, "=")[1]); + } + } else if (field.isAnnotationPresent(ExcelEnumFormat.class)) { + // 否则如果指定了@ExcelEnumFormat,则使用枚举的逻辑 + ExcelEnumFormat format = field.getDeclaredAnnotation(ExcelEnumFormat.class); + List values = EnumUtil.getFieldValues(format.enumClass(), format.textField()); + options = StreamUtils.toList(values, Convert::toStr); + } + if (ObjectUtil.isNotEmpty(options)) { + // 仅当下拉可选项不为空时执行 + if (options.size() > 20) { + // 这里限制如果可选项大于20,则使用额外表形式 + dropDownWithSheet(helper, workbook, sheet, index, options); + } else { + // 否则使用固定值形式 + dropDownWithSimple(helper, sheet, index, options); + } + } + } + if (CollUtil.isEmpty(dropDownOptions)) { + return; + } + dropDownOptions.forEach(everyOptions -> { + // 如果传递了下拉框选择器参数 + if (!everyOptions.getNextOptions().isEmpty()) { + // 当二级选项不为空时,使用额外关联表的形式 + dropDownLinkedOptions(helper, workbook, sheet, everyOptions); + } else if (everyOptions.getOptions().size() > 10) { + // 当一级选项参数个数大于10,使用额外表的形式 + dropDownWithSheet(helper, workbook, sheet, everyOptions.getIndex(), everyOptions.getOptions()); + } else { + // 否则使用默认形式 + dropDownWithSimple(helper, sheet, everyOptions.getIndex(), everyOptions.getOptions()); + } + }); + } + + /** + *

简单下拉框

+ * 直接将可选项拼接为指定列的数据校验值 + * + * @param celIndex 列index + * @param value 下拉选可选值 + */ + private void dropDownWithSimple(DataValidationHelper helper, Sheet sheet, Integer celIndex, List value) { + if (ObjectUtil.isEmpty(value)) { + return; + } + this.markOptionsToSheet(helper, sheet, celIndex, helper.createExplicitListConstraint(ArrayUtil.toArray(value, String.class))); + } + + /** + *

额外表格形式的级联下拉框

+ * + * @param options 额外表格形式存储的下拉可选项 + */ + private void dropDownLinkedOptions(DataValidationHelper helper, Workbook workbook, Sheet sheet, DropDownOptions options) { + String linkedOptionsSheetName = String.format("%s_%d", LINKED_OPTIONS_SHEET_NAME, currentLinkedOptionsSheetIndex); + // 创建联动下拉数据表 + Sheet linkedOptionsDataSheet = workbook.createSheet(WorkbookUtil.createSafeSheetName(linkedOptionsSheetName)); + // 将下拉表隐藏 + workbook.setSheetHidden(workbook.getSheetIndex(linkedOptionsDataSheet), true); + // 选项数据 + List firstOptions = options.getOptions(); + Map> secoundOptionsMap = options.getNextOptions(); + + // 采用按行填充数据的方式,避免出现数据无法写入的问题 + // Attempting to write a row in the range that is already written to disk + + // 使用ArrayList记载数据,防止乱序 + List columnNames = new ArrayList<>(); + // 写入第一行,即第一级的数据 + Row firstRow = linkedOptionsDataSheet.createRow(0); + for (int columnIndex = 0; columnIndex < firstOptions.size(); columnIndex++) { + String columnName = firstOptions.get(columnIndex); + firstRow.createCell(columnIndex) + .setCellValue(columnName); + columnNames.add(columnName); + } + + // 创建名称管理器 + Name name = workbook.createName(); + // 设置名称管理器的别名 + name.setNameName(linkedOptionsSheetName); + // 以横向第一行创建一级下拉拼接引用位置 + String firstOptionsFunction = String.format("%s!$%s$1:$%s$1", + linkedOptionsSheetName, + getExcelColumnName(0), + getExcelColumnName(firstOptions.size()) + ); + // 设置名称管理器的引用位置 + name.setRefersToFormula(firstOptionsFunction); + // 设置数据校验为序列模式,引用的是名称管理器中的别名 + this.markOptionsToSheet(helper, sheet, options.getIndex(), helper.createFormulaListConstraint(linkedOptionsSheetName)); + + // 创建二级选项的名称管理器 + for (int columIndex = 0; columIndex < columnNames.size(); columIndex++) { + // 列名 + String firstOptionsColumnName = getExcelColumnName(columIndex); + // 对应的一级值 + String thisFirstOptionsValue = columnNames.get(columIndex); + + // 以该一级选项值创建子名称管理器 + Name sonName = workbook.createName(); + // 设置名称管理器的别名 + sonName.setNameName(thisFirstOptionsValue); + // 以第二行该列数据拼接引用位置 + String sonFunction = String.format("%s!$%s$2:$%s$%d", + linkedOptionsSheetName, + firstOptionsColumnName, + firstOptionsColumnName, + // 二级选项存在则设置为(选项个数+1)行,否则设置为2行 + Math.max(Optional.ofNullable(secoundOptionsMap.get(thisFirstOptionsValue)) + .orElseGet(ArrayList::new).size(), 1) + 1 + ); + // 设置名称管理器的引用位置 + sonName.setRefersToFormula(sonFunction); + // 数据验证为序列模式,引用到每一个主表中的二级选项位置 + // 创建子项的名称管理器,只是为了使得Excel可以识别到数据 + String mainSheetFirstOptionsColumnName = getExcelColumnName(options.getIndex()); + for (int i = 0; i < 100; i++) { + // 以一级选项对应的主体所在位置创建二级下拉 + String secondOptionsFunction = String.format("=INDIRECT(%s%d)", mainSheetFirstOptionsColumnName, i + 1); + // 二级只能主表每一行的每一列添加二级校验 + markLinkedOptionsToSheet(helper, sheet, i, options.getNextIndex(), helper.createFormulaListConstraint(secondOptionsFunction)); + } + } + + // 将二级数据处理为按行区分 + Map> columnValueMap = new HashMap<>(); + int currentRow = 1; + while (currentRow >= 0) { + boolean flag = false; + List rowData = new ArrayList<>(); + for (String columnName : columnNames) { + List data = secoundOptionsMap.get(columnName); + if (CollUtil.isEmpty(data)) { + // 添加空字符串填充位置 + rowData.add(" "); + continue; + } + // 取第一个 + String str = data.get(0); + rowData.add(str); + // 通过移除的方式避免重复 + data.remove(0); + // 设置可以继续 + flag = true; + } + columnValueMap.put(currentRow, rowData); + // 可以继续,则增加行数,否则置为负数跳出循环 + if (flag) { + currentRow++; + } else { + currentRow = -1; + } + } + + // 填充第二级选项数据 + columnValueMap.forEach((rowIndex, rowValues) -> { + Row row = linkedOptionsDataSheet.createRow(rowIndex); + for (int columnIndex = 0; columnIndex < rowValues.size(); columnIndex++) { + String rowValue = rowValues.get(columnIndex); + // 填充位置的部分不渲染 + if (StrUtil.isNotBlank(rowValue)) { + row.createCell(columnIndex) + .setCellValue(rowValue); + } + } + }); + + currentLinkedOptionsSheetIndex++; + } + + /** + *

额外表格形式的普通下拉框

+ * 由于下拉框可选值数量过多,为提升Excel打开效率,使用额外表格形式做下拉 + * + * @param celIndex 下拉选 + * @param value 下拉选可选值 + */ + private void dropDownWithSheet(DataValidationHelper helper, Workbook workbook, Sheet sheet, Integer celIndex, List value) { + //由于poi的写出相关问题,超过100个会被临时写进硬盘,导致后续内存合并会出Attempting to write a row[] in the range [] that is already written to disk + String tmpOptionsSheetName = OPTIONS_SHEET_NAME + "_" + currentOptionsColumnIndex; + // 创建下拉数据表 + Sheet simpleDataSheet = Optional.ofNullable(workbook.getSheet(WorkbookUtil.createSafeSheetName(tmpOptionsSheetName))) + .orElseGet(() -> workbook.createSheet(WorkbookUtil.createSafeSheetName(tmpOptionsSheetName))); + // 将下拉表隐藏 + workbook.setSheetHidden(workbook.getSheetIndex(simpleDataSheet), true); + // 完善纵向的一级选项数据表 + for (int i = 0; i < value.size(); i++) { + int finalI = i; + // 获取每一选项行,如果没有则创建 + Row row = Optional.ofNullable(simpleDataSheet.getRow(i)) + .orElseGet(() -> simpleDataSheet.createRow(finalI)); + // 获取本级选项对应的选项列,如果没有则创建。上述采用多个sheet,默认索引为1列 + Cell cell = Optional.ofNullable(row.getCell(0)) + .orElseGet(() -> row.createCell(0)); + // 设置值 + cell.setCellValue(value.get(i)); + } + + // 创建名称管理器 + Name name = workbook.createName(); + // 设置名称管理器的别名 + String nameName = String.format("%s_%d", tmpOptionsSheetName, celIndex); + name.setNameName(nameName); + // 以纵向第一列创建一级下拉拼接引用位置 + String function = String.format("%s!$%s$1:$%s$%d", + tmpOptionsSheetName, + getExcelColumnName(0), + getExcelColumnName(0), + value.size()); + // 设置名称管理器的引用位置 + name.setRefersToFormula(function); + // 设置数据校验为序列模式,引用的是名称管理器中的别名 + this.markOptionsToSheet(helper, sheet, celIndex, helper.createFormulaListConstraint(nameName)); + currentOptionsColumnIndex++; + } + + /** + * 挂载下拉的列,仅限一级选项 + */ + private void markOptionsToSheet(DataValidationHelper helper, Sheet sheet, Integer celIndex, + DataValidationConstraint constraint) { + // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 + CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, celIndex, celIndex); + markDataValidationToSheet(helper, sheet, constraint, addressList); + } + + /** + * 挂载下拉的列,仅限二级选项 + */ + private void markLinkedOptionsToSheet(DataValidationHelper helper, Sheet sheet, Integer rowIndex, + Integer celIndex, DataValidationConstraint constraint) { + // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 + CellRangeAddressList addressList = new CellRangeAddressList(rowIndex, rowIndex, celIndex, celIndex); + markDataValidationToSheet(helper, sheet, constraint, addressList); + } + + /** + * 应用数据校验 + */ + private void markDataValidationToSheet(DataValidationHelper helper, Sheet sheet, + DataValidationConstraint constraint, CellRangeAddressList addressList) { + // 数据有效性对象 + DataValidation dataValidation = helper.createValidation(constraint, addressList); + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) { + //数据校验 + dataValidation.setSuppressDropDownArrow(true); + //错误提示 + dataValidation.setErrorStyle(DataValidation.ErrorStyle.STOP); + dataValidation.createErrorBox("提示", "此值与单元格定义数据不一致"); + dataValidation.setShowErrorBox(true); + //选定提示 + dataValidation.createPromptBox("填写说明:", "填写内容只能为下拉中数据,其他数据将导致导入失败"); + dataValidation.setShowPromptBox(true); + sheet.addValidationData(dataValidation); + } else { + dataValidation.setSuppressDropDownArrow(false); + } + sheet.addValidationData(dataValidation); + } + + /** + *

依据列index获取列名英文

+ * 依据列index转换为Excel中的列名英文 + *

例如第1列,index为0,解析出来为A列

+ * 第27列,index为26,解析为AA列 + *

第28列,index为27,解析为AB列

+ * + * @param columnIndex 列index + * @return 列index所在得英文名 + */ + private String getExcelColumnName(int columnIndex) { + // 26一循环的次数 + int columnCircleCount = columnIndex / 26; + // 26一循环内的位置 + int thisCircleColumnIndex = columnIndex % 26; + // 26一循环的次数大于0,则视为栏名至少两位 + String columnPrefix = columnCircleCount == 0 + ? StrUtil.EMPTY + : StrUtil.subWithLength(EXCEL_COLUMN_NAME, columnCircleCount - 1, 1); + // 从26一循环内取对应的栏位名 + String columnNext = StrUtil.subWithLength(EXCEL_COLUMN_NAME, thisCircleColumnIndex, 1); + // 将二者拼接即为最终的栏位名 + return columnPrefix + columnNext; + } +} diff --git a/src/main/java/org/dromara/common/excel/core/ExcelListener.java b/src/main/java/org/dromara/common/excel/core/ExcelListener.java new file mode 100644 index 0000000..9adb870 --- /dev/null +++ b/src/main/java/org/dromara/common/excel/core/ExcelListener.java @@ -0,0 +1,14 @@ +package org.dromara.common.excel.core; + +import cn.idev.excel.read.listener.ReadListener; + +/** + * Excel 导入监听 + * + * @author shihongwei + */ +public interface ExcelListener extends ReadListener { + + ExcelResult getExcelResult(); + +} diff --git a/src/main/java/org/dromara/common/excel/core/ExcelResult.java b/src/main/java/org/dromara/common/excel/core/ExcelResult.java new file mode 100644 index 0000000..75e0888 --- /dev/null +++ b/src/main/java/org/dromara/common/excel/core/ExcelResult.java @@ -0,0 +1,26 @@ +package org.dromara.common.excel.core; + +import java.util.List; + +/** + * excel返回对象 + * + * @author shihongwei + */ +public interface ExcelResult { + + /** + * 对象列表 + */ + List getList(); + + /** + * 错误列表 + */ + List getErrorList(); + + /** + * 导入回执 + */ + String getAnalysis(); +} diff --git a/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java b/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java new file mode 100644 index 0000000..69fe097 --- /dev/null +++ b/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java @@ -0,0 +1,123 @@ +package org.dromara.common.excel.handler; + +import cn.hutool.core.collection.CollUtil; +import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.metadata.data.DataFormatData; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.util.StyleUtil; +import cn.idev.excel.write.handler.CellWriteHandler; +import cn.idev.excel.write.handler.SheetWriteHandler; +import cn.idev.excel.write.handler.context.CellWriteHandlerContext; +import cn.idev.excel.write.metadata.holder.WriteSheetHolder; +import cn.idev.excel.write.metadata.style.WriteCellStyle; +import cn.idev.excel.write.metadata.style.WriteFont; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFRichTextString; +import org.dromara.common.excel.annotation.ExcelNotation; +import org.dromara.common.excel.annotation.ExcelRequired; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +/** + * 批注、必填 + * + * @author shihongwei + */ +public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler { + + /** + * 批注 + */ + private final Map notationMap; + + /** + * 头列字体颜色 + */ + private final Map headColumnMap; + + + public DataWriteHandler(Class clazz) { + notationMap = getNotationMap(clazz); + headColumnMap = getRequiredMap(clazz); + } + + @Override + public void afterCellDispose(CellWriteHandlerContext context) { + if (CollUtil.isEmpty(notationMap) && CollUtil.isEmpty(headColumnMap)) { + return; + } + // 第一行 + WriteCellData cellData = context.getFirstCellData(); + // 第一个格子 + WriteCellStyle writeCellStyle = cellData.getOrCreateStyle(); + + if (context.getHead()) { + DataFormatData dataFormatData = new DataFormatData(); + // 单元格设置为文本格式 + dataFormatData.setIndex((short) 49); + writeCellStyle.setDataFormatData(dataFormatData); + Cell cell = context.getCell(); + WriteSheetHolder writeSheetHolder = context.getWriteSheetHolder(); + Sheet sheet = writeSheetHolder.getSheet(); + Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); + Drawing drawing = sheet.createDrawingPatriarch(); + // 设置标题字体样式 + WriteFont headWriteFont = new WriteFont(); + // 加粗 + headWriteFont.setBold(true); + if (CollUtil.isNotEmpty(headColumnMap) && headColumnMap.containsKey(cell.getStringCellValue())) { + // 设置字体颜色 + headWriteFont.setColor(headColumnMap.get(cell.getStringCellValue())); + } + writeCellStyle.setWriteFont(headWriteFont); + CellStyle cellStyle = StyleUtil.buildCellStyle(workbook, null, writeCellStyle); + cell.setCellStyle(cellStyle); + + if (CollUtil.isNotEmpty(notationMap) && notationMap.containsKey(cell.getStringCellValue())) { + // 批注内容 + String notationContext = notationMap.get(cell.getStringCellValue()); + // 创建绘图对象 + Comment comment = drawing.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), 0, (short) 5, 5)); + comment.setString(new XSSFRichTextString(notationContext)); + cell.setCellComment(comment); + } + } + } + + /** + * 获取必填列 + */ + private static Map getRequiredMap(Class clazz) { + Map requiredMap = new HashMap<>(); + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + if (!field.isAnnotationPresent(ExcelRequired.class)) { + continue; + } + ExcelRequired excelRequired = field.getAnnotation(ExcelRequired.class); + ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); + requiredMap.put(excelProperty.value()[0], excelRequired.fontColor().getIndex()); + } + return requiredMap; + } + + /** + * 获取批注 + */ + private static Map getNotationMap(Class clazz) { + Map notationMap = new HashMap<>(); + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + if (!field.isAnnotationPresent(ExcelNotation.class)) { + continue; + } + ExcelNotation excelNotation = field.getAnnotation(ExcelNotation.class); + ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); + notationMap.put(excelProperty.value()[0], excelNotation.value()); + } + return notationMap; + } +} diff --git a/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java b/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java new file mode 100644 index 0000000..b6124f7 --- /dev/null +++ b/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java @@ -0,0 +1,490 @@ +package org.dromara.common.excel.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.resource.ClassPathResource; +import cn.hutool.core.util.IdUtil; +import cn.idev.excel.ExcelWriter; +import cn.idev.excel.FastExcel; +import cn.idev.excel.write.builder.ExcelWriterSheetBuilder; +import cn.idev.excel.write.metadata.WriteSheet; +import cn.idev.excel.write.metadata.fill.FillConfig; +import cn.idev.excel.write.metadata.fill.FillWrapper; +import cn.idev.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.file.FileUtils; +import org.dromara.common.excel.convert.ExcelBigNumberConvert; +import org.dromara.common.excel.convert.ExcelDateStringConvert; +import org.dromara.common.excel.core.*; +import org.dromara.common.excel.handler.DataWriteHandler; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +/** + * Excel相关处理 + * + * @author shihongwei + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ExcelUtil { + + /** + * 同步导入(适用于小数据量) + * + * @param is 输入流 + * @return 转换后集合 + */ + public static List importExcel(InputStream is, Class clazz) { + return FastExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync(); + } + + + /** + * 使用校验监听器 异步导入 同步返回 + * + * @param is 输入流 + * @param clazz 对象类型 + * @param isValidate 是否 Validator 检验 默认为是 + * @return 转换后集合 + */ + public static ExcelResult importExcel(InputStream is, Class clazz, boolean isValidate) { + DefaultExcelListener listener = new DefaultExcelListener<>(isValidate); + FastExcel.read(is, clazz, listener).sheet().doRead(); + return listener.getExcelResult(); + } + + /** + * 使用自定义监听器 异步导入 自定义返回 + * + * @param is 输入流 + * @param clazz 对象类型 + * @param listener 自定义监听器 + * @return 转换后集合 + */ + public static ExcelResult importExcel(InputStream is, Class clazz, ExcelListener listener) { + FastExcel.read(is, clazz, listener).sheet().doRead(); + return listener.getExcelResult(); + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param response 响应体 + */ + public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response) { + try { + resetResponse(sheetName, response); + ServletOutputStream os = response.getOutputStream(); + exportExcel(list, sheetName, clazz, false, os, null); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param response 响应体 + * @param options 级联下拉选 + */ + public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response, List options) { + try { + resetResponse(sheetName, response); + ServletOutputStream os = response.getOutputStream(); + exportExcel(list, sheetName, clazz, false, os, options); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param merge 是否合并单元格 + * @param response 响应体 + */ + public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, HttpServletResponse response) { + try { + resetResponse(sheetName, response); + ServletOutputStream os = response.getOutputStream(); + exportExcel(list, sheetName, clazz, merge, os, null); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param merge 是否合并单元格 + * @param response 响应体 + * @param options 级联下拉选 + */ + public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, HttpServletResponse response, List options) { + try { + resetResponse(sheetName, response); + ServletOutputStream os = response.getOutputStream(); + exportExcel(list, sheetName, clazz, merge, os, options); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param os 输出流 + */ + public static void exportExcel(List list, String sheetName, Class clazz, OutputStream os) { + exportExcel(list, sheetName, clazz, false, os, null); + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param os 输出流 + * @param options 级联下拉选内容 + */ + public static void exportExcel(List list, String sheetName, Class clazz, OutputStream os, List options) { + exportExcel(list, sheetName, clazz, false, os, options); + } + + /** + * 导出excel + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param merge 是否合并单元格 + * @param os 输出流 + */ + public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, + OutputStream os, List options) { + ExcelWriterSheetBuilder builder = FastExcel.write(os, clazz) + .autoCloseStream(false) + // 自动适配 + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + // 日期转换为字符串避免Excel显示#### + .registerConverter(new ExcelDateStringConvert()) + .registerWriteHandler(new DataWriteHandler(clazz)) + .sheet(sheetName); + if (merge) { + // 合并处理器 + builder.registerWriteHandler(new CellMergeStrategy(list, true)); + } + // 添加下拉框操作 + builder.registerWriteHandler(new ExcelDownHandler(options)); + builder.doWrite(list); + } + + /** + * 导出excel + * + * @param headType 带Excel注解的类型 + * @param os 输出流 + * @param options Excel下拉可选项 + * @param consumer 导出助手消费函数 + */ + public static void exportExcel(Class headType, OutputStream os, List options, Consumer> consumer) { + try (ExcelWriter writer = FastExcel.write(os, headType) + .autoCloseStream(false) + // 自动适配 + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + // 日期转换为字符串避免Excel显示#### + .registerConverter(new ExcelDateStringConvert()) + // 批注必填项处理 + .registerWriteHandler(new DataWriteHandler(headType)) + // 添加下拉框操作 + .registerWriteHandler(new ExcelDownHandler(options)) + .build()) { + // 执行消费函数 + consumer.accept(ExcelWriterWrapper.of(writer)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * 导出excel + * + * @param headType 带Excel注解的类型 + * @param os 输出流 + * @param consumer 导出助手消费函数 + */ + public static void exportExcel(Class headType, OutputStream os, Consumer> consumer) { + exportExcel(headType, os, null, consumer); + } + + /** + * 单表多数据模板导出 模板格式为 {.属性} + * + * @param filename 文件名 + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param response 响应体 + */ + public static void exportTemplate(List data, String filename, String templatePath, HttpServletResponse response) { + try { + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } + resetResponse(filename, response); + ServletOutputStream os = response.getOutputStream(); + exportTemplate(data, templatePath, os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 单表多数据模板导出 模板格式为 {.属性} + * + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param os 输出流 + */ + public static void exportTemplate(List data, String templatePath, OutputStream os) { + ClassPathResource templateResource = new ClassPathResource(templatePath); + ExcelWriter excelWriter = FastExcel.write(os) + .withTemplate(templateResource.getStream()) + .autoCloseStream(false) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + // 日期转换为字符串避免Excel显示#### + .registerConverter(new ExcelDateStringConvert()) + .registerWriteHandler(new DataWriteHandler(data.get(0).getClass())) + .build(); + WriteSheet writeSheet = FastExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + // 单表多数据导出 模板格式为 {.属性} + for (T d : data) { + excelWriter.fill(d, fillConfig, writeSheet); + } + excelWriter.finish(); + } + + /** + * 多表多数据模板导出 模板格式为 {key.属性} + * + * @param filename 文件名 + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param response 响应体 + */ + public static void exportTemplateMultiList(Map data, String filename, String templatePath, HttpServletResponse response) { + try { + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } + resetResponse(filename, response); + ServletOutputStream os = response.getOutputStream(); + exportTemplateMultiList(data, templatePath, os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 多sheet模板导出 模板格式为 {key.属性} + * + * @param filename 文件名 + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param response 响应体 + */ + public static void exportTemplateMultiSheet(List> data, String filename, String templatePath, HttpServletResponse response) { + try { + if (CollUtil.isEmpty(data)) { + throw new IllegalArgumentException("数据为空"); + } + resetResponse(filename, response); + ServletOutputStream os = response.getOutputStream(); + exportTemplateMultiSheet(data, templatePath, os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } + + /** + * 多表多数据模板导出 模板格式为 {key.属性} + * + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param os 输出流 + */ + public static void exportTemplateMultiList(Map data, String templatePath, OutputStream os) { + ClassPathResource templateResource = new ClassPathResource(templatePath); + ExcelWriter excelWriter = FastExcel.write(os) + .withTemplate(templateResource.getStream()) + .autoCloseStream(false) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + // 日期转换为字符串避免Excel显示#### + .registerConverter(new ExcelDateStringConvert()) + .build(); + WriteSheet writeSheet = FastExcel.writerSheet().build(); + for (Map.Entry map : data.entrySet()) { + // 设置列表后续还有数据 + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + if (map.getValue() instanceof Collection) { + // 多表导出必须使用 FillWrapper + excelWriter.fill(new FillWrapper(map.getKey(), (Collection) map.getValue()), fillConfig, writeSheet); + } else { + excelWriter.fill(map.getValue(), fillConfig, writeSheet); + } + } + excelWriter.finish(); + } + + /** + * 多sheet模板导出 模板格式为 {key.属性} + * + * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 + * 例如: excel/temp.xlsx + * 重点: 模板文件必须放置到启动类对应的 resource 目录下 + * @param data 模板需要的数据 + * @param os 输出流 + */ + public static void exportTemplateMultiSheet(List> data, String templatePath, OutputStream os) { + ClassPathResource templateResource = new ClassPathResource(templatePath); + ExcelWriter excelWriter = FastExcel.write(os) + .withTemplate(templateResource.getStream()) + .autoCloseStream(false) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + // 日期转换为字符串避免Excel显示#### + .registerConverter(new ExcelDateStringConvert()) + .build(); + for (int i = 0; i < data.size(); i++) { + WriteSheet writeSheet = FastExcel.writerSheet(i).build(); + for (Map.Entry map : data.get(i).entrySet()) { + // 设置列表后续还有数据 + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + if (map.getValue() instanceof Collection) { + // 多表导出必须使用 FillWrapper + excelWriter.fill(new FillWrapper(map.getKey(), (Collection) map.getValue()), fillConfig, writeSheet); + } else { + excelWriter.fill(map.getValue(), writeSheet); + } + } + } + excelWriter.finish(); + } + + /** + * 重置响应体 + */ + private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException { + String filename = encodingFilename(sheetName); + FileUtils.setAttachmentResponseHeader(response, filename); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"); + } + + /** + * 解析导出值 0=男,1=女,2=未知 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String convertByExp(String propertyValue, String converterExp, String separator) { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(StringUtils.SEPARATOR); + for (String item : convertSource) { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) { + for (String value : propertyValue.split(separator)) { + if (itemArray[0].equals(value)) { + propertyString.append(itemArray[1] + separator); + break; + } + } + } else { + if (itemArray[0].equals(propertyValue)) { + return itemArray[1]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 反向解析值 男=0,女=1,未知=2 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String reverseByExp(String propertyValue, String converterExp, String separator) { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(StringUtils.SEPARATOR); + for (String item : convertSource) { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) { + for (String value : propertyValue.split(separator)) { + if (itemArray[1].equals(value)) { + propertyString.append(itemArray[0] + separator); + break; + } + } + } else { + if (itemArray[1].equals(propertyValue)) { + return itemArray[0]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 编码文件名 + */ + public static String encodingFilename(String filename) { + return filename + ".xlsx"; + } + +} diff --git a/src/main/java/org/dromara/common/excel/utils/ExcelWriterWrapper.java b/src/main/java/org/dromara/common/excel/utils/ExcelWriterWrapper.java new file mode 100644 index 0000000..b33fdd5 --- /dev/null +++ b/src/main/java/org/dromara/common/excel/utils/ExcelWriterWrapper.java @@ -0,0 +1,127 @@ +package org.dromara.common.excel.utils; + +import cn.idev.excel.ExcelWriter; +import cn.idev.excel.FastExcel; +import cn.idev.excel.context.WriteContext; +import cn.idev.excel.write.builder.ExcelWriterSheetBuilder; +import cn.idev.excel.write.builder.ExcelWriterTableBuilder; +import cn.idev.excel.write.metadata.WriteSheet; +import cn.idev.excel.write.metadata.WriteTable; +import cn.idev.excel.write.metadata.fill.FillConfig; + +import java.util.Collection; +import java.util.function.Supplier; + +/** + * ExcelWriterWrapper Excel写出包装器 + *
+ * 提供了一组与 ExcelWriter 一一对应的写出方法,避免直接提供 ExcelWriter 而导致的一些不可控问题(比如提前关闭了IO流等) + * + * @author shihongwei + * @see ExcelWriter + */ +public record ExcelWriterWrapper(ExcelWriter excelWriter) { + + public void write(Collection data, WriteSheet writeSheet) { + excelWriter.write(data, writeSheet); + } + + public void write(Supplier> supplier, WriteSheet writeSheet) { + excelWriter.write(supplier.get(), writeSheet); + } + + public void write(Collection data, WriteSheet writeSheet, WriteTable writeTable) { + excelWriter.write(data, writeSheet, writeTable); + } + + public void write(Supplier> supplier, WriteSheet writeSheet, WriteTable writeTable) { + excelWriter.write(supplier.get(), writeSheet, writeTable); + } + + public void fill(Object data, WriteSheet writeSheet) { + excelWriter.fill(data, writeSheet); + } + + public void fill(Object data, FillConfig fillConfig, WriteSheet writeSheet) { + excelWriter.fill(data, fillConfig, writeSheet); + } + + public void fill(Supplier supplier, WriteSheet writeSheet) { + excelWriter.fill(supplier, writeSheet); + } + + public void fill(Supplier supplier, FillConfig fillConfig, WriteSheet writeSheet) { + excelWriter.fill(supplier, fillConfig, writeSheet); + } + + public WriteContext writeContext() { + return excelWriter.writeContext(); + } + + /** + * 创建一个 ExcelWriterWrapper + * + * @param excelWriter ExcelWriter + * @return ExcelWriterWrapper + */ + public static ExcelWriterWrapper of(ExcelWriter excelWriter) { + return new ExcelWriterWrapper<>(excelWriter); + } + + // -------------------------------- sheet start + + public static WriteSheet buildSheet(Integer sheetNo, String sheetName) { + return sheetBuilder(sheetNo, sheetName).build(); + } + + public static WriteSheet buildSheet(Integer sheetNo) { + return sheetBuilder(sheetNo).build(); + } + + public static WriteSheet buildSheet(String sheetName) { + return sheetBuilder(sheetName).build(); + } + + public static WriteSheet buildSheet() { + return sheetBuilder().build(); + } + + public static ExcelWriterSheetBuilder sheetBuilder(Integer sheetNo, String sheetName) { + return FastExcel.writerSheet(sheetNo, sheetName); + } + + public static ExcelWriterSheetBuilder sheetBuilder(Integer sheetNo) { + return FastExcel.writerSheet(sheetNo); + } + + public static ExcelWriterSheetBuilder sheetBuilder(String sheetName) { + return FastExcel.writerSheet(sheetName); + } + + public static ExcelWriterSheetBuilder sheetBuilder() { + return FastExcel.writerSheet(); + } + + // -------------------------------- sheet end + + // -------------------------------- table start + + public static WriteTable buildTable(Integer tableNo) { + return tableBuilder(tableNo).build(); + } + + public static WriteTable buildTable() { + return tableBuilder().build(); + } + + public static ExcelWriterTableBuilder tableBuilder(Integer tableNo) { + return FastExcel.writerTable(tableNo); + } + + public static ExcelWriterTableBuilder tableBuilder() { + return FastExcel.writerTable(); + } + + // -------------------------------- table end + +} diff --git a/src/main/java/org/dromara/common/idempotent/annotation/RepeatSubmit.java b/src/main/java/org/dromara/common/idempotent/annotation/RepeatSubmit.java new file mode 100644 index 0000000..447badf --- /dev/null +++ b/src/main/java/org/dromara/common/idempotent/annotation/RepeatSubmit.java @@ -0,0 +1,29 @@ +package org.dromara.common.idempotent.annotation; + +import java.lang.annotation.*; +import java.util.concurrent.TimeUnit; + +/** + * 自定义注解防止表单重复提交 + * + * @author shihongwei + */ +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RepeatSubmit { + + /** + * 间隔时间(ms),小于此时间视为重复提交 + */ + int interval() default 5000; + + TimeUnit timeUnit() default TimeUnit.MILLISECONDS; + + /** + * 提示消息 支持国际化 格式为 {code} + */ + String message() default "{repeat.submit.message}"; + +} diff --git a/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java b/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java new file mode 100644 index 0000000..3aacef0 --- /dev/null +++ b/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java @@ -0,0 +1,146 @@ +package org.dromara.common.idempotent.aspectj; + +import cn.dev33.satoken.SaManager; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.crypto.SecureUtil; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.dromara.common.core.constant.GlobalConstants; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MessageUtils; +import org.dromara.common.core.utils.ServletUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.common.redis.utils.RedisUtils; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; + +import java.time.Duration; +import java.util.Collection; +import java.util.Map; +import java.util.StringJoiner; + +/** + * 防止重复提交(参考美团GTIS防重系统) + * + * @author shihongwei + */ +@Aspect +public class RepeatSubmitAspect { + + private static final ThreadLocal KEY_CACHE = new ThreadLocal<>(); + + @Before("@annotation(repeatSubmit)") + public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable { + // 如果注解不为0 则使用注解数值 + long interval = repeatSubmit.timeUnit().toMillis(repeatSubmit.interval()); + + if (interval < 1000) { + throw new ServiceException("重复提交间隔时间不能小于'1'秒"); + } + HttpServletRequest request = ServletUtils.getRequest(); + String nowParams = argsArrayToString(point.getArgs()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = StringUtils.trimToEmpty(request.getHeader(SaManager.getConfig().getTokenName())); + + submitKey = SecureUtil.md5(submitKey + ":" + nowParams); + // 唯一标识(指定key + url + 消息头) + String cacheRepeatKey = GlobalConstants.REPEAT_SUBMIT_KEY + url + submitKey; + if (RedisUtils.setObjectIfAbsent(cacheRepeatKey, "", Duration.ofMillis(interval))) { + KEY_CACHE.set(cacheRepeatKey); + } else { + String message = repeatSubmit.message(); + if (StringUtils.startsWith(message, "{") && StringUtils.endsWith(message, "}")) { + message = MessageUtils.message(StringUtils.substring(message, 1, message.length() - 1)); + } + throw new ServiceException(message); + } + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(repeatSubmit)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Object jsonResult) { + if (jsonResult instanceof R r) { + try { + // 成功则不删除redis数据 保证在有效时间内无法重复提交 + if (r.getCode() == R.SUCCESS) { + return; + } + RedisUtils.deleteObject(KEY_CACHE.get()); + } finally { + KEY_CACHE.remove(); + } + } + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(repeatSubmit)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Exception e) { + RedisUtils.deleteObject(KEY_CACHE.get()); + KEY_CACHE.remove(); + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray) { + StringJoiner params = new StringJoiner(" "); + if (ArrayUtil.isEmpty(paramsArray)) { + return params.toString(); + } + for (Object o : paramsArray) { + if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) { + params.add(JsonUtils.toJsonString(o)); + } + } + return params.toString(); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) { + Class clazz = o.getClass(); + if (clazz.isArray()) { + return MultipartFile.class.isAssignableFrom(clazz.getComponentType()); + } else if (Collection.class.isAssignableFrom(clazz)) { + Collection collection = (Collection) o; + for (Object value : collection) { + return value instanceof MultipartFile; + } + } else if (Map.class.isAssignableFrom(clazz)) { + Map map = (Map) o; + for (Object value : map.values()) { + return value instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } + +} diff --git a/src/main/java/org/dromara/common/idempotent/config/IdempotentConfig.java b/src/main/java/org/dromara/common/idempotent/config/IdempotentConfig.java new file mode 100644 index 0000000..d3de0e9 --- /dev/null +++ b/src/main/java/org/dromara/common/idempotent/config/IdempotentConfig.java @@ -0,0 +1,21 @@ +package org.dromara.common.idempotent.config; + +import org.dromara.common.idempotent.aspectj.RepeatSubmitAspect; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.data.redis.connection.RedisConfiguration; + +/** + * 幂等功能配置 + * + * @author shihongwei + */ +@AutoConfiguration(after = RedisConfiguration.class) +public class IdempotentConfig { + + @Bean + public RepeatSubmitAspect repeatSubmitAspect() { + return new RepeatSubmitAspect(); + } + +} diff --git a/src/main/java/org/dromara/common/job/config/SnailJobConfig.java b/src/main/java/org/dromara/common/job/config/SnailJobConfig.java new file mode 100644 index 0000000..ce217c9 --- /dev/null +++ b/src/main/java/org/dromara/common/job/config/SnailJobConfig.java @@ -0,0 +1,37 @@ +package org.dromara.common.job.config; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import com.aizuda.snailjob.client.common.appender.SnailLogbackAppender; +import com.aizuda.snailjob.client.common.event.SnailClientStartingEvent; +import com.aizuda.snailjob.client.starter.EnableSnailJob; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * 启动定时任务 + * + * @author shihongwei + * @date 2024-05-17 + */ +@AutoConfiguration +@ConditionalOnProperty(prefix = "snail-job", name = "enabled", havingValue = "true") +@EnableScheduling +@EnableSnailJob +public class SnailJobConfig { + + @EventListener(SnailClientStartingEvent.class) + public void onStarting(SnailClientStartingEvent event) { + LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); + SnailLogbackAppender ca = new SnailLogbackAppender<>(); + ca.setName("snail_log_appender"); + ca.start(); + Logger rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME); + rootLogger.addAppender(ca); + } + +} diff --git a/src/main/java/org/dromara/common/json/config/JacksonConfig.java b/src/main/java/org/dromara/common/json/config/JacksonConfig.java new file mode 100644 index 0000000..bdaad2e --- /dev/null +++ b/src/main/java/org/dromara/common/json/config/JacksonConfig.java @@ -0,0 +1,55 @@ +package org.dromara.common.json.config; + +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.json.handler.BigNumberSerializer; +import org.dromara.common.json.handler.CustomDateDeserializer; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.context.annotation.Bean; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import java.util.TimeZone; + +/** + * jackson 配置 + * + * @author shihongwei + */ +@Slf4j +@AutoConfiguration(before = JacksonAutoConfiguration.class) +public class JacksonConfig { + + @Bean + public Module registerJavaTimeModule() { + // 全局配置序列化返回 JSON 处理 + JavaTimeModule javaTimeModule = new JavaTimeModule(); + javaTimeModule.addSerializer(Long.class, BigNumberSerializer.INSTANCE); + javaTimeModule.addSerializer(Long.TYPE, BigNumberSerializer.INSTANCE); + javaTimeModule.addSerializer(BigInteger.class, BigNumberSerializer.INSTANCE); + javaTimeModule.addSerializer(BigDecimal.class, ToStringSerializer.instance); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter)); + javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter)); + javaTimeModule.addDeserializer(Date.class, new CustomDateDeserializer()); + return javaTimeModule; + } + + @Bean + public Jackson2ObjectMapperBuilderCustomizer customizer() { + return builder -> { + builder.timeZone(TimeZone.getDefault()); + log.info("初始化 jackson 配置"); + }; + } + +} diff --git a/src/main/java/org/dromara/common/json/handler/BigDecimalTwoDecimalSerializer.java b/src/main/java/org/dromara/common/json/handler/BigDecimalTwoDecimalSerializer.java new file mode 100644 index 0000000..78631c9 --- /dev/null +++ b/src/main/java/org/dromara/common/json/handler/BigDecimalTwoDecimalSerializer.java @@ -0,0 +1,29 @@ +package org.dromara.common.json.handler; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Objects; + +/** + * BigDecimal 两位小数序列化 + * + * @author shihongwei + */ +public class BigDecimalTwoDecimalSerializer extends JsonSerializer { + + @Override + public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + if (Objects.isNull(value)) { + gen.writeNull(); + } else { + // 保留两位小数,四舍五入 + String formatted = value.setScale(2, RoundingMode.HALF_UP).toPlainString(); + gen.writeString(formatted); + } + } +} diff --git a/src/main/java/org/dromara/common/json/handler/BigNumberSerializer.java b/src/main/java/org/dromara/common/json/handler/BigNumberSerializer.java new file mode 100644 index 0000000..ec469c2 --- /dev/null +++ b/src/main/java/org/dromara/common/json/handler/BigNumberSerializer.java @@ -0,0 +1,42 @@ +package org.dromara.common.json.handler; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import com.fasterxml.jackson.databind.ser.std.NumberSerializer; + +import java.io.IOException; + +/** + * 超出 JS 最大最小值 处理 + * + * @author shihongwei + */ +@JacksonStdImpl +public class BigNumberSerializer extends NumberSerializer { + + /** + * 根据 JS Number.MAX_SAFE_INTEGER 与 Number.MIN_SAFE_INTEGER 得来 + */ + private static final long MAX_SAFE_INTEGER = 9007199254740991L; + private static final long MIN_SAFE_INTEGER = -9007199254740991L; + + /** + * 提供实例 + */ + public static final BigNumberSerializer INSTANCE = new BigNumberSerializer(Number.class); + + public BigNumberSerializer(Class rawType) { + super(rawType); + } + + @Override + public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException { + // 超出范围 序列化为字符串 + if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) { + super.serialize(value, gen, provider); + } else { + gen.writeString(value.toString()); + } + } +} diff --git a/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java b/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java new file mode 100644 index 0000000..c885a31 --- /dev/null +++ b/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java @@ -0,0 +1,37 @@ +package org.dromara.common.json.handler; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import org.dromara.common.core.utils.ObjectUtils; + +import java.io.IOException; +import java.util.Date; + +/** + * 自定义 Date 类型反序列化处理器(支持多种格式) + * + * @author shihongwei + */ +public class CustomDateDeserializer extends JsonDeserializer { + + /** + * 反序列化逻辑:将字符串转换为 Date 对象 + * + * @param p JSON 解析器,用于获取字符串值 + * @param ctxt 上下文环境(可用于获取更多配置) + * @return 转换后的 Date 对象,若为空字符串返回 null + * @throws IOException 当字符串格式非法或转换失败时抛出 + */ + @Override + public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + DateTime parse = DateUtil.parse(p.getText()); + if (ObjectUtils.isNull(parse)) { + return null; + } + return parse.toJdkDate(); + } + +} diff --git a/src/main/java/org/dromara/common/json/utils/JsonUtils.java b/src/main/java/org/dromara/common/json/utils/JsonUtils.java new file mode 100644 index 0000000..55008ad --- /dev/null +++ b/src/main/java/org/dromara/common/json/utils/JsonUtils.java @@ -0,0 +1,225 @@ +package org.dromara.common.json.utils; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * JSON 工具类 + * + * @author shihongwei + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class JsonUtils { + + private static final ObjectMapper OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class); + + public static ObjectMapper getObjectMapper() { + return OBJECT_MAPPER; + } + + /** + * 将对象转换为JSON格式的字符串 + * + * @param object 要转换的对象 + * @return JSON格式的字符串,如果对象为null,则返回null + * @throws RuntimeException 如果转换过程中发生JSON处理异常,则抛出运行时异常 + */ + public static String toJsonString(Object object) { + if (ObjectUtil.isNull(object)) { + return null; + } + try { + return OBJECT_MAPPER.writeValueAsString(object); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + /** + * 将JSON格式的字符串转换为指定类型的对象 + * + * @param text JSON格式的字符串 + * @param clazz 要转换的目标对象类型 + * @param 目标对象的泛型类型 + * @return 转换后的对象,如果字符串为空则返回null + * @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常 + */ + public static T parseObject(String text, Class clazz) { + if (StringUtils.isEmpty(text)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(text, clazz); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 将字节数组转换为指定类型的对象 + * + * @param bytes 字节数组 + * @param clazz 要转换的目标对象类型 + * @param 目标对象的泛型类型 + * @return 转换后的对象,如果字节数组为空则返回null + * @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常 + */ + public static T parseObject(byte[] bytes, Class clazz) { + if (ArrayUtil.isEmpty(bytes)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(bytes, clazz); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 将JSON格式的字符串转换为指定类型的对象,支持复杂类型 + * + * @param text JSON格式的字符串 + * @param typeReference 指定类型的TypeReference对象 + * @param 目标对象的泛型类型 + * @return 转换后的对象,如果字符串为空则返回null + * @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常 + */ + public static T parseObject(String text, TypeReference typeReference) { + if (StringUtils.isBlank(text)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(text, typeReference); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 将JSON格式的字符串转换为Dict对象 + * + * @param text JSON格式的字符串 + * @return 转换后的Dict对象,如果字符串为空或者不是JSON格式则返回null + * @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常 + */ + public static Dict parseMap(String text) { + if (StringUtils.isBlank(text)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructType(Dict.class)); + } catch (MismatchedInputException e) { + // 类型不匹配说明不是json + return null; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 将JSON格式的字符串转换为Dict对象的列表 + * + * @param text JSON格式的字符串 + * @return 转换后的Dict对象的列表,如果字符串为空则返回null + * @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常 + */ + public static List parseArrayMap(String text) { + if (StringUtils.isBlank(text)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, Dict.class)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 将JSON格式的字符串转换为指定类型对象的列表 + * + * @param text JSON格式的字符串 + * @param clazz 要转换的目标对象类型 + * @param 目标对象的泛型类型 + * @return 转换后的对象的列表,如果字符串为空则返回空列表 + * @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常 + */ + public static List parseArray(String text, Class clazz) { + if (StringUtils.isEmpty(text)) { + return new ArrayList<>(); + } + try { + return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 判断字符串是否为合法 JSON(对象或数组) + * + * @param str 待校验字符串 + * @return true = 合法 JSON,false = 非法或空 + */ + public static boolean isJson(String str) { + if (StringUtils.isBlank(str)) { + return false; + } + try { + OBJECT_MAPPER.readTree(str); + return true; + } catch (Exception e) { + return false; + } + } + + /** + * 判断字符串是否为 JSON 对象({}) + * + * @param str 待校验字符串 + * @return true = JSON 对象 + */ + public static boolean isJsonObject(String str) { + if (StringUtils.isBlank(str)) { + return false; + } + try { + JsonNode node = OBJECT_MAPPER.readTree(str); + return node.isObject(); + } catch (Exception e) { + return false; + } + } + + /** + * 判断字符串是否为 JSON 数组([]) + * + * @param str 待校验字符串 + * @return true = JSON 数组 + */ + public static boolean isJsonArray(String str) { + if (StringUtils.isBlank(str)) { + return false; + } + try { + JsonNode node = OBJECT_MAPPER.readTree(str); + return node.isArray(); + } catch (Exception e) { + return false; + } + } + +} diff --git a/src/main/java/org/dromara/common/json/validate/JsonPattern.java b/src/main/java/org/dromara/common/json/validate/JsonPattern.java new file mode 100644 index 0000000..6646549 --- /dev/null +++ b/src/main/java/org/dromara/common/json/validate/JsonPattern.java @@ -0,0 +1,33 @@ +package org.dromara.common.json.validate; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.*; + +/** + * JSON 格式校验注解 + * + * @author shihongwei + */ +@Documented +@Target({ElementType.METHOD, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = JsonPatternValidator.class) +public @interface JsonPattern { + + /** + * 限制 JSON 类型,默认为 {@link JsonType#ANY},即对象或数组都允许 + */ + JsonType type() default JsonType.ANY; + + /** + * 校验失败时的提示消息 + */ + String message() default "不是有效的 JSON 格式"; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} diff --git a/src/main/java/org/dromara/common/json/validate/JsonPatternValidator.java b/src/main/java/org/dromara/common/json/validate/JsonPatternValidator.java new file mode 100644 index 0000000..0f3e528 --- /dev/null +++ b/src/main/java/org/dromara/common/json/validate/JsonPatternValidator.java @@ -0,0 +1,51 @@ +package org.dromara.common.json.validate; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.json.utils.JsonUtils; + +/** + * JSON 格式校验器 + * + * @author shihongwei + */ +public class JsonPatternValidator implements ConstraintValidator { + + /** + * 注解中指定的 JSON 类型枚举 + */ + private JsonType jsonType; + + /** + * 初始化校验器,从注解中提取 JSON 类型 + * + * @param annotation 注解实例 + */ + @Override + public void initialize(JsonPattern annotation) { + this.jsonType = annotation.type(); + } + + /** + * 校验字符串是否为合法 JSON + * + * @param value 待校验字符串 + * @param context 校验上下文,可用于自定义错误信息 + * @return true = 合法 JSON 或为空,false = 非法 JSON + */ + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + if (StringUtils.isBlank(value)) { + // 交给 @NotBlank 或 @NotNull 控制是否允许为空 + return true; + } + // 根据 JSON 类型进行不同的校验 + return switch (jsonType) { + case ANY -> JsonUtils.isJson(value); + case OBJECT -> JsonUtils.isJsonObject(value); + case ARRAY -> JsonUtils.isJsonArray(value); + }; + } + +} diff --git a/src/main/java/org/dromara/common/json/validate/JsonType.java b/src/main/java/org/dromara/common/json/validate/JsonType.java new file mode 100644 index 0000000..58a939a --- /dev/null +++ b/src/main/java/org/dromara/common/json/validate/JsonType.java @@ -0,0 +1,30 @@ +package org.dromara.common.json.validate; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * JSON 类型枚举 + * + * @author shihongwei + */ +@Getter +@AllArgsConstructor +public enum JsonType { + + /** + * JSON 对象,例如 {"a":1} + */ + OBJECT, + + /** + * JSON 数组,例如 [1,2,3] + */ + ARRAY, + + /** + * 任意 JSON 类型,对象或数组都可以 + */ + ANY + +} diff --git a/src/main/java/org/dromara/common/log/annotation/Log.java b/src/main/java/org/dromara/common/log/annotation/Log.java new file mode 100644 index 0000000..3a6f6d2 --- /dev/null +++ b/src/main/java/org/dromara/common/log/annotation/Log.java @@ -0,0 +1,48 @@ +package org.dromara.common.log.annotation; + +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.log.enums.OperatorType; + +import java.lang.annotation.*; + +/** + * 自定义操作日志记录注解 + * + * @author shihongwei + */ +@Target({ElementType.PARAMETER, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log { + /** + * 模块 + */ + String title() default ""; + + /** + * 功能 + */ + BusinessType businessType() default BusinessType.OTHER; + + /** + * 操作人类别 + */ + OperatorType operatorType() default OperatorType.MANAGE; + + /** + * 是否保存请求的参数 + */ + boolean isSaveRequestData() default true; + + /** + * 是否保存响应的参数 + */ + boolean isSaveResponseData() default true; + + + /** + * 排除指定的请求参数 + */ + String[] excludeParamNames() default {}; + +} diff --git a/src/main/java/org/dromara/common/log/aspect/LogAspect.java b/src/main/java/org/dromara/common/log/aspect/LogAspect.java new file mode 100644 index 0000000..ade3ca7 --- /dev/null +++ b/src/main/java/org/dromara/common/log/aspect/LogAspect.java @@ -0,0 +1,231 @@ +package org.dromara.common.log.aspect; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.time.StopWatch; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.utils.ServletUtils; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessStatus; +import org.dromara.common.log.event.OperLogEvent; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.http.HttpMethod; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; + +import java.util.*; + +/** + * 操作日志记录处理 + * + * @author shihongwei + */ +@Slf4j +@Aspect +@AutoConfiguration +public class LogAspect { + + /** + * 排除敏感属性字段 + */ + public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; + + + /** + * 计时 key + */ + private static final ThreadLocal KEY_CACHE = new ThreadLocal<>(); + + /** + * 处理请求前执行 + */ + @Before(value = "@annotation(controllerLog)") + public void doBefore(JoinPoint joinPoint, Log controllerLog) { + StopWatch stopWatch = new StopWatch(); + KEY_CACHE.set(stopWatch); + stopWatch.start(); + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) { + handleLog(joinPoint, controllerLog, e, null); + } + + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) { + try { + + // *========数据库日志=========*// + OperLogEvent operLog = new OperLogEvent(); + operLog.setTenantId(LoginHelper.getTenantId()); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + // 请求的地址 + String ip = ServletUtils.getClientIP(); + operLog.setOperIp(ip); + operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); + LoginUser loginUser = LoginHelper.getLoginUser(); + operLog.setOperName(loginUser.getUsername()); + operLog.setDeptName(loginUser.getDeptName()); + + if (e != null) { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 3800)); + } + // 设置方法名称 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + // 设置请求方式 + operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); + // 处理设置注解上的参数 + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); + // 设置消耗时间 + StopWatch stopWatch = KEY_CACHE.get(); + stopWatch.stop(); + operLog.setCostTime(stopWatch.getDuration().toMillis()); + // 发布事件保存数据库 + SpringUtils.context().publishEvent(operLog); + } catch (Exception exp) { + // 记录本地异常日志 + log.error("异常信息:{}", exp.getMessage()); + } finally { + KEY_CACHE.remove(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param log 日志 + * @param operLog 操作日志 + * @throws Exception + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperLogEvent operLog, Object jsonResult) throws Exception { + // 设置action动作 + operLog.setBusinessType(log.businessType().ordinal()); + // 设置标题 + operLog.setTitle(log.title()); + // 设置操作人类别 + operLog.setOperatorType(log.operatorType().ordinal()); + // 是否需要保存request,参数和值 + if (log.isSaveRequestData()) { + // 获取参数的信息,传入到数据库中。 + setRequestValue(joinPoint, operLog, log.excludeParamNames()); + } + // 是否需要保存response,参数和值 + if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) { + operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 3800)); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @param operLog 操作日志 + * @throws Exception 异常 + */ + private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog, String[] excludeParamNames) throws Exception { + Map paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); + String requestMethod = operLog.getRequestMethod(); + if (MapUtil.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name())) { + String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); + operLog.setOperParam(StringUtils.substring(params, 0, 3800)); + } else { + MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES); + MapUtil.removeAny(paramsMap, excludeParamNames); + operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 3800)); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) { + StringJoiner params = new StringJoiner(" "); + if (ArrayUtil.isEmpty(paramsArray)) { + return params.toString(); + } + String[] exclude = ArrayUtil.addAll(excludeParamNames, EXCLUDE_PROPERTIES); + for (Object o : paramsArray) { + if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) { + String str = ""; + if (o instanceof List list) { + List list1 = new ArrayList<>(); + for (Object obj : list) { + String str1 = JsonUtils.toJsonString(obj); + Dict dict = JsonUtils.parseMap(str1); + if (MapUtil.isNotEmpty(dict)) { + MapUtil.removeAny(dict, exclude); + list1.add(dict); + } + } + str = JsonUtils.toJsonString(list1); + } else { + str = JsonUtils.toJsonString(o); + Dict dict = JsonUtils.parseMap(str); + if (MapUtil.isNotEmpty(dict)) { + MapUtil.removeAny(dict, exclude); + str = JsonUtils.toJsonString(dict); + } + } + params.add(str); + } + } + return params.toString(); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) { + Class clazz = o.getClass(); + if (clazz.isArray()) { + return MultipartFile.class.isAssignableFrom(clazz.getComponentType()); + } else if (Collection.class.isAssignableFrom(clazz)) { + Collection collection = (Collection) o; + for (Object value : collection) { + return value instanceof MultipartFile; + } + } else if (Map.class.isAssignableFrom(clazz)) { + Map map = (Map) o; + for (Object value : map.values()) { + return value instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/src/main/java/org/dromara/common/log/enums/BusinessStatus.java b/src/main/java/org/dromara/common/log/enums/BusinessStatus.java new file mode 100644 index 0000000..f697c76 --- /dev/null +++ b/src/main/java/org/dromara/common/log/enums/BusinessStatus.java @@ -0,0 +1,18 @@ +package org.dromara.common.log.enums; + +/** + * 操作状态 + * + * @author shihongwei + */ +public enum BusinessStatus { + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/src/main/java/org/dromara/common/log/enums/BusinessType.java b/src/main/java/org/dromara/common/log/enums/BusinessType.java new file mode 100644 index 0000000..8624ec3 --- /dev/null +++ b/src/main/java/org/dromara/common/log/enums/BusinessType.java @@ -0,0 +1,58 @@ +package org.dromara.common.log.enums; + +/** + * 业务操作类型 + * + * @author shihongwei + */ +public enum BusinessType { + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, +} diff --git a/src/main/java/org/dromara/common/log/enums/OperatorType.java b/src/main/java/org/dromara/common/log/enums/OperatorType.java new file mode 100644 index 0000000..04efe7e --- /dev/null +++ b/src/main/java/org/dromara/common/log/enums/OperatorType.java @@ -0,0 +1,23 @@ +package org.dromara.common.log.enums; + +/** + * 操作人类别 + * + * @author shihongwei + */ +public enum OperatorType { + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/src/main/java/org/dromara/common/log/event/LogininforEvent.java b/src/main/java/org/dromara/common/log/event/LogininforEvent.java new file mode 100644 index 0000000..240966f --- /dev/null +++ b/src/main/java/org/dromara/common/log/event/LogininforEvent.java @@ -0,0 +1,56 @@ +package org.dromara.common.log.event; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 登录事件 + * + * @author shihongwei + */ + +@Data +public class LogininforEvent implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 用户ID + */ + private Long userId; + + /** + * 用户账号 + */ + private String username; + + /** + * 登录状态 0成功 1失败 + */ + private String status; + + /** + * 提示消息 + */ + private String message; + + /** + * 请求体 + */ + private HttpServletRequest request; + + /** + * 其他参数 + */ + private Object[] args; + +} diff --git a/src/main/java/org/dromara/common/log/event/OperLogEvent.java b/src/main/java/org/dromara/common/log/event/OperLogEvent.java new file mode 100644 index 0000000..36e09c7 --- /dev/null +++ b/src/main/java/org/dromara/common/log/event/OperLogEvent.java @@ -0,0 +1,115 @@ +package org.dromara.common.log.event; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 操作日志事件 + * + * @author shihongwei + */ + +@Data +public class OperLogEvent implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + private Long operId; + + /** + * 租户ID + */ + private String tenantId; + + /** + * 操作模块 + */ + private String title; + + /** + * 业务类型(0其它 1新增 2修改 3删除) + */ + private Integer businessType; + + /** + * 业务类型数组 + */ + private Integer[] businessTypes; + + /** + * 请求方法 + */ + private String method; + + /** + * 请求方式 + */ + private String requestMethod; + + /** + * 操作类别(0其它 1后台用户 2手机端用户) + */ + private Integer operatorType; + + /** + * 操作人员 + */ + private String operName; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 请求url + */ + private String operUrl; + + /** + * 操作地址 + */ + private String operIp; + + /** + * 操作地点 + */ + private String operLocation; + + /** + * 请求参数 + */ + private String operParam; + + /** + * 返回参数 + */ + private String jsonResult; + + /** + * 操作状态(0正常 1异常) + */ + private Integer status; + + /** + * 错误消息 + */ + private String errorMsg; + + /** + * 操作时间 + */ + private Date operTime; + + /** + * 消耗时间 + */ + private Long costTime; +} diff --git a/src/main/java/org/dromara/common/mail/config/MailConfig.java b/src/main/java/org/dromara/common/mail/config/MailConfig.java new file mode 100644 index 0000000..a203af1 --- /dev/null +++ b/src/main/java/org/dromara/common/mail/config/MailConfig.java @@ -0,0 +1,37 @@ +package org.dromara.common.mail.config; + +import cn.hutool.extra.mail.MailAccount; +import org.dromara.common.mail.config.properties.MailProperties; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; + +/** + * JavaMail 配置 + * + * @author shihongwei + */ +@AutoConfiguration +@EnableConfigurationProperties(MailProperties.class) +public class MailConfig { + + @Bean + @ConditionalOnProperty(value = "mail.enabled", havingValue = "true") + public MailAccount mailAccount(MailProperties mailProperties) { + MailAccount account = new MailAccount(); + account.setHost(mailProperties.getHost()); + account.setPort(mailProperties.getPort()); + account.setAuth(mailProperties.getAuth()); + account.setFrom(mailProperties.getFrom()); + account.setUser(mailProperties.getUser()); + account.setPass(mailProperties.getPass()); + account.setSocketFactoryPort(mailProperties.getPort()); + account.setStarttlsEnable(mailProperties.getStarttlsEnable()); + account.setSslEnable(mailProperties.getSslEnable()); + account.setTimeout(mailProperties.getTimeout()); + account.setConnectionTimeout(mailProperties.getConnectionTimeout()); + return account; + } + +} diff --git a/src/main/java/org/dromara/common/mail/config/properties/MailProperties.java b/src/main/java/org/dromara/common/mail/config/properties/MailProperties.java new file mode 100644 index 0000000..f081a0e --- /dev/null +++ b/src/main/java/org/dromara/common/mail/config/properties/MailProperties.java @@ -0,0 +1,75 @@ +package org.dromara.common.mail.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * JavaMail 配置属性 + * + * @author shihongwei + */ +@Data +@ConfigurationProperties(prefix = "mail") +public class MailProperties { + + /** + * 过滤开关 + */ + private Boolean enabled; + + /** + * SMTP服务器域名 + */ + private String host; + + /** + * SMTP服务端口 + */ + private Integer port; + + /** + * 是否需要用户名密码验证 + */ + private Boolean auth; + + /** + * 用户名 + */ + private String user; + + /** + * 密码 + */ + private String pass; + + /** + * 发送方,遵循RFC-822标准
+ * 发件人可以是以下形式: + * + *
+     * 1. user@xxx.xx
+     * 2.  name <user@xxx.xx>
+     * 
+ */ + private String from; + + /** + * 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。它将纯文本连接升级为加密连接(TLS或SSL), 而不是使用一个单独的加密通信端口。 + */ + private Boolean starttlsEnable; + + /** + * 使用 SSL安全连接 + */ + private Boolean sslEnable; + + /** + * SMTP超时时长,单位毫秒,缺省值不超时 + */ + private Long timeout; + + /** + * Socket连接超时值,单位毫秒,缺省值不超时 + */ + private Long connectionTimeout; +} diff --git a/src/main/java/org/dromara/common/mail/utils/MailUtils.java b/src/main/java/org/dromara/common/mail/utils/MailUtils.java new file mode 100644 index 0000000..a28701f --- /dev/null +++ b/src/main/java/org/dromara/common/mail/utils/MailUtils.java @@ -0,0 +1,469 @@ +package org.dromara.common.mail.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.mail.JakartaMail; +import cn.hutool.extra.mail.JakartaUserPassAuthenticator; +import cn.hutool.extra.mail.MailAccount; +import jakarta.mail.Authenticator; +import jakarta.mail.Session; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; + +import java.io.File; +import java.io.InputStream; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * 邮件工具类 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class MailUtils { + + private static final MailAccount ACCOUNT = SpringUtils.getBean(MailAccount.class); + + /** + * 获取邮件发送实例 + */ + public static MailAccount getMailAccount() { + return ACCOUNT; + } + + /** + * 获取邮件发送实例 (自定义发送人以及授权码) + * + * @param user 发送人 + * @param pass 授权码 + */ + public static MailAccount getMailAccount(String from, String user, String pass) { + ACCOUNT.setFrom(StringUtils.blankToDefault(from, ACCOUNT.getFrom())); + ACCOUNT.setUser(StringUtils.blankToDefault(user, ACCOUNT.getUser())); + ACCOUNT.setPass(StringUtils.blankToDefault(pass, ACCOUNT.getPass())); + return ACCOUNT; + } + + /** + * 使用配置文件中设置的账户发送文本邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendText(String to, String subject, String content, File... files) { + return send(to, subject, content, false, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(String to, String subject, String content, File... files) { + return send(to, subject, content, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(String to, String subject, String content, boolean isHtml, File... files) { + return send(splitAddress(to), subject, content, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(String to, String cc, String bcc, String subject, String content, boolean isHtml, File... files) { + return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送文本邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + */ + public static String sendText(Collection tos, String subject, String content, File... files) { + return send(tos, subject, content, false, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(Collection tos, String subject, String content, File... files) { + return send(tos, subject, content, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(Collection tos, String subject, String content, boolean isHtml, File... files) { + return send(tos, null, null, subject, content, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(Collection tos, Collection ccs, Collection bccs, String subject, String content, boolean isHtml, File... files) { + return send(getMailAccount(), true, tos, ccs, bccs, subject, content, null, isHtml, files); + } + + // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件认证对象 + * @param to 收件人,多个收件人逗号或者分号隔开 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String send(MailAccount mailAccount, String to, String subject, String content, boolean isHtml, File... files) { + return send(mailAccount, splitAddress(to), subject, content, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + */ + public static String send(MailAccount mailAccount, Collection tos, String subject, String content, boolean isHtml, File... files) { + return send(mailAccount, tos, null, null, subject, content, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(MailAccount mailAccount, Collection tos, Collection ccs, Collection bccs, String subject, String content, boolean isHtml, File... files) { + return send(mailAccount, false, tos, ccs, bccs, subject, content, null, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(String to, String subject, String content, Map imageMap, File... files) { + return send(to, subject, content, imageMap, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(String to, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(splitAddress(to), subject, content, imageMap, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
+ * 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * + * @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(String to, String cc, String bcc, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, imageMap, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送HTML邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String sendHtml(Collection tos, String subject, String content, Map imageMap, File... files) { + return send(tos, subject, content, imageMap, true, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + */ + public static String send(Collection tos, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(tos, null, null, subject, content, imageMap, isHtml, files); + } + + /** + * 使用配置文件中设置的账户发送邮件,发送给多人 + * + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML + * @param files 附件列表 + * @return message-id + * @since 4.0.3 + */ + public static String send(Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(getMailAccount(), true, tos, ccs, bccs, subject, content, imageMap, isHtml, files); + } + + // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件认证对象 + * @param to 收件人,多个收件人逗号或者分号隔开 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 3.2.0 + */ + public static String send(MailAccount mailAccount, String to, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(mailAccount, splitAddress(to), subject, content, imageMap, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.6.3 + */ + public static String send(MailAccount mailAccount, Collection tos, String subject, String content, Map imageMap, boolean isHtml, File... files) { + return send(mailAccount, tos, null, null, subject, content, imageMap, isHtml, files); + } + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.6.3 + */ + public static String send(MailAccount mailAccount, Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, + boolean isHtml, File... files) { + return send(mailAccount, false, tos, ccs, bccs, subject, content, imageMap, isHtml, files); + } + + /** + * 根据配置文件,获取邮件客户端会话 + * + * @param mailAccount 邮件账户配置 + * @param isSingleton 是否单例(全局共享会话) + * @return {@link Session} + * @since 5.5.7 + */ + public static Session getSession(MailAccount mailAccount, boolean isSingleton) { + Authenticator authenticator = null; + if (mailAccount.isAuth()) { + authenticator = new JakartaUserPassAuthenticator(mailAccount.getUser(), mailAccount.getPass()); + } + + return isSingleton ? Session.getDefaultInstance(mailAccount.getSmtpProps(), authenticator) // + : Session.getInstance(mailAccount.getSmtpProps(), authenticator); + } + + // ------------------------------------------------------------------------------------------------------------------------ Private method start + + /** + * 发送邮件给多人 + * + * @param mailAccount 邮件帐户信息 + * @param useGlobalSession 是否全局共享Session + * @param tos 收件人列表 + * @param ccs 抄送人列表,可以为null或空 + * @param bccs 密送人列表,可以为null或空 + * @param subject 标题 + * @param content 正文 + * @param imageMap 图片与占位符,占位符格式为cid:${cid} + * @param isHtml 是否为HTML格式 + * @param files 附件列表 + * @return message-id + * @since 4.6.3 + */ + private static String send(MailAccount mailAccount, boolean useGlobalSession, Collection tos, Collection ccs, Collection bccs, String subject, String content, + Map imageMap, boolean isHtml, File... files) { + final JakartaMail mail = JakartaMail.create(mailAccount).setUseGlobalSession(useGlobalSession); + + // 可选抄送人 + if (CollUtil.isNotEmpty(ccs)) { + mail.setCcs(ccs.toArray(new String[0])); + } + // 可选密送人 + if (CollUtil.isNotEmpty(bccs)) { + mail.setBccs(bccs.toArray(new String[0])); + } + + mail.setTos(tos.toArray(new String[0])); + mail.setTitle(subject); + mail.setContent(content); + mail.setHtml(isHtml); + mail.setFiles(files); + + // 图片 + if (MapUtil.isNotEmpty(imageMap)) { + for (Entry entry : imageMap.entrySet()) { + mail.addImage(entry.getKey(), entry.getValue()); + // 关闭流 + IoUtil.close(entry.getValue()); + } + } + + return mail.send(); + } + + /** + * 将多个联系人转为列表,分隔符为逗号或者分号 + * + * @param addresses 多个联系人,如果为空返回null + * @return 联系人列表 + */ + private static List splitAddress(String addresses) { + if (StrUtil.isBlank(addresses)) { + return null; + } + + List result; + if (StrUtil.contains(addresses, CharUtil.COMMA)) { + result = StrUtil.splitTrim(addresses, CharUtil.COMMA); + } else if (StrUtil.contains(addresses, ';')) { + result = StrUtil.splitTrim(addresses, ';'); + } else { + result = CollUtil.newArrayList(addresses); + } + return result; + } + // ------------------------------------------------------------------------------------------------------------------------ Private method end +} diff --git a/src/main/java/org/dromara/common/mybatis/annotation/DataColumn.java b/src/main/java/org/dromara/common/mybatis/annotation/DataColumn.java new file mode 100644 index 0000000..070098d --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/annotation/DataColumn.java @@ -0,0 +1,40 @@ +package org.dromara.common.mybatis.annotation; + +import java.lang.annotation.*; + +/** + * 数据权限注解,用于标记数据权限的占位符关键字和替换值 + *

+ * 一个注解只能对应一个模板 + *

+ * + * @author shihongwei + * @version 3.5.0 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataColumn { + + /** + * 数据权限模板的占位符关键字,默认为 "deptName" + * + * @return 占位符关键字数组 + */ + String[] key() default "deptName"; + + /** + * 数据权限模板的占位符替换值,默认为 "dept_id" + * + * @return 占位符替换值数组 + */ + String[] value() default "dept_id"; + + /** + * 权限标识符 用于通过菜单权限标识符来获取数据权限 + * 拥有此标识符的角色 将不会拼接此角色的数据过滤sql + * + * @return 权限标识符 + */ + String permission() default ""; +} diff --git a/src/main/java/org/dromara/common/mybatis/annotation/DataPermission.java b/src/main/java/org/dromara/common/mybatis/annotation/DataPermission.java new file mode 100644 index 0000000..757870d --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/annotation/DataPermission.java @@ -0,0 +1,30 @@ +package org.dromara.common.mybatis.annotation; + +import java.lang.annotation.*; + +/** + * 数据权限组注解,用于标记数据权限配置数组 + * + * @author shihongwei + * @version 3.5.0 + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataPermission { + + /** + * 数据权限配置数组,用于指定数据权限的占位符关键字和替换值 + * + * @return 数据权限配置数组 + */ + DataColumn[] value(); + + /** + * 权限拼接标识符(用于指定连接语句的sql符号) + * 如不填 默认 select 用 OR 其他语句用 AND + * 内容 OR 或者 AND + */ + String joinStr() default ""; + +} diff --git a/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionAdvice.java b/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionAdvice.java new file mode 100644 index 0000000..80bf567 --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionAdvice.java @@ -0,0 +1,54 @@ +package org.dromara.common.mybatis.aspect; + +import lombok.extern.slf4j.Slf4j; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.dromara.common.mybatis.annotation.DataPermission; +import org.dromara.common.mybatis.helper.DataPermissionHelper; + +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * 数据权限注解Advice + * + * @author shihongwei + */ +@Slf4j +public class DataPermissionAdvice implements MethodInterceptor { + + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + Object target = invocation.getThis(); + Method method = invocation.getMethod(); + Object[] args = invocation.getArguments(); + // 设置权限注解 + DataPermissionHelper.setPermission(getDataPermissionAnnotation(target, method, args)); + try { + // 执行代理方法 + return invocation.proceed(); + } finally { + // 清除权限注解 + DataPermissionHelper.removePermission(); + } + } + + /** + * 获取数据权限注解 + */ + private DataPermission getDataPermissionAnnotation(Object target, Method method,Object[] args){ + DataPermission dataPermission = method.getAnnotation(DataPermission.class); + // 优先获取方法上的注解 + if (dataPermission != null) { + return dataPermission; + } + // 方法上没有注解,则获取类上的注解 + Class targetClass = target.getClass(); + // 如果是 JDK 动态代理,则获取真实的Class实例 + if (Proxy.isProxyClass(targetClass)) { + targetClass = targetClass.getInterfaces()[0]; + } + dataPermission = targetClass.getAnnotation(DataPermission.class); + return dataPermission; + } +} diff --git a/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionPointcut.java b/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionPointcut.java new file mode 100644 index 0000000..4545567 --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionPointcut.java @@ -0,0 +1,39 @@ +package org.dromara.common.mybatis.aspect; + +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.mybatis.annotation.DataPermission; +import org.springframework.aop.support.StaticMethodMatcherPointcut; + +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * 数据权限匹配切点 + * + * @author shihongwei + */ +@Slf4j +@SuppressWarnings("all") +public class DataPermissionPointcut extends StaticMethodMatcherPointcut { + + @Override + public boolean matches(Method method, Class targetClass) { + // 优先匹配方法 + // 数据权限注解不对继承生效,所以检查当前方法是否有注解即可,不再往上匹配父类或接口 + if (method.isAnnotationPresent(DataPermission.class)) { + return true; + } + + // MyBatis 的 Mapper 就是通过 JDK 动态代理实现的,所以这里需要检查是否匹配 JDK 的动态代理 + Class targetClassRef = targetClass; + if (Proxy.isProxyClass(targetClassRef)) { + // 数据权限注解不对继承生效,但由于 SpringIOC 容器拿到的实际上是 MyBatis 代理过后的 Mapper,而 targetClass.isAnnotationPresent 实际匹配的是 Proxy 类的注解,不会查找代理类。 + // 所以这里不能用 targetClass.isAnnotationPresent,只能用 AnnotatedElementUtils.hasAnnotation 或 targetClass.getInterfaces()[0].isAnnotationPresent 去做匹配,以检查被代理的 MapperClass 是否具有注解 + // 原理:JDK 动态代理本质上就是对接口进行实现然后对具体的接口实现做代理,所以直接通过接口可以拿到实际的 MapperClass + targetClassRef = targetClass.getInterfaces()[0]; + + } + return targetClassRef.isAnnotationPresent(DataPermission.class); + } + +} diff --git a/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionPointcutAdvisor.java b/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionPointcutAdvisor.java new file mode 100644 index 0000000..3dffee5 --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionPointcutAdvisor.java @@ -0,0 +1,33 @@ +package org.dromara.common.mybatis.aspect; + +import org.aopalliance.aop.Advice; +import org.springframework.aop.Pointcut; +import org.springframework.aop.support.AbstractPointcutAdvisor; + +/** + * 数据权限注解切面定义 + * + * @author shihongwei + */ +@SuppressWarnings("all") +public class DataPermissionPointcutAdvisor extends AbstractPointcutAdvisor { + + private final Advice advice; + private final Pointcut pointcut; + + public DataPermissionPointcutAdvisor() { + this.advice = new DataPermissionAdvice(); + this.pointcut = new DataPermissionPointcut(); + } + + @Override + public Pointcut getPointcut() { + return this.pointcut; + } + + @Override + public Advice getAdvice() { + return this.advice; + } + +} diff --git a/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java b/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java new file mode 100644 index 0000000..757c6ea --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java @@ -0,0 +1,142 @@ +package org.dromara.common.mybatis.config; + +import cn.hutool.core.net.NetUtil; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler; +import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; +import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; +import org.dromara.common.core.factory.YmlPropertySourceFactory; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.mybatis.aspect.DataPermissionPointcutAdvisor; +import org.dromara.common.mybatis.handler.InjectionMetaObjectHandler; +import org.dromara.common.mybatis.handler.MybatisExceptionHandler; +import org.dromara.common.mybatis.handler.PlusPostInitTableInfoHandler; +import org.dromara.common.mybatis.interceptor.PlusDataPermissionInterceptor; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.PropertySource; +import org.springframework.context.annotation.Role; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * mybatis-plus配置类(下方注释有插件介绍) + * + * @author shihongwei + */ +@Role(BeanDefinition.ROLE_INFRASTRUCTURE) +@EnableTransactionManagement(proxyTargetClass = true) +@MapperScan("${mybatis-plus.mapperPackage}") +@PropertySource(value = "classpath:common-mybatis.yml", factory = YmlPropertySourceFactory.class) +public class MybatisPlusConfig { + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 多租户插件 必须放到第一位 + try { + TenantLineInnerInterceptor tenant = SpringUtils.getBean(TenantLineInnerInterceptor.class); + interceptor.addInnerInterceptor(tenant); + } catch (BeansException ignore) { + } + // 数据权限处理 + interceptor.addInnerInterceptor(dataPermissionInterceptor()); + // 分页插件 + interceptor.addInnerInterceptor(paginationInnerInterceptor()); + // 乐观锁插件 + interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor()); + return interceptor; + } + + /** + * 数据权限拦截器 + */ + public PlusDataPermissionInterceptor dataPermissionInterceptor() { + return new PlusDataPermissionInterceptor(); + } + + /** + * 数据权限切面处理器 + */ + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + public DataPermissionPointcutAdvisor dataPermissionPointcutAdvisor() { + return new DataPermissionPointcutAdvisor(); + } + + /** + * 分页插件,自动识别数据库类型 + */ + public PaginationInnerInterceptor paginationInnerInterceptor() { + PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); + // 分页合理化 + paginationInnerInterceptor.setOverflow(true); + return paginationInnerInterceptor; + } + + /** + * 乐观锁插件 + */ + public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() { + return new OptimisticLockerInnerInterceptor(); + } + + /** + * 元对象字段填充控制器 + */ + @Bean + public MetaObjectHandler metaObjectHandler() { + return new InjectionMetaObjectHandler(); + } + + /** + * 使用网卡信息绑定雪花生成器 + * 防止集群雪花ID重复 + */ + @Bean + public IdentifierGenerator idGenerator() { + return new DefaultIdentifierGenerator(NetUtil.getLocalhost()); + } + + /** + * 异常处理器 + */ + @Bean + public MybatisExceptionHandler mybatisExceptionHandler() { + return new MybatisExceptionHandler(); + } + + /** + * 初始化表对象处理器 + */ + @Bean + public PostInitTableInfoHandler postInitTableInfoHandler() { + return new PlusPostInitTableInfoHandler(); + } + + /** + * PaginationInnerInterceptor 分页插件,自动识别数据库类型 + * https://baomidou.com/pages/97710a/ + * OptimisticLockerInnerInterceptor 乐观锁插件 + * https://baomidou.com/pages/0d93c0/ + * MetaObjectHandler 元对象字段填充控制器 + * https://baomidou.com/pages/4c6bcf/ + * ISqlInjector sql注入器 + * https://baomidou.com/pages/42ea4a/ + * BlockAttackInnerInterceptor 如果是对全表的删除或更新操作,就会终止该操作 + * https://baomidou.com/pages/f9a237/ + * IllegalSQLInnerInterceptor sql性能规范插件(垃圾SQL拦截) + * IdentifierGenerator 自定义主键策略 + * https://baomidou.com/pages/568eb2/ + * TenantLineInnerInterceptor 多租户插件 + * https://baomidou.com/pages/aef2f2/ + * DynamicTableNameInnerInterceptor 动态表名插件 + * https://baomidou.com/pages/2a45ff/ + */ + +} diff --git a/src/main/java/org/dromara/common/mybatis/core/domain/BaseEntity.java b/src/main/java/org/dromara/common/mybatis/core/domain/BaseEntity.java new file mode 100644 index 0000000..0fd033a --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/core/domain/BaseEntity.java @@ -0,0 +1,64 @@ +package org.dromara.common.mybatis.core.domain; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * Entity基类 + * + * @author shihongwei + */ +@Data +public class BaseEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 搜索值 + */ + @JsonIgnore + @TableField(exist = false) + private String searchValue; + + /** + * 创建部门 + */ + @TableField(fill = FieldFill.INSERT) + private Long createDept; + + @TableField(fill = FieldFill.INSERT) + private Long createBy; + + @TableField(fill = FieldFill.INSERT) + private String createByName; + + @TableField(fill = FieldFill.INSERT) + private Date createTime; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updateBy; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateByName; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** + * 请求参数 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @TableField(exist = false) + private Map params = new HashMap<>(); + +} diff --git a/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java b/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java new file mode 100644 index 0000000..a6e4fb3 --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java @@ -0,0 +1,334 @@ +package org.dromara.common.mybatis.core.mapper; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.toolkit.Db; +import org.apache.ibatis.logging.Log; +import org.apache.ibatis.logging.LogFactory; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StreamUtils; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +/** + * 自定义 Mapper 接口, 实现 自定义扩展 + * + * @param table 泛型 + * @param vo 泛型 + * @author shihongwei + * @since 2021-05-13 + */ +@SuppressWarnings("unchecked") +public interface BaseMapperPlus extends BaseMapper { + + Log log = LogFactory.getLog(BaseMapperPlus.class); + + /** + * 获取当前实例对象关联的泛型类型 V 的 Class 对象 + * + * @return 返回当前实例对象关联的泛型类型 V 的 Class 对象 + */ + default Class currentVoClass() { + return (Class) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[1]; + } + + /** + * 获取当前实例对象关联的泛型类型 T 的 Class 对象 + * + * @return 返回当前实例对象关联的泛型类型 T 的 Class 对象 + */ + default Class currentModelClass() { + return (Class) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[0]; + } + + /** + * 使用默认的查询条件查询并返回结果列表 + * + * @return 返回查询结果的列表 + */ + default List selectList() { + return this.selectList(new QueryWrapper<>()); + } + + /** + * 批量插入实体对象集合 + * + * @param entityList 实体对象集合 + * @return 插入操作是否成功的布尔值 + */ + default boolean insertBatch(Collection entityList) { + return Db.saveBatch(entityList); + } + + /** + * 批量根据ID更新实体对象集合 + * + * @param entityList 实体对象集合 + * @return 更新操作是否成功的布尔值 + */ + default boolean updateBatchById(Collection entityList) { + return Db.updateBatchById(entityList); + } + + /** + * 批量插入或更新实体对象集合 + * + * @param entityList 实体对象集合 + * @return 插入或更新操作是否成功的布尔值 + */ + default boolean insertOrUpdateBatch(Collection entityList) { + return Db.saveOrUpdateBatch(entityList); + } + + /** + * 批量插入实体对象集合并指定批处理大小 + * + * @param entityList 实体对象集合 + * @param batchSize 批处理大小 + * @return 插入操作是否成功的布尔值 + */ + default boolean insertBatch(Collection entityList, int batchSize) { + return Db.saveBatch(entityList, batchSize); + } + + /** + * 批量根据ID更新实体对象集合并指定批处理大小 + * + * @param entityList 实体对象集合 + * @param batchSize 批处理大小 + * @return 更新操作是否成功的布尔值 + */ + default boolean updateBatchById(Collection entityList, int batchSize) { + return Db.updateBatchById(entityList, batchSize); + } + + /** + * 批量插入或更新实体对象集合并指定批处理大小 + * + * @param entityList 实体对象集合 + * @param batchSize 批处理大小 + * @return 插入或更新操作是否成功的布尔值 + */ + default boolean insertOrUpdateBatch(Collection entityList, int batchSize) { + return Db.saveOrUpdateBatch(entityList, batchSize); + } + + /** + * 根据ID查询单个VO对象 + * + * @param id 主键ID + * @return 查询到的单个VO对象 + */ + default V selectVoById(Serializable id) { + return selectVoById(id, this.currentVoClass()); + } + + /** + * 根据ID查询单个VO对象并将其转换为指定的VO类 + * + * @param id 主键ID + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @return 查询到的单个VO对象,经过转换为指定的VO类后返回 + */ + default C selectVoById(Serializable id, Class voClass) { + T obj = this.selectById(id); + if (ObjectUtil.isNull(obj)) { + return null; + } + return MapstructUtils.convert(obj, voClass); + } + + /** + * 根据ID集合批量查询VO对象列表 + * + * @param idList 主键ID集合 + * @return 查询到的VO对象列表 + */ + default List selectVoByIds(Collection idList) { + return selectVoByIds(idList, this.currentVoClass()); + } + + /** + * 根据ID集合批量查询实体对象列表,并将其转换为指定的VO对象列表 + * + * @param idList 主键ID集合 + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @return 查询到的VO对象列表,经过转换为指定的VO类后返回 + */ + default List selectVoByIds(Collection idList, Class voClass) { + List list = this.selectByIds(idList); + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return MapstructUtils.convert(list, voClass); + } + + /** + * 根据查询条件Map查询VO对象列表 + * + * @param map 查询条件Map + * @return 查询到的VO对象列表 + */ + default List selectVoByMap(Map map) { + return selectVoByMap(map, this.currentVoClass()); + } + + /** + * 根据查询条件Map查询实体对象列表,并将其转换为指定的VO对象列表 + * + * @param map 查询条件Map + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @return 查询到的VO对象列表,经过转换为指定的VO类后返回 + */ + default List selectVoByMap(Map map, Class voClass) { + List list = this.selectByMap(map); + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return MapstructUtils.convert(list, voClass); + } + + /** + * 根据条件查询单个VO对象 + * + * @param wrapper 查询条件Wrapper + * @return 查询到的单个VO对象 + */ + default V selectVoOne(Wrapper wrapper) { + return selectVoOne(wrapper, this.currentVoClass()); + } + + /** + * 根据条件查询单个VO对象,并根据需要决定是否抛出异常 + * + * @param wrapper 查询条件Wrapper + * @param throwEx 是否抛出异常的标志 + * @return 查询到的单个VO对象 + */ + default V selectVoOne(Wrapper wrapper, boolean throwEx) { + return selectVoOne(wrapper, this.currentVoClass(), throwEx); + } + + /** + * 根据条件查询单个VO对象,并指定返回的VO对象的类型 + * + * @param wrapper 查询条件Wrapper + * @param voClass 返回的VO对象的Class对象 + * @param 返回的VO对象的类型 + * @return 查询到的单个VO对象,经过类型转换为指定的VO类后返回 + */ + default C selectVoOne(Wrapper wrapper, Class voClass) { + return selectVoOne(wrapper, voClass, true); + } + + /** + * 根据条件查询单个实体对象,并将其转换为指定的VO对象 + * + * @param wrapper 查询条件Wrapper + * @param voClass 要转换的VO类的Class对象 + * @param throwEx 是否抛出异常的标志 + * @param VO类的类型 + * @return 查询到的单个VO对象,经过转换为指定的VO类后返回 + */ + default C selectVoOne(Wrapper wrapper, Class voClass, boolean throwEx) { + T obj = this.selectOne(wrapper, throwEx); + if (ObjectUtil.isNull(obj)) { + return null; + } + return MapstructUtils.convert(obj, voClass); + } + + /** + * 查询所有VO对象列表 + * + * @return 查询到的VO对象列表 + */ + default List selectVoList() { + return selectVoList(new QueryWrapper<>(), this.currentVoClass()); + } + + /** + * 根据条件查询VO对象列表 + * + * @param wrapper 查询条件Wrapper + * @return 查询到的VO对象列表 + */ + default List selectVoList(Wrapper wrapper) { + return selectVoList(wrapper, this.currentVoClass()); + } + + /** + * 根据条件查询实体对象列表,并将其转换为指定的VO对象列表 + * + * @param wrapper 查询条件Wrapper + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @return 查询到的VO对象列表,经过转换为指定的VO类后返回 + */ + default List selectVoList(Wrapper wrapper, Class voClass) { + List list = this.selectList(wrapper); + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return MapstructUtils.convert(list, voClass); + } + + /** + * 根据条件分页查询VO对象列表 + * + * @param page 分页信息 + * @param wrapper 查询条件Wrapper + * @return 查询到的VO对象分页列表 + */ + default

> P selectVoPage(IPage page, Wrapper wrapper) { + return selectVoPage(page, wrapper, this.currentVoClass()); + } + + /** + * 根据条件分页查询实体对象列表,并将其转换为指定的VO对象分页列表 + * + * @param page 分页信息 + * @param wrapper 查询条件Wrapper + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @param

VO对象分页列表的类型 + * @return 查询到的VO对象分页列表,经过转换为指定的VO类后返回 + */ + default > P selectVoPage(IPage page, Wrapper wrapper, Class voClass) { + // 根据条件分页查询实体对象列表 + List list = this.selectList(page, wrapper); + // 创建一个新的VO对象分页列表,并设置分页信息 + IPage voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal()); + if (CollUtil.isEmpty(list)) { + return (P) voPage; + } + voPage.setRecords(MapstructUtils.convert(list, voClass)); + return (P) voPage; + } + + /** + * 根据条件查询符合条件的对象,并将其转换为指定类型的对象列表 + * + * @param wrapper 查询条件Wrapper + * @param mapper 转换函数,用于将查询到的对象转换为指定类型的对象 + * @param 要转换的对象的类型 + * @return 查询到的符合条件的对象列表,经过转换为指定类型的对象后返回 + */ + default List selectObjs(Wrapper wrapper, Function mapper) { + return StreamUtils.toList(this.selectObjs(wrapper), mapper); + } + +} diff --git a/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java b/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java new file mode 100644 index 0000000..938c966 --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java @@ -0,0 +1,127 @@ +package org.dromara.common.mybatis.core.page; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.sql.SqlUtil; + +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 分页查询实体类 + * + * @author shihongwei + */ +@Data +public class PageQuery implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 分页大小 + */ + private Integer pageSize; + + /** + * 当前页数 + */ + private Integer pageNum; + + /** + * 排序列 + */ + private String orderByColumn; + + /** + * 排序的方向desc或者asc + */ + private String isAsc; + + /** + * 当前记录起始索引 默认值 + */ + public static final int DEFAULT_PAGE_NUM = 1; + + /** + * 每页显示记录数 默认值 默认查全部 + */ + public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE; + + /** + * 构建分页对象 + */ + public Page build() { + Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM); + Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE); + if (pageNum <= 0) { + pageNum = DEFAULT_PAGE_NUM; + } + Page page = new Page<>(pageNum, pageSize); + List orderItems = buildOrderItem(); + if (CollUtil.isNotEmpty(orderItems)) { + page.addOrder(orderItems); + } + return page; + } + + /** + * 构建排序 + * + * 支持的用法如下: + * {isAsc:"asc",orderByColumn:"id"} order by id asc + * {isAsc:"asc",orderByColumn:"id,createTime"} order by id asc,create_time asc + * {isAsc:"desc",orderByColumn:"id,createTime"} order by id desc,create_time desc + * {isAsc:"asc,desc",orderByColumn:"id,createTime"} order by id asc,create_time desc + */ + private List buildOrderItem() { + if (StringUtils.isBlank(orderByColumn) || StringUtils.isBlank(isAsc)) { + return null; + } + String orderBy = SqlUtil.escapeOrderBySql(orderByColumn); + orderBy = StringUtils.toUnderScoreCase(orderBy); + + // 兼容前端排序类型 + isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"}); + + String[] orderByArr = orderBy.split(StringUtils.SEPARATOR); + String[] isAscArr = isAsc.split(StringUtils.SEPARATOR); + if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) { + throw new ServiceException("排序参数有误"); + } + + List list = new ArrayList<>(); + // 每个字段各自排序 + for (int i = 0; i < orderByArr.length; i++) { + String orderByStr = orderByArr[i]; + String isAscStr = isAscArr.length == 1 ? isAscArr[0] : isAscArr[i]; + if ("asc".equals(isAscStr)) { + list.add(OrderItem.asc(orderByStr)); + } else if ("desc".equals(isAscStr)) { + list.add(OrderItem.desc(orderByStr)); + } else { + throw new ServiceException("排序参数有误"); + } + } + return list; + } + + @JsonIgnore + public Integer getFirstNum() { + return (pageNum - 1) * pageSize; + } + + public PageQuery(Integer pageSize, Integer pageNum) { + this.pageSize = pageSize; + this.pageNum = pageNum; + } + +} diff --git a/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java b/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java new file mode 100644 index 0000000..904401e --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/core/page/TableDataInfo.java @@ -0,0 +1,107 @@ +package org.dromara.common.mybatis.core.page; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.http.HttpStatus; +import com.baomidou.mybatisplus.core.metadata.IPage; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author shihongwei + */ +@Data +@NoArgsConstructor +public class TableDataInfo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 总记录数 + */ + private long total; + + /** + * 列表数据 + */ + private List rows; + + /** + * 消息状态码 + */ + private int code; + + /** + * 消息内容 + */ + private String msg; + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List list, long total) { + this.rows = list; + this.total = total; + this.code = HttpStatus.HTTP_OK; + this.msg = "查询成功"; + } + + /** + * 根据分页对象构建表格分页数据对象 + */ + public static TableDataInfo build(IPage page) { + TableDataInfo rspData = new TableDataInfo<>(); + rspData.setCode(HttpStatus.HTTP_OK); + rspData.setMsg("查询成功"); + rspData.setRows(page.getRecords()); + rspData.setTotal(page.getTotal()); + return rspData; + } + + /** + * 根据数据列表构建表格分页数据对象 + */ + public static TableDataInfo build(List list) { + TableDataInfo rspData = new TableDataInfo<>(); + rspData.setCode(HttpStatus.HTTP_OK); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(list.size()); + return rspData; + } + + /** + * 构建表格分页数据对象 + */ + public static TableDataInfo build() { + TableDataInfo rspData = new TableDataInfo<>(); + rspData.setCode(HttpStatus.HTTP_OK); + rspData.setMsg("查询成功"); + return rspData; + } + + /** + * 根据原始数据列表和分页参数,构建表格分页数据对象(用于假分页) + * + * @param list 原始数据列表(全部数据) + * @param page 分页参数对象(包含当前页码、每页大小等) + * @return 构造好的分页结果 TableDataInfo + */ + public static TableDataInfo build(List list, IPage page) { + if (CollUtil.isEmpty(list)) { + return TableDataInfo.build(); + } + List pageList = CollUtil.page((int) page.getCurrent() - 1, (int) page.getSize(), list); + return new TableDataInfo<>(pageList, list.size()); + } + +} diff --git a/src/main/java/org/dromara/common/mybatis/enums/DataBaseType.java b/src/main/java/org/dromara/common/mybatis/enums/DataBaseType.java new file mode 100644 index 0000000..908dda1 --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/enums/DataBaseType.java @@ -0,0 +1,87 @@ +package org.dromara.common.mybatis.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.dromara.common.core.utils.StringUtils; + +/** + * 数据库类型 + * + * @author shihongwei + */ +@Getter +@AllArgsConstructor +public enum DataBaseType { + + /** + * MySQL + */ + MY_SQL("MySQL"), + + /** + * Oracle + */ + ORACLE("Oracle"), + + /** + * PostgreSQL + */ + POSTGRE_SQL("PostgreSQL"), + + /** + * SQL Server + */ + SQL_SERVER("Microsoft SQL Server"); + + /** + * 数据库类型 + */ + private final String type; + + /** + * 根据数据库产品名称查找对应的数据库类型 + * + * @param databaseProductName 数据库产品名称 + * @return 对应的数据库类型枚举值 + */ + public static DataBaseType find(String databaseProductName) { + if (StringUtils.isBlank(databaseProductName)) { + return MY_SQL; + } + for (DataBaseType type : values()) { + if (type.getType().equals(databaseProductName)) { + return type; + } + } + return MY_SQL; + } + + /** + * 判断是否为 MySQL 类型 + */ + public boolean isMySql() { + return this == MY_SQL; + } + + /** + * 判断是否为 Oracle 类型 + */ + public boolean isOracle() { + return this == ORACLE; + } + + /** + * 判断是否为 PostgreSQL 类型 + */ + public boolean isPostgreSql() { + return this == POSTGRE_SQL; + } + + /** + * 判断是否为 SQL Server 类型 + */ + public boolean isSqlServer() { + return this == SQL_SERVER; + } + +} diff --git a/src/main/java/org/dromara/common/mybatis/enums/DataScopeType.java b/src/main/java/org/dromara/common/mybatis/enums/DataScopeType.java new file mode 100644 index 0000000..6522deb --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/enums/DataScopeType.java @@ -0,0 +1,87 @@ +package org.dromara.common.mybatis.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.helper.DataPermissionHelper; + +/** + * 数据权限类型枚举 + *

+ * 支持使用 SpEL 模板表达式定义 SQL 查询条件 + * 内置数据: + * - {@code user}: 当前登录用户信息,参考 {@link LoginUser} + * 内置服务: + * - {@code sdss}: 系统数据权限服务,参考 ISysDataScopeService + * 如需扩展数据,可以通过 {@link DataPermissionHelper} 进行操作 + * 如需扩展服务,可以通过 ISysDataScopeService 自行编写 + *

+ * + * @author shihongwei + * @version 3.5.0 + */ +@Getter +@AllArgsConstructor +public enum DataScopeType { + + /** + * 全部数据权限 + */ + ALL("1", "", ""), + + /** + * 自定数据权限 + */ + CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", " 1 = 0 "), + + /** + * 部门数据权限 + */ + DEPT("3", " #{#deptName} = #{#user.deptId} ", " 1 = 0 "), + + /** + * 部门及以下数据权限 + */ + DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", " 1 = 0 "), + + /** + * 仅本人数据权限 + */ + SELF("5", " #{#userName} = #{#user.userId} ", " 1 = 0 "), + + /** + * 部门及以下或本人数据权限 + */ + DEPT_AND_CHILD_OR_SELF("6", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} ) OR #{#userName} = #{#user.userId} ", " 1 = 0 "); + + private final String code; + + /** + * SpEL 模板表达式,用于构建 SQL 查询条件 + */ + private final String sqlTemplate; + + /** + * 如果不满足 {@code sqlTemplate} 的条件,则使用此默认 SQL 表达式 + */ + private final String elseSql; + + /** + * 根据枚举代码查找对应的枚举值 + * + * @param code 枚举代码 + * @return 对应的枚举值,如果未找到则返回 null + */ + public static DataScopeType findCode(String code) { + if (StringUtils.isBlank(code)) { + return null; + } + for (DataScopeType type : values()) { + if (type.getCode().equals(code)) { + return type; + } + } + return null; + } +} diff --git a/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java b/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java new file mode 100644 index 0000000..037d8dd --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java @@ -0,0 +1,121 @@ +package org.dromara.common.mybatis.handler; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpStatus; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.reflection.MetaObject; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.ObjectUtils; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.satoken.utils.LoginHelper; + +import java.util.Date; + +/** + * MP注入处理器 + * + * @author shihongwei + * @date 2021/4/25 + */ +@Slf4j +public class InjectionMetaObjectHandler implements MetaObjectHandler { + + /** + * 如果用户不存在默认注入-1代表无用户 + */ + private static final Long DEFAULT_USER_ID = -1L; + + /** + * 插入填充方法,用于在插入数据时自动填充实体对象中的创建时间、更新时间、创建人、更新人等信息 + * + * @param metaObject 元对象,用于获取原始对象并进行填充 + */ + @Override + public void insertFill(MetaObject metaObject) { + try { + if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) { + // 获取当前时间作为创建时间和更新时间,如果创建时间不为空,则使用创建时间,否则使用当前时间 + Date current = ObjectUtils.notNull(baseEntity.getCreateTime(), new Date()); + baseEntity.setCreateTime(current); + baseEntity.setUpdateTime(current); + + // 如果创建人为空,则填充当前登录用户的信息 + if (ObjectUtil.isNull(baseEntity.getCreateBy())) { + LoginUser loginUser = getLoginUser(); + if (ObjectUtil.isNotNull(loginUser)) { + Long userId = loginUser.getUserId(); + String nickname = loginUser.getUsername(); + baseEntity.setCreateBy(userId); + baseEntity.setUpdateBy(userId); + baseEntity.setCreateByName(nickname); + baseEntity.setUpdateByName(nickname); + baseEntity.setCreateDept(ObjectUtils.notNull(baseEntity.getCreateDept(), loginUser.getDeptId())); + } else { + baseEntity.setCreateBy(DEFAULT_USER_ID); + baseEntity.setUpdateBy(DEFAULT_USER_ID); + baseEntity.setCreateByName(null); + baseEntity.setUpdateByName(null); + baseEntity.setCreateDept(ObjectUtils.notNull(baseEntity.getCreateDept(), DEFAULT_USER_ID)); + } + } + } else { + Date date = new Date(); + this.strictInsertFill(metaObject, "createTime", Date.class, date); + this.strictInsertFill(metaObject, "updateTime", Date.class, date); + } + } catch (Exception e) { + throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); + } + } + + /** + * 更新填充方法,用于在更新数据时自动填充实体对象中的更新时间和更新人信息 + * + * @param metaObject 元对象,用于获取原始对象并进行填充 + */ + @Override + public void updateFill(MetaObject metaObject) { + try { + if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) { + Date current = new Date(); + baseEntity.setUpdateTime(current); + + Long userId = LoginHelper.getUserId(); + if (ObjectUtil.isNotNull(userId)) { + baseEntity.setUpdateBy(userId); + LoginUser loginUser = getLoginUser(); + if (ObjectUtil.isNotNull(loginUser)) { + baseEntity.setUpdateByName(loginUser.getNickname()); + } else { + baseEntity.setUpdateByName(null); + } + } else { + baseEntity.setUpdateBy(DEFAULT_USER_ID); + baseEntity.setUpdateByName(null); + } + } else { + this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date()); + } + } catch (Exception e) { + throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); + } + } + + /** + * 获取当前登录用户信息 + * + * @return 当前登录用户的信息,如果用户未登录则返回 null + */ + private LoginUser getLoginUser() { + LoginUser loginUser; + try { + loginUser = LoginHelper.getLoginUser(); + } catch (Exception e) { + return null; + } + return loginUser; + } + +} diff --git a/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java b/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java new file mode 100644 index 0000000..0b8895d --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java @@ -0,0 +1,89 @@ +package org.dromara.common.mybatis.handler; + +import cn.dev33.satoken.exception.NotLoginException; +import cn.hutool.http.HttpStatus; +import com.baomidou.dynamic.datasource.exception.CannotFindDataSourceException; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.R; +import org.mybatis.spring.MyBatisSystemException; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * Mybatis异常处理器 + * + * @author shihongwei + */ +@Slf4j +@RestControllerAdvice +public class MybatisExceptionHandler { + + /** + * 主键或UNIQUE索引,数据重复异常 + */ + @ExceptionHandler(DuplicateKeyException.class) + public R handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',数据库中已存在记录'{}'", requestURI, e.getMessage()); + return R.fail(HttpStatus.HTTP_CONFLICT, "数据库中已存在该记录,请联系管理员确认"); + } + + /** + * Mybatis系统异常 通用处理 + */ + @ExceptionHandler(MyBatisSystemException.class) + public R handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + Throwable root = getRootCause(e); + if (root instanceof NotLoginException) { + log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, root.getMessage()); + return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败,无法访问系统资源"); + } + if (root instanceof CannotFindDataSourceException) { + log.error("请求地址'{}', 未找到数据源", requestURI); + return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, "未找到数据源,请联系管理员确认"); + } + log.error("请求地址'{}', Mybatis系统异常", requestURI, e); + return R.fail(HttpStatus.HTTP_INTERNAL_ERROR, e.getMessage()); + } + + /** + * 获取异常的根因(递归查找) + * + * @param e 当前异常 + * @return 根因异常(最底层的 cause) + *

+ * 逻辑说明: + * 1. 如果 e 没有 cause,说明 e 本身就是根因,直接返回 + * 2. 如果 e 的 cause 和自身相同(防止循环引用),也返回 e + * 3. 否则递归调用,继续向下寻找最底层的 cause + */ + public static Throwable getRootCause(Throwable e) { + Throwable cause = e.getCause(); + if (cause == null || cause == e) { + return e; + } + return getRootCause(cause); + } + + /** + * 在异常链中查找指定类型的异常 + * + * @param e 当前异常 + * @param clazz 目标异常类 + * @return 找到的指定类型异常,如果没有找到返回 null + */ + public static Throwable findCause(Throwable e, Class clazz) { + Throwable t = e; + while (t != null && t != t.getCause()) { + if (clazz.isInstance(t)) { + return t; + } + t = t.getCause(); + } + return null; + } + +} diff --git a/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java b/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java new file mode 100644 index 0000000..8780919 --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java @@ -0,0 +1,268 @@ +package org.dromara.common.mybatis.handler; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import org.dromara.common.core.domain.dto.RoleDTO; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.annotation.DataColumn; +import org.dromara.common.mybatis.annotation.DataPermission; +import org.dromara.common.mybatis.enums.DataScopeType; +import org.dromara.common.mybatis.helper.DataPermissionHelper; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.expression.*; +import org.springframework.expression.common.TemplateParserContext; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; + +import java.util.*; +import java.util.function.Function; + +/** + * 数据权限过滤 + * + * @author shihongwei + * @version 3.5.0 + */ +@Slf4j +public class PlusDataPermissionHandler { + + /** + * spel 解析器 + */ + private final ExpressionParser parser = new SpelExpressionParser(); + private final ParserContext parserContext = new TemplateParserContext(); + /** + * bean解析器 用于处理 spel 表达式中对 bean 的调用 + */ + private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory()); + + /** + * 获取数据过滤条件的 SQL 片段 + * + * @param where 原始的查询条件表达式 + * @param isSelect 是否为查询语句 + * @return 数据过滤条件的 SQL 片段 + */ + public Expression getSqlSegment(Expression where, boolean isSelect) { + try { + // 获取数据权限配置 + DataPermission dataPermission = getDataPermission(); + // 获取当前登录用户信息 + LoginUser currentUser = DataPermissionHelper.getVariable("user"); + if (ObjectUtil.isNull(currentUser)) { + try { + currentUser = LoginHelper.getLoginUser(); + } catch (Exception e) { + return where; + } + DataPermissionHelper.setVariable("user", currentUser); + } + if (ObjectUtil.isNull(currentUser)) { + return where; + } + // 如果是超级管理员或租户管理员,则不过滤数据 + if (LoginHelper.isSuperAdmin(currentUser.getUserId()) || LoginHelper.isTenantAdmin(currentUser.getRolePermission())) { + return where; + } + // 构造数据过滤条件的 SQL 片段 + String dataFilterSql = buildDataFilter(dataPermission, isSelect); + if (StringUtils.isBlank(dataFilterSql)) { + return where; + } + Expression expression = CCJSqlParserUtil.parseExpression(dataFilterSql); + // 数据权限使用单独的括号 防止与其他条件冲突 + ParenthesedExpressionList parenthesis = new ParenthesedExpressionList<>(expression); + if (ObjectUtil.isNotNull(where)) { + return new AndExpression(where, parenthesis); + } else { + return parenthesis; + } + } catch (JSQLParserException e) { + throw new ServiceException("数据权限解析异常 => " + e.getMessage()); + } finally { + DataPermissionHelper.removePermission(); + } + } + + /** + * 构建数据过滤条件的 SQL 语句 + * + * @param dataPermission 数据权限注解 + * @param isSelect 标志当前操作是否为查询操作,查询操作和更新或删除操作在处理过滤条件时会有不同的处理方式 + * @return 构建的数据过滤条件的 SQL 语句 + * @throws ServiceException 如果角色的数据范围异常或者 key 与 value 的长度不匹配,则抛出 ServiceException 异常 + */ + private String buildDataFilter(DataPermission dataPermission, boolean isSelect) { + // 更新或删除需满足所有条件 + String joinStr = isSelect ? " OR " : " AND "; + if (StringUtils.isNotBlank(dataPermission.joinStr())) { + joinStr = " " + dataPermission.joinStr() + " "; + } + LoginUser user = DataPermissionHelper.getVariable("user"); + Object defaultValue = "-1"; + NullSafeStandardEvaluationContext context = new NullSafeStandardEvaluationContext(defaultValue); + context.addPropertyAccessor(new NullSafePropertyAccessor(context.getPropertyAccessors().get(0), defaultValue)); + context.setBeanResolver(beanResolver); + DataPermissionHelper.getContext().forEach(context::setVariable); + Set conditions = new HashSet<>(); + // 优先设置变量 + List keys = new ArrayList<>(); + Map ignoreMap = new HashMap<>(); + for (DataColumn dataColumn : dataPermission.value()) { + if (dataColumn.key().length != dataColumn.value().length) { + throw new ServiceException("角色数据范围异常 => key与value长度不匹配"); + } + // 包含权限标识符 这直接跳过 + if (StringUtils.isNotBlank(dataColumn.permission()) && + CollUtil.contains(user.getMenuPermission(), dataColumn.permission()) + ) { + ignoreMap.put(dataColumn, Boolean.TRUE); + continue; + } + // 设置注解变量 key 为表达式变量 value 为变量值 + for (int i = 0; i < dataColumn.key().length; i++) { + context.setVariable(dataColumn.key()[i], dataColumn.value()[i]); + } + keys.addAll(Arrays.stream(dataColumn.key()).map(key -> "#" + key).toList()); + } + + for (RoleDTO role : user.getRoles()) { + user.setRoleId(role.getRoleId()); + // 获取角色权限泛型 + DataScopeType type = DataScopeType.findCode(role.getDataScope()); + if (ObjectUtil.isNull(type)) { + throw new ServiceException("角色数据范围异常 => " + role.getDataScope()); + } + // 全部数据权限直接返回 + if (type == DataScopeType.ALL) { + return StringUtils.EMPTY; + } + boolean isSuccess = false; + for (DataColumn dataColumn : dataPermission.value()) { + // 包含权限标识符 这直接跳过 + if (ignoreMap.containsKey(dataColumn)) { + // 修复多角色与权限标识符共用问题 https://gitee.com/dromara/RuoYi-Vue-Plus/issues/IB4CS4 + conditions.add(joinStr + " 1 = 1 "); + isSuccess = true; + continue; + } + // 不包含 key 变量 则不处理 + if (!StringUtils.containsAny(type.getSqlTemplate(), keys.toArray(String[]::new))) { + continue; + } + // 当前注解不满足模板 不处理 + if (!StringUtils.containsAny(type.getSqlTemplate(), dataColumn.key())) { + continue; + } + // 忽略数据权限 防止spel表达式内有其他sql查询导致死循环调用 + String sql = DataPermissionHelper.ignore(() -> + parser.parseExpression(type.getSqlTemplate(), parserContext).getValue(context, String.class) + ); + // 解析sql模板并填充 + conditions.add(joinStr + sql); + isSuccess = true; + } + // 未处理成功则填充兜底方案 + if (!isSuccess && StringUtils.isNotBlank(type.getElseSql())) { + conditions.add(joinStr + type.getElseSql()); + } + } + + if (CollUtil.isNotEmpty(conditions)) { + String sql = StreamUtils.join(conditions, Function.identity(), ""); + return sql.substring(joinStr.length()); + } + return StringUtils.EMPTY; + } + + /** + * 根据映射语句 ID 或类名获取对应的 DataPermission 注解对象 + * + * @return DataPermission 注解对象,如果不存在则返回 null + */ + public DataPermission getDataPermission() { + return DataPermissionHelper.getPermission(); + } + + /** + * 检查给定的映射语句 ID 是否有效,即是否能够找到对应的 DataPermission 注解对象 + * + * @return 如果找到对应的 DataPermission 注解对象,则返回 false;否则返回 true + */ + public boolean invalid() { + return getDataPermission() == null; + } + + /** + * 对所有null变量找不到的变量返回默认值 + */ + @AllArgsConstructor + private static class NullSafeStandardEvaluationContext extends StandardEvaluationContext { + + private final Object defaultValue; + + @Override + public Object lookupVariable(String name) { + Object obj = super.lookupVariable(name); + // 如果读取到的值是 null,则返回默认值 + if (obj == null) { + return defaultValue; + } + return obj; + } + + } + + /** + * 对所有null变量找不到的变量返回默认值 委托模式 将不需要处理的方法委托给原处理器 + */ + @AllArgsConstructor + private static class NullSafePropertyAccessor implements PropertyAccessor { + + private final PropertyAccessor delegate; + private final Object defaultValue; + + @Override + public Class[] getSpecificTargetClasses() { + return delegate.getSpecificTargetClasses(); + } + + @Override + public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { + return delegate.canRead(context, target, name); + } + + @Override + public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException { + TypedValue value = delegate.read(context, target, name); + // 如果读取到的值是 null,则返回默认值 + if (value.getValue() == null) { + return new TypedValue(defaultValue); + } + return value; + } + + @Override + public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { + return delegate.canWrite(context, target, name); + } + + @Override + public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException { + delegate.write(context, target, name, newValue); + } + } + +} diff --git a/src/main/java/org/dromara/common/mybatis/handler/PlusPostInitTableInfoHandler.java b/src/main/java/org/dromara/common/mybatis/handler/PlusPostInitTableInfoHandler.java new file mode 100644 index 0000000..ab82c08 --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/handler/PlusPostInitTableInfoHandler.java @@ -0,0 +1,27 @@ +package org.dromara.common.mybatis.handler; + +import cn.hutool.core.convert.Convert; +import com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import org.apache.ibatis.session.Configuration; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.reflect.ReflectUtils; + +/** + * 修改表信息初始化方式 + * 目前用于全局修改是否使用逻辑删除 + * + * @author shihongwei + */ +public class PlusPostInitTableInfoHandler implements PostInitTableInfoHandler { + + @Override + public void postTableInfo(TableInfo tableInfo, Configuration configuration) { + String flag = SpringUtils.getProperty("mybatis-plus.enableLogicDelete", "true"); + // 只有关闭时 统一设置false 为true时mp自动判断不处理 + if (!Convert.toBool(flag)) { + ReflectUtils.setFieldValue(tableInfo, "withLogicDelete", false); + } + } + +} diff --git a/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java b/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java new file mode 100644 index 0000000..309fa10 --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java @@ -0,0 +1,82 @@ +package org.dromara.common.mybatis.helper; + +import cn.hutool.core.convert.Convert; +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.mybatis.enums.DataBaseType; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * 数据库助手 + * + * @author shihongwei + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class DataBaseHelper { + + private static final DynamicRoutingDataSource DS = SpringUtils.getBean(DynamicRoutingDataSource.class); + + /** + * 获取当前数据源对应的数据库类型 + *

+ * 通过 DynamicRoutingDataSource 获取当前线程绑定的数据源, + * 然后从数据源获取数据库连接,利用连接的元数据获取数据库产品名称, + * 最后调用 DataBaseType.find 方法将数据库名称转换为对应的枚举类型 + * + * @return 当前数据库对应的 DataBaseType 枚举,找不到时默认返回 MY_SQL + * @throws ServiceException 当获取数据库连接或元数据出现异常时抛出业务异常 + */ + public static DataBaseType getDataBaseType() { + DataSource dataSource = DS.determineDataSource(); + try (Connection conn = dataSource.getConnection()) { + DatabaseMetaData metaData = conn.getMetaData(); + String databaseProductName = metaData.getDatabaseProductName(); + return DataBaseType.find(databaseProductName); + } catch (SQLException e) { + throw new ServiceException(e.getMessage()); + } + } + + /** + * 根据当前数据库类型,生成兼容的 FIND_IN_SET 语句片段 + *

+ * 用于判断指定值是否存在于逗号分隔的字符串列中,SQL写法根据不同数据库方言自动切换: + * - Oracle 使用 instr 函数 + * - PostgreSQL 使用 strpos 函数 + * - SQL Server 使用 charindex 函数 + * - 其他默认使用 MySQL 的 find_in_set 函数 + * + * @param var1 要查找的值(支持任意类型,内部会转换成字符串) + * @param var2 存储逗号分隔值的数据库列名 + * @return 适用于当前数据库的 SQL 条件字符串,通常用于 where 或 apply 中拼接 + */ + public static String findInSet(Object var1, String var2) { + String var = Convert.toStr(var1); + return switch (getDataBaseType()) { + // instr(',0,100,101,' , ',100,') <> 0 + case ORACLE -> "instr(','||%s||',' , ',%s,') <> 0".formatted(var2, var); + // (select strpos(',0,100,101,' , ',100,')) <> 0 + case POSTGRE_SQL -> "(select strpos(','||%s||',' , ',%s,')) <> 0".formatted(var2, var); + // charindex(',100,' , ',0,100,101,') <> 0 + case SQL_SERVER -> "charindex(',%s,' , ','+%s+',') <> 0".formatted(var, var2); + // find_in_set(100 , '0,100,101') + default -> "find_in_set('%s' , %s) <> 0".formatted(var, var2); + }; + } + + /** + * 获取当前加载的数据库名 + */ + public static List getDataSourceNameList() { + return new ArrayList<>(DS.getDataSources().keySet()); + } +} diff --git a/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java b/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java new file mode 100644 index 0000000..0ab3e4a --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java @@ -0,0 +1,176 @@ +package org.dromara.common.mybatis.helper; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.context.model.SaStorage; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy; +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.reflect.ReflectUtils; +import org.dromara.common.mybatis.annotation.DataPermission; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; +import java.util.function.Supplier; + +/** + * 数据权限助手 + * + * @author shihongwei + * @version 3.5.0 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@SuppressWarnings("unchecked cast") +public class DataPermissionHelper { + + private static final String DATA_PERMISSION_KEY = "data:permission"; + + private static final ThreadLocal> REENTRANT_IGNORE = ThreadLocal.withInitial(Stack::new); + + private static final ThreadLocal PERMISSION_CACHE = new ThreadLocal<>(); + + /** + * 获取当前执行mapper权限注解 + * + * @return 返回当前执行mapper权限注解 + */ + public static DataPermission getPermission() { + return PERMISSION_CACHE.get(); + } + + /** + * 设置当前执行mapper权限注解 + * + * @param dataPermission 数据权限注解 + */ + public static void setPermission(DataPermission dataPermission) { + PERMISSION_CACHE.set(dataPermission); + } + + /** + * 删除当前执行mapper权限注解 + */ + public static void removePermission() { + PERMISSION_CACHE.remove(); + } + + /** + * 从上下文中获取指定键的变量值,并将其转换为指定的类型 + * + * @param key 变量的键 + * @param 变量值的类型 + * @return 指定键的变量值,如果不存在则返回 null + */ + public static T getVariable(String key) { + Map context = getContext(); + return (T) context.get(key); + } + + /** + * 向上下文中设置指定键的变量值 + * + * @param key 要设置的变量的键 + * @param value 要设置的变量值 + */ + public static void setVariable(String key, Object value) { + Map context = getContext(); + context.put(key, value); + } + + /** + * 获取数据权限上下文 + * + * @return 存储在SaStorage中的Map对象,用于存储数据权限相关的上下文信息 + * @throws NullPointerException 如果数据权限上下文类型异常,则抛出NullPointerException + */ + public static Map getContext() { + SaStorage saStorage = SaHolder.getStorage(); + Object attribute = saStorage.get(DATA_PERMISSION_KEY); + if (ObjectUtil.isNull(attribute)) { + saStorage.set(DATA_PERMISSION_KEY, new HashMap<>()); + attribute = saStorage.get(DATA_PERMISSION_KEY); + } + if (attribute instanceof Map map) { + return map; + } + throw new NullPointerException("data permission context type exception"); + } + + private static IgnoreStrategy getIgnoreStrategy() { + Object ignoreStrategyLocal = ReflectUtils.getStaticFieldValue(ReflectUtils.getField(InterceptorIgnoreHelper.class, "IGNORE_STRATEGY_LOCAL")); + if (ignoreStrategyLocal instanceof ThreadLocal IGNORE_STRATEGY_LOCAL) { + if (IGNORE_STRATEGY_LOCAL.get() instanceof IgnoreStrategy ignoreStrategy) { + return ignoreStrategy; + } + } + return null; + } + + /** + * 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭) + */ + public static void enableIgnore() { + IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); + if (ObjectUtil.isNull(ignoreStrategy)) { + InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build()); + } else { + ignoreStrategy.setDataPermission(true); + } + Stack reentrantStack = REENTRANT_IGNORE.get(); + reentrantStack.push(reentrantStack.size() + 1); + } + + /** + * 关闭忽略数据权限 + */ + public static void disableIgnore() { + IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); + if (ObjectUtil.isNotNull(ignoreStrategy)) { + boolean noOtherIgnoreStrategy = !Boolean.TRUE.equals(ignoreStrategy.getDynamicTableName()) + && !Boolean.TRUE.equals(ignoreStrategy.getBlockAttack()) + && !Boolean.TRUE.equals(ignoreStrategy.getIllegalSql()) + && !Boolean.TRUE.equals(ignoreStrategy.getTenantLine()) + && CollectionUtil.isEmpty(ignoreStrategy.getOthers()); + Stack reentrantStack = REENTRANT_IGNORE.get(); + boolean empty = reentrantStack.isEmpty() || reentrantStack.pop() == 1; + if (noOtherIgnoreStrategy && empty) { + InterceptorIgnoreHelper.clearIgnoreStrategy(); + } else if (empty) { + ignoreStrategy.setDataPermission(false); + } + + } + } + + /** + * 在忽略数据权限中执行 + * + * @param handle 处理执行方法 + */ + public static void ignore(Runnable handle) { + enableIgnore(); + try { + handle.run(); + } finally { + disableIgnore(); + } + } + + /** + * 在忽略数据权限中执行 + * + * @param handle 处理执行方法 + */ + public static T ignore(Supplier handle) { + enableIgnore(); + try { + return handle.get(); + } finally { + disableIgnore(); + } + } + +} diff --git a/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java b/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java new file mode 100644 index 0000000..b0f6725 --- /dev/null +++ b/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java @@ -0,0 +1,172 @@ +package org.dromara.common.mybatis.interceptor; + +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; +import com.baomidou.mybatisplus.core.toolkit.PluginUtils; +import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler; +import com.baomidou.mybatisplus.extension.plugins.inner.BaseMultiTableInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; +import lombok.extern.slf4j.Slf4j; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.delete.Delete; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.select.SetOperationList; +import net.sf.jsqlparser.statement.update.Update; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlCommandType; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; +import org.dromara.common.mybatis.handler.PlusDataPermissionHandler; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; + +/** + * 数据权限拦截器 + * + * @author shihongwei + * @version 3.5.0 + */ +@Slf4j +public class PlusDataPermissionInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor { + + private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler(); + + /** + * 在执行查询之前,检查并处理数据权限相关逻辑 + * + * @param executor MyBatis 执行器对象 + * @param ms 映射语句对象 + * @param parameter 方法参数 + * @param rowBounds 分页对象 + * @param resultHandler 结果处理器 + * @param boundSql 绑定的 SQL 对象 + * @throws SQLException 如果发生 SQL 异常 + */ + @Override + public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { + // 检查是否需要忽略数据权限处理 + if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { + return; + } + // 检查是否缺少有效的数据权限注解 + if (dataPermissionHandler.invalid()) { + return; + } + // 解析 sql 分配对应方法 + PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql); + mpBs.sql(parserSingle(mpBs.sql(), ms.getId())); + } + + /** + * 在准备 SQL 语句之前,检查并处理更新和删除操作的数据权限相关逻辑 + * + * @param sh MyBatis StatementHandler 对象 + * @param connection 数据库连接对象 + * @param transactionTimeout 事务超时时间 + */ + @Override + public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { + PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh); + MappedStatement ms = mpSh.mappedStatement(); + // 获取 SQL 命令类型(增、删、改、查) + SqlCommandType sct = ms.getSqlCommandType(); + + // 只处理更新和删除操作的 SQL 语句 + if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) { + if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { + return; + } + // 检查是否缺少有效的数据权限注解 + if (dataPermissionHandler.invalid()) { + return; + } + PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql(); + mpBs.sql(parserMulti(mpBs.sql(), ms.getId())); + } + } + + /** + * 处理 SELECT 查询语句中的 WHERE 条件 + * + * @param select SELECT 查询对象 + * @param index 查询语句的索引 + * @param sql 查询语句 + * @param obj WHERE 条件参数 + */ + @Override + protected void processSelect(Select select, int index, String sql, Object obj) { + if (select instanceof PlainSelect) { + this.setWhere((PlainSelect) select, (String) obj); + } else if (select instanceof SetOperationList setOperationList) { + List + select + a.id as id, + a.code as value, + a.name as label + from address_components a + where a.province = '51' and a.town = '0' + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/companyBankAccount/HotCompanyBankAccountMapper.xml b/src/main/resources/mapper/companyBankAccount/HotCompanyBankAccountMapper.xml new file mode 100644 index 0000000..69fcb87 --- /dev/null +++ b/src/main/resources/mapper/companyBankAccount/HotCompanyBankAccountMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/companyThirdPlatform/HotCompanyThirdPlatformMapper.xml b/src/main/resources/mapper/companyThirdPlatform/HotCompanyThirdPlatformMapper.xml new file mode 100644 index 0000000..d139435 --- /dev/null +++ b/src/main/resources/mapper/companyThirdPlatform/HotCompanyThirdPlatformMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotAccidentLevelConfigMapper.xml b/src/main/resources/mapper/config/HotAccidentLevelConfigMapper.xml new file mode 100644 index 0000000..78f3fce --- /dev/null +++ b/src/main/resources/mapper/config/HotAccidentLevelConfigMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotAccidentLikelihoodLevelConfigMapper.xml b/src/main/resources/mapper/config/HotAccidentLikelihoodLevelConfigMapper.xml new file mode 100644 index 0000000..148889f --- /dev/null +++ b/src/main/resources/mapper/config/HotAccidentLikelihoodLevelConfigMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotAccidentSeverityLevelConfigMapper.xml b/src/main/resources/mapper/config/HotAccidentSeverityLevelConfigMapper.xml new file mode 100644 index 0000000..c7ee46b --- /dev/null +++ b/src/main/resources/mapper/config/HotAccidentSeverityLevelConfigMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotCompanyBasicConfigMapper.xml b/src/main/resources/mapper/config/HotCompanyBasicConfigMapper.xml new file mode 100644 index 0000000..299cc45 --- /dev/null +++ b/src/main/resources/mapper/config/HotCompanyBasicConfigMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotCompanyDeptConfigMapper.xml b/src/main/resources/mapper/config/HotCompanyDeptConfigMapper.xml new file mode 100644 index 0000000..a5074ba --- /dev/null +++ b/src/main/resources/mapper/config/HotCompanyDeptConfigMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotCourseMapper.xml b/src/main/resources/mapper/config/HotCourseMapper.xml new file mode 100644 index 0000000..f0609a7 --- /dev/null +++ b/src/main/resources/mapper/config/HotCourseMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotCourseResourceMapper.xml b/src/main/resources/mapper/config/HotCourseResourceMapper.xml new file mode 100644 index 0000000..8752fb0 --- /dev/null +++ b/src/main/resources/mapper/config/HotCourseResourceMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotDispatchTemplateMapper.xml b/src/main/resources/mapper/config/HotDispatchTemplateMapper.xml new file mode 100644 index 0000000..769eb7f --- /dev/null +++ b/src/main/resources/mapper/config/HotDispatchTemplateMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotDriverAnnualAssessmentConfigMapper.xml b/src/main/resources/mapper/config/HotDriverAnnualAssessmentConfigMapper.xml new file mode 100644 index 0000000..b8a0c3c --- /dev/null +++ b/src/main/resources/mapper/config/HotDriverAnnualAssessmentConfigMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotExamPaperMapper.xml b/src/main/resources/mapper/config/HotExamPaperMapper.xml new file mode 100644 index 0000000..db40616 --- /dev/null +++ b/src/main/resources/mapper/config/HotExamPaperMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotExamPaperQuestionMapper.xml b/src/main/resources/mapper/config/HotExamPaperQuestionMapper.xml new file mode 100644 index 0000000..dcf73ca --- /dev/null +++ b/src/main/resources/mapper/config/HotExamPaperQuestionMapper.xml @@ -0,0 +1,35 @@ + + + + + diff --git a/src/main/resources/mapper/config/HotHiddenDangerCheckConfigMapper.xml b/src/main/resources/mapper/config/HotHiddenDangerCheckConfigMapper.xml new file mode 100644 index 0000000..326cb27 --- /dev/null +++ b/src/main/resources/mapper/config/HotHiddenDangerCheckConfigMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotHiddenDangerInspectStoreMapper.xml b/src/main/resources/mapper/config/HotHiddenDangerInspectStoreMapper.xml new file mode 100644 index 0000000..c51809f --- /dev/null +++ b/src/main/resources/mapper/config/HotHiddenDangerInspectStoreMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotMediaResourceMapper.xml b/src/main/resources/mapper/config/HotMediaResourceMapper.xml new file mode 100644 index 0000000..eb72625 --- /dev/null +++ b/src/main/resources/mapper/config/HotMediaResourceMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotQuestionBankMapper.xml b/src/main/resources/mapper/config/HotQuestionBankMapper.xml new file mode 100644 index 0000000..b8fbabd --- /dev/null +++ b/src/main/resources/mapper/config/HotQuestionBankMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotRiskLevelConfigMapper.xml b/src/main/resources/mapper/config/HotRiskLevelConfigMapper.xml new file mode 100644 index 0000000..d7434ef --- /dev/null +++ b/src/main/resources/mapper/config/HotRiskLevelConfigMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotRiskScoreLevelMapper.xml b/src/main/resources/mapper/config/HotRiskScoreLevelMapper.xml new file mode 100644 index 0000000..9fbee59 --- /dev/null +++ b/src/main/resources/mapper/config/HotRiskScoreLevelMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotSignAuditPermissionConfigMapper.xml b/src/main/resources/mapper/config/HotSignAuditPermissionConfigMapper.xml new file mode 100644 index 0000000..f9a684b --- /dev/null +++ b/src/main/resources/mapper/config/HotSignAuditPermissionConfigMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotSquareInfoMapper.xml b/src/main/resources/mapper/config/HotSquareInfoMapper.xml new file mode 100644 index 0000000..f430393 --- /dev/null +++ b/src/main/resources/mapper/config/HotSquareInfoMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotSystemAgreementMapper.xml b/src/main/resources/mapper/config/HotSystemAgreementMapper.xml new file mode 100644 index 0000000..8df9ff0 --- /dev/null +++ b/src/main/resources/mapper/config/HotSystemAgreementMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotSystemTemplateMapper.xml b/src/main/resources/mapper/config/HotSystemTemplateMapper.xml new file mode 100644 index 0000000..c7691a4 --- /dev/null +++ b/src/main/resources/mapper/config/HotSystemTemplateMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotVehicleBrandModelMapper.xml b/src/main/resources/mapper/config/HotVehicleBrandModelMapper.xml new file mode 100644 index 0000000..24ed0e8 --- /dev/null +++ b/src/main/resources/mapper/config/HotVehicleBrandModelMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotVehicleInspectionConfigMapper.xml b/src/main/resources/mapper/config/HotVehicleInspectionConfigMapper.xml new file mode 100644 index 0000000..8a60dba --- /dev/null +++ b/src/main/resources/mapper/config/HotVehicleInspectionConfigMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotVehicleSecondaryMaintenanceConfigMapper.xml b/src/main/resources/mapper/config/HotVehicleSecondaryMaintenanceConfigMapper.xml new file mode 100644 index 0000000..360b638 --- /dev/null +++ b/src/main/resources/mapper/config/HotVehicleSecondaryMaintenanceConfigMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotVehicleThreeInspectConfigMapper.xml b/src/main/resources/mapper/config/HotVehicleThreeInspectConfigMapper.xml new file mode 100644 index 0000000..4a6fcaf --- /dev/null +++ b/src/main/resources/mapper/config/HotVehicleThreeInspectConfigMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotVehicleThreeInspectItemMapper.xml b/src/main/resources/mapper/config/HotVehicleThreeInspectItemMapper.xml new file mode 100644 index 0000000..4be8b62 --- /dev/null +++ b/src/main/resources/mapper/config/HotVehicleThreeInspectItemMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/config/HotVehicleTypeMapper.xml b/src/main/resources/mapper/config/HotVehicleTypeMapper.xml new file mode 100644 index 0000000..3035e00 --- /dev/null +++ b/src/main/resources/mapper/config/HotVehicleTypeMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/courseAnswer/HotTrainingAnswerRecordMapper.xml b/src/main/resources/mapper/courseAnswer/HotTrainingAnswerRecordMapper.xml new file mode 100644 index 0000000..2ff1f06 --- /dev/null +++ b/src/main/resources/mapper/courseAnswer/HotTrainingAnswerRecordMapper.xml @@ -0,0 +1,139 @@ + + + + + + + + + diff --git a/src/main/resources/mapper/driverManagement/HotDriverAnnualAssessmentMapper.xml b/src/main/resources/mapper/driverManagement/HotDriverAnnualAssessmentMapper.xml new file mode 100644 index 0000000..5a00530 --- /dev/null +++ b/src/main/resources/mapper/driverManagement/HotDriverAnnualAssessmentMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/driverManagement/HotDriverFamilyMemberMapper.xml b/src/main/resources/mapper/driverManagement/HotDriverFamilyMemberMapper.xml new file mode 100644 index 0000000..217e758 --- /dev/null +++ b/src/main/resources/mapper/driverManagement/HotDriverFamilyMemberMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/driverManagement/HotDriverFileSignMapper.xml b/src/main/resources/mapper/driverManagement/HotDriverFileSignMapper.xml new file mode 100644 index 0000000..b8da61b --- /dev/null +++ b/src/main/resources/mapper/driverManagement/HotDriverFileSignMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/driverManagement/HotDriverGroupMapper.xml b/src/main/resources/mapper/driverManagement/HotDriverGroupMapper.xml new file mode 100644 index 0000000..a9318fb --- /dev/null +++ b/src/main/resources/mapper/driverManagement/HotDriverGroupMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/driverManagement/HotDriverHealthReportMapper.xml b/src/main/resources/mapper/driverManagement/HotDriverHealthReportMapper.xml new file mode 100644 index 0000000..9d760c2 --- /dev/null +++ b/src/main/resources/mapper/driverManagement/HotDriverHealthReportMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/driverManagement/HotDriverInviteLogMapper.xml b/src/main/resources/mapper/driverManagement/HotDriverInviteLogMapper.xml new file mode 100644 index 0000000..1bdcf5c --- /dev/null +++ b/src/main/resources/mapper/driverManagement/HotDriverInviteLogMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/driverManagement/HotDriverLaborContractMapper.xml b/src/main/resources/mapper/driverManagement/HotDriverLaborContractMapper.xml new file mode 100644 index 0000000..48f7ae2 --- /dev/null +++ b/src/main/resources/mapper/driverManagement/HotDriverLaborContractMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/driverManagement/HotDriverMapper.xml b/src/main/resources/mapper/driverManagement/HotDriverMapper.xml new file mode 100644 index 0000000..a8497fa --- /dev/null +++ b/src/main/resources/mapper/driverManagement/HotDriverMapper.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/src/main/resources/mapper/driverManagement/HotDriverProfCertMapper.xml b/src/main/resources/mapper/driverManagement/HotDriverProfCertMapper.xml new file mode 100644 index 0000000..0c52879 --- /dev/null +++ b/src/main/resources/mapper/driverManagement/HotDriverProfCertMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/driverManagement/HotDriverRewardPunishmentMapper.xml b/src/main/resources/mapper/driverManagement/HotDriverRewardPunishmentMapper.xml new file mode 100644 index 0000000..45774b3 --- /dev/null +++ b/src/main/resources/mapper/driverManagement/HotDriverRewardPunishmentMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/driverManagement/HotDriverSkillAssessmentMapper.xml b/src/main/resources/mapper/driverManagement/HotDriverSkillAssessmentMapper.xml new file mode 100644 index 0000000..4560a96 --- /dev/null +++ b/src/main/resources/mapper/driverManagement/HotDriverSkillAssessmentMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/driverManagement/HotDriverWorkExperienceMapper.xml b/src/main/resources/mapper/driverManagement/HotDriverWorkExperienceMapper.xml new file mode 100644 index 0000000..008ae98 --- /dev/null +++ b/src/main/resources/mapper/driverManagement/HotDriverWorkExperienceMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/src/main/resources/mapper/generator/GenTableColumnMapper.xml new file mode 100644 index 0000000..f8f43b7 --- /dev/null +++ b/src/main/resources/mapper/generator/GenTableColumnMapper.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/src/main/resources/mapper/generator/GenTableMapper.xml b/src/main/resources/mapper/generator/GenTableMapper.xml new file mode 100644 index 0000000..78aa852 --- /dev/null +++ b/src/main/resources/mapper/generator/GenTableMapper.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + SELECT t.table_id, t.data_name, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark, + c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort + FROM gen_table t + LEFT JOIN gen_table_column c ON t.table_id = c.table_id + + + + + + + + + + diff --git a/src/main/resources/mapper/noticeManagerment/HotAdminNoticeMapper.xml b/src/main/resources/mapper/noticeManagerment/HotAdminNoticeMapper.xml new file mode 100644 index 0000000..d4e7362 --- /dev/null +++ b/src/main/resources/mapper/noticeManagerment/HotAdminNoticeMapper.xml @@ -0,0 +1,45 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/noticeManagerment/HotCompanyNoticeMapper.xml b/src/main/resources/mapper/noticeManagerment/HotCompanyNoticeMapper.xml new file mode 100644 index 0000000..94b9162 --- /dev/null +++ b/src/main/resources/mapper/noticeManagerment/HotCompanyNoticeMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/noticeManagerment/HotComplaintAdviceMapper.xml b/src/main/resources/mapper/noticeManagerment/HotComplaintAdviceMapper.xml new file mode 100644 index 0000000..b2a5e13 --- /dev/null +++ b/src/main/resources/mapper/noticeManagerment/HotComplaintAdviceMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/noticeManagerment/HotFileTypeConfigMapper.xml b/src/main/resources/mapper/noticeManagerment/HotFileTypeConfigMapper.xml new file mode 100644 index 0000000..bb446f8 --- /dev/null +++ b/src/main/resources/mapper/noticeManagerment/HotFileTypeConfigMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/noticeManagerment/HotLawRegulationMapper.xml b/src/main/resources/mapper/noticeManagerment/HotLawRegulationMapper.xml new file mode 100644 index 0000000..b2779d6 --- /dev/null +++ b/src/main/resources/mapper/noticeManagerment/HotLawRegulationMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/noticeManagerment/HotNoticeRecordMapper.xml b/src/main/resources/mapper/noticeManagerment/HotNoticeRecordMapper.xml new file mode 100644 index 0000000..25576f8 --- /dev/null +++ b/src/main/resources/mapper/noticeManagerment/HotNoticeRecordMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/noticeManagerment/HotNoticeReplyMapper.xml b/src/main/resources/mapper/noticeManagerment/HotNoticeReplyMapper.xml new file mode 100644 index 0000000..5690181 --- /dev/null +++ b/src/main/resources/mapper/noticeManagerment/HotNoticeReplyMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/noticeManagerment/HotNoticeSignDocumentMapper.xml b/src/main/resources/mapper/noticeManagerment/HotNoticeSignDocumentMapper.xml new file mode 100644 index 0000000..b49e013 --- /dev/null +++ b/src/main/resources/mapper/noticeManagerment/HotNoticeSignDocumentMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/noticeManagerment/HotNotificationIssueMapper.xml b/src/main/resources/mapper/noticeManagerment/HotNotificationIssueMapper.xml new file mode 100644 index 0000000..da5eace --- /dev/null +++ b/src/main/resources/mapper/noticeManagerment/HotNotificationIssueMapper.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/src/main/resources/mapper/noticeManagerment/HotSignFileDetailMapper.xml b/src/main/resources/mapper/noticeManagerment/HotSignFileDetailMapper.xml new file mode 100644 index 0000000..3dd8bda --- /dev/null +++ b/src/main/resources/mapper/noticeManagerment/HotSignFileDetailMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/noticeSignDocument/HotSystemNotificationMapper.xml b/src/main/resources/mapper/noticeSignDocument/HotSystemNotificationMapper.xml new file mode 100644 index 0000000..25849a3 --- /dev/null +++ b/src/main/resources/mapper/noticeSignDocument/HotSystemNotificationMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotContractManageMapper.xml b/src/main/resources/mapper/operationManagement/HotContractManageMapper.xml new file mode 100644 index 0000000..7436921 --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotContractManageMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotFundAccountMapper.xml b/src/main/resources/mapper/operationManagement/HotFundAccountMapper.xml new file mode 100644 index 0000000..3aebb46 --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotFundAccountMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotFundRechargeOrderMapper.xml b/src/main/resources/mapper/operationManagement/HotFundRechargeOrderMapper.xml new file mode 100644 index 0000000..7284d8b --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotFundRechargeOrderMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotFundTradeOrderMapper.xml b/src/main/resources/mapper/operationManagement/HotFundTradeOrderMapper.xml new file mode 100644 index 0000000..6ed453d --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotFundTradeOrderMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotHourPackageMapper.xml b/src/main/resources/mapper/operationManagement/HotHourPackageMapper.xml new file mode 100644 index 0000000..396ffa3 --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotHourPackageMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotHourPackagePurchaseMapper.xml b/src/main/resources/mapper/operationManagement/HotHourPackagePurchaseMapper.xml new file mode 100644 index 0000000..ec0155b --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotHourPackagePurchaseMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotHourPurchaseDetailMapper.xml b/src/main/resources/mapper/operationManagement/HotHourPurchaseDetailMapper.xml new file mode 100644 index 0000000..4e37921 --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotHourPurchaseDetailMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotHourUsageDetailMapper.xml b/src/main/resources/mapper/operationManagement/HotHourUsageDetailMapper.xml new file mode 100644 index 0000000..4d61b4e --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotHourUsageDetailMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotInvoiceManageMapper.xml b/src/main/resources/mapper/operationManagement/HotInvoiceManageMapper.xml new file mode 100644 index 0000000..7884f68 --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotInvoiceManageMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotPreJobPackageMapper.xml b/src/main/resources/mapper/operationManagement/HotPreJobPackageMapper.xml new file mode 100644 index 0000000..8e0f899 --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotPreJobPackageMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotPreJobPackagePurchaseMapper.xml b/src/main/resources/mapper/operationManagement/HotPreJobPackagePurchaseMapper.xml new file mode 100644 index 0000000..012d58b --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotPreJobPackagePurchaseMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotSmsCompanyStatMapper.xml b/src/main/resources/mapper/operationManagement/HotSmsCompanyStatMapper.xml new file mode 100644 index 0000000..31b88df --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotSmsCompanyStatMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotSmsDailyStatMapper.xml b/src/main/resources/mapper/operationManagement/HotSmsDailyStatMapper.xml new file mode 100644 index 0000000..1c2a7d4 --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotSmsDailyStatMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotSmsPackageMapper.xml b/src/main/resources/mapper/operationManagement/HotSmsPackageMapper.xml new file mode 100644 index 0000000..b85b340 --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotSmsPackageMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotSmsPackagePurchaseMapper.xml b/src/main/resources/mapper/operationManagement/HotSmsPackagePurchaseMapper.xml new file mode 100644 index 0000000..8c00599 --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotSmsPackagePurchaseMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotSmsRechargeRecordMapper.xml b/src/main/resources/mapper/operationManagement/HotSmsRechargeRecordMapper.xml new file mode 100644 index 0000000..2c9bdd4 --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotSmsRechargeRecordMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotSmsSendDetailMapper.xml b/src/main/resources/mapper/operationManagement/HotSmsSendDetailMapper.xml new file mode 100644 index 0000000..60f1778 --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotSmsSendDetailMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotTrainingFeedbackMapper.xml b/src/main/resources/mapper/operationManagement/HotTrainingFeedbackMapper.xml new file mode 100644 index 0000000..ccdbf5f --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotTrainingFeedbackMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/operationManagement/HotTrainingFeedbackReplyMapper.xml b/src/main/resources/mapper/operationManagement/HotTrainingFeedbackReplyMapper.xml new file mode 100644 index 0000000..bde8ead --- /dev/null +++ b/src/main/resources/mapper/operationManagement/HotTrainingFeedbackReplyMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/package-info.md b/src/main/resources/mapper/package-info.md new file mode 100644 index 0000000..c938b1e --- /dev/null +++ b/src/main/resources/mapper/package-info.md @@ -0,0 +1,3 @@ +java包使用 `.` 分割 resource 目录使用 `/` 分割 +
+此文件目的 防止文件夹粘连找不到 `xml` 文件 \ No newline at end of file diff --git a/src/main/resources/mapper/pay/PayAppMapper.xml b/src/main/resources/mapper/pay/PayAppMapper.xml new file mode 100644 index 0000000..9d0d8fb --- /dev/null +++ b/src/main/resources/mapper/pay/PayAppMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/pay/PayChannelMapper.xml b/src/main/resources/mapper/pay/PayChannelMapper.xml new file mode 100644 index 0000000..51b9420 --- /dev/null +++ b/src/main/resources/mapper/pay/PayChannelMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/pay/PayOrderExtensionMapper.xml b/src/main/resources/mapper/pay/PayOrderExtensionMapper.xml new file mode 100644 index 0000000..6cee755 --- /dev/null +++ b/src/main/resources/mapper/pay/PayOrderExtensionMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/pay/PayOrderMapper.xml b/src/main/resources/mapper/pay/PayOrderMapper.xml new file mode 100644 index 0000000..263c789 --- /dev/null +++ b/src/main/resources/mapper/pay/PayOrderMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/pay/PayWalletMapper.xml b/src/main/resources/mapper/pay/PayWalletMapper.xml new file mode 100644 index 0000000..5ed2b60 --- /dev/null +++ b/src/main/resources/mapper/pay/PayWalletMapper.xml @@ -0,0 +1,33 @@ + + + + + diff --git a/src/main/resources/mapper/pay/PayWalletTransactionMapper.xml b/src/main/resources/mapper/pay/PayWalletTransactionMapper.xml new file mode 100644 index 0000000..dfc7cd8 --- /dev/null +++ b/src/main/resources/mapper/pay/PayWalletTransactionMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/regulationManagement/HotPolicyMapper.xml b/src/main/resources/mapper/regulationManagement/HotPolicyMapper.xml new file mode 100644 index 0000000..a4933e3 --- /dev/null +++ b/src/main/resources/mapper/regulationManagement/HotPolicyMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/regulationManagement/HotPolicyRevisionMapper.xml b/src/main/resources/mapper/regulationManagement/HotPolicyRevisionMapper.xml new file mode 100644 index 0000000..446bad3 --- /dev/null +++ b/src/main/resources/mapper/regulationManagement/HotPolicyRevisionMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/reportStatistics/LedgerReportMapper.xml b/src/main/resources/mapper/reportStatistics/LedgerReportMapper.xml new file mode 100644 index 0000000..31b37b2 --- /dev/null +++ b/src/main/resources/mapper/reportStatistics/LedgerReportMapper.xml @@ -0,0 +1,1544 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotCompanyInsuranceMapper.xml b/src/main/resources/mapper/resourceManagement/HotCompanyInsuranceMapper.xml new file mode 100644 index 0000000..8755b8d --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotCompanyInsuranceMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotCompanyLaborContractMapper.xml b/src/main/resources/mapper/resourceManagement/HotCompanyLaborContractMapper.xml new file mode 100644 index 0000000..e7f17ff --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotCompanyLaborContractMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotCompanyPolicyMapper.xml b/src/main/resources/mapper/resourceManagement/HotCompanyPolicyMapper.xml new file mode 100644 index 0000000..28c884c --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotCompanyPolicyMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotCompanySafetyManagerMapper.xml b/src/main/resources/mapper/resourceManagement/HotCompanySafetyManagerMapper.xml new file mode 100644 index 0000000..3a0621d --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotCompanySafetyManagerMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotGovEnterpriseUnitMapper.xml b/src/main/resources/mapper/resourceManagement/HotGovEnterpriseUnitMapper.xml new file mode 100644 index 0000000..a899e7e --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotGovEnterpriseUnitMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotPersonnelConfigMapper.xml b/src/main/resources/mapper/resourceManagement/HotPersonnelConfigMapper.xml new file mode 100644 index 0000000..6872ff2 --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotPersonnelConfigMapper.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotTemporaryMapper.xml b/src/main/resources/mapper/resourceManagement/HotTemporaryMapper.xml new file mode 100644 index 0000000..7594d58 --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotTemporaryMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotVehicleAnnualReviewMapper.xml b/src/main/resources/mapper/resourceManagement/HotVehicleAnnualReviewMapper.xml new file mode 100644 index 0000000..0e9ae17 --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotVehicleAnnualReviewMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotVehicleCertReissueMapper.xml b/src/main/resources/mapper/resourceManagement/HotVehicleCertReissueMapper.xml new file mode 100644 index 0000000..a609f9d --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotVehicleCertReissueMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotVehicleChangeMapper.xml b/src/main/resources/mapper/resourceManagement/HotVehicleChangeMapper.xml new file mode 100644 index 0000000..bb00397 --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotVehicleChangeMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotVehicleClaimMapper.xml b/src/main/resources/mapper/resourceManagement/HotVehicleClaimMapper.xml new file mode 100644 index 0000000..55d2a01 --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotVehicleClaimMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotVehicleContractMapper.xml b/src/main/resources/mapper/resourceManagement/HotVehicleContractMapper.xml new file mode 100644 index 0000000..f478bc1 --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotVehicleContractMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotVehicleDeviceMapper.xml b/src/main/resources/mapper/resourceManagement/HotVehicleDeviceMapper.xml new file mode 100644 index 0000000..76afdf5 --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotVehicleDeviceMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotVehicleDriveLogMapper.xml b/src/main/resources/mapper/resourceManagement/HotVehicleDriveLogMapper.xml new file mode 100644 index 0000000..ace678a --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotVehicleDriveLogMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotVehicleInsuranceMapper.xml b/src/main/resources/mapper/resourceManagement/HotVehicleInsuranceMapper.xml new file mode 100644 index 0000000..25b4a14 --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotVehicleInsuranceMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotVehicleMaintenanceMapper.xml b/src/main/resources/mapper/resourceManagement/HotVehicleMaintenanceMapper.xml new file mode 100644 index 0000000..2c4f8d0 --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotVehicleMaintenanceMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotVehicleMapper.xml b/src/main/resources/mapper/resourceManagement/HotVehicleMapper.xml new file mode 100644 index 0000000..e66a8e0 --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotVehicleMapper.xml @@ -0,0 +1,42 @@ + + + + + + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotVehicleMileageMapper.xml b/src/main/resources/mapper/resourceManagement/HotVehicleMileageMapper.xml new file mode 100644 index 0000000..e0ef9fa --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotVehicleMileageMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotVehicleRepairMapper.xml b/src/main/resources/mapper/resourceManagement/HotVehicleRepairMapper.xml new file mode 100644 index 0000000..47dfe53 --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotVehicleRepairMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/HotVehicleTerminalInstallMapper.xml b/src/main/resources/mapper/resourceManagement/HotVehicleTerminalInstallMapper.xml new file mode 100644 index 0000000..333fa40 --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/HotVehicleTerminalInstallMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/SysCompanyMapper.xml b/src/main/resources/mapper/resourceManagement/SysCompanyMapper.xml new file mode 100644 index 0000000..d5303bd --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/SysCompanyMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/resourceManagement/SysUserLoginPortMapper.xml b/src/main/resources/mapper/resourceManagement/SysUserLoginPortMapper.xml new file mode 100644 index 0000000..84a9db7 --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/SysUserLoginPortMapper.xml @@ -0,0 +1,70 @@ + + + + + + + diff --git a/src/main/resources/mapper/resourceManagement/driverInfo/DriverPageMapper.xml b/src/main/resources/mapper/resourceManagement/driverInfo/DriverPageMapper.xml new file mode 100644 index 0000000..767fc21 --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/driverInfo/DriverPageMapper.xml @@ -0,0 +1,66 @@ + + + + + + diff --git a/src/main/resources/mapper/resourceManagement/vehicleInfo/VehicleInfoMapper.xml b/src/main/resources/mapper/resourceManagement/vehicleInfo/VehicleInfoMapper.xml new file mode 100644 index 0000000..faa6830 --- /dev/null +++ b/src/main/resources/mapper/resourceManagement/vehicleInfo/VehicleInfoMapper.xml @@ -0,0 +1,51 @@ + + + + + + + diff --git a/src/main/resources/mapper/securityManagement/HotAccidentArchiveMapper.xml b/src/main/resources/mapper/securityManagement/HotAccidentArchiveMapper.xml new file mode 100644 index 0000000..834d973 --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotAccidentArchiveMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/securityManagement/HotAccidentReportMapper.xml b/src/main/resources/mapper/securityManagement/HotAccidentReportMapper.xml new file mode 100644 index 0000000..a7dea99 --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotAccidentReportMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/securityManagement/HotHiddenDangerInspectionMapper.xml b/src/main/resources/mapper/securityManagement/HotHiddenDangerInspectionMapper.xml new file mode 100644 index 0000000..66168d6 --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotHiddenDangerInspectionMapper.xml @@ -0,0 +1,77 @@ + + + + + + + diff --git a/src/main/resources/mapper/securityManagement/HotHiddenDangerMapper.xml b/src/main/resources/mapper/securityManagement/HotHiddenDangerMapper.xml new file mode 100644 index 0000000..39ac45c --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotHiddenDangerMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/securityManagement/HotHiddenDangerPlanMapper.xml b/src/main/resources/mapper/securityManagement/HotHiddenDangerPlanMapper.xml new file mode 100644 index 0000000..ef62edc --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotHiddenDangerPlanMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/securityManagement/HotSafetyInvestmentMapper.xml b/src/main/resources/mapper/securityManagement/HotSafetyInvestmentMapper.xml new file mode 100644 index 0000000..81fa63c --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotSafetyInvestmentMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/securityManagement/HotSecurityMeetingAttendeeMapper.xml b/src/main/resources/mapper/securityManagement/HotSecurityMeetingAttendeeMapper.xml new file mode 100644 index 0000000..d3aaf94 --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotSecurityMeetingAttendeeMapper.xml @@ -0,0 +1,69 @@ + + + + + + + diff --git a/src/main/resources/mapper/securityManagement/HotSecurityMeetingMapper.xml b/src/main/resources/mapper/securityManagement/HotSecurityMeetingMapper.xml new file mode 100644 index 0000000..2a75ce8 --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotSecurityMeetingMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/securityManagement/HotServiceComplaintMapper.xml b/src/main/resources/mapper/securityManagement/HotServiceComplaintMapper.xml new file mode 100644 index 0000000..7cfcc47 --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotServiceComplaintMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/securityManagement/HotServiceQualityComplaintMapper.xml b/src/main/resources/mapper/securityManagement/HotServiceQualityComplaintMapper.xml new file mode 100644 index 0000000..753e66d --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotServiceQualityComplaintMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/securityManagement/HotTrainingAuditMapper.xml b/src/main/resources/mapper/securityManagement/HotTrainingAuditMapper.xml new file mode 100644 index 0000000..580945e --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotTrainingAuditMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/securityManagement/HotTrainingCourseConfigMapper.xml b/src/main/resources/mapper/securityManagement/HotTrainingCourseConfigMapper.xml new file mode 100644 index 0000000..3c74014 --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotTrainingCourseConfigMapper.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/src/main/resources/mapper/securityManagement/HotTrainingCourseRecordMapper.xml b/src/main/resources/mapper/securityManagement/HotTrainingCourseRecordMapper.xml new file mode 100644 index 0000000..5f42e67 --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotTrainingCourseRecordMapper.xml @@ -0,0 +1,57 @@ + + + + + + diff --git a/src/main/resources/mapper/securityManagement/HotTrainingMapper.xml b/src/main/resources/mapper/securityManagement/HotTrainingMapper.xml new file mode 100644 index 0000000..7c2be28 --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotTrainingMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/securityManagement/HotTrainingOutlineCourseMapper.xml b/src/main/resources/mapper/securityManagement/HotTrainingOutlineCourseMapper.xml new file mode 100644 index 0000000..945022c --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotTrainingOutlineCourseMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/securityManagement/HotTrainingOutlineMapper.xml b/src/main/resources/mapper/securityManagement/HotTrainingOutlineMapper.xml new file mode 100644 index 0000000..a3ff845 --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotTrainingOutlineMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/securityManagement/HotTrainingOutlineMonthMapper.xml b/src/main/resources/mapper/securityManagement/HotTrainingOutlineMonthMapper.xml new file mode 100644 index 0000000..e1ca3ac --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotTrainingOutlineMonthMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/securityManagement/HotTrainingParticipantMapper.xml b/src/main/resources/mapper/securityManagement/HotTrainingParticipantMapper.xml new file mode 100644 index 0000000..84f4d6b --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotTrainingParticipantMapper.xml @@ -0,0 +1,59 @@ + + + + + + + + + diff --git a/src/main/resources/mapper/securityManagement/HotVehicleEntryExitMapper.xml b/src/main/resources/mapper/securityManagement/HotVehicleEntryExitMapper.xml new file mode 100644 index 0000000..6c58312 --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotVehicleEntryExitMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/securityManagement/HotVehicleInoutCheckMapper.xml b/src/main/resources/mapper/securityManagement/HotVehicleInoutCheckMapper.xml new file mode 100644 index 0000000..6c6da7f --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotVehicleInoutCheckMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/securityManagement/HotVehicleThreeInspectMapper.xml b/src/main/resources/mapper/securityManagement/HotVehicleThreeInspectMapper.xml new file mode 100644 index 0000000..ee24695 --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotVehicleThreeInspectMapper.xml @@ -0,0 +1,76 @@ + + + + + + + diff --git a/src/main/resources/mapper/securityManagement/HotViolationInfoMapper.xml b/src/main/resources/mapper/securityManagement/HotViolationInfoMapper.xml new file mode 100644 index 0000000..5558d2a --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotViolationInfoMapper.xml @@ -0,0 +1,73 @@ + + + + + + + diff --git a/src/main/resources/mapper/securityManagement/HotViolationInterviewMapper.xml b/src/main/resources/mapper/securityManagement/HotViolationInterviewMapper.xml new file mode 100644 index 0000000..bd06cd5 --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotViolationInterviewMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/securityManagement/HotViolationRecordMapper.xml b/src/main/resources/mapper/securityManagement/HotViolationRecordMapper.xml new file mode 100644 index 0000000..eae4739 --- /dev/null +++ b/src/main/resources/mapper/securityManagement/HotViolationRecordMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/system/SysClientMapper.xml b/src/main/resources/mapper/system/SysClientMapper.xml new file mode 100644 index 0000000..fd150ad --- /dev/null +++ b/src/main/resources/mapper/system/SysClientMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysConfigMapper.xml b/src/main/resources/mapper/system/SysConfigMapper.xml new file mode 100644 index 0000000..e542a10 --- /dev/null +++ b/src/main/resources/mapper/system/SysConfigMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysDeptMapper.xml b/src/main/resources/mapper/system/SysDeptMapper.xml new file mode 100644 index 0000000..928ad27 --- /dev/null +++ b/src/main/resources/mapper/system/SysDeptMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysDictDataMapper.xml b/src/main/resources/mapper/system/SysDictDataMapper.xml new file mode 100644 index 0000000..6bcce51 --- /dev/null +++ b/src/main/resources/mapper/system/SysDictDataMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysDictTypeMapper.xml b/src/main/resources/mapper/system/SysDictTypeMapper.xml new file mode 100644 index 0000000..6975da4 --- /dev/null +++ b/src/main/resources/mapper/system/SysDictTypeMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysLogininforMapper.xml b/src/main/resources/mapper/system/SysLogininforMapper.xml new file mode 100644 index 0000000..c64b551 --- /dev/null +++ b/src/main/resources/mapper/system/SysLogininforMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysMenuMapper.xml b/src/main/resources/mapper/system/SysMenuMapper.xml new file mode 100644 index 0000000..9e78302 --- /dev/null +++ b/src/main/resources/mapper/system/SysMenuMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysNoticeMapper.xml b/src/main/resources/mapper/system/SysNoticeMapper.xml new file mode 100644 index 0000000..43f494d --- /dev/null +++ b/src/main/resources/mapper/system/SysNoticeMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysOperLogMapper.xml b/src/main/resources/mapper/system/SysOperLogMapper.xml new file mode 100644 index 0000000..5ef14ee --- /dev/null +++ b/src/main/resources/mapper/system/SysOperLogMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysOssConfigMapper.xml b/src/main/resources/mapper/system/SysOssConfigMapper.xml new file mode 100644 index 0000000..8c2c080 --- /dev/null +++ b/src/main/resources/mapper/system/SysOssConfigMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysOssMapper.xml b/src/main/resources/mapper/system/SysOssMapper.xml new file mode 100644 index 0000000..d9b25bd --- /dev/null +++ b/src/main/resources/mapper/system/SysOssMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysPostMapper.xml b/src/main/resources/mapper/system/SysPostMapper.xml new file mode 100644 index 0000000..a753500 --- /dev/null +++ b/src/main/resources/mapper/system/SysPostMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysRoleDeptMapper.xml b/src/main/resources/mapper/system/SysRoleDeptMapper.xml new file mode 100644 index 0000000..1705bb2 --- /dev/null +++ b/src/main/resources/mapper/system/SysRoleDeptMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysRoleMapper.xml b/src/main/resources/mapper/system/SysRoleMapper.xml new file mode 100644 index 0000000..7f69e01 --- /dev/null +++ b/src/main/resources/mapper/system/SysRoleMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysRoleMenuMapper.xml b/src/main/resources/mapper/system/SysRoleMenuMapper.xml new file mode 100644 index 0000000..f01dc5e --- /dev/null +++ b/src/main/resources/mapper/system/SysRoleMenuMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysRolePermissionMapper.xml b/src/main/resources/mapper/system/SysRolePermissionMapper.xml new file mode 100644 index 0000000..dae5f63 --- /dev/null +++ b/src/main/resources/mapper/system/SysRolePermissionMapper.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/mapper/system/SysSocialMapper.xml b/src/main/resources/mapper/system/SysSocialMapper.xml new file mode 100644 index 0000000..baa4b59 --- /dev/null +++ b/src/main/resources/mapper/system/SysSocialMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysTenantMapper.xml b/src/main/resources/mapper/system/SysTenantMapper.xml new file mode 100644 index 0000000..0d96e13 --- /dev/null +++ b/src/main/resources/mapper/system/SysTenantMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysTenantPackageMapper.xml b/src/main/resources/mapper/system/SysTenantPackageMapper.xml new file mode 100644 index 0000000..79cf4c5 --- /dev/null +++ b/src/main/resources/mapper/system/SysTenantPackageMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysUserMapper.xml b/src/main/resources/mapper/system/SysUserMapper.xml new file mode 100644 index 0000000..475c659 --- /dev/null +++ b/src/main/resources/mapper/system/SysUserMapper.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/system/SysUserPostMapper.xml b/src/main/resources/mapper/system/SysUserPostMapper.xml new file mode 100644 index 0000000..e9f2496 --- /dev/null +++ b/src/main/resources/mapper/system/SysUserPostMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysUserRoleMapper.xml b/src/main/resources/mapper/system/SysUserRoleMapper.xml new file mode 100644 index 0000000..6f7cedf --- /dev/null +++ b/src/main/resources/mapper/system/SysUserRoleMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/workbench/GovWorkbenchMapper.xml b/src/main/resources/mapper/workbench/GovWorkbenchMapper.xml new file mode 100644 index 0000000..70e9378 --- /dev/null +++ b/src/main/resources/mapper/workbench/GovWorkbenchMapper.xml @@ -0,0 +1,717 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/workbench/HeadquartersWorkbenchMapper.xml b/src/main/resources/mapper/workbench/HeadquartersWorkbenchMapper.xml new file mode 100644 index 0000000..e27203d --- /dev/null +++ b/src/main/resources/mapper/workbench/HeadquartersWorkbenchMapper.xml @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/workbench/HotHiddenDangerDailyStatsMapper.xml b/src/main/resources/mapper/workbench/HotHiddenDangerDailyStatsMapper.xml new file mode 100644 index 0000000..c57e403 --- /dev/null +++ b/src/main/resources/mapper/workbench/HotHiddenDangerDailyStatsMapper.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/workbench/HotTrainingDailyStatsMapper.xml b/src/main/resources/mapper/workbench/HotTrainingDailyStatsMapper.xml new file mode 100644 index 0000000..88e91d0 --- /dev/null +++ b/src/main/resources/mapper/workbench/HotTrainingDailyStatsMapper.xml @@ -0,0 +1,57 @@ + + + + + + INSERT INTO hot_training_daily_stats (company_id, + stats_date, + total_count, + completed_count, + uncompleted_count, + create_time, + update_time, + is_deleted) + SELECT tp.company_id AS company_id, + #{statsDate} AS stats_date, + COUNT(*) AS total_count, + SUM(CASE + WHEN tp.is_completed = 1 AND tp.complete_time IS NOT NULL AND tp.complete_time <= #{dayEnd} + THEN 1 + ELSE 0 END) AS completed_count, + COUNT(*) - SUM(CASE + WHEN tp.is_completed = 1 AND tp.complete_time IS NOT NULL AND + tp.complete_time <= #{dayEnd} + THEN 1 + ELSE 0 END) AS uncompleted_count, + NOW(), + NOW(), + 0 + FROM hot_training_participant tp + INNER JOIN sys_company c + ON c.id = tp.company_id + AND c.is_deleted = 0 + AND c.status = 1 + WHERE tp.is_deleted = 0 + AND tp.create_time IS NOT NULL + AND tp.create_time <= #{dayEnd} + GROUP BY tp.company_id ON DUPLICATE KEY + UPDATE + total_count = + VALUES (total_count), completed_count = + VALUES (completed_count), uncompleted_count = + VALUES (uncompleted_count), update_time = NOW(), is_deleted = 0 + + + + diff --git a/src/main/resources/mapper/workflow/FlwCategoryMapper.xml b/src/main/resources/mapper/workflow/FlwCategoryMapper.xml new file mode 100644 index 0000000..10c948d --- /dev/null +++ b/src/main/resources/mapper/workflow/FlwCategoryMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/workflow/FlwInstanceBizExtMapper.xml b/src/main/resources/mapper/workflow/FlwInstanceBizExtMapper.xml new file mode 100644 index 0000000..c2cc9c7 --- /dev/null +++ b/src/main/resources/mapper/workflow/FlwInstanceBizExtMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/workflow/FlwInstanceMapper.xml b/src/main/resources/mapper/workflow/FlwInstanceMapper.xml new file mode 100644 index 0000000..0ae7adb --- /dev/null +++ b/src/main/resources/mapper/workflow/FlwInstanceMapper.xml @@ -0,0 +1,39 @@ + + + + + + + + + diff --git a/src/main/resources/mapper/workflow/FlwSpelMapper.xml b/src/main/resources/mapper/workflow/FlwSpelMapper.xml new file mode 100644 index 0000000..03355f6 --- /dev/null +++ b/src/main/resources/mapper/workflow/FlwSpelMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/mapper/workflow/FlwTaskMapper.xml b/src/main/resources/mapper/workflow/FlwTaskMapper.xml new file mode 100644 index 0000000..c199928 --- /dev/null +++ b/src/main/resources/mapper/workflow/FlwTaskMapper.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/workflow/TestLeaveMapper.xml b/src/main/resources/mapper/workflow/TestLeaveMapper.xml new file mode 100644 index 0000000..d52f6b0 --- /dev/null +++ b/src/main/resources/mapper/workflow/TestLeaveMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/src/main/resources/spel-extension.json b/src/main/resources/spel-extension.json new file mode 100644 index 0000000..64a0e1b --- /dev/null +++ b/src/main/resources/spel-extension.json @@ -0,0 +1,7 @@ +{ + "org.dromara.common.ratelimiter.annotation.RateLimiter@key": { + "method": { + "parameters": true + } + } +} diff --git a/src/main/resources/spy.properties b/src/main/resources/spy.properties new file mode 100644 index 0000000..f3ed7d8 --- /dev/null +++ b/src/main/resources/spy.properties @@ -0,0 +1,20 @@ +# p6spy 性能分析插件配置文件 +modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory +# 自定义日志打印 +logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger +#日志输出到控制台 +appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger +# 使用日志系统记录 sql +#appender=com.p6spy.engine.spy.appender.Slf4JLogger +# 取消JDBC URL前缀 +useprefix=true +# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset. +excludecategories=info,debug,result,commit,resultset +# 日期格式 +dateformat=yyyy-MM-dd HH:mm:ss +# SQL语句打印时间格式 +databaseDialectTimestampFormat=yyyy-MM-dd HH:mm:ss +# 是否过滤 Log +filter=true +# 过滤 Log 时所排除的 sql 关键字,以逗号分隔 +exclude= diff --git a/src/main/resources/sql/sql规范.md b/src/main/resources/sql/sql规范.md new file mode 100644 index 0000000..4daf47f --- /dev/null +++ b/src/main/resources/sql/sql规范.md @@ -0,0 +1,49 @@ +1) 建表示例(统一规范,前缀 `hot_`) +CREATE TABLE `hot_xxx_example` +( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `company_id` BIGINT UNSIGNED NULL COMMENT '公司ID(由应用层保证必填)', + + -- 业务字段(根据实际场景补充) + `name` VARCHAR(100) NULL COMMENT '名称(应用层校验必填)', + `status` TINYINT NULL DEFAULT 1 COMMENT '状态:1=正常 0=禁用', + + -- 固定审计字段 + `create_dept` BIGINT NULL COMMENT '创建部门', + `create_by` BIGINT NULL COMMENT '创建者', + `create_by_name` VARCHAR(64) NULL COMMENT '创建者姓名', + `create_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_by` BIGINT NULL COMMENT '更新者', + `update_by_name` VARCHAR(64) NULL COMMENT '更新者姓名', + `update_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `is_deleted` TINYINT NULL DEFAULT 0 COMMENT '0=正常, 1=已删除', + + PRIMARY KEY (`id`), + KEY `idx_company_id` (`company_id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_general_ci COMMENT ='示例业务表'; + +2) 固定字段说明(所有业务表必须包含) + - `id`:BIGINT UNSIGNED 主键,自增 + - `company_id`:BIGINT UNSIGNED 非空,多租户分隔标识 + - `create_dept`:BIGINT,可空,创建部门 + - `create_by`:BIGINT,可空,创建者 + - `create_time`:DATETIME 非空,默认 `CURRENT_TIMESTAMP` + - `update_by`:BIGINT,可空,更新者 + - `update_time`:DATETIME 非空,默认 `CURRENT_TIMESTAMP` 且 `ON UPDATE CURRENT_TIMESTAMP` + - `is_deleted`:TINYINT 非空,逻辑删除标志(0=正常 1=删除) + +3) 其他规范要点 + - 命名规范:下划线蛇形命名(如 `hot_module_entity`),字段必须有 COMMENT + - 索引规范:公共检索字段需建索引;命名 `idx_字段名`,唯一键命名 `uk_字段名` + - 字段类型:布尔/枚举用 `TINYINT`;文本优先 `VARCHAR(<=255)`,长文本用 `TEXT`;URL 用 `VARCHAR(500~1024)` + - 非空约束:默认尽量允许 `NULL`,仅主键保持 `NOT NULL`;其他字段改由应用层进行必填校验;如需默认行为,优先使用 `DEFAULT` 而非强制 `NOT NULL` + - 字符集/排序:统一 `utf8mb4` + `utf8mb4_general_ci`;存储引擎统一 `InnoDB` + - 外键约束:默认不使用外键(不建 `FOREIGN KEY`),由应用程序保证关联完整性;必要关联通过普通索引/唯一键保障查询与唯一性;禁用数据库级联删除/更新,统一使用逻辑删除与事务控制 + +4) 外键约束策略(统一约定) + - 不创建数据库外键:跨库、跨表关联通过业务字段(如 `company_id`、`dept_id`、`user_id` 等)实现“逻辑外键” + - 一致性保障:在应用层进行存在性校验、状态校验与事务控制;删除采用逻辑删除(`is_deleted=1`),避免外键级联导致不可控扩散 + - 性能与演进:通过为逻辑外键创建合适索引(如 `KEY idx_company_id(company_id)`)保证查询性能;避免外键带来的锁冲突与迁移复杂性 + - 唯一与约束:业务唯一性由唯一索引实现(如 `uk_phone`);引用检查在服务层/DAO 层完成(查询验证、分布式事务或最终一致性策略) diff --git a/src/main/resources/sql/update.sql b/src/main/resources/sql/update.sql new file mode 100644 index 0000000..e48fe94 --- /dev/null +++ b/src/main/resources/sql/update.sql @@ -0,0 +1,108 @@ + + +ALTER TABLE `flow_category` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `flow_category` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `flow_definition` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `flow_definition` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `flow_instance` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `flow_instance` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `flow_instance_biz_ext` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `flow_instance_biz_ext` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `flow_node` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `flow_node` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `flow_skip` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `flow_skip` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `flow_spel` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `flow_spel` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `flow_task` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `flow_task` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `flow_user` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `flow_user` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `gen_table` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `gen_table` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `gen_table_column` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `gen_table_column` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `hot_accident_record` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `hot_accident_record` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `hot_company_attachment` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `hot_company_attachment` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `hot_driver_license` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `hot_driver_license` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `hot_policy` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `hot_policy` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `hot_policy_template` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `hot_policy_template` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `hot_violation_record` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `hot_violation_record` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `sys_client` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `sys_client` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `sys_config` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `sys_config` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `sys_dept` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `sys_dept` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `sys_dict_data` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `sys_dict_data` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `sys_dict_type` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `sys_dict_type` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `sys_menu` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `sys_menu` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `sys_notice` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `sys_notice` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `sys_oss` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `sys_oss` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `sys_post` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `sys_post` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `sys_role` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `sys_role` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `sys_social` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `sys_social` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `sys_tenant` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `sys_tenant` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `sys_tenant_package` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `sys_tenant_package` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `test_demo` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `test_demo` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `test_leave` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `test_leave` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `test_tree` ADD COLUMN `create_by_name` VARCHAR(100) NULL COMMENT '创建人显示名称'; +ALTER TABLE `test_tree` ADD COLUMN `update_by_name` VARCHAR(100) NULL COMMENT '最后修改人显示名称'; + +ALTER TABLE `hot_training_course_record` ADD COLUMN `signature_oss_id` BIGINT NULL COMMENT '学员签名OSS文件ID' AFTER `complete_time`; +ALTER TABLE `hot_training_course_record` + ADD COLUMN `is_use_hour_package` TINYINT NULL DEFAULT 0 COMMENT '是否已使用购买课时套餐:0=否 1=是' AFTER `signature_oss_id`; +ALTER TABLE `hot_training_course_record` + ADD COLUMN `hour_package_used_hours` BIGINT NULL DEFAULT 0 COMMENT '已扣减课时数' AFTER `is_use_hour_package`; +ALTER TABLE `sys_company_role` + ADD COLUMN `creator_user_id` BIGINT NULL COMMENT '创建人用户ID'; diff --git a/src/main/resources/sql/清洗车辆基本信息.sql b/src/main/resources/sql/清洗车辆基本信息.sql new file mode 100644 index 0000000..5c61f58 --- /dev/null +++ b/src/main/resources/sql/清洗车辆基本信息.sql @@ -0,0 +1,69 @@ +select cljc_id AS id, + cljc_cp AS brand_model, + CASE cljc_cllx WHEN '油车' THEN '1' when '挂车' THEN '4' WHEN '电车' THEN '2' ELSE NULL END AS vehicle_type, + cljc_ys AS body_color, + cljc_cpys AS plate_color, + cljc_dl AS fuel_type, + cljc_zmc AS manufacturer_name, + -- 货箱长度(INT类型) + CASE + WHEN TRIM(cljc_xnc) = '' THEN NULL + WHEN cljc_xnc REGEXP '^[0-9]+$' THEN CAST(cljc_xnc AS UNSIGNED) + ELSE NULL END AS cargo_length_mm, + -- 货箱宽度(INT类型) + CASE + WHEN TRIM(cljc_xnk) = '' THEN NULL + WHEN cljc_xnk REGEXP '^[0-9]+$' THEN CAST(cljc_xnk AS UNSIGNED) + ELSE NULL END AS cargo_width_mm, + -- 货箱高度(INT类型) + CASE + WHEN TRIM(cljc_xng) = '' THEN NULL + WHEN cljc_xng REGEXP '^[0-9]+$' THEN CAST(cljc_xng AS UNSIGNED) + ELSE NULL END AS cargo_height_mm, + -- 发动机排量(INT类型) + CASE + WHEN TRIM(cljc_dpl) = '' THEN NULL + WHEN cljc_dpl REGEXP '^[0-9]+$' THEN CAST(cljc_dpl AS UNSIGNED) + ELSE NULL END AS engine_displacement, + -- 发动机功率(新增INT类型转换) + CASE + WHEN TRIM(cljc_gl) = '' THEN NULL + WHEN cljc_gl REGEXP '^[0-9]+$' THEN CAST(cljc_gl AS UNSIGNED) + ELSE NULL END AS engine_power_kw, + -- 电机功率(INT类型) + CASE + WHEN TRIM(cljc_jgl) = '' THEN NULL + WHEN cljc_jgl REGEXP '^[0-9]+$' THEN CAST(cljc_jgl AS UNSIGNED) + ELSE NULL END AS motor_power_kw, + cljc_dxh AS drive_motor_model, + cljc_lt AS tire_count, + cljc_ltgg AS tire_spec, + -- 轴距(INT类型) + CASE + WHEN TRIM(cljc_zj) = '' THEN NULL + WHEN cljc_zj REGEXP '^[0-9]+$' THEN CAST(cljc_zj AS UNSIGNED) + ELSE NULL END AS wheelbase_mm, + cljc_czs AS axle_count, + cljc_dp AS chassis_model, + cljc_bsq AS transmission, + cljc_hsq AS retarder, + -- 货箱容积(DECIMAL类型) + CASE + WHEN TRIM(cljc_xrj) = '' THEN NULL + WHEN cljc_xrj REGEXP '^[0-9.]+$' THEN CAST(cljc_xrj AS DECIMAL(10, 2)) + ELSE NULL END AS cargo_volume, + cljc_pf AS emission_standard, + cljc_dlx AS battery_type, + -- 承保座位数(INT类型) + CASE + WHEN TRIM(cljc_zw) = '' THEN NULL + WHEN cljc_zw REGEXP '^[0-9]+$' THEN CAST(cljc_zw AS UNSIGNED) + ELSE NULL END AS insured_seat_count, + cljc_zdfs AS service_brake_type, + cljc_qlzd AS front_brake_type, + cljc_hlzd AS rear_brake_type, + cljc_zdbs AS abs_system, + cljc_kt AS air_conditioning, + cljc_gc AS domestic_flag, + cljc_clxh AS vehicle_model +from sy_pzclxx; \ No newline at end of file diff --git a/src/main/resources/templates/common/dailyTrainingCover.html.vm b/src/main/resources/templates/common/dailyTrainingCover.html.vm new file mode 100644 index 0000000..7d2f161 --- /dev/null +++ b/src/main/resources/templates/common/dailyTrainingCover.html.vm @@ -0,0 +1,20 @@ +

+
+ $!{regionName}道路运输行业 +
+
+ 安全生产教育培训 +
+
+ 记 +
+
+ 录 +
+
+ 单位:$!{companyName}(盖章) +
+
+ 20(   )年 +
+
\ No newline at end of file diff --git a/src/main/resources/templates/common/traingCover.html.vm b/src/main/resources/templates/common/traingCover.html.vm new file mode 100644 index 0000000..fa64a60 --- /dev/null +++ b/src/main/resources/templates/common/traingCover.html.vm @@ -0,0 +1,19 @@ +
+ +
+ $!{regionName} +
+ +
+ $!{coverName} +
+ +
+
+ 单位:$!{companyName} +
+
+ $!{printDate} +
+
+
\ No newline at end of file diff --git a/src/main/resources/templates/driver/accidentRecord.html.vm b/src/main/resources/templates/driver/accidentRecord.html.vm new file mode 100644 index 0000000..2fcae17 --- /dev/null +++ b/src/main/resources/templates/driver/accidentRecord.html.vm @@ -0,0 +1,91 @@ + + +
驾驶员安全行车事故记录
+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #foreach($item in $accidents) + + + + + + + + + + + + + + + #end + #if($accidents.size() == 0) + + + + #end + +
驾驶员姓名$!{driverName}
事故时间车牌号事故地点事故简要经过及原因死亡人数重伤人员轻伤人数经济损失(元)结案时间处理结果责任人签名备注
$!{item.accidentTime}$!{item.plateNumber}$!{item.occurLocation}$!{item.mainCause}$!{item.deathToll}$!{item.numberSeriouslyInjured}$!{item.numberMinorInjuries}$!{item.economicLoss}$!{item.caseClosingTime}$!{item.divisionResponsibilities} + #if($item.signatureUrl) + + #end + $!{item.remark}
暂无事故记录
diff --git a/src/main/resources/templates/driver/annualAssessmentRecord.html.vm b/src/main/resources/templates/driver/annualAssessmentRecord.html.vm new file mode 100644 index 0000000..ea737b9 --- /dev/null +++ b/src/main/resources/templates/driver/annualAssessmentRecord.html.vm @@ -0,0 +1,95 @@ + +
驾驶员年度考核记录
+ + + + + + + + + + + + + + + + + + + + + #foreach($cat in $categories) + #set($rowsize = $cat.details.size()) + #if($rowsize == 0) + + + + + + + #else + #set($last = $rowsize - 1) + #foreach($idx in [0..$last]) + #set($d = $cat.details.get($idx)) + + #if($idx == 0) + + #end + + #if($idx == 0) + + + #end + + #end + #end + #end + + + + + + + + + + + + + + + + + +
身份证号$!{idCardNo}姓名$!{name}
考核项目考核内容分值得分
$!{cat.name}$!{cat.value}$!{cat.score}
$!{cat.name}$!{d.content}$!{cat.value}$!{cat.score}
考核项目合计$!{totalValue}$!{totalScore}
考核结果:$!{result}考核时间:$!{assessTime}
考核人签名: + #if($evaluatorSignatureUrl) + + #end + 被考核人签名: + #if($assessedSignatureUrl) + + #end +
diff --git a/src/main/resources/templates/driver/driverApplicationForm.html.vm b/src/main/resources/templates/driver/driverApplicationForm.html.vm new file mode 100644 index 0000000..fcee33c --- /dev/null +++ b/src/main/resources/templates/driver/driverApplicationForm.html.vm @@ -0,0 +1,119 @@ +
+

驾驶员应聘表

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #foreach($work in $workExperiences) + + + + + + + #end + + + + + + + + + + + + + + + + + + #foreach($member in $familyMembers) + + + + + + + #end + + + + + + + + + + + + + + + + + + + + +
姓名${driver.name}性别$!{tool.getDictLabel('sys_user_sex', $driver.gender)}民族$!{tool.getDictLabel('nation', $driver.nation)}
出生年月$!{tool.formatDate($driver.birthDate, 'yyyy-MM')}籍贯$!{driver.nativePlace}学历$!{tool.getDictLabel( + 'hot_academic', $driver.education)}
户籍地址$!{driver.householdAddress}现居地$!{driver.currentAddress}
电话号码$!{driver.phone}
工作经历
时间工作单位职务主要工作内容
$!{work.time}$!{work.company}$!{work.post}$!{work.content}
受过何种处分或奖励$!{driver.rewardOrPunishment}
家庭成员
姓名与本人关系出生年月联系电话
$!{member.name}$!{member.relationship}$!{tool.formatDate($member.birthMonth, + 'yyyy-MM')}$!{member.phone}
声明 +

1、本人已了解公司相关应聘条件并自愿申请入职。

+

2、本人保证提供的信息真实有效,如有虚假信息,本人愿意承担相应后果。

+
+ 签名: + #if($driver.signatureUrl && $driver.signatureUrl != "") + + #elseif($driver.signature && $driver.signature != "") + $!{driver.signature} + #end +
+
人事意见$!{driver.hrOpinion}
部门意见$!{driver.deptOpinion}
公司领导意见$!{driver.leaderOpinion}
+
diff --git a/src/main/resources/templates/driver/driverLicenseCopy.html.vm b/src/main/resources/templates/driver/driverLicenseCopy.html.vm new file mode 100644 index 0000000..1d00852 --- /dev/null +++ b/src/main/resources/templates/driver/driverLicenseCopy.html.vm @@ -0,0 +1,57 @@ +
+

驾驶员机动车驾驶证复印件

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
姓名$!{driver.name}身份证号$!{driver.idCardNo}联系电话$!{driver.phone}
初次领证日期$!{driverLicenseFirstIssueDate}有效期$!{validPeriod}
驾驶证复印件
+ #set($mainImages = $tool.getOssUrls($driver.driverLicenseMainImg)) + #if($mainImages && $mainImages.size() > 0) +
+ +
+ #else +
+ 暂无驾驶证主页图片 +
+ #end + + #set($viceImages = $tool.getOssUrls($driver.driverLicenseViceImg)) + #if($viceImages && $viceImages.size() > 0) +
+ +
+ #else +
+ 暂无驾驶证副页图片 +
+ #end +
diff --git a/src/main/resources/templates/driver/idCardScan.html.vm b/src/main/resources/templates/driver/idCardScan.html.vm new file mode 100644 index 0000000..693a730 --- /dev/null +++ b/src/main/resources/templates/driver/idCardScan.html.vm @@ -0,0 +1,58 @@ +
+

身份证扫描件

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
姓名$!{driver.name}身份证号$!{driver.idCardNo}
联系电话$!{driver.phone}
身份证正面
+ #set($frontImages = $tool.getOssUrls($driver.idcardFrontUrl)) + #if($frontImages && $frontImages.size() > 0) +
+ +
+ #else +
+ 暂无正面图片 +
+ #end +
身份证反面
+ #set($backImages = $tool.getOssUrls($driver.idcardBackUrl)) + #if($backImages && $backImages.size() > 0) +
+ +
+ #else +
+ 暂无反面图片 +
+ #end +
diff --git a/src/main/resources/templates/driver/medicalExaminationReport.html.vm b/src/main/resources/templates/driver/medicalExaminationReport.html.vm new file mode 100644 index 0000000..25ebf73 --- /dev/null +++ b/src/main/resources/templates/driver/medicalExaminationReport.html.vm @@ -0,0 +1,62 @@ + +
驾驶员体检报告
+ + + + + + + + + + + + + + + + + + + #if($imageUrls && $imageUrls.size() > 0) + #foreach($img in $imageUrls) + + + + #end + #else + + + + #end +
姓名$!{name}身份证号$!{idCardNo}
体检报告日期$!{reportDate}体检结果$!{healthStatus}
备注$!{remark}
+ +
暂无体检报告
diff --git a/src/main/resources/templates/driver/qualificationCertificateCopy.html.vm b/src/main/resources/templates/driver/qualificationCertificateCopy.html.vm new file mode 100644 index 0000000..ef66bb3 --- /dev/null +++ b/src/main/resources/templates/driver/qualificationCertificateCopy.html.vm @@ -0,0 +1,46 @@ +
+

驾驶员从业资格证复印件

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
姓名$!{driver.name}
身份证号$!{driver.idCardNo}联系电话$!{driver.phone}
资格证种类$!{driver.qualificationType}资格证号$!{driver.qualificationNo}
复印件
+ #set($images = $tool.getOssUrls($driver.qualificationImg)) + #if($images && $images.size() > 0) +
+ +
+ #else +
+ 暂无从业资格证图片 +
+ #end +
diff --git a/src/main/resources/templates/driver/qualificationReview.html.vm b/src/main/resources/templates/driver/qualificationReview.html.vm new file mode 100644 index 0000000..ab0935c --- /dev/null +++ b/src/main/resources/templates/driver/qualificationReview.html.vm @@ -0,0 +1,167 @@ + + +
驾驶员资格审查及技能考核登记表
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #foreach($item in $examItems) + + + + + + + #end + + + + + + + + + + + + + + +
姓名$!{driver.name}性别$!{gender}民族$!{nation}学历$!{education}
身份证号$!{driver.idCardNo}户籍$!{driver.householdAddress}
准驾车型$!{driver.driverLicenseVehicleType}考试车型$!{examVehicleModel}
路考时间$!{assessDate}考试路线$!{examRoute}
驾驶证档案号$!{driver.driverLicenseArchiveNo}驾驶证有效期$!{dlValidPeriod}
从业资格证号$!{driver.qualificationNo}从业资格证有效期$!{qualificationValidEndDate}
+ 本人承诺无妨碍驾驶疾病及生理缺陷,已经驾驶   $!{drivingExperience}   年,共计   $!{drivingMileage}   万公里。 +
主考人员 + #if($chiefExaminerSignatureUrl) + + #end + 监考人员 + #if($invigilatorSignatureUrl) + + #end +
考试内容
$!{item.name} + #if($item.isExcellent)☑#else☐#end优 + + #if($item.isGood)☑#else☐#end良 + + #if($item.isPoor)☑#else☐#end差 +
主考意见$!{chiefOpinion}
监考意见$!{invigilatorOpinion}
公司领导意见$!{companyLeaderOpinion}
diff --git a/src/main/resources/templates/driver/rewardPunishmentRecord.html.vm b/src/main/resources/templates/driver/rewardPunishmentRecord.html.vm new file mode 100644 index 0000000..98386f2 --- /dev/null +++ b/src/main/resources/templates/driver/rewardPunishmentRecord.html.vm @@ -0,0 +1,53 @@ + +
奖惩记录表
+ + + + + + + + + + + + + + + + + + + + #foreach($r in $rows) + + + + + + #end + #if(!$rows || $rows.size() == 0) + + + + #end + +
姓名$!{name}身份证号$!{idCardNo}
日期奖惩事项奖惩标准
$!{r.eventDate}$!{r.item}$!{r.decision}
暂无奖惩记录
diff --git a/src/main/resources/templates/driver/violationRecord.html.vm b/src/main/resources/templates/driver/violationRecord.html.vm new file mode 100644 index 0000000..57f0f91 --- /dev/null +++ b/src/main/resources/templates/driver/violationRecord.html.vm @@ -0,0 +1,78 @@ + + +
驾驶员违章记录
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #foreach($item in $violations) + + + + + + + + + + + + #end + #if($violations.size() == 0) + + + + #end + +
驾驶员姓名$!{driverName}
发生时间车牌号违规地点违规内容违规类型罚款金额扣分数处理情况备注
$!{item.violationTime}$!{item.vehiclePlate}$!{item.violationLocation}$!{item.violationContent}$!{item.violationType}$!{item.fineAmount}$!{item.deductedPoints}$!{item.handlingStatus}$!{item.remark}
暂无违章记录
diff --git a/src/main/resources/templates/layout/printLayout.html.vm b/src/main/resources/templates/layout/printLayout.html.vm new file mode 100644 index 0000000..59f7b99 --- /dev/null +++ b/src/main/resources/templates/layout/printLayout.html.vm @@ -0,0 +1,181 @@ + + + + + ${title} + + + + #foreach($body in $bodies) +
+ +
+ $body +
+
+ #end + + + diff --git a/src/main/resources/templates/layout/printLayoutLandscape.html.vm b/src/main/resources/templates/layout/printLayoutLandscape.html.vm new file mode 100644 index 0000000..9ae0a7f --- /dev/null +++ b/src/main/resources/templates/layout/printLayoutLandscape.html.vm @@ -0,0 +1,134 @@ + + + + + ${title} + + + + #foreach($body in $bodies) +
+
+ $body +
+
+ #end + + + diff --git a/src/main/resources/templates/securityManage/gpsViolationRecord.html.vm b/src/main/resources/templates/securityManage/gpsViolationRecord.html.vm new file mode 100644 index 0000000..4b1edf4 --- /dev/null +++ b/src/main/resources/templates/securityManage/gpsViolationRecord.html.vm @@ -0,0 +1,90 @@ +#if($mode == "cover") +
+
+ $!{regionName}道路运输行业 +
+
+ 车辆动态监控违法行为处理 +
+
+ 记 +
+
+ 录 +
+
+ 单位:$!{companyName}(盖章) +
+
+ 20( )年 +
+
+#elseif($mode == "detail") +
+ 车辆动态监控违法行为处理记录表 +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
登记时间 + $!date.format('yyyy-MM-dd', $recordTime) + 违法违章事由GPS
驾驶员$!{info.driverName}车牌号$!{info.plateNumber}记录人$!{info.recorderName}
违法违章事件简要经过$!{info.violationContent}
处理情况(依据企业管理制度进行的处罚结果和告诫警示谈话内容)$!{info.disposalResult}
负责人签字 + #if($responsibleSignatureUrl) + + #end + 驾驶员签字 + #if($partySignatureUrl) + + #end +
备注$!{info.remark}
+#elseif($mode == "attachment") +
+ 承诺书 +
+ + #if($promiseImages && $promiseImages.size() > 0) + #foreach($img in $promiseImages) + + + + #end + #else + + + + #end +
+ +
暂无承诺书
+#end + diff --git a/src/main/resources/templates/securityManage/offlineMeeting.html.vm b/src/main/resources/templates/securityManage/offlineMeeting.html.vm new file mode 100644 index 0000000..80f39ff --- /dev/null +++ b/src/main/resources/templates/securityManage/offlineMeeting.html.vm @@ -0,0 +1,68 @@ +
+ 线下会议记录表 +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
会议名称$!{meeting.meetingTopic}
时间 + #if($meeting.startTime && $meeting.endTime) + $!tool.formatDate($!meeting.startTime, 'yyyy-MM-dd HH:mm') 至 $!tool.formatDate($!meeting.endTime, + 'yyyy-MM-dd HH:mm') + #elseif($meeting.startTime) + $!tool.formatDate($!meeting.startTime, 'yyyy-MM-dd HH:mm') + #end +
地点$!{meeting.location}
主持人$!{meeting.hostName}记录人$!{meeting.recorderName}
参会人员 + $!{meeting.participantNames} +
会议内容 + #if($meeting.meetingContent) +
$!{meeting.meetingContent}
+ #end +
下月安全
工作安排
+ #if($meeting.nextMonthPlan) +
$!{meeting.nextMonthPlan}
+ #end +
会议照片
+ #if($imgUrls && $imgUrls.size() > 0) +
+ #foreach($img in $imgUrls) + + #end +
+ #else +
暂无会议照片
+ #end +
diff --git a/src/main/resources/templates/securityManage/offlineTraining.html.vm b/src/main/resources/templates/securityManage/offlineTraining.html.vm new file mode 100644 index 0000000..9458874 --- /dev/null +++ b/src/main/resources/templates/securityManage/offlineTraining.html.vm @@ -0,0 +1,60 @@ +
+ 线下培训记录表 +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
主题$!{training.trainingSubject}
时间 + #if($training.startTime && $training.endTime) + $!tool.formatDate($!training.startTime, 'yyyy-MM-dd HH:mm') 至 $!tool.formatDate($!training.endTime, + 'yyyy-MM-dd HH:mm') + #elseif($training.startTime) + $!tool.formatDate($!training.startTime, 'yyyy-MM-dd HH:mm') + #end +
地点$!{training.location}
主持人$!{training.hostName}记录人$!{training.recorderName}
培训人员 + $!{training.participantNames} +
培训内容 + #if($training.trainingContent) +
$!{training.trainingContent}
+ #end +
培训照片
+ #if($imgUrls && $imgUrls.size() > 0) +
+ #foreach($img in $imgUrls) + + #end +
+ #else +
暂无培训照片
+ #end +
diff --git a/src/main/resources/templates/securityManage/offlineTrainingPhoto.html.vm b/src/main/resources/templates/securityManage/offlineTrainingPhoto.html.vm new file mode 100644 index 0000000..3112f48 --- /dev/null +++ b/src/main/resources/templates/securityManage/offlineTrainingPhoto.html.vm @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/main/resources/templates/securityManage/onlineEducationMonthlyStatistics.html.vm b/src/main/resources/templates/securityManage/onlineEducationMonthlyStatistics.html.vm new file mode 100644 index 0000000..3af5039 --- /dev/null +++ b/src/main/resources/templates/securityManage/onlineEducationMonthlyStatistics.html.vm @@ -0,0 +1,164 @@ +
+ 在线教育培训月度统计表 +
+
+ #if($training.startTime) + $!tool.formatDate($!training.startTime, 'yyyy年MM月') + + #elseif($training.endTime) + $!tool.formatDate($!training.endTime, 'yyyy年MM月') + #end +
+ + + + + + + + + + + + + + + + + + + + + + #if($trainingCourses && $trainingCourses.size() > 0) + #set($courseRowspan = $trainingCourses.size()) + #foreach($c in $trainingCourses) + + #if($foreach.count == 1) + + #end + + #if($foreach.count == 1) + + #end + + + #end + #else + + + + + #end + + + + + + + + + + + + + + #if($rows) + #foreach($r in $rows) + + + + + + + + + + #end + #else + + + + #end +
企业名称$!{companyName}教育培训人数 + #if($training.totalCount) + $!{training.totalCount} + #else +   + #end +
负责人 + #if($responsibleName) + $!{responsibleName} + #else +   + #end + 完成人数 + #if($completedCount) + $!{completedCount} + #elseif($training.completeCount) + $!{training.completeCount} + #else +   + #end +
教育培训日期 + #if($training.startTime && $training.endTime) + $!tool.formatDate($!training.startTime, 'yyyy年MM月dd日') + 至 + $!tool.formatDate($!training.endTime, 'yyyy年MM月dd日') + #elseif($training.startTime) + $!tool.formatDate($!training.startTime, 'yyyy年MM月dd日') + #elseif($training.endTime) + $!tool.formatDate($!training.endTime, 'yyyy年MM月dd日') + #else + 至 + #end + 计划学时 + #if($training.classHours) + $!{training.classHours} + #else +   + #end +
+ 教育培训内容 + + $!{c.index}、$!{c.courseName} + + 培训时长 + + #if($c.durationMin && $c.durationMin > 0) + $!{c.durationMin}分钟 + #else +   + #end +
教育培训内容 + #if($trainingContent) + #foreach($line in $trainingContent.split("\n")) + $foreach.count、$!{line}
+ #end + #else +   + #end +
序号从业人员车牌号码完成进度照片签名考试状态
$!{r.index}$!{r.userName}$!{r.plateNumber}$!{r.progressLabel} + #if($r.photoUrl) + + #end + + #if($r.signatureUrl) + + #end + + #if($r.examStatus) + $!{r.examStatus} + #else +   + #end +
 
+ +#if($verifyQrBase64) +
+ +
扫描查真伪
+
+#end diff --git a/src/main/resources/templates/securityManage/preJobTrainingRecord.html.vm b/src/main/resources/templates/securityManage/preJobTrainingRecord.html.vm new file mode 100644 index 0000000..099db56 --- /dev/null +++ b/src/main/resources/templates/securityManage/preJobTrainingRecord.html.vm @@ -0,0 +1,102 @@ +#foreach($detail in $details) +
+ 从业人员岗前培训考核卡 +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #if($detail.rows) + #set($rowCount = $detail.rows.size()) + #else + #set($rowCount = 0) + #end + #set($totalRows = $rowCount + 1) + + + + + + + + + + + + #foreach($row in $detail.rows) + + + + + + #if($foreach.count == 1) + + #end + + #end + + + + + + + + + + +
姓名$!{detail.driverName}性别$!{detail.gender}出生
年月
$!{detail.birthDate}
文化
程度
$!{detail.education}参加工
作时间
$!{detail.startWorkDate}进公司
日期
$!{detail.entryDateLabel}
身份证号$!{detail.idCardNo}岗位驾驶员家庭住址$!{detail.currentAddress}
从业人员
岗前培训 +
培训时间培训地点培训内容培训学时考核成绩
$!{row.trainingTime}$!{row.location}$!{row.courseName}$!{row.studyDuration}$!{detail.totalScore}
被考核人签名 + #if($detail.driverSignImgUrl) + + #end + 考核人签名 + #if($detail.auditorSignImgUrl) + + #end + 备注
+
+#end diff --git a/src/main/resources/templates/securityManage/safetyEducationTrainingRecord.html.vm b/src/main/resources/templates/securityManage/safetyEducationTrainingRecord.html.vm new file mode 100644 index 0000000..04bb635 --- /dev/null +++ b/src/main/resources/templates/securityManage/safetyEducationTrainingRecord.html.vm @@ -0,0 +1,81 @@ +#if($mode == "cover") + +#elseif($mode == "detail") +
+ 安全生产教育培训记录表 +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
主题$!{detail.planName}
时间$!{detail.trainingPeriod}地点$!{detail.location}
主持人$!{detail.host}记录人$!{detail.recorder}
应到人数$!{detail.expectedCount}实到人数$!{detail.actualCount}
缺席人数$!{detail.absentCount}
培训内容 + #if($detail.trainingContent) + #foreach($line in $detail.trainingContent.split("\n")) + $!{line}
+ #end + #end +
+#elseif($mode == "signIn") +
+ 安全生产教育培训签到册 +
+ + + + + + + + + #if($detail.participantList) + #foreach($p in $detail.participantList) + + + + + + + + #end + #end +
姓名车牌完成时间签名头像
$!{p.userName}$!{p.plateNumber} + #if($p.completeTimeLabel) + $!{p.completeTimeLabel} + #end + + #if($p.signatureUrl) + + #end + + #if($p.avatarUrl) + + #end +
+#end diff --git a/src/main/resources/templates/securityManage/safetyFireHazardInspectionRecord.html.vm b/src/main/resources/templates/securityManage/safetyFireHazardInspectionRecord.html.vm new file mode 100644 index 0000000..9793ed6 --- /dev/null +++ b/src/main/resources/templates/securityManage/safetyFireHazardInspectionRecord.html.vm @@ -0,0 +1,116 @@ +#if($mode == "cover") +
+
+ $!{regionName}道路运输行业 +
+
+ 安全生产(消防)隐患排查整改 +
+
+ 记 +
+
+ 录 +
+
+ 单位:$!{companyName}(盖章) +
+
+ 20( )年 +
+
+#elseif($mode == "detail") +
+ 安全生产(消防)隐患排查记录 +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
登记时间 + #if($inspection.checkDate) + $date.format('yyyy-MM-dd HH:mm', $inspection.checkDate) + #end + 检查部门安全管理部门
参加人员$!{inspection.checkerName}记录人$!{inspection.checkerName}
检查项 + $!{inspection.projectName} +
检查内容 + #if($checkContentLabel) + $!{checkContentLabel} + #end +
存在隐患 + #if($inspection.dangerDesc) + $!{inspection.dangerDesc} + #elseif($inspection.auditHasDanger == 0) + 无 + #end +
处理意见 + #if($inspection.auditConclusion) + $!{inspection.auditConclusion} + #end +
检查人签字 + #if($checkerSignUrl) + + #end + 责任人签字 + #if($approverSignUrl) + + #end +
+#elseif($mode == "photo") +
+ 检查照片 +
+ + + + + #if($photos && $photos.size() > 0) + + + + #else + + + + #end +
检查照片
+
+ #foreach($img in $photos) +
+ +
+ #end +
+
暂无检查照片
+#end diff --git a/src/main/resources/templates/securityManage/safetyMeetingRecord.html.vm b/src/main/resources/templates/securityManage/safetyMeetingRecord.html.vm new file mode 100644 index 0000000..54d5ba4 --- /dev/null +++ b/src/main/resources/templates/securityManage/safetyMeetingRecord.html.vm @@ -0,0 +1,124 @@ +#if($mode == "cover") +
+
+ $!{regionName}道路运输行业 +
+
+ 安全生产会议 +
+
+ 记 +
+
+ 录 +
+
+ 单位:$!{companyName}(盖章) +
+
+ 20( )年 +
+
+#elseif($mode == "detail") +
+ 安全生产会议记录表 +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
会议名称$!{meeting.meetingTopic}
时间 + #if($meeting.startTime) + $date.format('yyyy-MM-dd', $meeting.startTime) + #end + 地点$!{meeting.location}
应到人数$!{attendees.size()}实到人数 + #set($actualCount = 0) + #foreach($a in $attendees) + #if($a.finishTime) + #set($actualCount = $actualCount + 1) + #end + #end + $!actualCount +
缺席人数 + #set($absentCount = $attendees.size() - $actualCount) + $!absentCount +
主持人$!{meeting.hostName}记录人$!{meeting.recorderName}
会议内容 + #if($meeting.meetingContent) + #foreach($line in $meeting.meetingContent.split("\n")) + $!{line}
+ #end + #end +
+#elseif($mode == "signIn") +
+ 参会人员签到表 +
+ + + + + + + + #if($attendees && $attendees.size() > 0) + #foreach($a in $attendees) + + + + + + + #end + #else + + + + #end +
姓名签到时间签名参会照片
$!{a.name} + #if($a.finishTime) + $date.format('yyyy-MM-dd HH:mm', $a.finishTime) + #end + + #if($a.attendeeSignOssId) + #set($signUrl = $!oss.selectUrlByIds($a.attendeeSignOssId)) + #if($signUrl) + + #end + #end + + #if($a.attendeePhotoOssId) + #set($photoUrl = $!oss.selectUrlByIds($a.attendeePhotoOssId)) + #if($photoUrl) + + #end + #end +
暂无参会人员
+#end + diff --git a/src/main/resources/templates/securityManage/safetyProductionInputRecord.html.vm b/src/main/resources/templates/securityManage/safetyProductionInputRecord.html.vm new file mode 100644 index 0000000..4777cf9 --- /dev/null +++ b/src/main/resources/templates/securityManage/safetyProductionInputRecord.html.vm @@ -0,0 +1,77 @@ +#if($mode == "cover") +
+
+ $!{regionName}道路运输行业 +
+
+ 安全生产投入 +
+
+ 登记本 +
+
+ 单位:$!{companyName}(盖章) +
+
+ 20(   )年 +
+
+#elseif($mode == "detail") +
+ 安全生产投入登记表 +
+
+ 拟投入安全生产经费金额:$!{totalAmount}(单位:元) +
+ + + + + + + + + #foreach($item in $records) + + + + + + + + #end +
日期支出项目具体内容投入金额负责人签字备注
+ #if($item.investmentTime) + $date.format('yyyy-MM-dd', $item.investmentTime) + #end + + #if($item.investmentName && $item.quantity) + $!{item.investmentName}(数量:$!{item.quantity}) + #elseif($item.investmentName) + $!{item.investmentName} + #end + + #if($item.investmentAmount) + $!{item.investmentAmount} + #end + + $!{item.remark} +
+#elseif($mode == "receipt") +
+ 票据 +
+ + + + +
+ #if($receiptImages && $receiptImages.size() > 0) + #foreach($img in $receiptImages) + + #end + #else + 无票据 + #end +
+#end diff --git a/src/main/resources/templates/securityManage/serviceQualityRecord.html.vm b/src/main/resources/templates/securityManage/serviceQualityRecord.html.vm new file mode 100644 index 0000000..5befbe8 --- /dev/null +++ b/src/main/resources/templates/securityManage/serviceQualityRecord.html.vm @@ -0,0 +1,93 @@ +#if($mode == "cover") +
+
+ $!{regionName}道路运输行业 +
+
+ 安全生产服务质量 +
+
+ 记 +
+
+ 录 +
+
+ 单位:$!{companyName}(盖章) +
+
+ 20( )年 +
+
+#elseif($mode == "detail") +
+ 安全生产服务(投诉、媒体曝光)质量记录表 +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
投诉人姓名$!{complaint.complainantName}投诉时间 + #if($complaint.caseTime) + $date.format('yyyy-MM-dd', $complaint.caseTime) + #end +
投诉人联系方式$!{complaint.complainantPhone}投诉方式$!{complaint.complaintChannel}
投诉对象/服务 + 投诉对象:$!{complaintTargetLabel}    投诉服务:$!{complaint.complaintService} +
投诉内容 + #if($complaint.caseSummary) + #foreach($line in $complaint.caseSummary.split("\n")) + $!{line}
+ #end + #end +
投诉人要求 + #if($complaint.complaintRequest) + #foreach($line in $complaint.complaintRequest.split("\n")) + $!{line}
+ #end + #else + 无 + #end +
处理情况 + #if($complaint.handleResult) + #foreach($line in $complaint.handleResult.split("\n")) + $!{line}
+ #end + #end +
+ 受理人签字
+ #if($acceptorSignUrl) + + #end +

+ 处理人签字
+ #if($handlerSignUrl) + + #end +
+#end diff --git a/src/main/resources/templates/securityManage/studyDetail.html.vm b/src/main/resources/templates/securityManage/studyDetail.html.vm new file mode 100644 index 0000000..7ec7324 --- /dev/null +++ b/src/main/resources/templates/securityManage/studyDetail.html.vm @@ -0,0 +1,133 @@ +
+ #if($training.startTime) + $!tool.formatDate($!training.startTime, "yyyy年MM月")在线安全学习考试 + #else + 在线安全学习考试 + #end +
+ +
+ 姓名:$!{data.userName} +      + 考核: + #if($data.examScore && $data.passScore) + #if($data.examScore >= $data.passScore) + 合格 + #else + 不合格 + #end + #else + 不合格 + #end +
+ + + + + + + + #if($data.courseList) + #foreach($course in $data.courseList) + + + + + + #end + #end +
课程名称学习分钟数完成学习时间
$!{course.courseName} + #if($course.duration) + $!{course.duration}分钟 + #end + + #if($course.completeTime) + $!tool.formatDate($!course.completeTime, "yyyy年MM月dd日") + #else + 未完成 + #end +
+ +
+
+
课件名称
+
$!{data.trainingName}
+
+
+
考试信息
+
+ 试卷(测试) + #if($data.singleCount)(单选:$!{data.singleCount})#end + #if($data.multiCount)(多选:$!{data.multiCount})#end + #if($data.judgeCount)(判断:$!{data.judgeCount})#end + #if($data.totalScore)(总分:$!{data.totalScore}分)#end +
+
+
+
学习分数/总分
+
+ $!{data.examScore}/$!{data.totalScore} +
+
+
+
题目统计
+
+ #if($data.singleCount && $data.singleCount > 0) + 单选共($!{data.singleCount}题),总分:$!{data.singleTotalScore}分 + #end + #if($data.multiCount && $data.multiCount > 0) +  多选共($!{data.multiCount}题),总分:$!{data.multiTotalScore}分 + #end + #if($data.judgeCount && $data.judgeCount > 0) +  判断共($!{data.judgeCount}题),总分:$!{data.judgeTotalScore}分 + #end +
+
+
+ + + + #if($data.singleChoiceList && $data.singleChoiceList.size() > 0) + + + + #foreach($r in $data.singleChoiceList) + #parse("templates/securityManage/studyDetailQuestion.html.vm") + #end + #end + + + #if($data.multiChoiceList && $data.multiChoiceList.size() > 0) + + + + #foreach($r in $data.multiChoiceList) + #parse("templates/securityManage/studyDetailQuestion.html.vm") + #end + #end + + + #if($data.judgeList && $data.judgeList.size() > 0) + + + + #foreach($r in $data.judgeList) + #parse("templates/securityManage/studyDetailQuestion.html.vm") + #end + #end +
+ 一、单选题(共$!{data.singleCount}题,总分$!{data.singleTotalScore}分) +
+ #if($data.singleChoiceList && $data.singleChoiceList.size() > 0)二#else一#end、多选题(共$!{data.multiCount}题,总分$!{data.multiTotalScore}分) +
+ #if(($data.singleChoiceList && $data.singleChoiceList.size() > 0) && ($data.multiChoiceList && $data.multiChoiceList.size() > 0))三 + #elseif(($data.singleChoiceList && $data.singleChoiceList.size() > 0) || ($data.multiChoiceList && $data.multiChoiceList.size() > 0))二 + #else一#end、判断题(共$!{data.judgeCount}题,总分$!{data.judgeTotalScore}分) +
+## 文档末尾二维码(仅渲染一次) +#if($verifyQrBase64) +
+ +
扫描查真伪
+
+#end \ No newline at end of file diff --git a/src/main/resources/templates/securityManage/studyDetailQuestion.html.vm b/src/main/resources/templates/securityManage/studyDetailQuestion.html.vm new file mode 100644 index 0000000..65e8e23 --- /dev/null +++ b/src/main/resources/templates/securityManage/studyDetailQuestion.html.vm @@ -0,0 +1,73 @@ +
+ #if($r.questionOrder)$!{r.questionOrder}、#end$!{r.questionDesc} +
+ A: $!{r.optionA} +
+ B: $!{r.optionB} +
+ C: $!{r.optionC} +
+ D: $!{r.optionD} +
+ E: $!{r.optionE} +
+ F: $!{r.optionF} +
+ 正确答案:$!{r.correctAnswers} + + 选择答案:$!{r.selectedAnswers} +
+ 分数: + #if("$!r.gainedScore" != "")$!{r.gainedScore}#end + + 难度等级: + #if($r.difficulty == 1) + 普通 + #elseif($r.difficulty == 2) + 困难 + #elseif($r.difficulty == 3) + 极难 + #else + $!{r.difficulty} + #end +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #if($record.courseList && $record.courseList.size() > 0) + #set($courseList = $record.courseList) + #else + #set($courseList = [{"courseName": ""}]) + #end + #set($rowspan = $courseList.size() + 1) + + + + + + + + + + + + #foreach($c in $courseList) + + + + + + + #end + + + + +
企业名称$!{companyName}学习人员$!{record.userName}
身份证号$!{record.idCardNo}车牌号$!{record.plateNumber}
教育培训日期 + #if($training.startTime && $training.endTime) + $!tool.formatDate($!training.startTime, 'yyyy年MM月dd日') 至 $!tool.formatDate($!training.endTime, + 'yyyy年MM月dd日') + #elseif($training.startTime) + $!tool.formatDate($!training.startTime, 'yyyy年MM月dd日') + #elseif($training.endTime) + $!tool.formatDate($!training.endTime, 'yyyy年MM月dd日') + #else +   + #end +
计划学时 + #if($training.classHours) + $!{training.classHours}学时 + #else +   + #end + 学习分钟数/计划分钟数 + #if($record.learnMinutes && $record.planMinutes) + $!{record.learnMinutes}/$!{record.planMinutes}分钟 + #else +   + #end +
在线学习
抓拍照片
+ #if($record.photoUrls && $record.photoUrls.size() > 0) + #foreach($url in $record.photoUrls) + + #end + #end +
培训内容 + 学习时间课程名称时长是否补学
+ #if($c.completeTime) + $!tool.formatDate($!c.completeTime, 'yyyy-MM-dd HH:mm:ss') + #end + $!{c.courseName} + #if($c.duration) + #set($minutes = $c.duration / 60) + #set($seconds = $c.duration % 60) + $!{minutes}分$!{seconds}秒 + #end + $!{c.isMakeUp}
+
+ 学习人员签名: + #if($record.signatureUrl) + + #end +
+
+ +#if($verifyQrBase64) +
+ +
扫描查真伪
+
+#end diff --git a/src/main/resources/templates/securityManage/trainingAssessmentDetail.html.vm b/src/main/resources/templates/securityManage/trainingAssessmentDetail.html.vm new file mode 100644 index 0000000..c54686e --- /dev/null +++ b/src/main/resources/templates/securityManage/trainingAssessmentDetail.html.vm @@ -0,0 +1,68 @@ + + +
+

考核明细表

+
+
打印时间:$!printTime
+ + + + + + + + + + + + + + + + #foreach($item in $list) + + + + + + + + + + + #end + +
序号名称手机号人员类型完成时间签名是否补学是否完成
$foreach.count$!item.userName$!item.phone$!item.personTypeStr$!item.completeTime + #if($!item.signatureUrl && $!item.signatureUrl != "") + + #end + $!item.isMakeUpStr$!item.isCompletedStr
diff --git a/src/main/resources/templates/securityManage/trainingOutline.html.vm b/src/main/resources/templates/securityManage/trainingOutline.html.vm new file mode 100644 index 0000000..4eaba84 --- /dev/null +++ b/src/main/resources/templates/securityManage/trainingOutline.html.vm @@ -0,0 +1,70 @@ + + +#foreach($outline in $list) +
+ $!{outline.outlineYear}年度安全教育培训计划大纲 +
+ + + + + + + + + + + + + #if($outline.rows && $outline.rows.size() > 0) + #foreach($row in $outline.rows) + + #if($row.monthRowSpan > 0) + + + #end + + + + + #end + #else + + + + #end + +
月份培训主题课程名称课时(分钟)培训内容摘要
$!{row.planMonth}$!{row.topic}$!{row.courseName}$!{row.durationMinute}$!{row.courseDesc}
暂无培训计划数据
+ +
+ 打印时间:$!{printTime} +
+ + #if($foreach.hasNext) +
+ #end +#end diff --git a/src/main/resources/templates/securityManage/violationHandlingRecord.html.vm b/src/main/resources/templates/securityManage/violationHandlingRecord.html.vm new file mode 100644 index 0000000..6c248ea --- /dev/null +++ b/src/main/resources/templates/securityManage/violationHandlingRecord.html.vm @@ -0,0 +1,114 @@ +#if($mode == "cover") +
+
+ $!{regionName}道路运输行业 +
+
+ 违法违章处理 +
+
+ 记 +
+
+ 录 +
+
+ 单位:$!{companyName}(盖章) +
+
+ 20( )年 +
+
+#elseif($mode == "detail") +
+ 违法违章处理记录表 +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
登记时间 + $!date.format('yyyy-MM-dd', $recordTime) + 违法违章事由$!{info.violationContent}
驾驶员$!{info.driverName}车牌号$!{info.plateNumber}记录人$!{info.recorderName}
违法违章事件简要经过$!{info.violationContent}
处理情况(依据企业管理制度进行的处罚结果和告诫警示谈话内容)$!{info.disposalResult}
负责人签字 + #if($responsibleSignatureUrl) + + #end + 驾驶员签字 + #if($partySignatureUrl) + + #end +
备注$!{info.remark}
+#elseif($mode == "attachment") +
+ 承诺书及约谈照片 +
+ + + + + #if($promiseImages && $promiseImages.size() > 0) + #foreach($img in $promiseImages) + + + + #end + #else + + + + #end +
承诺书
+ +
暂无承诺书
+ + + + + #if($interviewImages && $interviewImages.size() > 0) + + + + #else + + + + #end +
约谈照片
+
+ #foreach($img in $interviewImages) +
+ +
+ #end +
+
暂无约谈照片
+#end diff --git a/src/main/resources/templates/securityManage/violationInvestigationRecord.html.vm b/src/main/resources/templates/securityManage/violationInvestigationRecord.html.vm new file mode 100644 index 0000000..cb5829f --- /dev/null +++ b/src/main/resources/templates/securityManage/violationInvestigationRecord.html.vm @@ -0,0 +1,57 @@ +#if($mode == "cover") +
+
+ $!{regionName}道路运输行业 +
+
+ 违法违章信息排查 +
+
+ 单位:$!{companyName}(盖章) +
+
+ 20( )年 +
+
+#elseif($mode == "detail") +
+ 违法违章信息排查登记表 +
+ + + + + + + + + + + + + + + + + + #set($index = 1) + #if($details) + #foreach($item in $details) + + + + + + + + #set($index = $index + 1) + #end + #end + #if(!$details || $details.size() == 0) + + + + #end + +
填表人$!{managerName}排查时间$!{investigateDate}
序号违法违章车辆驾驶员违法违章信息备注
$index$!{item.plateNumber}$!{item.driverName}$!{item.violationContent}$!{item.remark}
暂无数据
+#end diff --git a/src/main/resources/templates/vehicle/baseInfo.html.vm b/src/main/resources/templates/vehicle/baseInfo.html.vm new file mode 100644 index 0000000..78107fd --- /dev/null +++ b/src/main/resources/templates/vehicle/baseInfo.html.vm @@ -0,0 +1,97 @@ +
+

公司车辆基本信息记录

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
机动车所有人$!{company.companyName}车牌号$!{vehicle.plateNumber}
地址$!{company.officeAddress} $!{vehicle.address}车辆类型$!{vehicle.vehicleType}
使用性质$!{vehicle.useNature}品牌型号$!{vehicle.brandModel}
车架号$!{vehicle.frameNumber}发动机号$!{vehicle.engineNumber}
注册日期$!tool.formatDate($!vehicle.firstRegisterDate, 'yyyy-MM-dd')发证日期$!tool.formatDate($!vehicle.certificateIssueDate, 'yyyy-MM-dd')
核定载人数(人)$!{vehicle.ratedPassengerCount}总质量(KG)$!{vehicle.grossWeightKg}
整备质量(KG)$!{vehicle.curbWeightKg}核定载质量(KG)$!{vehicle.ratedLoadKg}
外廓尺寸(mm)$!{outerDimensions}准牵引总质量(KG)$!{vehicle.towingMassKg}
强制报废期止$!tool.formatDate($!vehicle.mandatoryScrapEndDate, 'yyyy-MM-dd')检验有效期$!{vehicle.inspectionValidMonth}
车身颜色$!{vehicle.color}燃料种类$!{vehicle.fuelType}
发动机排量(ml)$!{vehicle.engineDisplacementMl}发动机净功率(KW)$!{vehicle.enginePowerKw}
轮胎数$!{vehicle.tireCount}轴距(mm)$!{vehicle.wheelbaseMm}
货箱内部尺寸(mm)$!{cargoInnerDimensions}出厂日期 + $!tool.formatDate($!vehicle.manufactureDate, 'yyyy-MM-dd') +
+ + diff --git a/src/main/resources/templates/vehicle/claimRecord.html.vm b/src/main/resources/templates/vehicle/claimRecord.html.vm new file mode 100644 index 0000000..83c908c --- /dev/null +++ b/src/main/resources/templates/vehicle/claimRecord.html.vm @@ -0,0 +1,75 @@ +
+

车辆出险记录

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #if($images && $images.size() > 0) + #if($images.size() == 1) + + + + #else + #foreach($img in $images) + #if($foreach.index % 2 == 0) + + + + #end + #end + #end + #else + + + + #end +
车牌号$!{record.plateNumber}承保险种$!{record.insuranceType}
起保日期$!tool.formatDate($!record.assessDate, 'yyyy-MM-dd')终保日期$!tool.formatDate($!record.insuranceExpireDate, 'yyyy-MM-dd')
保险公司$!{record.insurerName}出险日期$!tool.formatDate($!record.incidentTime, 'yyyy-MM-dd')
出险事项$!{record.claimItems}
备注$!{record.remark}
+
+
+ +
+
+
+
+ #end +
+ +
+ #if($foreach.index % 2 == 1 || !$foreach.hasNext) +
+
+ 暂无图片记录 +
diff --git a/src/main/resources/templates/vehicle/inspectionReport.html.vm b/src/main/resources/templates/vehicle/inspectionReport.html.vm new file mode 100644 index 0000000..0f40087 --- /dev/null +++ b/src/main/resources/templates/vehicle/inspectionReport.html.vm @@ -0,0 +1,45 @@ +
+

安全技术检测报告

+
+ + + + + + + + + + + + + + + + + + + + + +
车牌号$!{review.plateNumber}检验日期$!tool.formatDate($!review.reviewDate, 'yyyy-MM-dd')
技术等级 + #if($!review.evaluationLevel == 1) + 一级 + #elseif($!review.evaluationLevel == 2) + 二级 + #else + $!{review.evaluationLevel} + #end +
+ #if($images && $images.size() > 0) + #foreach($img in $images) +
+ +
+ #end + #else +
+ 暂无检测报告图片 +
+ #end +
diff --git a/src/main/resources/templates/vehicle/licenseInfo.html.vm b/src/main/resources/templates/vehicle/licenseInfo.html.vm new file mode 100644 index 0000000..bd0d964 --- /dev/null +++ b/src/main/resources/templates/vehicle/licenseInfo.html.vm @@ -0,0 +1,117 @@ +
+

车辆行驶证、运输证

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + #set($totalCount = 0) + + ## Loop through dlOther images + #foreach($img in $images.dlOther) + #if($totalCount % 2 == 0) + + #end + + #if($totalCount % 2 == 1) + + #end + #set($totalCount = $totalCount + 1) + #end + + ## Loop through body images + #if($images.body && $images.body.size() > 0) + #foreach($img in $images.body) + #if($totalCount % 2 == 0) + + #end + + #if($totalCount % 2 == 1) + + #end + #set($totalCount = $totalCount + 1) + #end + #end + + ## Close any open row + #if($totalCount % 2 != 0) + + #end +
车牌号$!{vehicle.plateNumber}
行驶证
发证日期$!tool.formatDate($!vehicle.certificateIssueDate, 'yyyy-MM-dd')检验有效期$!{vehicle.inspectionValidMonth}
+ #if($images.dlMain && $images.dlMain.size() > 0) + + #end + + #if($images.dlAux && $images.dlAux.size() > 0) + + #end +
+ +
+ +
+ +
+ +
+

车辆行驶证、运输证

+
+ + + + + + + + + + + + + + + + + + + + + + + #if($images.tl && $images.tl.size() > 0) + #foreach($img in $images.tl) + + + + #end + #else + + + + #end +
运输证
运输证号$!{vehicle.transportLicenseNo}车牌号$!{vehicle.plateNumber}
有效期$!{vehicle.transportValidMonth}
+ +
+ 运输证照片 +
diff --git a/src/main/resources/templates/vehicle/maintenanceRecord.html.vm b/src/main/resources/templates/vehicle/maintenanceRecord.html.vm new file mode 100644 index 0000000..44fc963 --- /dev/null +++ b/src/main/resources/templates/vehicle/maintenanceRecord.html.vm @@ -0,0 +1,119 @@ +
+

二级维护记录

+
+ + + + + + + + + + + + + + + + + + + + + + + + + #if($factoryCertImages && $factoryCertImages.size() > 0) + #foreach($img in $factoryCertImages) + + + + #end + #end + + + #if($finalInspectImages && $finalInspectImages.size() > 0) + + + + #foreach($img in $finalInspectImages) + + + + #end + #end + + + #if($processInspectImages && $processInspectImages.size() > 0) + + + + #foreach($img in $processInspectImages) + + + + #end + #end + + + #if($entryInspectImages && $entryInspectImages.size() > 0) + + + + #foreach($img in $entryInspectImages) + + + + #end + #end + + #if((!$factoryCertImages || $factoryCertImages.size() == 0) && (!$finalInspectImages || $finalInspectImages.size() == 0) && (!$processInspectImages || $processInspectImages.size() == 0) && (!$entryInspectImages || $entryInspectImages.size() == 0)) + + + + #end +
车牌号$!{record.plateNumber}
托修方$!{record.transferor}维护完成日期$!tool.formatDate($!record.finishTime, 'yyyy-MM-dd')
+ 竣工出厂合格证 +
+ + + + +
+ +
+
+ 竣工检验单 +
+ + + + +
+ +
+
+ 过程检验单 +
+ + + + +
+ +
+
+ 进厂检验单 +
+ + + + +
+ +
+
+ 暂无维护图片记录 +
diff --git a/src/main/resources/templates/vehicle/mileageRecord.html.vm b/src/main/resources/templates/vehicle/mileageRecord.html.vm new file mode 100644 index 0000000..c202d8d --- /dev/null +++ b/src/main/resources/templates/vehicle/mileageRecord.html.vm @@ -0,0 +1,44 @@ +
+

车辆行驶里程记录

+
+ + + + + + + + + + + + + #if($mileageList && $mileageList.size() > 0) + + #else + + #end + + + + + + + #if($mileageList && $mileageList.size() > 0) + #foreach($mileage in $mileageList) + + + + + + + + #end + #else + + + + #end +
车牌号$!{mileageList.get(0).plateNumber}
日期行驶里程(公里)备注
$!tool.formatDate($!mileage.startDate, 'yyyy-MM-dd HH:mm')$!tool.formatDate($!mileage.endDate, 'yyyy-MM-dd HH:mm')$!{mileage.mileageKm}$!{mileage.remark}
+ 暂无行驶里程记录 +
diff --git a/src/main/resources/templates/vehicle/repairRecord.html.vm b/src/main/resources/templates/vehicle/repairRecord.html.vm new file mode 100644 index 0000000..baf2ae0 --- /dev/null +++ b/src/main/resources/templates/vehicle/repairRecord.html.vm @@ -0,0 +1,81 @@ +
+

车辆维护与维修记录

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #if($images && $images.size() > 0) + #if($images.size() == 1) + + + + #else + #foreach($img in $images) + #if($foreach.index % 2 == 0) + + + + #end + #end + #end + #else + + + + #end +
车牌号$!{record.plateNumber}记录人$!{record.recorder}
维修类别$!{record.repairCategory}维修完成日期$!tool.formatDate($!record.finishDate, 'yyyy-MM-dd')
维修单位$!{record.repairUnit}维修日期$!tool.formatDate($!record.repairDate, 'yyyy-MM-dd')
主要维修内容 + #if($record.replacedParts && $record.replacedParts.trim() != '') + 更换部件:$!{record.replacedParts}
+ #end + #if($record.partSpec && $record.partSpec.trim() != '') + 部件规格:$!{record.partSpec}
+ #end + #if($record.repairContent && $record.repairContent.trim() != '') + 维修内容:$!{record.repairContent} + #end +
+
+
+ +
+
+
+
+ #end +
+ +
+ #if($foreach.index % 2 == 1 || !$foreach.hasNext) +
+
+ 暂无维修凭据图片 +
diff --git a/src/main/resources/templates/vehicle/trafficAccidentRecord.html.vm b/src/main/resources/templates/vehicle/trafficAccidentRecord.html.vm new file mode 100644 index 0000000..3db7f41 --- /dev/null +++ b/src/main/resources/templates/vehicle/trafficAccidentRecord.html.vm @@ -0,0 +1,92 @@ +
+

交通事故记录

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
车牌号$!{record.plateNumber}记录人$!{record.reporterName}
发生时间$!tool.formatDate($!record.createTime, 'yyyy-MM-dd HH:mm')直接经济损失(元)$!{record.economicLoss}
事故类别$!{record.accidentPattern}
事故地点$!{record.occurLocation}
主要情况及事故原因$!{record.mainCause}
责任划分$!{record.divisionResponsibilities}
责任人员处理情况$!{record.accidentSituation}
+ +
+

交通事故记录

+
+ + + + + #if($images && $images.size() > 0) + #if($images.size() == 1) + + + + #else + #foreach($img in $images) + #if($foreach.index % 2 == 0) + + + + #end + #end + #end + #else + + + + #end +
+ 责任认定书 +
+
+
+ +
+
+
+
+ #end +
+ +
+ #if($foreach.index % 2 == 1 || !$foreach.hasNext) +
+
+ 暂无附件 +
diff --git a/src/main/resources/templates/vehicle/vehicleThreeInspectForm.html.vm b/src/main/resources/templates/vehicle/vehicleThreeInspectForm.html.vm new file mode 100644 index 0000000..f68de31 --- /dev/null +++ b/src/main/resources/templates/vehicle/vehicleThreeInspectForm.html.vm @@ -0,0 +1,165 @@ + + +
+

车辆三检检查表单

+
+
打印时间:$!printTime
+ + + + + + + + + + + + + + +
所属企业$!companyName车牌号$!plateNumber
+ +#macro(renderPhase $phaseData $title) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
$!title
+ #if($phaseData && $phaseData.items && $phaseData.items.size() > 0) + #foreach($item in $phaseData.items) + $!item.index. $!item.title:$!item.result
+ #end + #else + 无 + #end +
检查附件 + #if($phaseData && $phaseData.attachImages && $phaseData.attachImages.size() > 0) +
+ #foreach($img in $phaseData.attachImages) +
+ +
+ #end +
+ #else + 暂无 + #end +
检查时间$!phaseData.inspectTime检查员签名 + #if($phaseData && $phaseData.inspectorSignUrl) + + #end +
是否误报$!phaseData.isFalseReport是否存在隐患$!phaseData.hasHiddenDanger
审核结论$!phaseData.auditResult审核人$!phaseData.auditorName
审核时间$!phaseData.auditTime审核人签名 + #if($phaseData && $phaseData.auditorSignUrl) + + #end +
+#end + +#if($phase1) + #renderPhase($phase1 "出车前检查") +#end + +#if($phase2) + #renderPhase($phase2 "行车中检查") +#end + +#if($phase3) + #renderPhase($phase3 "收车后检查") +#end diff --git a/src/main/resources/templates/vehicle/vehicleThreeInspectRecord.html.vm b/src/main/resources/templates/vehicle/vehicleThreeInspectRecord.html.vm new file mode 100644 index 0000000..ba92056 --- /dev/null +++ b/src/main/resources/templates/vehicle/vehicleThreeInspectRecord.html.vm @@ -0,0 +1,80 @@ + + +
+

车辆三检记录表

+
+
打印时间:$!printTime
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #foreach($item in $list) + + + + + + + + + + + + + + + + + + + + #end + +
序号车辆出车检查行车检查收车检查
检查时间状态隐患检查时间状态隐患检查时间状态收车隐患
$foreach.count$!item.plateNumber$!item.startPhaseTime$!item.startPhaseStatus$!item.startPhaseHiddenDanger$!item.runningPhaseTime$!item.runningPhaseStatus$!item.runningPhaseHiddenDanger$!item.endPhaseTime$!item.endPhaseStatus$!item.endPhaseHiddenDanger
diff --git a/src/main/resources/vm/java/bo.java.vm b/src/main/resources/vm/java/bo.java.vm new file mode 100644 index 0000000..3112ffb --- /dev/null +++ b/src/main/resources/vm/java/bo.java.vm @@ -0,0 +1,50 @@ +package ${packageName}.domain.bo; + +import ${packageName}.domain.${ClassName}; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; +#foreach ($import in $importList) +import ${import}; +#end + +/** + * ${functionName}业务对象 ${tableName} + * + * @author shihongwei + * @date ${datetime} + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = ${ClassName}.class, reverseConvertGenerate = false) +public class ${ClassName}Bo extends BaseEntity { + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField) && ($column.query || $column.insert || $column.edit)) + /** + * $column.columnComment + */ +#if($column.insert && $column.edit) +#set($Group="AddGroup.class, EditGroup.class") +#elseif($column.insert) +#set($Group="AddGroup.class") +#elseif($column.edit) +#set($Group="EditGroup.class") +#end +#if($column.required) +#if($column.javaType == 'String') + @NotBlank(message = "$column.columnComment不能为空", groups = { $Group }) +#else + @NotNull(message = "$column.columnComment不能为空", groups = { $Group }) +#end +#end + private $column.javaType $column.javaField; + +#end +#end + +} diff --git a/src/main/resources/vm/java/controller.java.vm b/src/main/resources/vm/java/controller.java.vm new file mode 100644 index 0000000..70bd325 --- /dev/null +++ b/src/main/resources/vm/java/controller.java.vm @@ -0,0 +1,128 @@ +package ${packageName}.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import ${packageName}.domain.vo.${ClassName}Vo; +import ${packageName}.domain.bo.${ClassName}Bo; +import ${packageName}.service.I${ClassName}Service; +#if($table.crud) +import org.dromara.common.mybatis.core.page.TableDataInfo; +#elseif($table.tree) +#end +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; + +/** + * ${functionName} + * + * @author shihongwei + * @date ${datetime} + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/${moduleName}/${businessName}") +@Tag(name = "${functionName}", description = "${functionName}管理") +public class ${ClassName}Controller extends BaseController { + + private final I${ClassName}Service ${className}Service; + + /** + * 查询${functionName}列表 + */ +//@SaCheckPermission("${permissionPrefix}:list") + @GetMapping("/list") +#if($table.crud) + @Operation(summary = "分页查询${functionName}列表") + public TableDataInfo<${ClassName}Vo> list(${ClassName}Bo bo, PageQuery pageQuery) { + return ${className}Service.queryPageList(bo, pageQuery); + } +#elseif($table.tree) + @Operation(summary = "查询${functionName}列表") + public R> list(${ClassName}Bo bo) { + List<${ClassName}Vo> list = ${className}Service.queryList(bo); + return R.ok(list); + } +#end + + /** + * 导出${functionName}列表 + */ + //@SaCheckPermission("${permissionPrefix}:export") + @Log(title = "${functionName}", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @Operation(summary = "导出${functionName}列表") + public void export(${ClassName}Bo bo, HttpServletResponse response) { + List<${ClassName}Vo> list = ${className}Service.queryList(bo); + ExcelUtil.exportExcel(list, "${functionName}", ${ClassName}Vo.class, response); + } + + /** + * 获取${functionName}详细信息 + * + * @param ${pkColumn.javaField} 主键 + */ + //@SaCheckPermission("${permissionPrefix}:query") + @GetMapping("/{${pkColumn.javaField}}") + @Operation(summary = "获取${functionName}详细信息") + public R<${ClassName}Vo> getInfo(@NotNull(message = "主键不能为空") + @Parameter(name = "${pkColumn.javaField}", description = "主键", required = true, example = "1") + @PathVariable ${pkColumn.javaType} ${pkColumn.javaField}) { + return R.ok(${className}Service.queryById(${pkColumn.javaField})); + } + + /** + * 新增${functionName} + */ + //@SaCheckPermission("${permissionPrefix}:add") + @Log(title = "${functionName}", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + @Operation(summary = "新增${functionName}") + public R add(@Validated(AddGroup.class) @RequestBody ${ClassName}Bo bo) { + return toAjax(${className}Service.insertByBo(bo)); + } + + /** + * 修改${functionName} + */ + //@SaCheckPermission("${permissionPrefix}:edit") + @Log(title = "${functionName}", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + @Operation(summary = "修改${functionName}") + public R edit(@Validated(EditGroup.class) @RequestBody ${ClassName}Bo bo) { + return toAjax(${className}Service.updateByBo(bo)); + } + + /** + * 删除${functionName} + * + * @param ${pkColumn.javaField}s 主键串 + */ + //@SaCheckPermission("${permissionPrefix}:remove") + @Log(title = "${functionName}", businessType = BusinessType.DELETE) + @DeleteMapping("/{${pkColumn.javaField}s}") + @Operation(summary = "删除${functionName}") + public R remove(@NotEmpty(message = "主键不能为空") + @Parameter(name = "${pkColumn.javaField}s", description = "主键串", required = true, example = "1,2,3") + @PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) { + return toAjax(${className}Service.deleteWithValidByIds(List.of(${pkColumn.javaField}s), true)); + } +} diff --git a/src/main/resources/vm/java/domain.java.vm b/src/main/resources/vm/java/domain.java.vm new file mode 100644 index 0000000..d54292a --- /dev/null +++ b/src/main/resources/vm/java/domain.java.vm @@ -0,0 +1,60 @@ +package ${packageName}.domain; + +#foreach ($column in $columns) +#if($column.javaField=='tenantId') +#set($IsTenant=1) +#end +#end +#if($IsTenant==1) +import org.dromara.common.tenant.core.TenantEntity; +#else +import org.dromara.common.mybatis.core.domain.BaseEntity; +#end +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +#foreach ($import in $importList) +import ${import}; +#end + +import java.io.Serial; + +/** + * ${functionName}对象 ${tableName} + * + * @author shihongwei + * @date ${datetime} + */ +#if($IsTenant==1) +#set($Entity="TenantEntity") +#else +#set($Entity="BaseEntity") +#end +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("${tableName}") +public class ${ClassName} extends ${Entity} { + + @Serial + private static final long serialVersionUID = 1L; + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) + /** + * $column.columnComment + */ +#if($column.javaField=='isDeleted' || $column.columnName=='is_deleted') + @TableLogic +#end +#if($column.javaField=='version') + @Version +#end +#if($column.isPk==1) + @TableId(value = "$column.columnName") +#end + private $column.javaType $column.javaField; + +#end +#end + +} diff --git a/src/main/resources/vm/java/mapper.java.vm b/src/main/resources/vm/java/mapper.java.vm new file mode 100644 index 0000000..8b7f077 --- /dev/null +++ b/src/main/resources/vm/java/mapper.java.vm @@ -0,0 +1,17 @@ +package ${packageName}.mapper; + +import ${packageName}.domain.${ClassName}; +import ${packageName}.domain.vo.${ClassName}Vo; +import org.apache.ibatis.annotations.Mapper; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * ${functionName}Mapper接口 + * + * @author shihongwei + * @date ${datetime} + */ + @Mapper +public interface ${ClassName}Mapper extends BaseMapperPlus<${ClassName}, ${ClassName}Vo> { + +} diff --git a/src/main/resources/vm/java/service.java.vm b/src/main/resources/vm/java/service.java.vm new file mode 100644 index 0000000..fa3f8cf --- /dev/null +++ b/src/main/resources/vm/java/service.java.vm @@ -0,0 +1,72 @@ +package ${packageName}.service; + +import ${packageName}.domain.vo.${ClassName}Vo; +import ${packageName}.domain.bo.${ClassName}Bo; +#if($table.crud) +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +#end + +import java.util.Collection; +import java.util.List; + +/** + * ${functionName}Service接口 + * + * @author shihongwei + * @date ${datetime} + */ +public interface I${ClassName}Service { + + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} 主键 + * @return ${functionName} + */ + ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}); + +#if($table.crud) + /** + * 分页查询${functionName}列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return ${functionName}分页列表 + */ + TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery); +#end + + /** + * 查询符合条件的${functionName}列表 + * + * @param bo 查询条件 + * @return ${functionName}列表 + */ + List<${ClassName}Vo> queryList(${ClassName}Bo bo); + + /** + * 新增${functionName} + * + * @param bo ${functionName} + * @return 是否新增成功 + */ + Boolean insertByBo(${ClassName}Bo bo); + + /** + * 修改${functionName} + * + * @param bo ${functionName} + * @return 是否修改成功 + */ + Boolean updateByBo(${ClassName}Bo bo); + + /** + * 校验并批量删除${functionName}信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid); +} diff --git a/src/main/resources/vm/java/serviceImpl.java.vm b/src/main/resources/vm/java/serviceImpl.java.vm new file mode 100644 index 0000000..0013e1b --- /dev/null +++ b/src/main/resources/vm/java/serviceImpl.java.vm @@ -0,0 +1,160 @@ +package ${packageName}.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +#if($table.crud) +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +#end +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import ${packageName}.domain.bo.${ClassName}Bo; +import ${packageName}.domain.vo.${ClassName}Vo; +import ${packageName}.domain.${ClassName}; +import ${packageName}.mapper.${ClassName}Mapper; +import ${packageName}.service.I${ClassName}Service; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * ${functionName}Service业务层处理 + * + * @author shihongwei + * @date ${datetime} + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class ${ClassName}ServiceImpl implements I${ClassName}Service { + + private final ${ClassName}Mapper baseMapper; + + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} 主键 + * @return ${functionName} + */ + @Override + public ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}){ + return baseMapper.selectVoById(${pkColumn.javaField}); + } + +#if($table.crud) + /** + * 分页查询${functionName}列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return ${functionName}分页列表 + */ + @Override + public TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery) { + LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo); + Page<${ClassName}Vo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } +#end + + /** + * 查询符合条件的${functionName}列表 + * + * @param bo 查询条件 + * @return ${functionName}列表 + */ + @Override + public List<${ClassName}Vo> queryList(${ClassName}Bo bo) { + LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper<${ClassName}> buildQueryWrapper(${ClassName}Bo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper<${ClassName}> lqw = Wrappers.lambdaQuery(); +#foreach($column in $columns) +#if($column.query) +#set($queryType=$column.queryType) +#set($javaField=$column.javaField) +#set($javaType=$column.javaType) +#set($columnName=$column.columnName) +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#set($mpMethod=$column.queryType.toLowerCase()) +#if($queryType != 'BETWEEN') +#if($javaType == 'String') +#set($condition='StringUtils.isNotBlank(bo.get'+$AttrName+'())') +#else +#set($condition='bo.get'+$AttrName+'() != null') +#end + lqw.$mpMethod($condition, ${ClassName}::get$AttrName, bo.get$AttrName()); +#else + lqw.between(params.get("begin$AttrName") != null && params.get("end$AttrName") != null, + ${ClassName}::get$AttrName ,params.get("begin$AttrName"), params.get("end$AttrName")); +#end +#end +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#if($column.isPk==1) + lqw.orderByAsc(${ClassName}::get$AttrName); +#end +#end + return lqw; + } + + /** + * 新增${functionName} + * + * @param bo ${functionName} + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(${ClassName}Bo bo) { + ${ClassName} add = MapstructUtils.convert(bo, ${ClassName}.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; +#set($pk=$pkColumn.javaField.substring(0,1).toUpperCase() + ${pkColumn.javaField.substring(1)}) + if (flag) { + bo.set$pk(add.get$pk()); + } + return flag; + } + + /** + * 修改${functionName} + * + * @param bo ${functionName} + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(${ClassName}Bo bo) { + ${ClassName} update = MapstructUtils.convert(bo, ${ClassName}.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(${ClassName} entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除${functionName}信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/src/main/resources/vm/java/vo.java.vm b/src/main/resources/vm/java/vo.java.vm new file mode 100644 index 0000000..4dc9dc4 --- /dev/null +++ b/src/main/resources/vm/java/vo.java.vm @@ -0,0 +1,66 @@ +package ${packageName}.domain.vo; + +#foreach ($import in $importList) +import ${import}; +#end +import ${packageName}.domain.${ClassName}; +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * ${functionName}视图对象 ${tableName} + * + * @author shihongwei + * @date ${datetime} + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = ${ClassName}.class) +public class ${ClassName}Vo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + +#foreach ($column in $columns) +#if($column.list) + /** + * $column.columnComment + */ +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if(${column.dictType} && ${column.dictType} != '') + @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "${column.dictType}") +#elseif($parentheseIndex != -1) + @ExcelProperty(value = "${comment}", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "$column.readConverterExp()") +#else + @ExcelProperty(value = "${comment}") +#end + private $column.javaType $column.javaField; + +#if($column.htmlType == "imageUpload") + /** + * ${column.columnComment}Url + */ + @Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "${column.javaField}") + private String ${column.javaField}Url; +#end +#end +#end + +} diff --git a/src/main/resources/vm/sql/oracle/sql.vm b/src/main/resources/vm/sql/oracle/sql.vm new file mode 100644 index 0000000..f6638be --- /dev/null +++ b/src/main/resources/vm/sql/oracle/sql.vm @@ -0,0 +1,19 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 103, 1, sysdate, null, null, '${functionName}菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 103, 1, sysdate, null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 103, 1, sysdate, null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 103, 1, sysdate, null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 103, 1, sysdate, null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 103, 1, sysdate, null, null, ''); diff --git a/src/main/resources/vm/sql/postgres/sql.vm b/src/main/resources/vm/sql/postgres/sql.vm new file mode 100644 index 0000000..0923392 --- /dev/null +++ b/src/main/resources/vm/sql/postgres/sql.vm @@ -0,0 +1,20 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 103, 1, now(), null, null, '${functionName}菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 103, 1, now(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 103, 1, now(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 103, 1, now(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 103, 1, now(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 103, 1, now(), null, null, ''); + diff --git a/src/main/resources/vm/sql/sql.vm b/src/main/resources/vm/sql/sql.vm new file mode 100644 index 0000000..01824c2 --- /dev/null +++ b/src/main/resources/vm/sql/sql.vm @@ -0,0 +1,19 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 103, 1, sysdate(), null, null, '${functionName}菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 103, 1, sysdate(), null, null, ''); diff --git a/src/main/resources/vm/sql/sqlserver/sql.vm b/src/main/resources/vm/sql/sqlserver/sql.vm new file mode 100644 index 0000000..bdf166e --- /dev/null +++ b/src/main/resources/vm/sql/sqlserver/sql.vm @@ -0,0 +1,19 @@ +-- 菜单 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[0]}, '${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 103, 1, getdate(), null, null, '${functionName}菜单'); + +-- 按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 103, 1, getdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 103, 1, getdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 103, 1, getdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 103, 1, getdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 103, 1, getdate(), null, null, ''); diff --git a/src/main/resources/vm/ts/api.ts.vm b/src/main/resources/vm/ts/api.ts.vm new file mode 100644 index 0000000..3aa4a5f --- /dev/null +++ b/src/main/resources/vm/ts/api.ts.vm @@ -0,0 +1,63 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { ${BusinessName}VO, ${BusinessName}Form, ${BusinessName}Query } from '@/api/${moduleName}/${businessName}/types'; + +/** + * 查询${functionName}列表 + * @param query + * @returns {*} + */ + +export const list${BusinessName} = (query?: ${BusinessName}Query): AxiosPromise<${BusinessName}VO[]> => { + return request({ + url: '/${moduleName}/${businessName}/list', + method: 'get', + params: query + }); +}; + +/** + * 查询${functionName}详细 + * @param ${pkColumn.javaField} + */ +export const get${BusinessName} = (${pkColumn.javaField}: string | number): AxiosPromise<${BusinessName}VO> => { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'get' + }); +}; + +/** + * 新增${functionName} + * @param data + */ +export const add${BusinessName} = (data: ${BusinessName}Form) => { + return request({ + url: '/${moduleName}/${businessName}', + method: 'post', + data: data + }); +}; + +/** + * 修改${functionName} + * @param data + */ +export const update${BusinessName} = (data: ${BusinessName}Form) => { + return request({ + url: '/${moduleName}/${businessName}', + method: 'put', + data: data + }); +}; + +/** + * 删除${functionName} + * @param ${pkColumn.javaField} + */ +export const del${BusinessName} = (${pkColumn.javaField}: string | number | Array) => { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'delete' + }); +}; diff --git a/src/main/resources/vm/ts/types.ts.vm b/src/main/resources/vm/ts/types.ts.vm new file mode 100644 index 0000000..eeef5f5 --- /dev/null +++ b/src/main/resources/vm/ts/types.ts.vm @@ -0,0 +1,61 @@ +export interface ${BusinessName}VO { +#foreach ($column in $columns) +#if($column.list) + /** + * $column.columnComment + */ + $column.javaField:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) string | number; + #elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number; + #elseif($column.javaType == 'Boolean') boolean; + #else string; + #end +#if($column.htmlType == "imageUpload") + /** + * ${column.columnComment}Url + */ + ${column.javaField}Url: string; +#end +#end +#end +#if ($table.tree) + /** + * 子对象 + */ + children: ${BusinessName}VO[]; +#end +} + +export interface ${BusinessName}Form extends BaseEntity { +#foreach ($column in $columns) +#if($column.insert || $column.edit) + /** + * $column.columnComment + */ + $column.javaField?:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) string | number; + #elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number; + #elseif($column.javaType == 'Boolean') boolean; + #else string; + #end +#end +#end +} + +export interface ${BusinessName}Query #if(!${treeCode})extends PageQuery #end{ + +#foreach ($column in $columns) +#if($column.query) + /** + * $column.columnComment + */ + $column.javaField?:#if($column.javaField.indexOf("id") != -1 || $column.javaField.indexOf("Id") != -1) string | number; + #elseif($column.javaType == 'Long' || $column.javaType == 'Integer' || $column.javaType == 'Double' || $column.javaType == 'Float' || $column.javaType == 'BigDecimal') number; + #elseif($column.javaType == 'Boolean') boolean; + #else string; + #end +#end +#end + /** + * 日期范围参数 + */ + params?: any; +} diff --git a/src/main/resources/vm/vue/index-tree.vue.vm b/src/main/resources/vm/vue/index-tree.vue.vm new file mode 100644 index 0000000..fa4e05f --- /dev/null +++ b/src/main/resources/vm/vue/index-tree.vue.vm @@ -0,0 +1,499 @@ + + + diff --git a/src/main/resources/vm/vue/index.vue.vm b/src/main/resources/vm/vue/index.vue.vm new file mode 100644 index 0000000..934b9b9 --- /dev/null +++ b/src/main/resources/vm/vue/index.vue.vm @@ -0,0 +1,459 @@ + + + diff --git a/src/main/resources/vm/xml/mapper.xml.vm b/src/main/resources/vm/xml/mapper.xml.vm new file mode 100644 index 0000000..1c76ad8 --- /dev/null +++ b/src/main/resources/vm/xml/mapper.xml.vm @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/resources/上线准备.txt b/src/main/resources/上线准备.txt new file mode 100644 index 0000000..917030a --- /dev/null +++ b/src/main/resources/上线准备.txt @@ -0,0 +1,30 @@ +# 要清除的表 + +# 更改oss地址 + +# 更改nginx配置,以适配大文件上传,在http模块添加:client_max_body_size 100M; + +# nginx配置 解决h5 预览pdf问题 +http { + include mime.types; + types { + application/javascript mjs; + application/javascript mjs.map; + } + ... +} + +# 密码:123.\HOT.WJ + +账号: +lksy_lz +密码: +htwjj369 +短信宝: https://console.smsbao.com/#/index + +# 企业二维码注册链接 +register-url: http://hott.xiaoshi98.top + +# pdf预览连接 gotenberg + +# app 验真与外网地址 \ No newline at end of file diff --git a/src/main/resources/企业权限依赖配置.json b/src/main/resources/企业权限依赖配置.json new file mode 100644 index 0000000..fe463ba --- /dev/null +++ b/src/main/resources/企业权限依赖配置.json @@ -0,0 +1,13 @@ +{ + "basics": [ + "system:oss:upload", + "system:oss:query" + ], + "dependencies": { + "securityManagement:vehicleThreeInspect:list": ["driverManagement:driver:list", "resourceManagement:vehicle:list", "config:vehicleThreeInspectConfig:list", "config:signAuditPermissionConfig:list", "resourceManagement:companySafetyManager:list"], + "securityManagement:hiddenDangerPlan:list": ["config:hiddenDangerCheckConfig:list"], + "securityManagement:accidentArchive:list": ["config:accidentLevelConfig:list", "config:accidentSeverityLevelConfig:list", "config:accidentLikelihoodLevelConfig:list"], + "noticeManagerment:noticeRecord:list": ["system:user:list"] + } +} + diff --git a/src/main/resources/动态生成清理脚本.sql b/src/main/resources/动态生成清理脚本.sql new file mode 100644 index 0000000..957816e --- /dev/null +++ b/src/main/resources/动态生成清理脚本.sql @@ -0,0 +1,54 @@ +-- 动态生成清理脚本的SQL +-- 运行此查询,它会返回一系列 TRUNCATE 语句 +-- 逻辑: +-- 1. 包含所有 hot_ 开头的表 +-- 2. 包含 sys_flow_ 开头的表 +-- 3. 排除 _config 结尾的配置表 +-- 4. 排除 _template 结尾的模板表 +-- 5. 排除特定的系统/资源保留表 + +SELECT CONCAT('TRUNCATE TABLE ', TABLE_NAME, ';') AS cleanup_script +FROM information_schema.TABLES +WHERE TABLE_SCHEMA = (SELECT DATABASE()) + AND ( + -- 规则1:所有 hot_ 开头的表 + (TABLE_NAME LIKE 'hot_%' + -- 排除配置表 + AND TABLE_NAME NOT LIKE '%_config' + -- 排除模板表 + AND TABLE_NAME NOT LIKE '%_template' + -- 排除特定保留表 + AND TABLE_NAME NOT IN ( + 'hot_course_resource', + 'hot_media_resource', + 'hot_question_bank', + 'hot_exam_paper', + 'hot_system_agreement', + 'hot_law_regulation', + 'hot_vehicle_type', + 'hot_vehicle_brand_model', + 'hot_company_basic_config', + 'hot_company_dept_config', + 'hot_driver_annual_assessment_config', + 'hot_sign_audit_permission_config', + 'hot_vehicle_three_inspect_config', + 'hot_hidden_danger_inspect_store', + 'hot_hidden_danger_check_config', + 'hot_company_basic_config', + 'hot_vehicle_inspection_config', + 'hot_vehicle_secondary_maintenance_config', + 'hot_accident_likelihood_level_config', + 'hot_accident_severity_level_config', + 'hot_accident_level_config', + 'hot_risk_level_config', + 'hot_risk_score_level', + 'hot_vehicle_brand_model', + 'hot_system_template', + 'hot_file_type_config' + ) + ) + OR + -- 规则2:流程相关表 + TABLE_NAME IN ('sys_flow_instance', 'sys_flow_task', 'sys_flow_task_his') + ) +ORDER BY TABLE_NAME; diff --git a/src/main/resources/清理测试数据脚本.sql b/src/main/resources/清理测试数据脚本.sql new file mode 100644 index 0000000..404f5c7 --- /dev/null +++ b/src/main/resources/清理测试数据脚本.sql @@ -0,0 +1,22 @@ +-- 使用动态生成清理脚本的sql + + +delete from hot_system_template where company_id != 1; +delete from hot_vehicle_three_inspect_config where company_id != 1; +delete from hot_company_dept_config where company_id != 1; +delete from hot_driver_annual_assessment_config where company_id != 1; +delete from hot_hidden_danger_check_config where company_id != 1; + + +delete from sys_user where phonenumber not in ('15888888888', '15588888888'); +delete from sys_user_role where user_id not in(2011415737717006338, 1); +delete from sys_user_login_port where user_id not in(2011415737717006338, 1); + +delete +from sys_company_role; +delete +from sys_company; +delete +from hot_gov_enterprise_unit; +delete +from hot_personnel_config; diff --git a/src/main/resources/清理脚本.sql b/src/main/resources/清理脚本.sql new file mode 100644 index 0000000..b0b9aad --- /dev/null +++ b/src/main/resources/清理脚本.sql @@ -0,0 +1,83 @@ +DELIMITER +$$ + +DROP PROCEDURE IF EXISTS truncate_hot_tables $$ +CREATE PROCEDURE truncate_hot_tables() +BEGIN + DECLARE +done INT DEFAULT 0; + DECLARE +v_table_name VARCHAR(128); + + DECLARE +cur CURSOR FOR +SELECT TABLE_NAME +FROM information_schema.TABLES +WHERE TABLE_SCHEMA = DATABASE() + AND ( + ( + LEFT(TABLE_NAME, 4) = 'hot_' + AND RIGHT (TABLE_NAME, 7) <> '_config' + AND RIGHT (TABLE_NAME, 9) <> '_template' + AND TABLE_NAME NOT IN ( + 'hot_course_resource', + 'hot_media_resource', + 'hot_question_bank', + 'hot_exam_paper', + 'hot_system_agreement', + 'hot_law_regulation', + 'hot_vehicle_type', + 'hot_vehicle_brand_model', + 'hot_company_basic_config', + 'hot_company_dept_config', + 'hot_driver_annual_assessment_config', + 'hot_sign_audit_permission_config', + 'hot_vehicle_three_inspect_config', + 'hot_hidden_danger_inspect_store', + 'hot_hidden_danger_check_config', + 'hot_vehicle_inspection_config', + 'hot_vehicle_secondary_maintenance_config', + 'hot_accident_likelihood_level_config', + 'hot_accident_severity_level_config', + 'hot_accident_level_config', + 'hot_risk_level_config', + 'hot_risk_score_level', + 'hot_system_template', + 'hot_file_type_config' + ) + ) + OR TABLE_NAME IN ('sys_flow_instance', 'sys_flow_task', 'sys_flow_task_his') + ) +ORDER BY TABLE_NAME; + +DECLARE +CONTINUE HANDLER FOR NOT FOUND SET done = 1; + + SET +FOREIGN_KEY_CHECKS = 0; + +OPEN cur; +read_loop +: LOOP + FETCH cur INTO v_table_name; + IF +done = 1 THEN + LEAVE read_loop; +END IF; + + SET +@sql = CONCAT('TRUNCATE TABLE `', v_table_name, '`'); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +END LOOP; +CLOSE cur; + +SET +FOREIGN_KEY_CHECKS = 1; +END $$ + +DELIMITER ; + +CALL truncate_hot_tables(); +DROP PROCEDURE truncate_hot_tables; \ No newline at end of file diff --git a/src/test/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrServiceImplTest.java b/src/test/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrServiceImplTest.java new file mode 100644 index 0000000..3d65004 --- /dev/null +++ b/src/test/java/com/hotwj/platform/integration/ocr/baidu/BaiduOcrServiceImplTest.java @@ -0,0 +1,95 @@ +package com.hotwj.platform.integration.ocr.baidu; + +import com.hotwj.platform.config.vehicleType.mapper.HotVehicleTypeMapper; +import com.hotwj.platform.integration.ocr.baidu.domain.bo.HandwritingRecognizeBo; +import com.hotwj.platform.integration.ocr.baidu.domain.vo.HandwritingOcrVo; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.service.DictService; +import org.dromara.system.service.ISysOssService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class BaiduOcrServiceImplTest { + + @Mock + private ISysOssService ossService; + @Mock + private DictService dictService; + @Mock + private HotVehicleTypeMapper vehicleTypeMapper; + @Mock + private BaiduOcrHttpClient httpClient; + + private BaiduOcrServiceImpl service; + private BaiduOcrProperties properties; + + @BeforeEach + void setUp() { + properties = new BaiduOcrProperties(); + properties.setApiKey("ak"); + properties.setSecretKey("sk"); + properties.setTokenUrl("https://token"); + properties.setHandwritingUrl("https://handwriting"); + properties.setRetryTimes(2); + properties.setTokenTimeoutMs(2000); + properties.setHandwritingTimeoutMs(5000); + service = new BaiduOcrServiceImpl(properties, ossService, dictService, vehicleTypeMapper, httpClient); + } + + @Test + void recognizeHandwriting_containsMatch_shouldSuccess() { + when(httpClient.postForm(eq("https://token"), anyString(), anyInt())) + .thenReturn(new BaiduOcrHttpResponse(200, "{\"access_token\":\"token-1\",\"expires_in\":1800}")); + when(httpClient.postForm(startsWith("https://handwriting"), anyString(), anyInt())) + .thenReturn(new BaiduOcrHttpResponse(200, "{\"words_result_num\":2,\"words_result\":[{\"words\":\"张三\"},{\"words\":\"签名\"}]}")); + HandwritingRecognizeBo bo = new HandwritingRecognizeBo(); + bo.setImage("data:image/png;base64,aGVsbG8="); + bo.setTargetName("张三"); + bo.setMatchMode("contains"); + bo.setSaveImage(false); + HandwritingOcrVo vo = service.recognizeHandwriting(bo); + assertEquals(2, vo.getWordsResultNum()); + assertEquals("张三签名", vo.getWords()); + assertEquals(true, vo.getMatched()); + assertEquals("匹配成功", vo.getReason()); + } + + @Test + void recognizeHandwriting_serverError_shouldRetryThenSuccess() { + when(httpClient.postForm(eq("https://token"), anyString(), anyInt())) + .thenReturn(new BaiduOcrHttpResponse(200, "{\"access_token\":\"token-2\",\"expires_in\":1800}")); + when(httpClient.postForm(contains("https://handwriting"), anyString(), anyInt())) + .thenReturn(new BaiduOcrHttpResponse(500, "server error")) + .thenReturn(new BaiduOcrHttpResponse(500, "server error")) + .thenReturn(new BaiduOcrHttpResponse(200, "{\"words_result_num\":1,\"words_result\":[{\"words\":\"李四\"}]}")); + HandwritingRecognizeBo bo = new HandwritingRecognizeBo(); + bo.setImage("aGVsbG8="); + bo.setTargetName("李四"); + bo.setMatchMode("strict"); + bo.setSaveImage(false); + HandwritingOcrVo vo = service.recognizeHandwriting(bo); + assertTrue(Boolean.TRUE.equals(vo.getMatched())); + verify(httpClient, times(3)).postForm(contains("https://handwriting"), anyString(), anyInt()); + } + + @Test + void recognizeHandwriting_clientError_shouldFailWithoutRetry() { + when(httpClient.postForm(eq("https://token"), anyString(), anyInt())) + .thenReturn(new BaiduOcrHttpResponse(200, "{\"access_token\":\"token-3\",\"expires_in\":1800}")); + when(httpClient.postForm(contains("https://handwriting"), anyString(), anyInt())) + .thenReturn(new BaiduOcrHttpResponse(401, "unauthorized")); + HandwritingRecognizeBo bo = new HandwritingRecognizeBo(); + bo.setImage("aGVsbG8="); + ServiceException ex = assertThrows(ServiceException.class, () -> service.recognizeHandwriting(bo)); + assertTrue(ex.getMessage().contains("第三方调用失败")); + verify(httpClient, times(1)).postForm(contains("https://handwriting"), anyString(), anyInt()); + } +}