This commit is contained in:
2026-05-13 16:14:53 +08:00
commit e90729baae
2084 changed files with 203995 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@@ -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/

29
Dockerfile Normal file
View File

@@ -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

63
README.md Normal file
View File

@@ -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-322JWT 集成。
- 缓存与并发:`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`

139
REPORT_SYSTEM_GUIDE.md Normal file
View File

@@ -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<String, Object> prepareData(Map<String, Object> 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<String> 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<String> driverIds, List<String> 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*

43
deploy.sh Normal file
View File

@@ -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

View File

@@ -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 (培训课程学习记录表)**

99
docs/dev_specs.md Normal file
View File

@@ -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 `<table>`
* 边框合并: `border-collapse: collapse;`
* 边框线条: 统一使用细线 (`1px solid #000``#333`),避免出现粗细不一的视觉效果。
* **禁止**使用 `<div>` 模拟表格边框,防止分页截断错位。
### 2.2 图片展示 (核心优化)
* **容器**: 图片必须放置在表格单元格 (`<td>`) 内部。
* **布局方式**: 使用 Flex 布局 (`display: flex; flex-wrap: wrap;`)。
* **动态宽度逻辑** (Velocity):
* **单张图片**: 宽度设为 **98%** (占满整行),居中显示。
* **多张图片**: 宽度设为 **48%** (一行两张),保留 1% 间距。
* **样式代码参考**:
```html
<div style="display: flex; flex-wrap: wrap; justify-content: flex-start;">
#if($images.size() == 1)
#set($widthStyle = "width: 98%;")
#else
#set($widthStyle = "width: 48%;")
#end
#foreach($img in $images)
<div style="$!widthStyle height: 350px; margin: 1%; display: flex; align-items: center; justify-content: center; border: 1px solid #eee;">
<img src="$!img" style="max-width: 100%; max-height: 100%; object-fit: contain;" />
</div>
#end
</div>
```
### 2.3 水印设置
* **位置调整**: 为防止 A4 纵向打印时最后一行水印被截断Y 轴偏移量需增加。
* **代码位置**: `PdfWatermarkUtil.java`
* **参数**: `y + 100` (原为 `y + 50`)。
---
## 3. 后端接口开发规范 (Java)
### 3.1 接口定义
* **Controller**: `VehicleFileController`
* **Service**: `IVehicleFilePrintService`
* **请求方式**: `POST` (支持批量打印)
* **参数**: `List<Long> 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<String>`。
* **必须**通过 `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`
* **特殊要求**:
* **纯表格布局**: 整个页面(包括标题、信息、四类图片)全部在 `<table>` 内部。
* **标题样式**: 表格内的分类标题(如“竣工检验单”)**不需要**背景色,字体加粗居中。
* **图片布局**: 严格执行“单张全宽、多张半宽”的动态布局规则。

542
pom.xml Normal file
View File

@@ -0,0 +1,542 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.7</version>
<relativePath/>
</parent>
<groupId>com.hotwj</groupId>
<artifactId>hot-platform</artifactId>
<version>1.0.0</version>
<name>hot-backend</name>
<description>hot平台后端</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ssXXX</maven.build.timestamp.format>
<springdoc.version>2.8.13</springdoc.version>
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
<fastexcel.version>1.3.0</fastexcel.version>
<velocity.version>2.3</velocity.version>
<satoken.version>1.44.0</satoken.version>
<mybatis-plus.version>3.5.14</mybatis-plus.version>
<p6spy.version>3.9.1</p6spy.version>
<hutool.version>5.8.40</hutool.version>
<spring-boot-admin.version>3.5.5</spring-boot-admin.version>
<redisson.version>3.51.0</redisson.version>
<lock4j.version>2.2.7</lock4j.version>
<dynamic-ds.version>4.3.1</dynamic-ds.version>
<snailjob.version>1.8.0</snailjob.version>
<mapstruct-plus.version>1.5.0</mapstruct-plus.version>
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
<lombok.version>1.18.40</lombok.version>
<bouncycastle.version>1.80</bouncycastle.version>
<justauth.version>1.16.7</justauth.version>
<!-- 离线IP地址定位库 -->
<ip2region.version>2.7.0</ip2region.version>
<!-- OSS 配置 -->
<aws.sdk.version>2.28.22</aws.sdk.version>
<!-- SMS 配置 -->
<sms4j.version>3.3.5</sms4j.version>
<!-- 限制框架中的fastjson版本 -->
<fastjson.version>2.0.60</fastjson.version>
<!-- 面向运行时的D-ORM依赖 -->
<anyline.version>8.7.2-20250603</anyline.version>
<!-- 工作流配置 -->
<warm-flow.version>1.8.2</warm-flow.version>
<mysql.version>9.5.0</mysql.version>
<!-- 工具 -->
<commons-text.version>1.15.0</commons-text.version>
<!-- 插件版本 -->
<maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>
<maven-war-plugin.version>3.4.0</maven-war-plugin.version>
<maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
<maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version>
<flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
<!-- 打包默认跳过测试 -->
<skipTests>true</skipTests>
</properties>
<dependencies>
<!-- MySQL Driver -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--常用工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.18.0</version>
</dependency>
<!-- ImageIO 扩展支持CMYK JPG等更多格式 -->
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-tiff</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-webp</artifactId>
<version>3.12.0</version>
</dependency>
<!-- Hutool工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- MapStruct 工具类 -->
<dependency>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
<version>${mapstruct-plus.version}</version>
</dependency>
<!-- 离线IP地址定位库 -->
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>${ip2region.version}</version>
</dependency>
<!-- 加密工具类 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<!-- MyBatis-Plus 依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
<optional>true</optional>
</dependency>
<!-- jdk 11+ 引入可选模块 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- 快速导出Excel工具类 -->
<dependency>
<groupId>cn.idev.excel</groupId>
<artifactId>fastexcel</artifactId>
<version>${fastexcel.version}</version>
</dependency>
<!-- WebSocket 支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- SnailJob client -->
<dependency>
<groupId>com.aizuda</groupId>
<artifactId>snail-job-client-starter</artifactId>
<version>${snailjob.version}</version>
</dependency>
<!-- SnailJob 核心作业组件 -->
<dependency>
<groupId>com.aizuda</groupId>
<artifactId>snail-job-client-job-core</artifactId>
<version>${snailjob.version}</version>
</dependency>
<!-- Lombok 注解库(编译期) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!-- 邮件发送Jakarta Mail API + 实现Angus Mail -->
<dependency>
<groupId>jakarta.mail</groupId>
<artifactId>jakarta.mail-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.angus</groupId>
<artifactId>jakarta.mail</artifactId>
</dependency>
<!-- dynamic-datasource 多数据源-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
<version>${dynamic-ds.version}</version>
</dependency>
<!-- sql性能分析插件 -->
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>${p6spy.version}</version>
</dependency>
<!-- AWS SDK for Java 2.x -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>${aws.sdk.version}</version>
<exclusions>
<!-- 将基于 CRT 的 HTTP 客户端从类路径中移除 -->
<exclusion>
<groupId>software.amazon.awssdk</groupId>
<artifactId>aws-crt-client</artifactId>
</exclusion>
<!-- 将基于 Apache 的 HTTP 客户端从类路径中移除 -->
<exclusion>
<groupId>software.amazon.awssdk</groupId>
<artifactId>apache-client</artifactId>
</exclusion>
<!-- 将配置基于 URL 连接的 HTTP 客户端从类路径中移除 -->
<exclusion>
<groupId>software.amazon.awssdk</groupId>
<artifactId>url-connection-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入基于 Netty 的 HTTP 客户端 -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>netty-nio-client</artifactId>
<version>${aws.sdk.version}</version>
</dependency>
<!-- 基于 AWS CRT 的 S3 客户端的性能增强的 S3 传输管理器 -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3-transfer-manager</artifactId>
<version>${aws.sdk.version}</version>
</dependency>
<!--redisson-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>${redisson.version}</version>
</dependency>
<!-- 分布式锁Lock4j 与 Redisson 集成 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
<version>${lock4j.version}</version>
</dependency>
<!-- 本地缓存Caffeine -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<!-- Sa-Token 权限认证, 在线文档http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-starter</artifactId>
<version>${satoken.version}</version>
</dependency>
<!-- Sa-Token 整合 jwt -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jwt</artifactId>
<version>${satoken.version}</version>
</dependency>
<!-- 短信发送框架SMS4J -->
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-spring-boot-starter</artifactId>
<version>${sms4j.version}</version>
</dependency>
<!-- 第三方登录JustAuth -->
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>${justauth.version}</version>
</dependency>
<!-- SpringBoot Web容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- web 容器使用 undertow 性能更强 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- Spring Boot 监控端点Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>net.jthink</groupId>
<artifactId>jaudiotagger</artifactId>
<version>3.0.1</version>
</dependency>
<!-- mp4parser -->
<dependency>
<groupId>org.mp4parser</groupId>
<artifactId>isoparser</artifactId>
<version>1.9.41</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<!--velocity代码生成使用模板 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
</dependency>
<!-- AnyLine 与 Spring Data JDBC 适配 -->
<dependency>
<groupId>org.anyline</groupId>
<artifactId>anyline-environment-spring-data-jdbc</artifactId>
<version>${anyline.version}</version>
</dependency>
<!-- AnyLine MySQL JDBC 适配器 -->
<dependency>
<groupId>org.anyline</groupId>
<artifactId>anyline-data-jdbc-mysql</artifactId>
<version>${anyline.version}</version>
</dependency>
<!-- 工作流Warm Flow 与 MyBatis-Plus 集成 -->
<dependency>
<groupId>org.dromara.warm</groupId>
<artifactId>warm-flow-mybatis-plus-sb3-starter</artifactId>
<version>${warm-flow.version}</version>
</dependency>
<!-- 工作流Warm Flow Web 插件 UI -->
<dependency>
<groupId>org.dromara.warm</groupId>
<artifactId>warm-flow-plugin-ui-sb-web</artifactId>
<version>${warm-flow.version}</version>
</dependency>
<!-- JetBrains 注解(@NotNull 等) -->
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>24.1.0</version>
</dependency>
<!-- Json解析 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.60</version>
</dependency>
<!-- SpringDoc OpenAPI 3 (Swagger) for Spring Boot 3.x -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<!-- 拼音转换工具pinyin4j -->
<dependency>
<groupId>com.belerweb</groupId>
<artifactId>pinyin4j</artifactId>
<version>2.5.0</version>
</dependency>
<!-- 二维码生成工具 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.5.2</version>
</dependency>
<!-- PDFBox for PDF manipulation -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.31</version>
</dependency>
<!-- POI for Office documents -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>${commons-text.version}</version> <!-- 请使用最新版本 -->
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>timestamp-property</id>
<goals>
<goal>timestamp-property</goal>
</goals>
<configuration>
<name>local.build.time</name>
<pattern>yyyy-MM-dd HH:mm:ss</pattern>
<timeZone>Asia/Shanghai</timeZone>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<manifestEntries>
<Implementation-Version>${project.version}</Implementation-Version>
<Build-Time>${local.build.time}</Build-Time>
</manifestEntries>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<path>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-processor</artifactId>
<version>${mapstruct-plus.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.5.Final</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
<configuration>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
<manifestEntries>
<Implementation-Version>${project.version}</Implementation-Version>
<Build-Time>${local.build.time}</Build-Time>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<warName>${project.artifactId}</warName>
</configuration>
</plugin>
</plugins>
</build>
</project>

827
sql/1289/1.sql Normal file
View File

@@ -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 ='培训反馈回复表';

241
sql/1289/2_pay_module.sql Normal file
View File

@@ -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"}

View File

@@ -0,0 +1,2 @@
ALTER TABLE sys_user_login_port
ADD COLUMN avatar VARCHAR(500) COMMENT '头像';

View File

@@ -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 ='公司公告阅读记录';

View File

@@ -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`);

View File

@@ -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`;

View File

@@ -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`;

57
sql/flow_init.sql Normal file
View File

@@ -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='流程处理历史表';

4
sql/flow_update_read.sql Normal file
View File

@@ -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 '是否已读';

View File

@@ -0,0 +1 @@
ALTER TABLE sys_user_login_port MODIFY COLUMN avatar BIGINT COMMENT '头像(OSS ID)';

View File

@@ -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`;

View File

@@ -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`;

View File

@@ -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<List<AddressVo>> tree() {
// 查询数据库,查询所有区域树
List<AddressVo> addressVoList = publicMapper.selectList();
// 组装成树
List<AddressVo> tree = AddressVo.buildTree(addressVoList);
return R.ok(tree);
}
}

View File

@@ -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<SysUserLoginPortVo> 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<HotDriver> 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;
}
}

View File

@@ -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<AddressVo> selectList();
/**
* 单SQL透视根据6位区划代码返回“省 市 区”全名
*/
String selectRegionFullNameByCode(String code);
/**
* 根据code查询
*/
AddressVo selectAddressByCode(String code);
/**
* 根据用户ID列表查询用户名称 (逗号分隔)
*/
String selectUserNamesByIds(@org.apache.ibatis.annotations.Param("userIds") List<Long> userIds);
/**
* 根据课程ID列表查询课程名称 (逗号分隔)
*/
String selectCourseNamesByIds(@org.apache.ibatis.annotations.Param("courseIds") List<Long> courseIds);
/**
* 根据ID列表查询参会人员姓名驾驶员 + 安全管理员),按中文顿号拼接
*/
String selectAttendeeNamesByIds(@org.apache.ibatis.annotations.Param("ids") List<String> ids);
}

View File

@@ -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<PhotoEvidenceVo> 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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<HotPhotoEvidence, PhotoEvidenceVo> {
}

View File

@@ -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);
}

View File

@@ -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<String> 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();
}
}
}
}

View File

@@ -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<String, Object> dataMap);
/**
* 为指定的签订文件明细记录准备模板所需的数据映射
*
* @param detailId 签订文件明细ID (hot_sign_file_detail.id)
* @return 包含所有可替换变量的Map
*/
Map<String, Object> prepareDataMapForSignDetail(Long detailId);
}

View File

@@ -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<String, Object> dataMap) {
if (StrUtil.isBlank(templateContent)) {
return "";
}
Map<String, Object> 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<String, Object> prepareDataMapForSignDetail(Long detailId) {
Map<String, Object> 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 "<img src=\"" + url + "\" style=\"display:inline-block !important; max-height: 66px; max-width: 160px; vertical-align: middle; margin: 0 2px;\" />";
}
private String buildSealImgTag(String url) {
return "<span style=\"display:inline-block; position:relative; width:0; height:0; line-height:0; vertical-align:middle; overflow:visible;\"><img src=\"" + url + "\" style=\"position:absolute; left:-35px; top:-60px; max-height:120px; max-width:220px; z-index:9; pointer-events:none;\" /></span>";
}
}

View File

@@ -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 "";
}
}
}

View File

@@ -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);
}
}

View File

@@ -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("<br>") && !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;
}
}
}

View File

@@ -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());
}
}

View File

@@ -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<AddressVo> children;
public static List<AddressVo> buildTree(List<AddressVo> list) {
Map<String, AddressVo> map = new HashMap<>();
List<AddressVo> 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<AddressVo> 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;
}
}

View File

@@ -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<HotAccidentLevelConfigVo> 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<HotAccidentLevelConfigVo> list = hotAccidentLevelConfigService.queryList(bo);
ExcelUtil.exportExcel(list, "事故等级划分", HotAccidentLevelConfigVo.class, response);
}
/**
* 获取事故等级划分详细信息
*
* @param id 主键
*/
//@SaCheckPermission("config:accidentLevelConfig:query")
@GetMapping("/{id}")
@Operation(summary = "获取事故等级划分详细信息")
public R<HotAccidentLevelConfigVo> 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<Void> 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<Void> 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<Void> remove(@NotEmpty(message = "主键不能为空")
@Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3")
@PathVariable Long[] ids) {
return toAjax(hotAccidentLevelConfigService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<HotAccidentLevelConfig, HotAccidentLevelConfigVo> {
}

View File

@@ -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<HotAccidentLevelConfigVo> queryPageList(HotAccidentLevelConfigBo bo, PageQuery pageQuery);
/**
* 查询符合条件的事故等级划分列表
*
* @param bo 查询条件
* @return 事故等级划分列表
*/
List<HotAccidentLevelConfigVo> 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<Long> ids, Boolean isValid);
}

View File

@@ -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<HotAccidentLevelConfigVo> queryPageList(HotAccidentLevelConfigBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<HotAccidentLevelConfig> lqw = buildQueryWrapper(bo);
Page<HotAccidentLevelConfigVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的事故等级划分列表
*
* @param bo 查询条件
* @return 事故等级划分列表
*/
@Override
public List<HotAccidentLevelConfigVo> queryList(HotAccidentLevelConfigBo bo) {
LambdaQueryWrapper<HotAccidentLevelConfig> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<HotAccidentLevelConfig> buildQueryWrapper(HotAccidentLevelConfigBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<HotAccidentLevelConfig> 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<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

View File

@@ -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<HotAccidentLikelihoodLevelConfigVo> 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<HotAccidentLikelihoodLevelConfigVo> list = hotAccidentLikelihoodLevelConfigService.queryList(bo);
ExcelUtil.exportExcel(list, "事故等级划分(可能性)", HotAccidentLikelihoodLevelConfigVo.class, response);
}
/**
* 获取事故等级划分(可能性)详细信息
*
* @param id 主键
*/
//@SaCheckPermission("config:accidentLikelihoodLevelConfig:query")
@GetMapping("/{id}")
@Operation(summary = "获取事故等级划分(可能性)详细信息")
public R<HotAccidentLikelihoodLevelConfigVo> 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<Void> 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<Void> 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<Void> remove(@NotEmpty(message = "主键不能为空")
@Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3")
@PathVariable Long[] ids) {
return toAjax(hotAccidentLikelihoodLevelConfigService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<HotAccidentLikelihoodLevelConfig, HotAccidentLikelihoodLevelConfigVo> {
}

View File

@@ -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<HotAccidentLikelihoodLevelConfigVo> queryPageList(HotAccidentLikelihoodLevelConfigBo bo, PageQuery pageQuery);
/**
* 查询符合条件的事故等级划分(可能性)列表
*
* @param bo 查询条件
* @return 事故等级划分(可能性)列表
*/
List<HotAccidentLikelihoodLevelConfigVo> 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<Long> ids, Boolean isValid);
}

View File

@@ -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<HotAccidentLikelihoodLevelConfigVo> queryPageList(HotAccidentLikelihoodLevelConfigBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<HotAccidentLikelihoodLevelConfig> lqw = buildQueryWrapper(bo);
Page<HotAccidentLikelihoodLevelConfigVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的事故等级划分(可能性)列表
*
* @param bo 查询条件
* @return 事故等级划分(可能性)列表
*/
@Override
public List<HotAccidentLikelihoodLevelConfigVo> queryList(HotAccidentLikelihoodLevelConfigBo bo) {
LambdaQueryWrapper<HotAccidentLikelihoodLevelConfig> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<HotAccidentLikelihoodLevelConfig> buildQueryWrapper(HotAccidentLikelihoodLevelConfigBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<HotAccidentLikelihoodLevelConfig> 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<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

View File

@@ -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<HotAccidentSeverityLevelConfigVo> 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<HotAccidentSeverityLevelConfigVo> list = hotAccidentSeverityLevelConfigService.queryList(bo);
ExcelUtil.exportExcel(list, "事故等级划分(严重程度)", HotAccidentSeverityLevelConfigVo.class, response);
}
/**
* 获取事故等级划分(严重程度)详细信息
*
* @param id 主键
*/
//@SaCheckPermission("config:accidentSeverityLevelConfig:query")
@GetMapping("/{id}")
@Operation(summary = "获取事故等级划分(严重程度)详细信息")
public R<HotAccidentSeverityLevelConfigVo> 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<Void> 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<Void> 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<Void> remove(@NotEmpty(message = "主键不能为空")
@Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3")
@PathVariable Long[] ids) {
return toAjax(hotAccidentSeverityLevelConfigService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<HotAccidentSeverityLevelConfig, HotAccidentSeverityLevelConfigVo> {
}

View File

@@ -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<HotAccidentSeverityLevelConfigVo> queryPageList(HotAccidentSeverityLevelConfigBo bo, PageQuery pageQuery);
/**
* 查询符合条件的事故等级划分(严重程度)列表
*
* @param bo 查询条件
* @return 事故等级划分(严重程度)列表
*/
List<HotAccidentSeverityLevelConfigVo> 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<Long> ids, Boolean isValid);
}

View File

@@ -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<HotAccidentSeverityLevelConfigVo> queryPageList(HotAccidentSeverityLevelConfigBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<HotAccidentSeverityLevelConfig> lqw = buildQueryWrapper(bo);
Page<HotAccidentSeverityLevelConfigVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的事故等级划分(严重程度)列表
*
* @param bo 查询条件
* @return 事故等级划分(严重程度)列表
*/
@Override
public List<HotAccidentSeverityLevelConfigVo> queryList(HotAccidentSeverityLevelConfigBo bo) {
LambdaQueryWrapper<HotAccidentSeverityLevelConfig> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<HotAccidentSeverityLevelConfig> buildQueryWrapper(HotAccidentSeverityLevelConfigBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<HotAccidentSeverityLevelConfig> 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<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

View File

@@ -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<HotCompanyBasicConfigVo> 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<HotCompanyBasicConfigVo> list = hotCompanyBasicConfigService.queryList(bo);
ExcelUtil.exportExcel(list, "企业基础配置", HotCompanyBasicConfigVo.class, response);
}
/**
* 获取企业基础配置详细信息
*
* @param id 主键
*/
//@SaCheckPermission("config:companyBasicConfig:query")
@GetMapping("/{id}")
@Operation(summary = "获取企业基础配置详细信息")
public R<HotCompanyBasicConfigVo> 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<Void> 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<Void> 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<Void> remove(@NotEmpty(message = "主键不能为空")
@Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3")
@PathVariable Long[] ids) {
return toAjax(hotCompanyBasicConfigService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<HotCompanyBasicConfig, HotCompanyBasicConfigVo> {
}

View File

@@ -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<HotCompanyBasicConfigVo> queryPageList(HotCompanyBasicConfigBo bo, PageQuery pageQuery);
/**
* 查询符合条件的企业基础配置列表
*
* @param bo 查询条件
* @return 企业基础配置列表
*/
List<HotCompanyBasicConfigVo> 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<Long> ids, Boolean isValid);
}

View File

@@ -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<HotCompanyBasicConfigVo> queryPageList(HotCompanyBasicConfigBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<HotCompanyBasicConfig> lqw = buildQueryWrapper(bo);
Page<HotCompanyBasicConfigVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的企业基础配置列表
*
* @param bo 查询条件
* @return 企业基础配置列表
*/
@Override
public List<HotCompanyBasicConfigVo> queryList(HotCompanyBasicConfigBo bo) {
LambdaQueryWrapper<HotCompanyBasicConfig> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<HotCompanyBasicConfig> buildQueryWrapper(HotCompanyBasicConfigBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<HotCompanyBasicConfig> 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<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
//通知
private void notifyForbidPeriod(Long companyId, String startTime, String endTime) {
List<HotCompanySafetyManager> managers = managerMapper.selectList(
Wrappers.<HotCompanySafetyManager>lambdaQuery()
.eq(HotCompanySafetyManager::getCompanyId, companyId)
.eq(HotCompanySafetyManager::getIsDeleted, 0L)
.eq(HotCompanySafetyManager::getIsResigned, 0L)
.eq(HotCompanySafetyManager::getStatus, 1L)
);
List<HotDriver> drivers = driverMapper.selectList(
Wrappers.<HotDriver>lambdaQuery()
.eq(HotDriver::getCompanyId, companyId)
.eq(HotDriver::getIsDeleted, 0L)
.eq(HotDriver::getStatus, 1L)
);
String content = "企业已配置禁学期间:" + startTime + "" + endTime + ",期间禁止学习。";
if (managers != null && !managers.isEmpty()) {
List<String> 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<String> 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);
}
}
}
}

View File

@@ -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<HotCompanyDeptConfigVo> 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<HotCompanyDeptConfigVo> list = hotCompanyDeptConfigService.queryList(bo);
ExcelUtil.exportExcel(list, "企业部门配置", HotCompanyDeptConfigVo.class, response);
}
/**
* 获取企业部门配置详细信息
*
* @param id 主键
*/
//@SaCheckPermission("config:companyDeptConfig:query")
@GetMapping("/{id}")
@Operation(summary = "获取企业部门配置详细信息")
public R<HotCompanyDeptConfigVo> 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<Void> 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<Void> 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<Void> remove(@NotEmpty(message = "主键不能为空")
@Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3")
@PathVariable Long[] ids) {
return toAjax(hotCompanyDeptConfigService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<HotCompanyDeptConfig, HotCompanyDeptConfigVo> {
}

View File

@@ -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<HotCompanyDeptConfigVo> queryPageList(HotCompanyDeptConfigBo bo, PageQuery pageQuery);
/**
* 查询符合条件的企业部门配置列表
*
* @param bo 查询条件
* @return 企业部门配置列表
*/
List<HotCompanyDeptConfigVo> 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<Long> ids, Boolean isValid);
}

View File

@@ -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<HotCompanyDeptConfigVo> queryPageList(HotCompanyDeptConfigBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<HotCompanyDeptConfig> lqw = buildQueryWrapper(bo);
Page<HotCompanyDeptConfigVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的企业部门配置列表
*
* @param bo 查询条件
* @return 企业部门配置列表
*/
@Override
public List<HotCompanyDeptConfigVo> queryList(HotCompanyDeptConfigBo bo) {
LambdaQueryWrapper<HotCompanyDeptConfig> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<HotCompanyDeptConfig> buildQueryWrapper(HotCompanyDeptConfigBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<HotCompanyDeptConfig> 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<Long> ids, Boolean isValid) {
if (isValid) {
Long count = safetyManagerMapper.selectCount(Wrappers.<HotCompanySafetyManager>lambdaQuery()
.in(HotCompanySafetyManager::getDeptId, ids)
.eq(HotCompanySafetyManager::getIsResigned, 0L));
if (count > 0) {
throw new ServiceException("该部门有未离职人员不可删除");
}
}
return baseMapper.deleteByIds(ids) > 0;
}
}

View File

@@ -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<HotCourseVo> 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<HotCourseVo> list = hotCourseService.queryList(bo);
ExcelUtil.exportExcel(list, "课程库", HotCourseVo.class, response);
}
/**
* 获取课程库详细信息
*
* @param id 主键
*/
//@SaCheckPermission("config:course:query")
@GetMapping("/{id}")
@Operation(summary = "获取课程库详细信息")
public R<HotCourseVo> 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<Void> 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<Void> 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<Void> remove(@NotEmpty(message = "主键不能为空")
@Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3")
@PathVariable Long[] ids) {
return toAjax(hotCourseService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<HotCourse, HotCourseVo> {
}

View File

@@ -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<HotCourseVo> queryPageList(HotCourseBo bo, PageQuery pageQuery);
/**
* 查询符合条件的课程库列表
*
* @param bo 查询条件
* @return 课程库列表
*/
List<HotCourseVo> 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<Long> ids, Boolean isValid);
}

View File

@@ -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<HotCourseVo> queryPageList(HotCourseBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<HotCourse> lqw = buildQueryWrapper(bo);
Page<HotCourseVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的课程库列表
*
* @param bo 查询条件
* @return 课程库列表
*/
@Override
public List<HotCourseVo> queryList(HotCourseBo bo) {
LambdaQueryWrapper<HotCourse> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<HotCourse> buildQueryWrapper(HotCourseBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<HotCourse> 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<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

View File

@@ -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<HotCourseResourceVo> 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<HotCourseResourceVo> list = hotCourseResourceService.queryList(bo);
ExcelUtil.exportExcel(list, "课程-资源关联", HotCourseResourceVo.class, response);
}
/**
* 获取课程-资源关联详细信息
*
* @param id 主键
*/
//@SaCheckPermission("config:courseResource:query")
@GetMapping("/{id}")
@Operation(summary = "获取课程-资源关联详细信息")
public R<HotCourseResourceVo> 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<Void> 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<Void> 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<Void> remove(@NotEmpty(message = "主键不能为空")
@Parameter(name = "ids", description = "主键串", required = true, example = "1,2,3")
@PathVariable Long[] ids) {
return toAjax(hotCourseResourceService.deleteWithValidByIds(List.of(ids), true));
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<HotCourseResource, HotCourseResourceVo> {
}

View File

@@ -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<HotCourseResourceVo> queryPageList(HotCourseResourceBo bo, PageQuery pageQuery);
/**
* 查询符合条件的课程-资源关联列表
*
* @param bo 查询条件
* @return 课程-资源关联列表
*/
List<HotCourseResourceVo> 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<Long> ids, Boolean isValid);
}

View File

@@ -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<HotCourseResourceVo> queryPageList(HotCourseResourceBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<HotCourseResource> lqw = buildQueryWrapper(bo);
Page<HotCourseResourceVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的课程-资源关联列表
*
* @param bo 查询条件
* @return 课程-资源关联列表
*/
@Override
public List<HotCourseResourceVo> queryList(HotCourseResourceBo bo) {
LambdaQueryWrapper<HotCourseResource> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<HotCourseResource> buildQueryWrapper(HotCourseResourceBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<HotCourseResource> 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<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

View File

@@ -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<HotDispatchTemplateVo> 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<HotDispatchTemplateVo> list = hotDispatchTemplateService.queryList(bo);
ExcelUtil.exportExcel(list, "下发模板", HotDispatchTemplateVo.class, response);
}
/**
* 获取下发模板详细信息
*
* @param id 主键
*/
//@SaCheckPermission("config:dispatchTemplate:query")
@GetMapping("/{id}")
@Operation(summary = "获取下发模板详细信息")
public R<HotDispatchTemplateVo> 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<Void> 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<Void> 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<Void> 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<Void> useDefault() {
return toAjax(hotDispatchTemplateService.useDefault());
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<HotDispatchTemplate, HotDispatchTemplateVo> {
}

View File

@@ -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<HotDispatchTemplateVo> queryPageList(HotDispatchTemplateBo bo, PageQuery pageQuery);
/**
* 查询符合条件的下发模板列表
*
* @param bo 查询条件
* @return 下发模板列表
*/
List<HotDispatchTemplateVo> 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<Long> ids, Boolean isValid);
Boolean useDefault();
}

View File

@@ -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<HotDispatchTemplateVo> queryPageList(HotDispatchTemplateBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<HotDispatchTemplate> lqw = buildQueryWrapper(bo);
Page<HotDispatchTemplateVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的下发模板列表
*
* @param bo 查询条件
* @return 下发模板列表
*/
@Override
public List<HotDispatchTemplateVo> queryList(HotDispatchTemplateBo bo) {
LambdaQueryWrapper<HotDispatchTemplate> 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<HotDispatchTemplate> defaultConfigs = baseMapper.selectList(
Wrappers.lambdaQuery(HotDispatchTemplate.class)
.eq(HotDispatchTemplate::getCompanyId, 1L)
);
if (CollUtil.isEmpty(defaultConfigs)) {
throw new ServiceException("默认配置不存在,请联系管理员");
}
List<HotDispatchTemplate> 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<HotDispatchTemplate> buildQueryWrapper(HotDispatchTemplateBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<HotDispatchTemplate> 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<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

View File

@@ -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<HotDriverAnnualAssessmentConfigVo> 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<HotDriverAnnualAssessmentConfigVo> list = hotDriverAnnualAssessmentConfigService.queryList(bo);
ExcelUtil.exportExcel(list, "驾驶员年度考核配置", HotDriverAnnualAssessmentConfigVo.class, response);
}
/**
* 获取驾驶员年度考核配置详细信息
*
* @param id 主键
*/
//@SaCheckPermission("config:driverAnnualAssessmentConfig:query")
@GetMapping("/{id}")
public R<HotDriverAnnualAssessmentConfigVo> 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<Void> 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<Void> 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<Void> 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<Void> useDefault() {
return toAjax(hotDriverAnnualAssessmentConfigService.useDefault());
}
/**
* 一键清空
*/
//@SaCheckPermission("config:driverAnnualAssessmentConfig:remove")
@Log(title = "驾驶员年度考核配置", businessType = BusinessType.DELETE)
@RepeatSubmit()
@PostMapping("/clearAll")
public R<Void> clearAll() {
return toAjax(hotDriverAnnualAssessmentConfigService.clearAll());
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<HotDriverAnnualAssessmentConfig, HotDriverAnnualAssessmentConfigVo> {
}

View File

@@ -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<HotDriverAnnualAssessmentConfigVo> queryPageList(HotDriverAnnualAssessmentConfigBo bo, PageQuery pageQuery);
/**
* 查询符合条件的驾驶员年度考核配置列表
*
* @param bo 查询条件
* @return 驾驶员年度考核配置列表
*/
List<HotDriverAnnualAssessmentConfigVo> 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<Long> ids, Boolean isValid);
/**
* 采用默认配置
*
* @return 结果
*/
Boolean useDefault();
/**
* 清空当前公司所有配置
*
* @return 是否成功
*/
Boolean clearAll();
}

View File

@@ -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<HotDriverAnnualAssessmentConfig> defaultConfigs = baseMapper.selectList(
Wrappers.lambdaQuery(HotDriverAnnualAssessmentConfig.class)
.eq(HotDriverAnnualAssessmentConfig::getCompanyId, 1L)
);
if (CollUtil.isEmpty(defaultConfigs)) {
throw new ServiceException("默认配置不存在,请联系管理员");
}
List<HotDriverAnnualAssessmentConfig> 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<HotDriverAnnualAssessmentConfigVo> queryPageList(HotDriverAnnualAssessmentConfigBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<HotDriverAnnualAssessmentConfig> lqw = buildQueryWrapper(bo);
Page<HotDriverAnnualAssessmentConfigVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的驾驶员年度考核配置列表
*
* @param bo 查询条件
* @return 驾驶员年度考核配置列表
*/
@Override
public List<HotDriverAnnualAssessmentConfigVo> queryList(HotDriverAnnualAssessmentConfigBo bo) {
LambdaQueryWrapper<HotDriverAnnualAssessmentConfig> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<HotDriverAnnualAssessmentConfig> buildQueryWrapper(HotDriverAnnualAssessmentConfigBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<HotDriverAnnualAssessmentConfig> 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<HotDriverAnnualAssessmentConfig> lqw = Wrappers.lambdaQuery();
lqw.eq(HotDriverAnnualAssessmentConfig::getCompanyId, companyId);
lqw.eq(HotDriverAnnualAssessmentConfig::getIsEnabled, ENABLED_STATUS);
lqw.ne(excludeId != null, HotDriverAnnualAssessmentConfig::getId, excludeId);
List<HotDriverAnnualAssessmentConfig> 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<Long> 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;
}
}

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