4362 lines
165 KiB
Markdown
4362 lines
165 KiB
Markdown
# EMS 后端业务流程与架构深度解析
|
||
|
||
本文档旨在深入剖析应急管理系统(EMS)后端的核心业务流程、分层架构以及关键技术机制。通过对典型请求生命周期的分析,揭示系统各组件如何协同工作,以实现高效、可靠的业务功能。
|
||
在您的项目中,主要有以下几类常见的注解:
|
||
|
||
### 1. JPA (Java Persistence API) 注解 - 用于数据库交互
|
||
这类注解通常用在 model 或 entity 包下的类中,用于将一个Java对象映射到数据库的一张表。
|
||
|
||
@Entity : 声明这个类是一个实体类,它对应数据库中的一张表。
|
||
@Table(name = "aqi_data") : 正如您在 `AqiData.java` 中看到的,这个注解用来指定该实体类对应数据库中的具体表名。在这里, AqiData 类对应 aqi_data 这张表。
|
||
@Id : 指定这个字段是表的主键。
|
||
@Column(name = "user_id") : 指定字段对应数据库表中的列名。
|
||
@GeneratedValue : 定义主键的生成策略(例如,自增长)。
|
||
### 2. Spring 框架注解 - 用于管理组件和处理请求
|
||
这是项目中最核心的注解,用于实现依赖注入(DI)和面向切面编程(AOP)。
|
||
|
||
@Component : 通用的组件注解,表明这个类由 Spring 容器管理。
|
||
@Service : 通常用在业务逻辑层(Service层),是 @Component 的一种特化。
|
||
@Repository : 通常用在数据访问层(DAO/Repository层),也是 @Component 的一种特化。
|
||
@RestController : 用在控制器(Controller)上,表明这个类处理HTTP请求,并且返回的数据会自动序列化为JSON格式。
|
||
@Autowired : 自动注入依赖。Spring会自动寻找匹配的Bean(由 @Component 等注解标记的类的实例)并注入到该字段。
|
||
@RequestMapping , @GetMapping , @PostMapping : 将HTTP请求的URL路径映射到具体的处理方法上。
|
||
### 3. Lombok 注解 - 用于减少样板代码
|
||
Lombok是一个非常实用的Java库,可以通过注解在编译时自动生成代码。
|
||
|
||
@Data : 一个非常方便的组合注解,会自动为类的所有字段生成 getter 、 setter 方法,以及 toString() , equals() , hashCode() 方法。
|
||
@NoArgsConstructor : 生成一个无参构造函数。
|
||
@AllArgsConstructor : 生成一个包含所有字段的构造函数。
|
||
@Slf4j : 为类自动创建一个 log 静态常量,方便您直接使用 log.info(...) 等方法打印日志。
|
||
|
||
## 一、 系统核心思想与分层架构
|
||
|
||
EMS 后端遵循业界主流的**分层架构**设计,旨在实现**高内聚、低耦合**的目标。这种架构将应用程序划分为不同的逻辑层,每一层都承担明确的职责。各层之间通过定义好的接口进行通信,使得系统更易于开发、测试、维护和扩展。
|
||
|
||
|
||
### 1.1 分层架构概览
|
||
|
||
系统从上至下主要分为以下几个核心层级,对应于项目中的不同包:
|
||
|
||
- **Controller (控制层)**: `com.dne.ems.controller`
|
||
- **职责**: 作为应用的入口,负责接收来自客户端(前端)的 HTTP 请求,解析请求参数,并调用服务层的相应方法来处理业务逻辑。它不包含任何业务逻辑,仅作为请求的调度和转发中心。
|
||
- **交互**: 将处理结果(通常是 DTO 对象)封装成统一的 JSON 格式(`ApiResponse`)返回给客户端。
|
||
|
||
- **Service (服务层)**: `com.dne.ems.service`
|
||
- **职责**: 系统的核心,负责实现所有复杂的业务逻辑。它编排一个或多个 Repository 来完成特定功能,处理数据、执行计算、实施业务规则,并处理事务。
|
||
- **交互**: 被 Controller 层调用,同时调用 Repository 层来访问数据库。
|
||
|
||
- **Repository (数据访问层)**: `com.dne.ems.repository`
|
||
- **职责**: 定义数据持久化的接口,负责与数据库进行交互。通过继承 Spring Data JPA 的 `JpaRepository`,开发者无需编写具体的 SQL 语句即可实现对数据的增删改查(CRUD)以及复杂的查询。
|
||
- **交互**: 被 Service 层调用,直接操作数据库。
|
||
|
||
- **Model (模型层)**: `com.dne.ems.model`
|
||
- **职责**: 定义系统的领域对象,即与数据库表结构一一对应的实体类(Entity)。它们使用 JPA 注解(如 `@Entity`, `@Table`, `@Id`)来映射数据库表和字段。
|
||
- **交互**: 在 Repository 层和 Service 层之间传递数据。
|
||
|
||
- **DTO (数据传输对象)**: `com.dne.ems.dto`
|
||
- **职责**: 作为各层之间,特别是 Controller 层与外部客户端之间的数据载体。DTO 的设计可以根据 API 的需要进行定制,隐藏内部领域模型的复杂性,避免暴露不必要的数据,从而提高系统的安全性与灵活性。
|
||
|
||
### 1.2 辅助模块
|
||
|
||
除了核心分层,系统还包含一系列重要的辅助模块,以支持整体功能的实现:
|
||
|
||
- **Security (安全模块)**: `com.dne.ems.security`
|
||
- **职责**: 基于 Spring Security 实现用户认证(Authentication)和授权(Authorization)。它包含 JWT 的生成与校验、密码加密、以及精细化的接口访问控制策略。
|
||
|
||
- **Exception (异常处理模块)**: `com.dne.ems.exception`
|
||
- **职责**: 提供全局统一的异常处理机制。通过 `@ControllerAdvice`,系统可以捕获在业务流程中抛出的各类异常,并将其转换为对客户端友好的、标准化的错误响应。
|
||
|
||
- **Config (配置模块)**: `com.dne.ems.config`
|
||
- **职责**: 存放应用的配置类,如 Spring Security 配置、Web MVC 配置、以及应用启动时的数据初始化逻辑(`DataInitializer`)。该模块为EMS系统提供了完整的基础设施支持,包括数据初始化、文件存储配置、API文档配置、HTTP客户端配置、WebSocket配置以及数据转换器等核心配置组件。
|
||
|
||
- **Controller (控制器模块)**: `com.dne.ems.controller`
|
||
- **职责**: 作为系统的API入口层,负责处理所有HTTP请求,包括用户认证、反馈管理、任务分配、数据可视化等核心业务功能。该模块包含14个专业化控制器,每个控制器专注于特定的业务领域,提供RESTful API接口,支持权限控制、参数验证、分页查询等功能。
|
||
|
||
- **Event/Listener (事件驱动模块)**: `com.dne.ems.event`, `com.dne.ems.listener`
|
||
- **职责**: 实现应用内的异步处理和解耦。例如,用户认证失败时,可以发布一个 `AuthenticationFailureEvent` 事件,由专门的监听器 `AuthenticationFailureEventListener` 异步处理登录失败次数的记录,而不会阻塞主认证流程。
|
||
|
||
## 二、 项目亮点与创新
|
||
|
||
本项目在传统的城市管理系统基础上,融合了多项现代技术与创新理念,旨在打造一个**智能、高效、协同**的城市环境治理平台。其核心亮点体现在以下几个方面:
|
||
|
||
### 2.1 业务全流程智能化
|
||
|
||
- **AI 预审核**: 公众提交的反馈信息不再需要人工进行初步筛选。系统集成的 AI 模型能够自动对反馈内容(包括文本和图像)进行分析,判断其有效性、紧急程度,并进行初步的分类。这极大地减轻了主管人员的工作负担,并将他们的精力解放出来,专注于更复杂的决策和任务分配。
|
||
- **智能任务推荐与调度**: 在任务分配环节,系统能够基于网格员的当前位置、历史任务完成效率、技能特长以及当前负载情况,智能推荐最合适的执行人选。在紧急情况下,系统甚至可以自动进行任务的调度与指派,确保问题得到最快响应。
|
||
|
||
### 2.2 事件驱动的异步架构
|
||
|
||
- **松耦合与高响应**: 项目广泛采用了**事件驱动架构 (EDA)**。例如,当一个反馈提交后,系统会发布一个 `FeedbackSubmittedEvent` 事件,后续的 AI 分析、通知发送等多个操作都由独立的监听器异步执行。这种设计使得核心业务流程(如提交反馈)能够瞬间完成,极大地提升了用户体验和系统的吞吐量。同时,模块之间通过事件进行通信,而不是直接调用,实现了高度解耦,使得系统更容易扩展和维护。
|
||
|
||
### 2.3 全方位的数据驱动决策
|
||
|
||
- **实时数据大屏**: 决策者不再依赖于滞后的纸质报告。系统提供了一个动态、可交互的数据驾驶舱,实时展示城市环境的各项关键指标(KPIs),如 AQI 分布、任务完成率、反馈热力图等。所有数据都以直观的图表形式呈现,帮助管理者快速洞察问题、发现趋势,从而做出更科学的决策。
|
||
- **绩效与资源优化**: 系统通过对历史数据的深度分析,能够评估不同区域的环境治理成效、各个网格员的工作效率,为绩效考核提供客观依据,并为未来的资源调配和政策制定提供数据支持。
|
||
|
||
### 2.4 四端一体的协同工作平台
|
||
|
||
- **角色精准赋能**: 系统为四类核心用户——**公众、网格员、主管、决策者**——分别设计了专属的客户端(Web 或移动应用),精准满足不同角色的工作需求。
|
||
- **公众端**: 简化提交流程,提供实时进度查询。
|
||
- **网格员端**: 移动化接单、处理、上报,并提供导航支持。
|
||
- **主管端**: 集中审核、智能分配、实时监控。
|
||
- **决策者端**: 宏观数据洞察,支持战略决策。
|
||
- **无缝信息流转**: 通过统一的后端平台,信息可以在四个客户端之间无缝流转,形成从问题发现、任务分配、现场处理到决策分析的闭环管理,打破了传统工作模式中的信息孤岛。
|
||
|
||
## 三、 核心业务流程与关键机制深度解析
|
||
|
||
下面,我们将以具体的业务场景和技术实现为例,详细拆解请求在系统分层架构中的流转过程。
|
||
|
||
### 2.1 用户认证与授权
|
||
|
||
此流程是系统的安全基石,涉及用户登录、权限验证和 Token 管理。
|
||
|
||
#### API 接口
|
||
|
||
- **用户注册**: `POST /api/auth/signup`
|
||
- **用户登录**: `POST /api/auth/login`
|
||
- **发送验证码**: `POST /api/auth/send-verification-code`
|
||
- **请求重置密码**: `POST /api/auth/request-password-reset`
|
||
- **重置密码**: `POST /api/auth/reset-password`
|
||
|
||
**场景:用户使用用户名和密码登录 (POST /api/auth/login)**
|
||
|
||
1. **前端 -> Controller (`AuthController`)**
|
||
- **动作**: 用户在登录页输入凭据,前端发送 `POST` 请求到 `/api/auth/login`,请求体为包含 `username` 和 `password` 的 `LoginRequest` DTO。
|
||
- **Controller 角色**: `AuthController` 的 `authenticateUser` 方法接收请求。`@Valid` 注解确保了输入的基本格式正确。Controller 将 `LoginRequest` 直接传递给 `AuthService` 进行处理。
|
||
|
||
2. **Controller -> Service (`AuthService`)**
|
||
- **动作**: `AuthService` 调用 Spring Security 的 `AuthenticationManager` 的 `authenticate` 方法。`AuthenticationManager` 是认证的核心入口。
|
||
- **Service 角色**: `AuthService` 编排了整个认证流程:
|
||
- **凭证封装**: 将用户名和密码封装成 `UsernamePasswordAuthenticationToken`。
|
||
- **认证委托**: 将此 `token` 交给 `AuthenticationManager`。`AuthenticationManager` 会遍历其内部的 `AuthenticationProvider` 列表(在我们的例子中是 `DaoAuthenticationProvider`)来寻找能处理此 `token` 的提供者。
|
||
- **用户查找**: `DaoAuthenticationProvider` 会使用 `UserDetailsService` (我们的实现是 `UserDetailsServiceImpl`) 的 `loadUserByUsername` 方法,根据用户名去数据库查找用户。`UserDetailsServiceImpl` 会调用 `UserRepository`。
|
||
- **密码比对**: `DaoAuthenticationProvider` 使用 `PasswordEncoder` (我们配置的 `BCryptPasswordEncoder`) 比较用户提交的密码和数据库中存储的加密哈希值。
|
||
- **生成 JWT**: 如果认证成功,`AuthService` 调用 `JwtTokenProvider` 的 `generateToken` 方法,为认证通过的 `Authentication` 对象生成一个 JWT。
|
||
|
||
3. **Service -> Repository (`UserRepository`)**
|
||
- **动作**: `UserDetailsServiceImpl` 调用 `userRepository.findByUsername(username)`。
|
||
- **Repository 角色**: `UserRepository` 执行 `SELECT` 查询,从 `users` 表中找到对应的用户记录,并将其封装成 `User` 实体对象返回。
|
||
|
||
4. **安全组件交互 (`JwtTokenProvider`, `UserDetailsServiceImpl`)**
|
||
- `UserDetailsServiceImpl`: 实现了 Spring Security 的 `UserDetailsService` 接口,是连接应用用户数据和 Spring Security 框架的桥梁。
|
||
- `JwtTokenProvider`: 负责 JWT 的生成和解析。`generateToken` 方法会设置主题(用户名)、签发时间、过期时间,并使用预设的密钥和算法(HS512)进行签名。
|
||
|
||
5. **Controller -> 前端**
|
||
- **动作**: `AuthService` 返回生成的 JWT。`AuthController` 将此 JWT 包装在 `JwtAuthenticationResponse` DTO 中,再放入 `ApiResponse.success()`,以 HTTP 200 状态码返回给前端。
|
||
- **后续请求**: 前端在后续所有需要认证的请求的 `Authorization` 头中携带此 JWT (`Bearer <token>`)。Spring Security 的 `JwtAuthenticationFilter` 会在每个请求到达 Controller 前拦截并验证此 Token。
|
||
|
||
### 2.2 公众反馈管理
|
||
|
||
#### API 接口
|
||
|
||
- **提交反馈 (JSON)**: `POST /api/feedback/submit-json`
|
||
- **提交反馈 (Multipart)**: `POST /api/feedback/submit`
|
||
- **获取所有反馈**: `GET /api/feedback`
|
||
- **公众提交反馈**: `POST /api/public/feedback`
|
||
|
||
**场景:公众提交带图片的反馈 (POST /api/public/feedback)**
|
||
|
||
1. **前端 -> Controller (`PublicController`)**
|
||
- **动作**: 公众用户填写反馈表单(描述、位置、图片),前端以 `multipart/form-data` 格式提交到 `/api/public/feedback`。
|
||
- **Controller 角色**: `createFeedback` 方法使用 `@RequestPart` 分别接收反馈内容的 DTO (`FeedbackCreateDTO`) 和文件 (`MultipartFile`)。`@Valid` 确保 DTO 数据合规。Controller 随即调用 `FeedbackService`。
|
||
|
||
2. **Controller -> Service (`FeedbackService` & `FileStorageService`)**
|
||
- **动作**: `FeedbackService` 的 `createFeedback` 方法被调用。
|
||
- **Service 角色**:
|
||
- **文件处理**: 如果 `file` 存在,`FeedbackService` 首先调用 `FileStorageService` 的 `storeFile` 方法。`FileStorageService` 会生成唯一文件名,将文件存储在服务器的 `uploads` 目录下,并返回可访问的文件路径。
|
||
- **实体创建**: `FeedbackService` 创建一个新的 `Feedback` 实体。
|
||
- **数据映射**: 使用 `ModelMapper` 将 `FeedbackCreateDTO` 的数据(如 `description`, `location`)映射到 `Feedback` 实体上。
|
||
- **关联文件**: 将 `FileStorageService` 返回的文件路径设置到 `Feedback` 实体的 `imageUrl` 字段。
|
||
- **状态设定**: 设置反馈的初始状态为 `PENDING`。
|
||
- **持久化**: 调用 `feedbackRepository.save(newFeedback)`。
|
||
|
||
3. **Service -> Repository (`FeedbackRepository`)**
|
||
- **动作**: `feedbackRepository.save()` 执行 `INSERT` SQL 语句,将新的反馈记录存入 `feedbacks` 表。
|
||
|
||
4. **Controller -> 前端**
|
||
- **动作**: `FeedbackService` 返回成功创建的 `Feedback` 对象的 DTO。`PublicController` 将其包装在 `ApiResponse.success()` 中,以 HTTP 201 (Created) 状态码返回,表示资源创建成功。
|
||
|
||
### 2.3 任务生命周期管理
|
||
|
||
#### API 接口
|
||
|
||
- **获取待审核的反馈列表**: `GET /api/supervisor/reviews`
|
||
- **批准反馈**: `POST /api/supervisor/reviews/{feedbackId}/approve`
|
||
- **拒绝反馈**: `POST /api/supervisor/reviews/{feedbackId}/reject`
|
||
- **获取未分配的反馈**: `GET /api/tasks/unassigned`
|
||
- **获取可用的网格员**: `GET /api/tasks/grid-workers`
|
||
- **分配任务**: `POST /api/tasks/assign`
|
||
- **获取任务列表**: `GET /api/management/tasks`
|
||
- **分配任务**: `POST /api/management/tasks/{taskId}/assign`
|
||
- **获取任务详情**: `GET /api/management/tasks/{taskId}`
|
||
- **审核任务**: `POST /api/management/tasks/{taskId}/review`
|
||
- **取消任务**: `POST /api/management/tasks/{taskId}/cancel`
|
||
- **直接创建任务**: `POST /api/management/tasks`
|
||
- **获取待处理的反馈**: `GET /api/management/tasks/feedback`
|
||
- **从反馈创建任务**: `POST /api/management/tasks/feedback/{feedbackId}/create-task`
|
||
- **获取我的任务**: `GET /api/worker`
|
||
- **获取任务详情**: `GET /api/worker/{taskId}`
|
||
- **接受任务**: `POST /api/worker/{taskId}/accept`
|
||
- **提交任务**: `POST /api/worker/{taskId}/submit`
|
||
|
||
**场景:主管审核反馈并转换为任务 (POST /api/supervisor/feedback/{feedbackId}/convert)**
|
||
|
||
1. **前端 -> Controller (`SupervisorController`)**
|
||
- **动作**: 主管在管理界面查看一条反馈,点击“转换为任务”按钮。前端发送 `POST` 请求到 `/api/supervisor/feedback/456/convert`,请求体中可能包含指派信息 `TaskFromFeedbackDTO`。
|
||
- **Controller 角色**: `convertFeedbackToTask` 方法接收请求,解析 `feedbackId` 和 DTO,然后调用 `TaskService`。
|
||
|
||
2. **Controller -> Service (`TaskService` & `FeedbackService`)**
|
||
- **动作**: `TaskService` 的 `createTaskFromFeedback` 方法被调用。
|
||
- **Service 角色**: 这是一个跨领域服务的协调过程。
|
||
- **获取反馈**: 调用 `feedbackRepository.findById(feedbackId)` 获取原始反馈信息。如果不存在,抛出 `ResourceNotFoundException`。
|
||
- **状态检查**: 检查反馈是否已经是“已处理”状态,防止重复转换。
|
||
- **创建任务实体**: 创建一个新的 `Task` 实体。
|
||
- **数据迁移**: 将 `Feedback` 的 `description`, `location` 等信息复制到 `Task` 实体中。
|
||
- **设置任务属性**: 根据 `TaskFromFeedbackDTO` 设置任务的标题、截止日期、指派的网格员(如果提供了 `workerId`)。如果未指派,任务状态为 `PENDING`;如果已指派,状态为 `ASSIGNED`。
|
||
- **持久化任务**: 调用 `taskRepository.save(newTask)`。
|
||
- **更新反馈状态**: 更新 `Feedback` 实体的状态为 `PROCESSED`,并调用 `feedbackRepository.save(feedback)`。
|
||
- **事务管理**: 整个方法应该被 `@Transactional` 注解标记,确保创建任务和更新反馈状态这两个操作要么同时成功,要么同时失败。
|
||
|
||
3. **Service -> Repository (`TaskRepository`, `FeedbackRepository`)**
|
||
- **动作**: `TaskRepository` 插入一条新任务记录。`FeedbackRepository` 更新对应的反馈记录状态。
|
||
|
||
4. **Controller -> 前端**
|
||
- **动作**: `TaskService` 返回新创建的任务的 DTO。`SupervisorController` 将其包装在 `ApiResponse.success()` 中返回,前端可以根据返回的任务信息进行页面跳转或状态更新。
|
||
|
||
### 2.4 数据可视化与决策支持
|
||
|
||
#### API 接口
|
||
|
||
- **获取仪表盘统计数据**: `GET /api/dashboard/stats`
|
||
- **获取 AQI 分布**: `GET /api/dashboard/reports/aqi-distribution`
|
||
- **获取污染超标月度趋势**: `GET /api/dashboard/reports/pollution-trend`
|
||
- **获取网格覆盖率**: `GET /api/dashboard/reports/grid-coverage`
|
||
- **获取反馈热力图数据**: `GET /api/dashboard/map/heatmap`
|
||
- **获取污染类型统计**: `GET /api/dashboard/reports/pollution-stats`
|
||
- **获取任务完成情况统计**: `GET /api/dashboard/reports/task-completion-stats`
|
||
- **获取 AQI 热力图数据**: `GET /api/dashboard/map/aqi-heatmap`
|
||
|
||
### 2.5 智能分配算法
|
||
|
||
#### API 接口
|
||
|
||
- **获取任务的推荐人选**: `GET /api/tasks/{taskId}/recommendations`
|
||
|
||
**场景:主管为新任务寻求系统推荐的执行人 (GET /api/tasks/123/recommendations)**
|
||
|
||
1. **前端 -> Controller (`TaskManagementController`)**
|
||
- **动作**: 主管在任务分配界面,点击“推荐人选”按钮。前端向 `/api/tasks/123/recommendations` 发送 `GET` 请求。
|
||
- **Controller 角色**: `getTaskRecommendations` 方法接收请求,并调用 `TaskRecommendationService`。
|
||
|
||
2. **Controller -> Service (`TaskRecommendationService`)**
|
||
- **动作**: `TaskRecommendationService` 的 `recommendWorkersForTask` 方法被调用。
|
||
- **Service 角色**: 这是智能分配算法的核心实现。
|
||
- **获取任务信息**: 首先,从 `taskRepository` 获取任务详情,特别是任务的地理位置和类型。
|
||
- **筛选候选人**: 从 `userRepository` 获取所有角色为“网格员”且状态为“可用”的用户列表。
|
||
- **为每位候选人评分**: 遍历候选人列表,根据一系列预设标准计算综合得分。这是一个典型的加权评分模型:
|
||
- **距离 (40%)**: 计算网格员当前位置与任务位置的直线距离。距离越近,得分越高。`Score = (MaxDistance - WorkerDistance) / MaxDistance`。
|
||
- **当前负载 (30%)**: 查询该网格员当前状态为 `ASSIGNED` 或 `IN_PROGRESS` 的任务数量。任务越少,得分越高。`Score = (MaxLoad - WorkerLoad) / MaxLoad`。
|
||
- **技能匹配度 (20%)**: 如果任务有特定的技能要求(如“管道维修”),检查网格员的技能标签。匹配则得满分,否则得0分。
|
||
- **历史表现 (10%)**: 查询该网格员过去完成类似任务的平均耗时和评价。表现越好,得分越高。
|
||
- **加权总分**: `Total Score = Score_Distance * 0.4 + Score_Load * 0.3 + Score_Skill * 0.2 + Score_Performance * 0.1`。
|
||
- **排序**: 根据总分对所有候选人进行降序排序。
|
||
|
||
3. **Service -> Repository (Multiple Repositories)**
|
||
- **动作**: `TaskRecommendationService` 会与 `TaskRepository`, `UserRepository`, `WorkerLocationRepository` (假设有这样一个实时位置库) 等多个 Repository 交互,以获取计算所需的数据。
|
||
|
||
4. **Controller -> 前端**
|
||
- **动作**: `TaskRecommendationService` 返回一个包含网格员信息和他们各自推荐分数的 DTO 列表。`TaskManagementController` 将此列表包装在 `ApiResponse.success()` 中返回。前端界面会高亮显示得分最高的几位候选人,并附上推荐理由(如“距离最近”,“负载最低”),辅助主管做出最终决策。
|
||
|
||
**场景:决策者查看仪表盘 (GET /api/dashboard)**
|
||
|
||
1. **前端 -> Controller (`DashboardController`)**
|
||
- **动作**: 决策者访问仪表盘页面,前端发送 `GET` 请求到 `/api/dashboard`。
|
||
- **Controller 角色**: `getDashboardData` 方法直接调用 `DashboardService`。
|
||
|
||
2. **Controller -> Service (`DashboardService`)**
|
||
- **动作**: `DashboardService` 的 `getDashboardData` 方法被调用。
|
||
- **Service 角色**: 这是一个典型的数据聚合服务。
|
||
- **并行/串行查询**: `DashboardService` 会向多个 Repository 发起查询请求,以收集构建仪表盘所需的所有数据。
|
||
- **任务统计**: 调用 `taskRepository` 的自定义聚合查询方法(如 `countByStatus`)来获取不同状态的任务数。
|
||
- **反馈统计**: 调用 `feedbackRepository.countByStatus` 获取不同状态的反馈数。
|
||
- **人员概览**: 调用 `personnelRepository.count()` 获取总人数。
|
||
- **AQI 数据**: 调用 `aqiDataRepository.findLatest()` 获取最新的空气质量数据。
|
||
- **数据组装**: 将所有查询到的零散数据,组装成一个专为前端仪表盘设计的 `DashboardDTO` 对象。
|
||
|
||
3. **Service -> Repository (多个)**
|
||
- **动作**: `TaskRepository`, `FeedbackRepository`, `PersonnelRepository` 等分别执行高效的数据库聚合查询(如 `SELECT status, COUNT(*) FROM tasks GROUP BY status`)。
|
||
- **Repository 角色**: Repository 接口中定义了这些自定义查询方法,使用 `@Query` 注解编写 JPQL 或原生 SQL,直接利用数据库的计算能力,避免在应用服务层进行大量数据处理。
|
||
|
||
4. **Controller -> 前端**
|
||
- **动作**: `DashboardService` 返回填充了完整数据的 `DashboardDTO`。`DashboardController` 将其包装在 `ApiResponse.success()` 中,以 HTTP 200 状态码返回。前端收到 JSON 数据后,使用图表库(如 ECharts)进行渲染。
|
||
|
||
### 2.5 网格与人员管理
|
||
|
||
#### API 接口
|
||
|
||
- **获取网格列表**: `GET /api/grids`
|
||
- **更新网格信息**: `PATCH /api/grids/{gridId}`
|
||
- **创建用户**: `POST /api/personnel/users`
|
||
- **获取用户列表**: `GET /api/personnel/users`
|
||
- **获取单个用户**: `GET /api/personnel/users/{userId}`
|
||
- **更新用户信息**: `PATCH /api/personnel/users/{userId}`
|
||
- **更新用户角色**: `PUT /api/personnel/users/{userId}/role`
|
||
- **删除用户**: `DELETE /api/personnel/users/{userId}`
|
||
- **更新个人资料**: `PATCH /api/me`
|
||
- **更新我的位置**: `POST /api/me/location`
|
||
- **获取我的反馈历史**: `GET /api/me/feedback`
|
||
|
||
**场景:管理员分页查询所有人员信息 (GET /api/management/personnel?page=0&size=10)**
|
||
|
||
1. **前端 -> Controller (`ManagementController`)**
|
||
- **动作**: 管理员在人员管理页面,前端发送带分页参数的 `GET` 请求。
|
||
- **Controller 角色**: `getAllPersonnel` 方法使用 `@RequestParam` 接收分页参数,并将其封装成 `Pageable` 对象,然后调用 `PersonnelService`。
|
||
|
||
2. **Controller -> Service (`PersonnelService`)**
|
||
- **动作**: `PersonnelService` 的 `findAll` 方法接收 `Pageable` 对象。
|
||
- **Service 角色**: 直接将 `Pageable` 对象传递给 `PersonnelRepository`。对于更复杂的查询(例如,按姓名或角色搜索),Service 层会接收额外的查询参数,并调用 `Specification` 或 `QueryDSL` 来构建动态查询。
|
||
|
||
3. **Service -> Repository (`PersonnelRepository`)**
|
||
- **动作**: `personnelRepository.findAll(pageable)` 被调用。
|
||
- **Repository 角色**: Spring Data JPA 会根据 `Pageable` 参数自动生成带有分页和排序的 SQL 查询(如 `SELECT ... FROM personnel LIMIT ? OFFSET ?`),并执行。它返回一个 `Page<Personnel>` 对象,其中包含了当前页的数据列表、总页数、总元素数量等完整的的分页信息。
|
||
|
||
4. **Controller -> 前端**
|
||
- **动作**: `PersonnelService` 返回 `Page<Personnel>` 对象。`ManagementController` 将其直接放入 `ApiResponse.success()` 中返回。前端根据返回的 `Page` 对象渲染数据列表和分页控件。
|
||
|
||
### 2.6 AI 辅助功能
|
||
|
||
**场景:AI 辅助生成任务报告摘要 (POST /api/ai/summarize)**
|
||
|
||
1. **前端 -> Controller (`AiController`)**
|
||
- **动作**: 用户在任务详情页输入或粘贴长篇报告文本,点击“生成摘要”。前端将文本内容包装在 `SummarizeRequest` DTO 中,发送 `POST` 请求。
|
||
- **Controller 角色**: `summarizeText` 方法接收 DTO,并调用 `AiService`。
|
||
|
||
2. **Controller -> Service (`AiService`)**
|
||
- **动作**: `AiService` 的 `getSummary` 方法被调用。
|
||
- **Service 角色**: 这是与外部 AI 服务交互的核心。
|
||
- **构建请求**: 根据所使用的 AI 服务(如 OpenAI, Google Gemini, 或其他私有化部署模型)的 API 规范,构建 HTTP 请求。这通常涉及设置 API Key、请求头、以及包含待摘要文本的 JSON 请求体。
|
||
- **API 调用**: 使用 `RestTemplate` 或 `WebClient` 向 AI 服务的 API 端点发送请求。
|
||
- **解析响应**: 接收 AI 服务返回的 JSON 响应,并从中提取出生成的摘要文本。
|
||
- **异常处理**: 处理网络超时、API 认证失败、或 AI 服务返回错误码等异常情况。
|
||
|
||
3. **Controller -> 前端**
|
||
- **动作**: `AiService` 返回提取出的摘要字符串。`AiController` 将其包装在 `ApiResponse.success()` 中返回给前端,前端将摘要显示在页面上。
|
||
|
||
### 2.7 关键技术机制深度解析
|
||
|
||
#### A. 文件上传与管理 (`FileStorageService`)
|
||
- **流程**: `Controller` 接收 `MultipartFile` -> `Service` 调用 `FileStorageService.storeFile()` -> `FileStorageService` 生成唯一文件名 (e.g., `UUID.randomUUID().toString()`) 并拼接原始文件扩展名 -> 使用 `StringUtils.cleanPath` 清理文件名防止路径遍历攻击 -> 构建目标存储路径 `Path targetLocation = this.fileStorageLocation.resolve(fileName)` -> 使用 `Files.copy` 将文件输入流写入目标路径,并使用 `StandardCopyOption.REPLACE_EXISTING` 覆盖同名文件 -> 返回通过 `ServletUriComponentsBuilder` 构建的、可从外部访问的文件下载 URI。
|
||
- **关键点**:
|
||
- **配置化**: 文件存储路径 (`file.upload-dir`) 在 `application.properties` 中配置,具有灵活性。
|
||
- **安全性**: 明确处理了 `FileNameContainsInvalidCharactersException` 和 `FileStorageException`。
|
||
- **资源服务**: 提供了 `loadFileAsResource` 方法,可以将存储的文件作为 `Resource` 对象加载,以便于下载。
|
||
|
||
### 2.8 地图与路径规划
|
||
|
||
#### A. 地图服务 (`MapService`)
|
||
- **获取完整地图网格**: `GET /api/map/grid`
|
||
- **创建或更新地图单元格**: `POST /api/map/grid`
|
||
- **初始化地图**: `POST /api/map/initialize`
|
||
|
||
#### B. 路径规划服务 (`PathfindingService`)
|
||
- **寻找路径**: `POST /api/pathfinding/find`
|
||
|
||
#### B. 文件服务 (`FileService`)
|
||
- **下载文件**: `GET /api/files/download/{fileName}`
|
||
- **查看文件**: `GET /api/files/view/{fileName}`
|
||
|
||
#### C. 邮件与验证码服务 (`EmailService`)
|
||
- **流程**: `AuthService` 调用 `EmailService.sendVerificationCode(email)` -> `EmailService` 使用 `Random` 生成6位数字验证码 -> 将 `email:code` 键值对存入 `verificationCodeCache` (一个 `Caffeine` 缓存实例),并设置5分钟过期 -> 使用 `JavaMailSender` 创建 `SimpleMailMessage`,设置收件人、主题和正文 -> `mailSender.send(message)` 发送邮件。
|
||
|
||
## 三、API 接口文档
|
||
|
||
本部分详细列出了系统提供的所有API接口,包括其功能描述、请求路径、方法、所需权限以及请求/响应示例。
|
||
|
||
### 1. 认证模块 (Auth)
|
||
**基础路径**: `/api/auth`
|
||
|
||
#### 1.1 用户注册
|
||
- **功能描述**: 注册一个新用户。
|
||
- **请求路径**: `/api/auth/signup`
|
||
- **请求方法**: `POST`
|
||
- **所需权限**: 无
|
||
- **请求体**: `SignUpRequest`
|
||
|
||
#### 1.2 用户登录
|
||
- **功能描述**: 用户登录并获取 JWT Token。
|
||
- **请求路径**: `/api/auth/login`
|
||
- **请求方法**: `POST`
|
||
- **所需权限**: 无
|
||
- **请求体**: `LoginRequest`
|
||
|
||
#### 1.3 发送验证码
|
||
- **功能描述**: 请求发送验证码到指定邮箱。
|
||
- **请求路径**: `/api/auth/send-verification-code`
|
||
- **请求方法**: `POST`
|
||
- **所需权限**: 无
|
||
- **查询参数**: `email` (string, 必需)
|
||
|
||
#### 1.4 请求重置密码
|
||
- **功能描述**: 请求发送重置密码的链接或令牌。
|
||
- **请求路径**: `/api/auth/request-password-reset`
|
||
- **请求方法**: `POST`
|
||
- **所需权限**: 无
|
||
- **请求体**: `PasswordResetRequest`
|
||
|
||
#### 1.5 重置密码
|
||
- **功能描述**: 使用令牌重置密码。
|
||
- **请求路径**: `/api/auth/reset-password`
|
||
- **请求方法**: `POST`
|
||
- **所需权限**: 无
|
||
- **请求体**: `PasswordResetDto`
|
||
|
||
### 2. 仪表盘模块 (Dashboard)
|
||
**基础路径**: `/api/dashboard`
|
||
**所需权限**: `DECISION_MAKER`
|
||
|
||
#### 2.1 获取仪表盘统计数据
|
||
- **请求路径**: `/api/dashboard/stats`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 2.2 获取 AQI 分布
|
||
- **请求路径**: `/api/dashboard/reports/aqi-distribution`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 2.3 获取污染超标月度趋势
|
||
- **请求路径**: `/api/dashboard/reports/pollution-trend`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 2.4 获取网格覆盖率
|
||
- **请求路径**: `/api/dashboard/reports/grid-coverage`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 2.5 获取反馈热力图数据
|
||
- **请求路径**: `/api/dashboard/map/heatmap`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 2.6 获取污染类型统计
|
||
- **请求路径**: `/api/dashboard/reports/pollution-stats`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 2.7 获取任务完成情况统计
|
||
- **请求路径**: `/api/dashboard/reports/task-completion-stats`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 2.8 获取 AQI 热力图数据
|
||
- **请求路径**: `/api/dashboard/map/aqi-heatmap`
|
||
- **请求方法**: `GET`
|
||
|
||
### 3. 反馈模块 (Feedback)
|
||
**基础路径**: `/api/feedback`
|
||
|
||
#### 3.1 提交反馈 (JSON)
|
||
- **功能描述**: 用于测试,使用 JSON 提交反馈。
|
||
- **请求路径**: `/api/feedback/submit-json`
|
||
- **请求方法**: `POST`
|
||
- **所需权限**: 已认证用户
|
||
- **请求体**: `FeedbackSubmissionRequest`
|
||
|
||
#### 3.2 提交反馈 (Multipart)
|
||
- **功能描述**: 提交反馈,可包含文件。
|
||
- **请求路径**: `/api/feedback/submit`
|
||
- **请求方法**: `POST`
|
||
- **所需权限**: 已认证用户
|
||
- **请求体**: `multipart/form-data`
|
||
|
||
#### 3.3 获取所有反馈
|
||
- **功能描述**: 获取所有反馈(分页)。
|
||
- **请求路径**: `/api/feedback`
|
||
- **请求方法**: `GET`
|
||
- **所需权限**: `ADMIN`
|
||
|
||
### 4. 文件模块 (File)
|
||
**基础路径**: `/api/files`
|
||
**所需权限**: 公开访问
|
||
|
||
#### 4.1 下载文件
|
||
- **请求路径**: `/api/files/download/{fileName}`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 4.2 查看文件
|
||
- **请求路径**: `/api/files/view/{fileName}`
|
||
- **请求方法**: `GET`
|
||
|
||
### 5. 网格模块 (Grid)
|
||
**基础路径**: `/api/grids`
|
||
**所需权限**: `ADMIN` 或 `DECISION_MAKER`
|
||
|
||
#### 5.1 获取网格列表
|
||
- **请求路径**: `/api/grids`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 5.2 更新网格信息
|
||
- **请求路径**: `/api/grids/{gridId}`
|
||
- **请求方法**: `PATCH`
|
||
- **所需权限**: `ADMIN`
|
||
|
||
### 6. 网格员任务模块 (Worker)
|
||
**基础路径**: `/api/worker`
|
||
**所需权限**: `GRID_WORKER`
|
||
|
||
#### 6.1 获取我的任务
|
||
- **请求路径**: `/api/worker`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 6.2 获取任务详情
|
||
- **请求路径**: `/api/worker/{taskId}`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 6.3 接受任务
|
||
- **请求路径**: `/api/worker/{taskId}/accept`
|
||
- **请求方法**: `POST`
|
||
|
||
#### 6.4 提交任务
|
||
- **请求路径**: `/api/worker/{taskId}/submit`
|
||
- **请求方法**: `POST`
|
||
|
||
### 7. 地图模块 (Map)
|
||
**基础路径**: `/api/map`
|
||
**所需权限**: 公开访问
|
||
|
||
#### 7.1 获取完整地图网格
|
||
- **请求路径**: `/api/map/grid`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 7.2 创建或更新地图单元格
|
||
- **请求路径**: `/api/map/grid`
|
||
- **请求方法**: `POST`
|
||
|
||
#### 7.3 初始化地图
|
||
- **请求路径**: `/api/map/initialize`
|
||
- **请求方法**: `POST`
|
||
|
||
### 8. 路径规划模块 (Pathfinding)
|
||
**基础路径**: `/api/pathfinding`
|
||
**所需权限**: 公开访问
|
||
|
||
#### 8.1 寻找路径
|
||
- **功能描述**: 使用 A* 算法在两点之间寻找最短路径。
|
||
- **请求路径**: `/api/pathfinding/find`
|
||
- **请求方法**: `POST`
|
||
|
||
### 9. 人员管理模块 (Personnel)
|
||
**基础路径**: `/api/personnel`
|
||
**所需权限**: `ADMIN`
|
||
|
||
#### 9.1 创建用户
|
||
- **请求路径**: `/api/personnel/users`
|
||
- **请求方法**: `POST`
|
||
|
||
#### 9.2 获取用户列表
|
||
- **请求路径**: `/api/personnel/users`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 9.3 获取单个用户
|
||
- **请求路径**: `/api/personnel/users/{userId}`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 9.4 更新用户信息
|
||
- **请求路径**: `/api/personnel/users/{userId}`
|
||
- **请求方法**: `PATCH`
|
||
|
||
#### 9.5 更新用户角色
|
||
- **请求路径**: `/api/personnel/users/{userId}/role`
|
||
- **请求方法**: `PUT`
|
||
|
||
#### 9.6 删除用户
|
||
- **请求路径**: `/api/personnel/users/{userId}`
|
||
- **请求方法**: `DELETE`
|
||
|
||
### 10. 个人资料模块 (Me)
|
||
**基础路径**: `/api/me`
|
||
**所需权限**: 已认证用户
|
||
|
||
#### 10.1 更新个人资料
|
||
- **请求路径**: `/api/me`
|
||
- **请求方法**: `PATCH`
|
||
|
||
#### 10.2 更新我的位置
|
||
- **请求路径**: `/api/me/location`
|
||
- **请求方法**: `POST`
|
||
|
||
#### 10.3 获取我的反馈历史
|
||
- **请求路径**: `/api/me/feedback`
|
||
- **请求方法**: `GET`
|
||
|
||
### 11. 公共接口模块 (Public)
|
||
**基础路径**: `/api/public`
|
||
**所需权限**: 公开访问
|
||
|
||
#### 11.1 公众提交反馈
|
||
- **请求路径**: `/api/public/feedback`
|
||
- **请求方法**: `POST`
|
||
|
||
### 12. 主管模块 (Supervisor)
|
||
**基础路径**: `/api/supervisor`
|
||
**所需权限**: `SUPERVISOR` 或 `ADMIN`
|
||
|
||
#### 12.1 获取待审核的反馈列表
|
||
- **请求路径**: `/api/supervisor/reviews`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 12.2 批准反馈
|
||
- **请求路径**: `/api/supervisor/reviews/{feedbackId}/approve`
|
||
- **请求方法**: `POST`
|
||
|
||
#### 12.3 拒绝反馈
|
||
- **请求路径**: `/api/supervisor/reviews/{feedbackId}/reject`
|
||
- **请求方法**: `POST`
|
||
|
||
### 13. 任务分配模块 (Tasks)
|
||
**基础路径**: `/api/tasks`
|
||
**所需权限**: `ADMIN` 或 `SUPERVISOR`
|
||
|
||
#### 13.1 获取未分配的反馈
|
||
- **请求路径**: `/api/tasks/unassigned`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 13.2 获取可用的网格员
|
||
- **请求路径**: `/api/tasks/grid-workers`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 13.3 分配任务
|
||
- **请求路径**: `/api/tasks/assign`
|
||
- **请求方法**: `POST`
|
||
|
||
### 14. 任务管理模块 (Management)
|
||
**基础路径**: `/api/management/tasks`
|
||
**所需权限**: `SUPERVISOR`
|
||
|
||
#### 14.1 获取任务列表
|
||
- **请求路径**: `/api/management/tasks`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 14.2 分配任务
|
||
- **请求路径**: `/api/management/tasks/{taskId}/assign`
|
||
- **请求方法**: `POST`
|
||
|
||
#### 14.3 获取任务详情
|
||
- **请求路径**: `/api/management/tasks/{taskId}`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 14.4 审核任务
|
||
- **请求路径**: `/api/management/tasks/{taskId}/review`
|
||
- **请求方法**: `POST`
|
||
|
||
#### 14.5 取消任务
|
||
- **请求路径**: `/api/management/tasks/{taskId}/cancel`
|
||
- **请求方法**: `POST`
|
||
|
||
#### 14.6 直接创建任务
|
||
- **请求路径**: `/api/management/tasks`
|
||
- **请求方法**: `POST`
|
||
|
||
#### 14.7 获取待处理的反馈
|
||
- **请求路径**: `/api/management/tasks/feedback`
|
||
- **请求方法**: `GET`
|
||
|
||
#### 14.8 从反馈创建任务
|
||
- **请求路径**: `/api/management/tasks/feedback/{feedbackId}/create-task`
|
||
- **请求方法**: `POST`
|
||
- **验证流程**: 用户提交验证码 -> `AuthService` 调用 `EmailService.verifyCode(email, code)` -> `EmailService` 从缓存中通过 `verificationCodeCache.getIfPresent(email)` 获取验证码,并与用户提交的 `code` 进行比对。如果匹配且存在,则验证成功并从缓存中移除该验证码 (`cache.invalidate(email)`)。
|
||
|
||
#### C. 登录安全与尝试锁定 (`AuthenticationFailureEventListener`)
|
||
- **流程**: 用户登录失败 -> Spring Security 的 `ProviderManager` 认证失败后,会发布一个 `AuthenticationFailureBadCredentialsEvent` 事件 -> `AuthenticationFailureEventListener` (一个实现了 `ApplicationListener` 的组件) 的 `onApplicationEvent` 方法被触发 -> 监听器从事件中获取用户名 -> 调用 `LoginAttemptService.loginFailed(username)` -> `LoginAttemptService` 使用 `Caffeine` 缓存 `attemptsCache` 来记录失败次数。每次失败,计数器加一。
|
||
- **锁定逻辑**: `LoginAttemptService` 的 `isBlocked(username)` 方法检查失败次数是否超过最大允许次数(如 `MAX_ATTEMPT = 5`)。在 `UserDetailsServiceImpl` 的 `loadUserByUsername` 方法的开头,会先调用 `loginAttemptService.isBlocked()`。如果用户已被锁定,则直接抛出 `LockedException`,阻止后续的数据库查询和密码比对,实现登录锁定。
|
||
|
||
#### D. A* 路径规划 (`PathfindingService`)
|
||
- **流程**: `PathfindingController` 调用 `PathfindingService.findPath()` -> `PathfindingService` 初始化一个 `Grid` 对象,该对象包含地图的宽度、高度和障碍物位置集合 -> A* 算法的核心 `AStar.findPath()` 被调用,它接收 `Grid` 和起止点 `Node` 作为参数 -> 算法内部使用 `PriorityQueue` 作为开放列表(优先处理f值最低的节点),使用 `HashSet` 作为关闭列表 -> 循环开始:从开放列表取出当前节点,若为终点则通过 `retracePath` 回溯构建路径 -> 否则,遍历当前节点的邻居节点,计算其 g值(到起点的代价)和 h值(到终点的预估代价,即启发式函数),若邻居节点是更优路径,则更新其代价和父节点,并加入开放列表。
|
||
- **关键点**:
|
||
- **数据结构**: `Node` 对象包含了坐标、g/h/f代价值、父节点引用和是否为障碍物等信息。
|
||
- **启发式函数**: 使用曼哈顿距离 `getDistance(nodeA, nodeB)` 作为启发式函数,适用于网格地图。
|
||
- **可扩展性**: `Grid` 可以从数据库、文件或动态生成,使得路径规划可以适应不同的地图场景。
|
||
|
||
## 四、配置模块详细解析
|
||
|
||
配置模块(`com.dne.ems.config`)是EMS系统的基础设施核心,为整个应用提供了完整的配置支持和初始化功能。本节将详细解析各个配置组件的功能、实现原理和使用方式。
|
||
|
||
### 4.1 数据初始化配置 (`DataInitializer.java`)
|
||
|
||
#### 功能概述
|
||
`DataInitializer` 是系统启动时的数据初始化组件,实现了 `CommandLineRunner` 接口,确保在Spring Boot应用完全启动后自动执行数据初始化逻辑。
|
||
|
||
#### 核心特性
|
||
- **自动执行**: 实现 `CommandLineRunner` 接口,在应用启动后自动运行
|
||
- **幂等性**: 检查数据是否已存在,避免重复创建
|
||
- **完整性**: 创建系统运行所需的所有基础数据
|
||
- **日志记录**: 包含详细的初始化过程日志
|
||
|
||
#### 初始化内容详解
|
||
|
||
**1. 用户账户初始化**
|
||
- 创建五种角色的测试账户:管理员、网格员、决策者、主管、公众监督员
|
||
- 使用BCrypt加密存储密码
|
||
- 为每个角色分配相应的权限和属性
|
||
|
||
**默认账户信息**:
|
||
```
|
||
管理员: admin@aizhangz.top / Admin@123
|
||
网格员: worker@aizhangz.top / Worker@123
|
||
决策者: decision.maker@aizhangz.top / Decision@123
|
||
主管: supervisor@aizhangz.top / Supervisor@123
|
||
公众监督员: public.supervisor@aizhangz.top / Public@123
|
||
```
|
||
|
||
**2. 网格数据初始化**
|
||
- 生成40x40的完整城市网格系统
|
||
- 覆盖4个主要城市:常州市、无锡市、苏州市、南京市
|
||
- 每个网格单元包含坐标、城市归属、区域信息
|
||
- 支持网格员分配和区域管理
|
||
|
||
**3. 地图网格同步**
|
||
- 将业务网格数据同步到寻路网格
|
||
- 支持A*寻路算法的地图表示
|
||
- 处理障碍物和可通行区域的标记
|
||
|
||
**4. AQI数据初始化**
|
||
- 创建空气质量指数的历史数据
|
||
- 生成实时AQI监测数据
|
||
- 为数据可视化和分析提供基础数据
|
||
|
||
**5. 反馈和任务数据**
|
||
- 创建示例环境问题反馈
|
||
- 初始化任务分配记录
|
||
- 建立完整的业务流程测试数据
|
||
|
||
#### 技术实现要点
|
||
```java
|
||
@Component
|
||
@Slf4j
|
||
public class DataInitializer implements CommandLineRunner {
|
||
|
||
@Override
|
||
public void run(String... args) throws Exception {
|
||
// 检查数据是否已初始化
|
||
if (isDataAlreadyInitialized()) {
|
||
log.info("数据已初始化,跳过初始化过程");
|
||
return;
|
||
}
|
||
|
||
// 执行各项初始化
|
||
initializeUsers();
|
||
initializeGrids();
|
||
initializeAqiData();
|
||
initializeFeedbacks();
|
||
initializeTasks();
|
||
|
||
log.info("数据初始化完成");
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4.2 文件存储配置 (`FileStorageProperties.java`)
|
||
|
||
#### 功能概述
|
||
`FileStorageProperties` 管理文件上传存储的相关配置,使用Spring Boot的配置属性绑定机制。
|
||
|
||
#### 配置属性
|
||
- `file.upload-dir`: 指定上传文件的存储目录
|
||
- 支持相对路径和绝对路径
|
||
- 可通过环境变量或配置文件动态配置
|
||
|
||
#### 使用方式
|
||
**application.yml配置示例**:
|
||
```yaml
|
||
file:
|
||
upload-dir: ./uploads
|
||
# 或使用绝对路径
|
||
# upload-dir: /var/app/uploads
|
||
```
|
||
|
||
**Java配置类**:
|
||
```java
|
||
@ConfigurationProperties(prefix = "file")
|
||
@Data
|
||
public class FileStorageProperties {
|
||
private String uploadDir;
|
||
}
|
||
```
|
||
|
||
### 4.3 OpenAPI配置 (`OpenApiConfig.java`)
|
||
|
||
#### 功能概述
|
||
配置Swagger API文档和JWT认证,为开发和测试提供完整的API文档界面。
|
||
|
||
#### 主要特性
|
||
- **API文档生成**: 自动生成完整的REST API文档
|
||
- **JWT认证集成**: 支持在Swagger UI中进行JWT认证测试
|
||
- **安全配置**: 为所有API端点添加安全要求
|
||
- **在线测试**: 提供交互式API测试界面
|
||
|
||
#### 访问方式
|
||
- Swagger UI: `http://localhost:8080/swagger-ui.html`
|
||
- API文档JSON: `http://localhost:8080/v3/api-docs`
|
||
|
||
#### 安全配置详解
|
||
```java
|
||
@Configuration
|
||
public class OpenApiConfig {
|
||
|
||
@Bean
|
||
public OpenAPI customOpenAPI() {
|
||
return new OpenAPI()
|
||
.info(new Info()
|
||
.title("EMS API")
|
||
.version("1.0")
|
||
.description("环境监测系统API文档"))
|
||
.addSecurityItem(new SecurityRequirement().addList("Bearer Authentication"))
|
||
.components(new Components()
|
||
.addSecuritySchemes("Bearer Authentication",
|
||
new SecurityScheme()
|
||
.type(SecurityScheme.Type.HTTP)
|
||
.scheme("bearer")
|
||
.bearerFormat("JWT")));
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4.4 RestTemplate配置 (`RestTemplateConfig.java`)
|
||
|
||
#### 功能概述
|
||
提供HTTP客户端功能,用于与外部服务进行同步通信。
|
||
|
||
#### 主要用途
|
||
- **外部API集成**: 调用第三方服务API
|
||
- **微服务通信**: 在分布式架构中进行服务间调用
|
||
- **数据同步**: 从外部数据源获取信息
|
||
- **第三方认证**: 集成外部认证服务
|
||
|
||
#### 配置示例
|
||
```java
|
||
@Configuration
|
||
public class RestTemplateConfig {
|
||
|
||
@Bean
|
||
public RestTemplate restTemplate() {
|
||
RestTemplate restTemplate = new RestTemplate();
|
||
|
||
// 配置连接超时和读取超时
|
||
HttpComponentsClientHttpRequestFactory factory =
|
||
new HttpComponentsClientHttpRequestFactory();
|
||
factory.setConnectTimeout(5000);
|
||
factory.setReadTimeout(10000);
|
||
restTemplate.setRequestFactory(factory);
|
||
|
||
return restTemplate;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 使用示例
|
||
```java
|
||
@Service
|
||
public class ExternalApiService {
|
||
|
||
@Autowired
|
||
private RestTemplate restTemplate;
|
||
|
||
public String callExternalApi(String url) {
|
||
return restTemplate.getForObject(url, String.class);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4.5 WebClient配置 (`WebClientConfig.java`)
|
||
|
||
#### 功能概述
|
||
提供响应式HTTP客户端功能,支持非阻塞I/O操作,适用于高并发场景。
|
||
|
||
#### 主要优势
|
||
- **非阻塞I/O**: 提高系统吞吐量和响应性能
|
||
- **响应式编程**: 支持Reactor响应式编程模型
|
||
- **流式处理**: 支持数据流的实时处理
|
||
- **背压处理**: 自动处理生产者和消费者速度不匹配的情况
|
||
|
||
#### 配置示例
|
||
```java
|
||
@Configuration
|
||
public class WebClientConfig {
|
||
|
||
@Bean
|
||
public WebClient webClient() {
|
||
return WebClient.builder()
|
||
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024))
|
||
.build();
|
||
}
|
||
|
||
@Bean
|
||
public WebClient.Builder webClientBuilder() {
|
||
return WebClient.builder();
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 使用示例
|
||
```java
|
||
@Service
|
||
public class ReactiveApiService {
|
||
|
||
@Autowired
|
||
private WebClient webClient;
|
||
|
||
public Mono<String> callApiAsync(String url) {
|
||
return webClient.get()
|
||
.uri(url)
|
||
.retrieve()
|
||
.bodyToMono(String.class);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4.6 WebSocket配置 (`WebSocketConfig.java`)
|
||
|
||
#### 功能概述
|
||
WebSocket通信配置,为实时通信功能预留扩展空间。
|
||
|
||
#### 预期用途
|
||
- **实时通知**: 任务状态变更的实时推送
|
||
- **在线协作**: 多用户协同操作
|
||
- **实时监控**: 系统状态和数据的实时更新
|
||
- **即时消息**: 用户间的即时通信
|
||
|
||
#### 扩展示例
|
||
```java
|
||
@Configuration
|
||
@EnableWebSocket
|
||
public class WebSocketConfig implements WebSocketConfigurer {
|
||
|
||
@Override
|
||
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
|
||
registry.addHandler(new NotificationWebSocketHandler(), "/ws/notifications")
|
||
.setAllowedOrigins("*");
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4.7 字符串列表转换器 (`StringListConverter.java`)
|
||
|
||
#### 功能概述
|
||
JPA实体属性转换器,用于在数据库存储和Java对象之间转换字符串列表。
|
||
|
||
#### 主要特性
|
||
- **类型转换**: `List<String>` ↔ JSON字符串
|
||
- **JSON序列化**: 使用Jackson ObjectMapper
|
||
- **空值处理**: 支持null和空列表的正确处理
|
||
- **异常处理**: 包含完整的错误处理和日志记录
|
||
|
||
#### 实现原理
|
||
```java
|
||
@Converter
|
||
public class StringListConverter implements AttributeConverter<List<String>, String> {
|
||
|
||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||
|
||
@Override
|
||
public String convertToDatabaseColumn(List<String> attribute) {
|
||
if (attribute == null || attribute.isEmpty()) {
|
||
return null;
|
||
}
|
||
try {
|
||
return objectMapper.writeValueAsString(attribute);
|
||
} catch (JsonProcessingException e) {
|
||
log.error("转换List<String>到JSON失败", e);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public List<String> convertToEntityAttribute(String dbData) {
|
||
if (dbData == null || dbData.trim().isEmpty()) {
|
||
return new ArrayList<>();
|
||
}
|
||
try {
|
||
return objectMapper.readValue(dbData,
|
||
new TypeReference<List<String>>() {});
|
||
} catch (JsonProcessingException e) {
|
||
log.error("转换JSON到List<String>失败", e);
|
||
return new ArrayList<>();
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 使用示例
|
||
```java
|
||
@Entity
|
||
public class UserAccount {
|
||
|
||
@Convert(converter = StringListConverter.class)
|
||
@Column(name = "skills", columnDefinition = "TEXT")
|
||
private List<String> skills;
|
||
|
||
@Convert(converter = StringListConverter.class)
|
||
@Column(name = "tags", columnDefinition = "TEXT")
|
||
private List<String> tags;
|
||
}
|
||
```
|
||
|
||
### 4.8 配置模块最佳实践
|
||
|
||
#### 1. 配置外部化
|
||
- 使用`@ConfigurationProperties`进行类型安全的配置绑定
|
||
- 支持多环境配置(dev、test、prod)
|
||
- 使用配置验证确保配置的正确性
|
||
|
||
#### 2. 安全考虑
|
||
- 敏感配置信息使用环境变量或加密存储
|
||
- API密钥和数据库密码不硬编码
|
||
- 文件上传路径的安全验证
|
||
|
||
#### 3. 性能优化
|
||
- HTTP客户端连接池配置
|
||
- 合理设置超时时间
|
||
- 缓存配置的优化
|
||
|
||
#### 4. 监控和日志
|
||
- 配置变更的审计日志
|
||
- 初始化过程的详细记录
|
||
- 配置加载失败的错误处理
|
||
|
||
### 4.9 配置模块总结
|
||
|
||
配置模块为EMS系统提供了完整的基础设施支持:
|
||
|
||
1. **数据初始化**: 确保系统启动时具备完整的测试数据和基础配置
|
||
2. **文件管理**: 提供灵活的文件存储配置和管理机制
|
||
3. **API文档**: 支持完整的接口文档生成和在线测试
|
||
4. **HTTP客户端**: 提供同步和异步的外部服务调用能力
|
||
5. **数据转换**: 支持复杂数据类型的数据库存储转换
|
||
|
||
## 五、控制器模块详细解析
|
||
|
||
控制器模块(`com.dne.ems.controller`)是EMS系统的API入口层,负责处理所有HTTP请求并提供RESTful API接口。该模块包含14个专业化控制器,每个控制器专注于特定的业务领域,形成了完整的API生态系统。
|
||
|
||
### 5.1 认证控制器 (`AuthController.java`)
|
||
|
||
#### 功能概述
|
||
认证控制器负责用户身份验证和账户管理的核心功能,是系统安全的第一道防线。
|
||
|
||
#### 主要接口
|
||
- **用户注册** (`POST /api/auth/signup`): 新用户账户创建
|
||
- **用户登录** (`POST /api/auth/login`): 用户身份验证,返回JWT令牌
|
||
- **发送验证码** (`POST /api/auth/send-verification-code`): 注册验证码发送
|
||
- **密码重置** (`POST /api/auth/send-password-reset-code`): 密码重置验证码发送
|
||
- **验证码重置密码** (`POST /api/auth/reset-password-with-code`): 使用验证码重置密码
|
||
|
||
#### 核心特性
|
||
- **JWT令牌管理**: 生成和验证JSON Web Token
|
||
- **邮箱验证**: 支持邮箱验证码验证机制
|
||
- **密码安全**: 多种密码重置方式,确保账户安全
|
||
- **参数验证**: 使用Bean Validation进行请求参数校验
|
||
|
||
#### 代码示例
|
||
```java
|
||
@PostMapping("/login")
|
||
public ResponseEntity<JwtAuthenticationResponse> signIn(@Valid @RequestBody LoginRequest request) {
|
||
return ResponseEntity.ok(authService.signIn(request));
|
||
}
|
||
|
||
@PostMapping("/send-verification-code")
|
||
public ResponseEntity<Void> sendVerificationCode(@RequestParam @NotBlank @Email String email) {
|
||
verificationCodeService.sendVerificationCode(email);
|
||
return ResponseEntity.ok().build();
|
||
}
|
||
```
|
||
|
||
### 5.2 仪表盘控制器 (`DashboardController.java`)
|
||
|
||
#### 功能概述
|
||
仪表盘控制器为决策者和管理员提供全面的数据可视化和统计分析功能,是数据驱动决策的核心支撑。
|
||
|
||
#### 主要接口
|
||
- **核心统计数据** (`GET /api/dashboard/stats`): 系统关键指标统计
|
||
- **AQI分布数据** (`GET /api/dashboard/reports/aqi-distribution`): 空气质量指数分布
|
||
- **月度超标趋势** (`GET /api/dashboard/reports/monthly-exceedance-trend`): 污染超标趋势分析
|
||
- **网格覆盖率** (`GET /api/dashboard/reports/grid-coverage`): 各城市网格覆盖情况
|
||
- **反馈热力图** (`GET /api/dashboard/map/heatmap`): 反馈分布热力图数据
|
||
- **污染物统计** (`GET /api/dashboard/reports/pollution-stats`): 各类污染物统计
|
||
- **任务完成统计** (`GET /api/dashboard/reports/task-completion-stats`): 任务执行效率统计
|
||
- **AQI热力图** (`GET /api/dashboard/map/aqi-heatmap`): AQI分布热力图
|
||
- **污染物阈值管理** (`GET /api/dashboard/thresholds`): 污染物阈值配置
|
||
|
||
#### 权限控制
|
||
```java
|
||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||
public ResponseEntity<DashboardStatsDTO> getDashboardStats() {
|
||
// 仅决策者和管理员可访问
|
||
}
|
||
```
|
||
|
||
#### 数据可视化支持
|
||
- **多维度统计**: 支持时间、地域、类型等多维度数据分析
|
||
- **实时数据**: 提供实时更新的环境监测数据
|
||
- **热力图数据**: 支持地理信息系统(GIS)的数据可视化
|
||
- **趋势分析**: 历史数据趋势和预测分析
|
||
|
||
### 5.3 反馈控制器 (`FeedbackController.java`)
|
||
|
||
#### 功能概述
|
||
反馈控制器处理公众环境问题反馈的全生命周期管理,是公众参与环境治理的重要渠道。
|
||
|
||
#### 主要接口
|
||
- **提交反馈** (`POST /api/feedback/submit`): 支持文件上传的反馈提交
|
||
- **JSON格式提交** (`POST /api/feedback/submit-json`): 测试用的JSON格式提交
|
||
- **获取反馈列表** (`GET /api/feedback`): 支持多条件过滤的分页查询
|
||
- **反馈详情** (`GET /api/feedback/{id}`): 获取特定反馈的详细信息
|
||
- **反馈统计** (`GET /api/feedback/stats`): 反馈数据统计分析
|
||
|
||
#### 高级查询功能
|
||
```java
|
||
@GetMapping
|
||
public ResponseEntity<Page<FeedbackResponseDTO>> getAllFeedback(
|
||
@AuthenticationPrincipal CustomUserDetails userDetails,
|
||
@RequestParam(required = false) FeedbackStatus status,
|
||
@RequestParam(required = false) PollutionType pollutionType,
|
||
@RequestParam(required = false) SeverityLevel severityLevel,
|
||
@RequestParam(required = false) String cityName,
|
||
@RequestParam(required = false) String districtName,
|
||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
|
||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate,
|
||
@RequestParam(required = false) String keyword,
|
||
Pageable pageable) {
|
||
// 支持多维度过滤查询
|
||
}
|
||
```
|
||
|
||
#### 文件上传支持
|
||
- **多文件上传**: 支持同时上传多个附件文件
|
||
- **文件类型验证**: 限制上传文件的类型和大小
|
||
- **安全存储**: 文件安全存储和访问控制
|
||
|
||
### 5.4 文件控制器 (`FileController.java`)
|
||
|
||
#### 功能概述
|
||
文件控制器提供文件下载和在线预览功能,支持系统中所有文件资源的访问管理。
|
||
|
||
#### 主要接口
|
||
- **文件下载** (`GET /api/files/{filename}`): 文件下载功能
|
||
- **文件预览** (`GET /api/view/{fileName}`): 在线文件预览
|
||
|
||
#### 技术特性
|
||
- **MIME类型检测**: 自动检测文件类型并设置正确的Content-Type
|
||
- **安全下载**: 防止路径遍历攻击
|
||
- **流式传输**: 支持大文件的流式下载
|
||
|
||
#### 实现示例
|
||
```java
|
||
@GetMapping("/files/{filename:.+}")
|
||
public ResponseEntity<Resource> downloadFile(@PathVariable String filename, HttpServletRequest request) {
|
||
Resource resource = fileStorageService.loadFileAsResource(filename);
|
||
|
||
String contentType = null;
|
||
try {
|
||
contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
|
||
} catch (IOException ex) {
|
||
logger.info("Could not determine file type.");
|
||
}
|
||
|
||
if (contentType == null) {
|
||
contentType = "application/octet-stream";
|
||
}
|
||
|
||
return ResponseEntity.ok()
|
||
.contentType(MediaType.parseMediaType(contentType))
|
||
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + resource.getFilename() + "\"")
|
||
.body(resource);
|
||
}
|
||
```
|
||
|
||
### 5.5 网格控制器 (`GridController.java`)
|
||
|
||
#### 功能概述
|
||
网格控制器负责城市网格化管理,包括网格数据维护和网格员分配管理。
|
||
|
||
#### 主要接口
|
||
- **网格列表** (`GET /api/grids`): 获取所有网格信息
|
||
- **更新网格** (`PATCH /api/grids/{id}`): 更新网格信息
|
||
- **网格覆盖率** (`GET /api/grids/coverage`): 网格覆盖率统计
|
||
- **分配网格员** (`POST /api/grids/{gridId}/assign`): 将网格员分配到指定网格
|
||
- **移除网格员** (`POST /api/grids/{gridId}/unassign`): 从网格中移除网格员
|
||
- **坐标分配** (`POST /api/grids/coordinates/{gridX}/{gridY}/assign`): 通过坐标分配网格员
|
||
|
||
#### 网格员管理
|
||
```java
|
||
@PostMapping("/{gridId}/assign")
|
||
@PreAuthorize("hasRole('ADMIN')")
|
||
public ResponseEntity<?> assignGridWorker(
|
||
@PathVariable Long gridId,
|
||
@RequestBody Map<String, Long> request) {
|
||
Long userId = request.get("userId");
|
||
|
||
// 验证用户角色
|
||
if (user.getRole() != Role.GRID_WORKER) {
|
||
return ResponseEntity.badRequest().body("只能分配网格员角色的用户");
|
||
}
|
||
|
||
// 分配网格坐标
|
||
user.setGridX(grid.getGridX());
|
||
user.setGridY(grid.getGridY());
|
||
userAccountRepository.save(user);
|
||
|
||
return ResponseEntity.ok().build();
|
||
}
|
||
```
|
||
|
||
### 5.6 网格员任务控制器 (`GridWorkerTaskController.java`)
|
||
|
||
#### 功能概述
|
||
专为网格员设计的任务管理控制器,提供任务接收、处理和提交的完整工作流程。
|
||
|
||
#### 主要接口
|
||
- **我的任务** (`GET /api/worker`): 获取分配给当前网格员的任务列表
|
||
- **接受任务** (`POST /api/worker/{taskId}/accept`): 接受指定任务
|
||
- **提交任务** (`POST /api/worker/{taskId}/submit`): 提交任务完成情况
|
||
- **任务详情** (`GET /api/worker/{taskId}`): 查看任务详细信息
|
||
|
||
#### 权限控制
|
||
```java
|
||
@RestController
|
||
@RequestMapping("/api/worker")
|
||
@RequiredArgsConstructor
|
||
@PreAuthorize("hasRole('GRID_WORKER')")
|
||
public class GridWorkerTaskController {
|
||
// 整个控制器仅限网格员角色访问
|
||
}
|
||
```
|
||
|
||
#### 任务提交功能
|
||
```java
|
||
@PostMapping(value = "/{taskId}/submit", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||
public ResponseEntity<TaskSummaryDTO> submitTask(
|
||
@PathVariable Long taskId,
|
||
@RequestPart("comments") @Valid String comments,
|
||
@RequestPart(value = "files", required = false) List<MultipartFile> files,
|
||
@AuthenticationPrincipal CustomUserDetails userDetails) {
|
||
// 支持文件上传的任务提交
|
||
}
|
||
```
|
||
|
||
### 5.7 地图控制器 (`MapController.java`)
|
||
|
||
#### 功能概述
|
||
地图控制器管理系统的地图网格数据,为路径规划和地理信息展示提供基础数据支持。
|
||
|
||
#### 主要接口
|
||
- **获取地图** (`GET /api/map/grid`): 获取完整的地图网格数据
|
||
- **更新网格** (`POST /api/map/grid`): 创建或更新地图网格单元
|
||
- **初始化地图** (`POST /api/map/initialize`): 初始化指定大小的地图
|
||
|
||
#### 地图初始化
|
||
```java
|
||
@PostMapping("/initialize")
|
||
@Transactional
|
||
public ResponseEntity<String> initializeMap(
|
||
@RequestParam(defaultValue = "20") int width,
|
||
@RequestParam(defaultValue = "20") int height) {
|
||
|
||
mapGridRepository.deleteAll();
|
||
mapGridRepository.flush();
|
||
|
||
for (int y = 0; y < height; y++) {
|
||
for (int x = 0; x < width; x++) {
|
||
MapGrid cell = MapGrid.builder()
|
||
.x(x)
|
||
.y(y)
|
||
.isObstacle(false)
|
||
.terrainType("ground")
|
||
.build();
|
||
mapGridRepository.save(cell);
|
||
}
|
||
}
|
||
return ResponseEntity.ok("Initialized a " + width + "x" + height + " map.");
|
||
}
|
||
```
|
||
|
||
### 5.8 路径规划控制器 (`PathfindingController.java`)
|
||
|
||
#### 功能概述
|
||
路径规划控制器提供A*寻路算法功能,为网格员提供最优路径导航服务。
|
||
|
||
#### 主要接口
|
||
- **路径查找** (`POST /api/pathfinding/find`): 计算两点之间的最优路径
|
||
|
||
#### A*算法实现
|
||
```java
|
||
@PostMapping("/find")
|
||
public ResponseEntity<List<Point>> findPath(@RequestBody PathfindingRequest request) {
|
||
if (request == null) {
|
||
return ResponseEntity.badRequest().build();
|
||
}
|
||
Point start = new Point(request.getStartX(), request.getStartY());
|
||
Point end = new Point(request.getEndX(), request.getEndY());
|
||
|
||
List<Point> path = aStarService.findPath(start, end);
|
||
return ResponseEntity.ok(path);
|
||
}
|
||
```
|
||
|
||
#### 算法特性
|
||
- **障碍物避让**: 考虑地图中的障碍物
|
||
- **最优路径**: 计算距离最短的路径
|
||
- **实时计算**: 快速响应路径查询请求
|
||
|
||
### 5.9 人员管理控制器 (`PersonnelController.java`)
|
||
|
||
#### 功能概述
|
||
人员管理控制器提供用户账号的完整生命周期管理,包括创建、查询、更新和删除操作。
|
||
|
||
#### 主要接口
|
||
- **创建用户** (`POST /api/personnel/users`): 创建新用户账号
|
||
- **用户列表** (`GET /api/personnel/users`): 分页查询用户列表
|
||
- **用户详情** (`GET /api/personnel/users/{userId}`): 获取用户详细信息
|
||
- **更新用户** (`PATCH /api/personnel/users/{userId}`): 更新用户信息
|
||
- **更新角色** (`PUT /api/personnel/users/{userId}/role`): 更新用户角色
|
||
- **删除用户** (`DELETE /api/personnel/users/{userId}`): 删除用户账号
|
||
|
||
#### 权限控制
|
||
```java
|
||
@RestController
|
||
@RequestMapping("/api/personnel")
|
||
@RequiredArgsConstructor
|
||
@PreAuthorize("hasRole('ADMIN')")
|
||
public class PersonnelController {
|
||
// 仅管理员可访问人员管理功能
|
||
}
|
||
```
|
||
|
||
#### 高级查询
|
||
```java
|
||
@GetMapping("/users")
|
||
@PreAuthorize("hasRole('ADMIN') or hasRole('GRID_WORKER')")
|
||
public ResponseEntity<PageDTO<UserAccount>> getUsers(
|
||
@RequestParam(required = false) Role role,
|
||
@RequestParam(required = false) String name,
|
||
Pageable pageable) {
|
||
// 支持按角色和姓名过滤的分页查询
|
||
}
|
||
```
|
||
|
||
### 5.10 个人资料控制器 (`ProfileController.java`)
|
||
|
||
#### 功能概述
|
||
个人资料控制器处理认证用户的个人数据管理,提供用户自助服务功能。
|
||
|
||
#### 主要接口
|
||
- **反馈历史** (`GET /api/me/feedback`): 获取当前用户的反馈提交历史
|
||
|
||
#### 用户数据隔离
|
||
```java
|
||
@GetMapping("/feedback")
|
||
@PreAuthorize("isAuthenticated()")
|
||
public ResponseEntity<List<UserFeedbackSummaryDTO>> getMyFeedbackHistory(
|
||
@AuthenticationPrincipal CustomUserDetails userDetails,
|
||
Pageable pageable) {
|
||
// 仅返回当前用户的数据
|
||
Page<UserFeedbackSummaryDTO> historyPage = userFeedbackService
|
||
.getFeedbackHistoryByUserId(userDetails.getId(), pageable);
|
||
return ResponseEntity.ok(historyPage.getContent());
|
||
}
|
||
```
|
||
|
||
### 5.11 公共接口控制器 (`PublicController.java`)
|
||
|
||
#### 功能概述
|
||
公共接口控制器提供无需认证的公共API,主要服务于匿名用户的反馈提交。
|
||
|
||
#### 主要接口
|
||
- **公共反馈提交** (`POST /api/public/feedback`): 支持匿名用户提交环境问题反馈
|
||
|
||
#### 匿名访问支持
|
||
```java
|
||
@PostMapping(value = "/feedback", consumes = "multipart/form-data")
|
||
public ResponseEntity<Feedback> submitPublicFeedback(
|
||
@Valid @RequestPart("feedback") PublicFeedbackRequest request,
|
||
@RequestPart(value = "files", required = false) MultipartFile[] files) {
|
||
// 无需认证即可提交反馈
|
||
Feedback createdFeedback = feedbackService.createPublicFeedback(request, files);
|
||
return new ResponseEntity<>(createdFeedback, HttpStatus.CREATED);
|
||
}
|
||
```
|
||
|
||
### 5.12 主管控制器 (`SupervisorController.java`)
|
||
|
||
#### 功能概述
|
||
主管控制器专为主管角色设计,提供反馈审核和管理功能。
|
||
|
||
#### 主要接口
|
||
- **待审核反馈** (`GET /api/supervisor/reviews`): 获取待审核的反馈列表
|
||
- **审核通过** (`POST /api/supervisor/reviews/{feedbackId}/approve`): 审核通过反馈
|
||
- **审核拒绝** (`POST /api/supervisor/reviews/{feedbackId}/reject`): 审核拒绝反馈
|
||
|
||
#### 审核流程
|
||
```java
|
||
@PostMapping("/reviews/{feedbackId}/approve")
|
||
@PreAuthorize("hasAnyRole('SUPERVISOR', 'ADMIN')")
|
||
public ResponseEntity<Void> approveFeedback(@PathVariable Long feedbackId) {
|
||
supervisorService.approveFeedback(feedbackId);
|
||
return ResponseEntity.ok().build();
|
||
}
|
||
|
||
@PostMapping("/reviews/{feedbackId}/reject")
|
||
@PreAuthorize("hasAnyRole('SUPERVISOR', 'ADMIN')")
|
||
public ResponseEntity<Void> rejectFeedback(
|
||
@PathVariable Long feedbackId,
|
||
@RequestBody RejectFeedbackRequest request) {
|
||
supervisorService.rejectFeedback(feedbackId, request);
|
||
return ResponseEntity.ok().build();
|
||
}
|
||
```
|
||
|
||
### 5.13 任务分配控制器 (`TaskAssignmentController.java`)
|
||
|
||
#### 功能概述
|
||
任务分配控制器处理任务的智能分配和人员调度功能。
|
||
|
||
#### 主要接口
|
||
- **未分配任务** (`GET /api/tasks/unassigned`): 获取未分配的任务列表
|
||
- **可用网格员** (`GET /api/tasks/grid-workers`): 获取可用的网格工作人员
|
||
- **分配任务** (`POST /api/tasks/assign`): 将任务分配给指定工作人员
|
||
|
||
#### 智能分配
|
||
```java
|
||
@PostMapping("/assign")
|
||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPERVISOR')")
|
||
public ResponseEntity<Assignment> assignTask(
|
||
@RequestBody AssignmentRequest request,
|
||
@AuthenticationPrincipal CustomUserDetails adminDetails) {
|
||
|
||
Long assignerId = adminDetails.getId();
|
||
Assignment newAssignment = taskAssignmentService.assignTask(
|
||
request.feedbackId(),
|
||
request.assigneeId(),
|
||
assignerId
|
||
);
|
||
return ResponseEntity.ok(newAssignment);
|
||
}
|
||
```
|
||
|
||
### 5.14 任务管理控制器 (`TaskManagementController.java`)
|
||
|
||
#### 功能概述
|
||
任务管理控制器提供任务全生命周期的管理功能,是任务管理的核心控制器。
|
||
|
||
#### 主要接口
|
||
- **任务列表** (`GET /api/management/tasks`): 支持多条件过滤的任务分页查询
|
||
- **分配任务** (`POST /api/management/tasks/{taskId}/assign`): 分配任务给工作人员
|
||
- **任务详情** (`GET /api/management/tasks/{taskId}`): 获取任务详细信息
|
||
- **审核任务** (`POST /api/management/tasks/{taskId}/review`): 审核任务完成情况
|
||
- **取消任务** (`POST /api/management/tasks/{taskId}/cancel`): 取消任务
|
||
- **创建任务** (`POST /api/management/tasks`): 创建新任务
|
||
- **待处理反馈** (`GET /api/management/tasks/feedback`): 获取待处理的反馈
|
||
- **从反馈创建任务** (`POST /api/management/tasks/feedback/{feedbackId}/create-task`): 从反馈创建任务
|
||
|
||
#### 高级过滤查询
|
||
```java
|
||
@GetMapping
|
||
@PreAuthorize("hasAnyAuthority('ADMIN', 'SUPERVISOR', 'DECISION_MAKER')")
|
||
public ResponseEntity<List<TaskSummaryDTO>> getTasks(
|
||
@RequestParam(required = false) TaskStatus status,
|
||
@RequestParam(required = false) Long assigneeId,
|
||
@RequestParam(required = false) SeverityLevel severity,
|
||
@RequestParam(required = false) PollutionType pollutionType,
|
||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
|
||
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate,
|
||
Pageable pageable) {
|
||
// 支持多维度过滤的任务查询
|
||
}
|
||
```
|
||
|
||
#### 任务状态流转
|
||
```java
|
||
@PostMapping("/{taskId}/review")
|
||
public ResponseEntity<TaskDetailDTO> reviewTask(
|
||
@PathVariable Long taskId,
|
||
@RequestBody @Valid TaskApprovalRequest request) {
|
||
TaskDetailDTO updatedTask = taskManagementService.reviewTask(taskId, request);
|
||
return ResponseEntity.ok(updatedTask);
|
||
}
|
||
|
||
@PostMapping("/{taskId}/cancel")
|
||
public ResponseEntity<TaskDetailDTO> cancelTask(@PathVariable Long taskId) {
|
||
TaskDetailDTO updatedTask = taskManagementService.cancelTask(taskId);
|
||
return ResponseEntity.ok(updatedTask);
|
||
}
|
||
```
|
||
|
||
### 5.15 控制器模块最佳实践
|
||
|
||
#### 1. RESTful API设计
|
||
- **统一的URL命名规范**: 使用名词复数形式,避免动词
|
||
- **HTTP方法语义化**: GET查询、POST创建、PUT/PATCH更新、DELETE删除
|
||
- **状态码规范**: 正确使用HTTP状态码表达操作结果
|
||
- **版本控制**: 通过URL路径进行API版本管理
|
||
|
||
#### 2. 安全控制
|
||
- **细粒度权限控制**: 使用`@PreAuthorize`进行方法级权限控制
|
||
- **输入验证**: 使用Bean Validation进行参数校验
|
||
- **敏感数据保护**: 避免在响应中暴露敏感信息(如密码)
|
||
- **CSRF防护**: 对状态变更操作进行CSRF保护
|
||
|
||
#### 3. 异常处理
|
||
- **全局异常处理**: 使用`@ControllerAdvice`统一处理异常
|
||
- **友好错误信息**: 为客户端提供清晰的错误描述
|
||
- **日志记录**: 记录详细的错误日志便于问题排查
|
||
|
||
#### 4. 性能优化
|
||
- **分页查询**: 对大数据量查询使用分页机制
|
||
- **缓存策略**: 对频繁查询的数据使用缓存
|
||
- **异步处理**: 对耗时操作使用异步处理
|
||
- **数据传输优化**: 使用DTO减少不必要的数据传输
|
||
|
||
#### 5. 文档和测试
|
||
- **API文档**: 使用Swagger/OpenAPI生成完整的API文档
|
||
- **单元测试**: 为每个控制器方法编写单元测试
|
||
- **集成测试**: 测试完整的请求-响应流程
|
||
- **性能测试**: 对关键接口进行性能测试
|
||
|
||
### 5.16 控制器模块总结
|
||
|
||
控制器模块为EMS系统提供了完整的API服务层:
|
||
|
||
1. **全面的功能覆盖**: 14个专业化控制器覆盖了系统的所有核心功能
|
||
2. **严格的权限控制**: 基于角色的访问控制确保系统安全
|
||
3. **灵活的查询支持**: 支持多维度过滤、分页、排序等高级查询功能
|
||
4. **完善的文件处理**: 支持文件上传、下载、预览等文件操作
|
||
5. **智能的任务管理**: 提供完整的任务生命周期管理和智能分配功能
|
||
6. **实时的数据可视化**: 为决策支持提供丰富的数据分析接口
|
||
7. **用户友好的接口设计**: 遵循RESTful设计原则,提供直观易用的API
|
||
|
||
通过这些控制器的协同工作,EMS系统能够为不同角色的用户提供专业化、个性化的服务,实现了从公众反馈到问题解决的完整业务闭环。
|
||
6. **扩展性**: 为WebSocket等实时通信功能预留扩展空间
|
||
|
||
这些配置组件共同构成了一个健壮、可扩展、易维护的系统基础架构,为EMS系统的稳定运行和功能扩展提供了坚实的技术保障。
|
||
|
||
---
|
||
|
||
# DTO模块详细解析
|
||
|
||
## 概述
|
||
|
||
DTO(Data Transfer Object)模块位于 `com.dne.ems.dto` 包下,包含51个数据传输对象,负责在不同层之间传递数据,确保数据的封装性和安全性。DTO模块按功能分类,涵盖用户管理、任务管理、反馈处理、地图网格、统计分析和AI集成等核心业务领域。
|
||
|
||
## 核心功能分类
|
||
|
||
### 1. 用户管理相关DTO
|
||
|
||
#### 用户注册和认证
|
||
- **SignUpRequest**: 用户注册请求,包含姓名、邮箱、手机、密码、角色和验证码
|
||
- **LoginRequest**: 用户登录请求,封装邮箱和密码
|
||
- **UserRegistrationRequest**: 公众用户注册请求,支持密码复杂度验证
|
||
- **JwtAuthenticationResponse**: JWT认证响应,返回访问令牌
|
||
|
||
#### 用户信息管理
|
||
- **UserCreationRequest**: 管理员创建用户请求,支持角色分配和区域设置
|
||
- **UserUpdateRequest**: 用户信息更新请求,支持多字段批量更新
|
||
- **UserRoleUpdateRequest**: 用户角色更新请求,网格员需要位置信息
|
||
- **UserInfoDTO**: 用户基本信息传输对象,隐藏敏感数据
|
||
- **UserSummaryDTO**: 用户摘要信息,用于列表展示
|
||
|
||
#### 密码管理
|
||
- **PasswordResetRequest**: 密码重置邮件请求
|
||
- **PasswordResetDto**: 通过令牌重置密码
|
||
- **PasswordResetWithCodeDto**: 通过验证码重置密码
|
||
|
||
### 2. 任务管理相关DTO
|
||
|
||
#### 任务创建和分配
|
||
- **TaskCreationRequest**: 任务创建请求,包含标题、描述、污染类型、严重级别和位置
|
||
- **TaskAssignmentRequest**: 任务分配请求,指定受理人ID
|
||
- **TaskFromFeedbackRequest**: 从反馈创建任务请求,设置严重级别
|
||
|
||
#### 任务处理和审核
|
||
- **TaskSubmissionRequest**: 任务提交请求,包含处理意见
|
||
- **TaskApprovalRequest**: 任务审核请求,支持通过或拒绝
|
||
- **TaskRejectionRequest**: 任务拒绝请求,必须提供拒绝原因
|
||
|
||
#### 任务信息展示
|
||
- **TaskDTO**: 任务基本信息,包含ID、标题、描述和状态
|
||
- **TaskSummaryDTO**: 任务摘要信息,用于列表展示
|
||
- **TaskDetailDTO**: 任务详细信息,包含完整的任务数据和历史记录
|
||
- **TaskInfoDTO**: 任务简要信息,包含状态和受理人
|
||
- **TaskHistoryDTO**: 任务历史记录,追踪状态变更
|
||
- **TaskStatsDTO**: 任务统计数据,计算完成率
|
||
|
||
### 3. 反馈处理相关DTO
|
||
|
||
#### 反馈提交
|
||
- **PublicFeedbackRequest**: 公众反馈提交请求,支持匿名提交
|
||
- **FeedbackSubmissionRequest**: 反馈提交请求,包含位置和附件信息
|
||
- **AqiDataSubmissionRequest**: AQI数据提交请求,专门处理空气质量数据
|
||
|
||
#### 反馈处理
|
||
- **ProcessFeedbackRequest**: 反馈处理请求,支持批准或采取行动
|
||
- **RejectFeedbackRequest**: 反馈拒绝请求,需要提供拒绝原因
|
||
|
||
#### 反馈信息展示
|
||
- **FeedbackDTO**: 反馈详细信息传输对象
|
||
- **FeedbackResponseDTO**: 反馈响应数据封装
|
||
- **UserFeedbackSummaryDTO**: 用户反馈历史摘要
|
||
- **FeedbackStatsResponse**: 反馈统计数据响应
|
||
|
||
### 4. 地图网格相关DTO
|
||
|
||
#### 网格管理
|
||
- **GridUpdateRequest**: 网格属性更新请求
|
||
- **GridAssignmentRequest**: 网格员分配请求
|
||
- **GridCoverageDTO**: 网格覆盖统计数据
|
||
- **CityBlueprint**: 城市网格蓝图生成
|
||
|
||
#### 位置和路径
|
||
- **LocationUpdateRequest**: 用户位置更新请求
|
||
- **PathfindingRequest**: A*路径查找请求,包含起点和终点
|
||
- **Point**: 二维坐标点表示
|
||
|
||
### 5. 统计分析相关DTO
|
||
|
||
#### 仪表盘统计
|
||
- **DashboardStatsDTO**: 仪表盘关键统计数据
|
||
- **PollutionStatsDTO**: 污染物统计数据
|
||
- **TrendDataPointDTO**: 趋势数据点,用于时间序列分析
|
||
|
||
#### 热力图数据
|
||
- **HeatmapPointDTO**: 通用热力图数据点
|
||
- **AqiHeatmapPointDTO**: AQI热力图专用数据点
|
||
- **AqiDistributionDTO**: AQI分布统计数据
|
||
- **PollutantThresholdDTO**: 污染物阈值信息
|
||
|
||
### 6. 通用工具DTO
|
||
|
||
#### 分页和响应
|
||
- **PageDTO**: 分页查询结果封装,支持泛型
|
||
- **ErrorResponseDTO**: API错误响应标准格式
|
||
- **AttachmentDTO**: 附件信息封装
|
||
- **AssigneeInfoDTO**: 任务受理人信息
|
||
|
||
### 7. AI集成相关DTO
|
||
|
||
#### 火山引擎AI聊天
|
||
- **VolcanoChatRequest**: 火山引擎聊天API请求,支持多轮对话和工具调用
|
||
- **VolcanoChatResponse**: 火山引擎聊天API响应,包含AI回复和工具调用结果
|
||
|
||
## 核心代码示例
|
||
|
||
### 任务创建请求DTO
|
||
```java
|
||
public record TaskCreationRequest(
|
||
@NotBlank(message = "Title is mandatory")
|
||
String title,
|
||
String description,
|
||
@NotNull(message = "Pollution type is mandatory")
|
||
PollutionType pollutionType,
|
||
@NotNull(message = "Severity level is mandatory")
|
||
SeverityLevel severityLevel,
|
||
@NotNull(message = "Location information is mandatory")
|
||
LocationDTO location
|
||
) {
|
||
public record LocationDTO(
|
||
@NotBlank(message = "Address is mandatory")
|
||
String address,
|
||
@NotNull(message = "Grid X coordinate is mandatory")
|
||
Integer gridX,
|
||
@NotNull(message = "Grid Y coordinate is mandatory")
|
||
Integer gridY
|
||
) {}
|
||
}
|
||
```
|
||
|
||
### 分页查询结果DTO
|
||
```java
|
||
public record PageDTO<T>(
|
||
List<T> content,
|
||
int page,
|
||
int size,
|
||
long totalElements,
|
||
int totalPages,
|
||
boolean first,
|
||
boolean last
|
||
) {
|
||
public static <T> PageDTO<T> fromPage(Page<T> page) {
|
||
return new PageDTO<>(
|
||
page.getContent(),
|
||
page.getNumber(),
|
||
page.getSize(),
|
||
page.getTotalElements(),
|
||
page.getTotalPages(),
|
||
page.isFirst(),
|
||
page.isLast()
|
||
);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 用户信息DTO工厂方法
|
||
```java
|
||
@Data
|
||
@NoArgsConstructor
|
||
@AllArgsConstructor
|
||
public class UserInfoDTO {
|
||
private Long id;
|
||
private String name;
|
||
private String phone;
|
||
private String email;
|
||
|
||
public static UserInfoDTO fromEntity(UserAccount user) {
|
||
if (user == null) {
|
||
return null;
|
||
}
|
||
return new UserInfoDTO(
|
||
user.getId(),
|
||
user.getName(),
|
||
user.getPhone(),
|
||
user.getEmail()
|
||
);
|
||
}
|
||
}
|
||
```
|
||
|
||
### AI聊天请求DTO
|
||
```java
|
||
@Data
|
||
@Builder
|
||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||
public class VolcanoChatRequest {
|
||
private String model;
|
||
private List<Message> messages;
|
||
private List<Tool> tools;
|
||
|
||
@Data
|
||
@SuperBuilder
|
||
public static class Message {
|
||
private String role;
|
||
private List<ContentPart> content;
|
||
}
|
||
|
||
@Data
|
||
@SuperBuilder
|
||
public static class ContentPart {
|
||
private String type;
|
||
private String text;
|
||
@JsonProperty("image_url")
|
||
private ImageUrl imageUrl;
|
||
}
|
||
}
|
||
```
|
||
|
||
## 设计特点
|
||
|
||
### 1. 数据验证
|
||
- 使用Jakarta Validation注解进行参数校验
|
||
- 自定义验证注解(如@ValidPassword、@GridWorkerLocationRequired)
|
||
- 分层验证策略,确保数据完整性
|
||
|
||
### 2. 类型安全
|
||
- 大量使用Record类型,确保不可变性
|
||
- 泛型支持,如PageDTO<T>提供类型安全的分页
|
||
- 枚举类型约束,避免无效值
|
||
|
||
### 3. 序列化优化
|
||
- Jackson注解配置,控制JSON序列化行为
|
||
- @JsonInclude(JsonInclude.Include.NON_NULL)避免空值传输
|
||
- @JsonProperty处理字段名映射
|
||
|
||
### 4. 业务语义
|
||
- 清晰的命名约定,Request/Response/DTO后缀明确用途
|
||
- 丰富的JavaDoc文档,说明业务规则和使用场景
|
||
- 嵌套记录结构,体现数据层次关系
|
||
|
||
## DTO模块最佳实践
|
||
|
||
1. **不可变设计**:优先使用Record类型,确保数据传输对象的不可变性
|
||
2. **验证完整性**:在DTO层进行数据验证,避免无效数据进入业务层
|
||
3. **类型安全**:使用强类型和泛型,减少运行时错误
|
||
4. **文档完善**:提供详细的JavaDoc,说明字段含义和业务规则
|
||
5. **序列化优化**:合理使用Jackson注解,优化网络传输效率
|
||
6. **分层隔离**:DTO与实体类分离,避免业务逻辑泄露到表示层
|
||
|
||
## DTO模块总结
|
||
|
||
DTO模块为EMS系统提供了完整的数据传输层:
|
||
|
||
1. **全面的功能覆盖**: 51个DTO类覆盖了系统的所有核心功能领域
|
||
2. **严格的数据验证**: 多层次验证机制确保数据质量和安全性
|
||
3. **灵活的数据结构**: 支持复杂嵌套结构和泛型,满足不同业务需求
|
||
4. **优化的传输效率**: 通过合理的序列化配置减少网络传输开销
|
||
5. **清晰的业务语义**: 命名规范和文档完善,便于开发和维护
|
||
6. **类型安全保障**: Record类型和强类型约束减少运行时错误
|
||
7. **扩展性支持**: 为AI集成等新功能提供了良好的数据结构基础
|
||
|
||
通过这些DTO的协同工作,EMS系统能够在各层之间安全、高效地传递数据,为整个系统的稳定运行和功能扩展提供了坚实的数据传输基础。
|
||
|
||
---
|
||
|
||
# Event模块详细解析
|
||
|
||
## 概述
|
||
|
||
Event(事件)模块位于 `com.dne.ems.event` 包下,包含2个应用事件类,实现了系统的事件驱动架构。通过Spring的事件发布机制,实现了业务模块之间的松耦合通信,支持异步处理和系统扩展。
|
||
|
||
## 核心事件类
|
||
|
||
### 1. FeedbackSubmittedForAiReviewEvent
|
||
|
||
#### 功能概述
|
||
- **用途**: 反馈提交AI审核事件
|
||
- **触发时机**: 当公众提交反馈后,系统需要进行AI预审核时发布
|
||
- **业务价值**: 实现反馈提交与AI审核的解耦,支持异步AI分析
|
||
|
||
#### 核心特性
|
||
- **继承关系**: 继承自Spring的`ApplicationEvent`基类
|
||
- **数据载体**: 携带完整的`Feedback`实体对象
|
||
- **事件源**: 支持指定事件发布源,便于追踪和调试
|
||
|
||
#### 代码实现
|
||
```java
|
||
package com.dne.ems.event;
|
||
|
||
import org.springframework.context.ApplicationEvent;
|
||
import com.dne.ems.model.Feedback;
|
||
|
||
public class FeedbackSubmittedForAiReviewEvent extends ApplicationEvent {
|
||
private final Feedback feedback;
|
||
|
||
public FeedbackSubmittedForAiReviewEvent(Object source, Feedback feedback) {
|
||
super(source);
|
||
this.feedback = feedback;
|
||
}
|
||
|
||
public Feedback getFeedback() {
|
||
return feedback;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 使用场景
|
||
1. **反馈提交后**: 在`FeedbackService`中提交反馈成功后发布此事件
|
||
2. **AI审核触发**: 事件监听器接收到事件后,调用AI服务进行内容分析
|
||
3. **异步处理**: 避免反馈提交流程被AI分析耗时阻塞
|
||
|
||
### 2. TaskReadyForAssignmentEvent
|
||
|
||
#### 功能概述
|
||
- **用途**: 任务准备分配事件
|
||
- **触发时机**: 当任务创建完成或状态变更为可分配时发布
|
||
- **业务价值**: 实现任务创建与分配逻辑的解耦,支持智能分配算法
|
||
|
||
#### 核心特性
|
||
- **继承关系**: 继承自Spring的`ApplicationEvent`基类
|
||
- **数据载体**: 携带完整的`Task`实体对象
|
||
- **Lombok支持**: 使用`@Getter`注解简化代码
|
||
- **不可变性**: 任务对象使用`final`修饰,确保事件数据不被修改
|
||
|
||
#### 代码实现
|
||
```java
|
||
package com.dne.ems.event;
|
||
|
||
import org.springframework.context.ApplicationEvent;
|
||
import com.dne.ems.model.Task;
|
||
import lombok.Getter;
|
||
|
||
@Getter
|
||
public class TaskReadyForAssignmentEvent extends ApplicationEvent {
|
||
private final Task task;
|
||
|
||
public TaskReadyForAssignmentEvent(Object source, Task task) {
|
||
super(source);
|
||
this.task = task;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 使用场景
|
||
1. **任务创建后**: 在`TaskService`中创建新任务后发布此事件
|
||
2. **状态变更**: 任务状态从其他状态变更为待分配时发布
|
||
3. **智能分配**: 事件监听器可以触发智能分配算法,推荐最佳执行人
|
||
4. **通知机制**: 通知相关人员有新任务需要分配
|
||
|
||
## 事件驱动架构优势
|
||
|
||
### 1. 松耦合设计
|
||
- **模块独立**: 事件发布者和监听者之间没有直接依赖关系
|
||
- **易于扩展**: 可以随时添加新的事件监听器而不影响现有代码
|
||
- **职责分离**: 每个模块专注于自己的核心业务逻辑
|
||
|
||
### 2. 异步处理能力
|
||
- **性能提升**: 耗时操作(如AI分析)不会阻塞主业务流程
|
||
- **用户体验**: 用户操作可以立即得到响应
|
||
- **系统吞吐**: 提高系统整体处理能力
|
||
|
||
### 3. 可观测性
|
||
- **事件追踪**: 可以记录事件的发布和处理过程
|
||
- **调试支持**: 便于排查业务流程中的问题
|
||
- **监控集成**: 可以与监控系统集成,实时观察系统状态
|
||
|
||
## 事件处理流程
|
||
|
||
### 反馈AI审核流程
|
||
```
|
||
1. 用户提交反馈 → FeedbackService.createFeedback()
|
||
2. 保存反馈到数据库
|
||
3. 发布 FeedbackSubmittedForAiReviewEvent
|
||
4. AI审核监听器接收事件
|
||
5. 调用AI服务分析反馈内容
|
||
6. 更新反馈状态和AI分析结果
|
||
```
|
||
|
||
### 任务分配流程
|
||
```
|
||
1. 创建新任务 → TaskService.createTask()
|
||
2. 保存任务到数据库
|
||
3. 发布 TaskReadyForAssignmentEvent
|
||
4. 任务分配监听器接收事件
|
||
5. 执行智能分配算法
|
||
6. 推荐最佳执行人或自动分配
|
||
```
|
||
|
||
## 技术实现细节
|
||
|
||
### 1. Spring事件机制
|
||
- **ApplicationEvent**: 所有自定义事件的基类
|
||
- **ApplicationEventPublisher**: 用于发布事件的接口
|
||
- **@EventListener**: 标记事件监听方法的注解
|
||
- **@Async**: 支持异步事件处理
|
||
|
||
### 2. 事件发布示例
|
||
```java
|
||
@Service
|
||
public class FeedbackService {
|
||
@Autowired
|
||
private ApplicationEventPublisher eventPublisher;
|
||
|
||
public Feedback createFeedback(FeedbackRequest request) {
|
||
// 保存反馈
|
||
Feedback feedback = feedbackRepository.save(newFeedback);
|
||
|
||
// 发布事件
|
||
eventPublisher.publishEvent(
|
||
new FeedbackSubmittedForAiReviewEvent(this, feedback)
|
||
);
|
||
|
||
return feedback;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. 事件监听示例
|
||
```java
|
||
@Component
|
||
public class FeedbackEventListener {
|
||
|
||
@EventListener
|
||
@Async
|
||
public void handleFeedbackSubmitted(FeedbackSubmittedForAiReviewEvent event) {
|
||
Feedback feedback = event.getFeedback();
|
||
// 执行AI审核逻辑
|
||
aiReviewService.reviewFeedback(feedback);
|
||
}
|
||
}
|
||
```
|
||
|
||
## Event模块最佳实践
|
||
|
||
1. **事件命名**: 使用清晰的命名约定,体现事件的业务含义
|
||
2. **数据封装**: 事件中携带的数据应该是完整且不可变的
|
||
3. **异步处理**: 对于耗时操作,使用`@Async`注解实现异步处理
|
||
4. **异常处理**: 事件监听器中要有完善的异常处理机制
|
||
5. **幂等性**: 确保事件处理的幂等性,避免重复处理问题
|
||
6. **监控日志**: 记录事件的发布和处理日志,便于问题排查
|
||
|
||
## Event模块总结
|
||
|
||
Event模块为EMS系统提供了强大的事件驱动能力:
|
||
|
||
1. **解耦架构**: 通过事件机制实现模块间的松耦合通信
|
||
2. **异步处理**: 支持耗时操作的异步执行,提升系统性能
|
||
3. **扩展性强**: 易于添加新的业务逻辑而不影响现有代码
|
||
4. **可观测性**: 提供清晰的业务流程追踪和监控能力
|
||
5. **Spring集成**: 充分利用Spring框架的事件机制和异步支持
|
||
|
||
虽然目前只有2个事件类,但它们为系统的核心业务流程(反馈处理和任务分配)提供了关键的解耦和异步处理能力,为系统的可扩展性和性能优化奠定了坚实基础。
|
||
|
||
---
|
||
|
||
# Exception模块详细解析
|
||
|
||
## 概述
|
||
|
||
Exception(异常处理)模块位于 `com.dne.ems.exception` 包下,包含7个异常处理相关的类,构建了完整的异常处理体系。通过自定义异常类和全局异常处理器,实现了统一的错误响应格式和完善的异常处理机制,提升了系统的健壮性和用户体验。
|
||
|
||
## 核心异常类
|
||
|
||
### 1. ErrorResponseDTO
|
||
|
||
#### 功能概述
|
||
- **用途**: 标准化API错误响应数据传输对象
|
||
- **作用**: 为所有API端点提供一致的错误结构
|
||
- **业务价值**: 统一错误响应格式,提升前端处理效率
|
||
|
||
#### 核心特性
|
||
- **Lombok支持**: 使用`@Data`、`@AllArgsConstructor`、`@NoArgsConstructor`注解
|
||
- **时间戳**: 记录错误发生的精确时间
|
||
- **状态码**: HTTP状态码,便于前端判断错误类型
|
||
- **错误分类**: 提供错误类型和详细消息
|
||
- **路径追踪**: 记录发生错误的请求路径
|
||
|
||
#### 代码实现
|
||
```java
|
||
@Data
|
||
@AllArgsConstructor
|
||
@NoArgsConstructor
|
||
public class ErrorResponseDTO {
|
||
private LocalDateTime timestamp;
|
||
private int status;
|
||
private String error;
|
||
private String message;
|
||
private String path;
|
||
}
|
||
```
|
||
|
||
### 2. ResourceNotFoundException
|
||
|
||
#### 功能概述
|
||
- **用途**: 资源不存在异常
|
||
- **HTTP状态**: 404 Not Found
|
||
- **触发场景**: 查询的资源不存在时抛出
|
||
- **业务价值**: 明确区分资源不存在的情况
|
||
|
||
#### 核心特性
|
||
- **Spring集成**: 使用`@ResponseStatus(HttpStatus.NOT_FOUND)`注解
|
||
- **自动映射**: 异常抛出时自动返回404状态码
|
||
- **继承关系**: 继承自`RuntimeException`,支持非检查异常
|
||
|
||
#### 使用场景
|
||
1. **用户查询**: 查询不存在的用户ID
|
||
2. **任务查询**: 查询不存在的任务ID
|
||
3. **反馈查询**: 查询不存在的反馈记录
|
||
4. **文件查询**: 查询不存在的附件文件
|
||
|
||
### 3. InvalidOperationException
|
||
|
||
#### 功能概述
|
||
- **用途**: 无效操作异常
|
||
- **HTTP状态**: 400 Bad Request
|
||
- **触发场景**: 业务规则不允许的操作时抛出
|
||
- **业务价值**: 保护业务逻辑完整性
|
||
|
||
#### 核心特性
|
||
- **业务保护**: 防止违反业务规则的操作
|
||
- **状态映射**: 自动返回400状态码
|
||
- **简洁设计**: 只提供消息构造器,专注业务逻辑
|
||
|
||
#### 使用场景
|
||
1. **状态变更**: 任务状态不允许的变更操作
|
||
2. **权限检查**: 用户权限不足的操作
|
||
3. **数据验证**: 业务数据不符合规则
|
||
4. **流程控制**: 违反业务流程的操作
|
||
|
||
### 4. UserAlreadyExistsException
|
||
|
||
#### 功能概述
|
||
- **用途**: 用户已存在异常
|
||
- **HTTP状态**: 409 Conflict
|
||
- **触发场景**: 注册时用户名或邮箱已存在
|
||
- **业务价值**: 防止重复用户注册
|
||
|
||
#### 核心特性
|
||
- **冲突处理**: 使用409状态码表示资源冲突
|
||
- **注册保护**: 确保用户唯一性
|
||
- **清晰语义**: 明确表达用户重复的问题
|
||
|
||
#### 使用场景
|
||
1. **用户注册**: 用户名已被占用
|
||
2. **邮箱注册**: 邮箱地址已被使用
|
||
3. **账号创建**: 管理员创建重复账号
|
||
|
||
### 5. FileNotFoundException
|
||
|
||
#### 功能概述
|
||
- **用途**: 文件不存在异常
|
||
- **HTTP状态**: 404 Not Found
|
||
- **触发场景**: 请求的文件不存在时抛出
|
||
- **业务价值**: 专门处理文件相关的404错误
|
||
|
||
#### 核心特性
|
||
- **文件专用**: 专门处理文件操作异常
|
||
- **双构造器**: 支持消息和原因链的构造
|
||
- **异常链**: 支持异常原因追踪
|
||
|
||
#### 使用场景
|
||
1. **附件下载**: 下载不存在的附件文件
|
||
2. **图片访问**: 访问不存在的图片资源
|
||
3. **文档查看**: 查看已删除的文档
|
||
|
||
### 6. FileStorageException
|
||
|
||
#### 功能概述
|
||
- **用途**: 文件存储异常
|
||
- **HTTP状态**: 默认500(通过全局处理器处理)
|
||
- **触发场景**: 文件存储操作失败时抛出
|
||
- **业务价值**: 统一处理文件存储相关错误
|
||
|
||
#### 核心特性
|
||
- **存储专用**: 专门处理文件存储异常
|
||
- **异常链支持**: 保留原始异常信息
|
||
- **灵活处理**: 可通过全局处理器自定义响应
|
||
|
||
#### 使用场景
|
||
1. **文件上传**: 上传过程中的存储失败
|
||
2. **文件删除**: 删除文件时的IO错误
|
||
3. **文件移动**: 文件移动或重命名失败
|
||
4. **磁盘空间**: 存储空间不足的情况
|
||
|
||
### 7. GlobalExceptionHandler
|
||
|
||
#### 功能概述
|
||
- **用途**: 全局异常处理器
|
||
- **作用**: 统一处理控制器层抛出的异常
|
||
- **业务价值**: 提供一致的错误响应和完善的日志记录
|
||
|
||
#### 核心特性
|
||
- **全局捕获**: 使用`@ControllerAdvice`注解实现全局异常捕获
|
||
- **分类处理**: 针对不同异常类型提供专门的处理方法
|
||
- **日志记录**: 使用`@Slf4j`记录详细的错误日志
|
||
- **标准响应**: 返回统一格式的错误响应
|
||
|
||
#### 处理的异常类型
|
||
|
||
##### ResourceNotFoundException处理
|
||
```java
|
||
@ExceptionHandler(ResourceNotFoundException.class)
|
||
public ResponseEntity<ErrorResponseDTO> handleResourceNotFoundException(
|
||
ResourceNotFoundException ex, WebRequest request) {
|
||
ErrorResponseDTO errorResponse = new ErrorResponseDTO(
|
||
LocalDateTime.now(),
|
||
HttpStatus.NOT_FOUND.value(),
|
||
"Not Found",
|
||
ex.getMessage(),
|
||
request.getDescription(false).replace("uri=", ""));
|
||
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
|
||
}
|
||
```
|
||
|
||
##### InvalidOperationException处理
|
||
```java
|
||
@ExceptionHandler(InvalidOperationException.class)
|
||
public ResponseEntity<ErrorResponseDTO> handleInvalidOperationException(
|
||
InvalidOperationException ex, WebRequest request) {
|
||
ErrorResponseDTO errorResponse = new ErrorResponseDTO(
|
||
LocalDateTime.now(),
|
||
HttpStatus.BAD_REQUEST.value(),
|
||
"Bad Request",
|
||
ex.getMessage(),
|
||
request.getDescription(false).replace("uri=", ""));
|
||
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
|
||
}
|
||
```
|
||
|
||
##### 全局异常处理
|
||
```java
|
||
@ExceptionHandler(Exception.class)
|
||
public ResponseEntity<?> handleGlobalException(Exception ex, WebRequest request) {
|
||
log.error("捕获到全局未处理异常! 请求: {}", request.getDescription(false), ex);
|
||
|
||
Map<String, String> errorDetails = Map.of(
|
||
"message", "服务器内部发生未知错误,请联系技术支持",
|
||
"error", ex.getClass().getName(),
|
||
"details", ex.getMessage()
|
||
);
|
||
|
||
return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
|
||
}
|
||
```
|
||
|
||
## 异常处理架构优势
|
||
|
||
### 1. 统一错误格式
|
||
- **一致性**: 所有API错误都使用相同的响应格式
|
||
- **可预测**: 前端可以统一处理错误响应
|
||
- **信息完整**: 包含时间戳、状态码、错误类型、消息和路径
|
||
|
||
### 2. 分层异常处理
|
||
- **业务异常**: 自定义异常类处理特定业务场景
|
||
- **系统异常**: 全局处理器兜底处理未预期异常
|
||
- **HTTP映射**: 自动将异常映射为合适的HTTP状态码
|
||
|
||
### 3. 完善的日志记录
|
||
- **错误追踪**: 详细记录异常发生的上下文
|
||
- **问题定位**: 便于开发人员快速定位问题
|
||
- **监控集成**: 支持与监控系统集成
|
||
|
||
### 4. 用户友好
|
||
- **清晰消息**: 提供用户可理解的错误消息
|
||
- **状态码**: 正确的HTTP状态码便于前端处理
|
||
- **安全性**: 不暴露系统内部敏感信息
|
||
|
||
## 异常处理流程
|
||
|
||
### 标准异常处理流程
|
||
```
|
||
1. 业务层抛出自定义异常
|
||
2. 控制器层捕获异常
|
||
3. GlobalExceptionHandler拦截异常
|
||
4. 根据异常类型选择处理方法
|
||
5. 构建ErrorResponseDTO响应
|
||
6. 记录错误日志
|
||
7. 返回标准化错误响应
|
||
```
|
||
|
||
### 异常分类处理
|
||
```
|
||
业务异常:
|
||
├── ResourceNotFoundException (404) - 资源不存在
|
||
├── InvalidOperationException (400) - 无效操作
|
||
├── UserAlreadyExistsException (409) - 用户已存在
|
||
├── FileNotFoundException (404) - 文件不存在
|
||
└── FileStorageException (500) - 文件存储错误
|
||
|
||
系统异常:
|
||
└── Exception (500) - 未处理的系统异常
|
||
```
|
||
|
||
## Exception模块最佳实践
|
||
|
||
1. **异常分类**: 根据业务场景创建专门的异常类
|
||
2. **状态码映射**: 使用`@ResponseStatus`注解自动映射HTTP状态码
|
||
3. **消息设计**: 提供清晰、用户友好的错误消息
|
||
4. **日志记录**: 在全局处理器中记录详细的错误日志
|
||
5. **安全考虑**: 避免在错误响应中暴露敏感信息
|
||
6. **异常链**: 保留原始异常信息便于问题追踪
|
||
7. **统一格式**: 使用ErrorResponseDTO确保响应格式一致
|
||
|
||
## Exception模块总结
|
||
|
||
Exception模块为EMS系统提供了完善的异常处理能力:
|
||
|
||
1. **完整体系**: 涵盖业务异常、系统异常和文件异常的完整处理体系
|
||
2. **统一响应**: 通过ErrorResponseDTO提供一致的错误响应格式
|
||
3. **自动映射**: 利用Spring的@ResponseStatus注解自动映射HTTP状态码
|
||
4. **全局处理**: GlobalExceptionHandler提供兜底的异常处理机制
|
||
5. **日志完善**: 详细的错误日志记录便于问题追踪和系统监控
|
||
6. **用户友好**: 清晰的错误消息和正确的状态码提升用户体验
|
||
7. **安全可靠**: 在提供有用信息的同时保护系统安全
|
||
|
||
通过这套异常处理机制,EMS系统能够优雅地处理各种异常情况,为用户提供清晰的错误反馈,为开发人员提供完善的错误追踪能力,大大提升了系统的健壮性和可维护性。
|
||
|
||
---
|
||
|
||
# Listener模块详细解析
|
||
|
||
## 概述
|
||
|
||
Listener(监听器)模块位于 `com.dne.ems.listener` 包下,包含3个事件监听器类,实现了系统的事件驱动响应机制。通过监听Spring Security认证事件和自定义业务事件,实现了安全控制、用户状态管理和业务流程自动化,为系统的安全性和业务连续性提供了重要保障。
|
||
|
||
## 核心监听器类
|
||
|
||
### 1. AuthenticationFailureEventListener
|
||
|
||
#### 功能概述
|
||
- **用途**: 认证失败事件监听器
|
||
- **监听事件**: `AuthenticationFailureBadCredentialsEvent`
|
||
- **触发场景**: 用户登录认证失败时
|
||
- **业务价值**: 实现登录失败次数统计和IP锁定机制
|
||
|
||
#### 核心特性
|
||
- **Spring Security集成**: 监听Spring Security的认证失败事件
|
||
- **IP地址获取**: 智能获取真实客户端IP地址
|
||
- **代理支持**: 支持X-Forwarded-For头部处理
|
||
- **安全防护**: 防止暴力破解攻击
|
||
|
||
#### 代码实现
|
||
```java
|
||
@Component
|
||
public class AuthenticationFailureEventListener
|
||
implements ApplicationListener<AuthenticationFailureBadCredentialsEvent> {
|
||
|
||
@Autowired
|
||
private LoginAttemptService loginAttemptService;
|
||
|
||
@Autowired
|
||
private HttpServletRequest request;
|
||
|
||
@Override
|
||
public void onApplicationEvent(AuthenticationFailureBadCredentialsEvent event) {
|
||
final String xfHeader = request.getHeader("X-Forwarded-For");
|
||
String ip;
|
||
if (xfHeader == null || xfHeader.isEmpty() || !xfHeader.contains(request.getRemoteAddr())) {
|
||
ip = request.getRemoteAddr();
|
||
} else {
|
||
ip = xfHeader.split(",")[0];
|
||
}
|
||
|
||
// 使用IP地址作为锁定键
|
||
loginAttemptService.loginFailed(ip);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### IP地址获取逻辑
|
||
1. **代理检测**: 检查X-Forwarded-For头部是否存在
|
||
2. **真实IP**: 优先使用代理转发的真实客户端IP
|
||
3. **直连IP**: 无代理时使用直接连接的IP地址
|
||
4. **安全验证**: 验证代理IP的合法性
|
||
|
||
#### 使用场景
|
||
1. **登录保护**: 记录失败的登录尝试
|
||
2. **暴力破解防护**: 限制同一IP的登录尝试次数
|
||
3. **安全审计**: 记录可疑的登录行为
|
||
4. **IP黑名单**: 自动封禁恶意IP地址
|
||
|
||
### 2. AuthenticationSuccessEventListener
|
||
|
||
#### 功能概述
|
||
- **用途**: 认证成功事件监听器
|
||
- **监听事件**: `AuthenticationSuccessEvent`
|
||
- **触发场景**: 用户登录认证成功时
|
||
- **业务价值**: 重置用户登录失败计数和解除账户锁定
|
||
|
||
#### 核心特性
|
||
- **状态重置**: 成功登录后重置失败计数
|
||
- **锁定解除**: 自动解除账户锁定状态
|
||
- **用户管理**: 维护用户账户状态的一致性
|
||
- **安全恢复**: 恢复正常用户的访问权限
|
||
|
||
#### 代码实现
|
||
```java
|
||
@Component
|
||
public class AuthenticationSuccessEventListener
|
||
implements ApplicationListener<AuthenticationSuccessEvent> {
|
||
|
||
@Autowired
|
||
private UserAccountRepository userAccountRepository;
|
||
|
||
@Override
|
||
public void onApplicationEvent(@NonNull AuthenticationSuccessEvent event) {
|
||
Object principal = event.getAuthentication().getPrincipal();
|
||
|
||
if (principal instanceof UserDetails userDetails) {
|
||
String username = userDetails.getUsername();
|
||
UserAccount user = userAccountRepository.findByEmail(username).orElse(null);
|
||
|
||
if (user != null && user.getFailedLoginAttempts() > 0) {
|
||
user.setFailedLoginAttempts(0);
|
||
user.setLockoutEndTime(null);
|
||
userAccountRepository.save(user);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 处理流程
|
||
1. **身份验证**: 确认认证主体是UserDetails实例
|
||
2. **用户查找**: 根据用户名查找用户账户
|
||
3. **状态检查**: 检查是否存在失败登录记录
|
||
4. **状态重置**: 清零失败次数并解除锁定
|
||
5. **数据持久化**: 保存更新后的用户状态
|
||
|
||
#### 使用场景
|
||
1. **账户恢复**: 成功登录后恢复账户正常状态
|
||
2. **计数重置**: 清除之前的失败登录记录
|
||
3. **锁定解除**: 自动解除临时锁定状态
|
||
4. **状态同步**: 保持用户状态的一致性
|
||
|
||
### 3. FeedbackEventListener
|
||
|
||
#### 功能概述
|
||
- **用途**: 反馈事件监听器
|
||
- **监听事件**: `FeedbackSubmittedForAiReviewEvent`
|
||
- **触发场景**: 反馈提交需要AI审核时
|
||
- **业务价值**: 实现反馈的异步AI审核处理
|
||
|
||
#### 核心特性
|
||
- **异步处理**: 使用`@Async`注解实现异步执行
|
||
- **事件驱动**: 基于Spring的事件机制
|
||
- **AI集成**: 调用AI服务进行内容审核
|
||
- **日志记录**: 详细记录处理过程
|
||
|
||
#### 代码实现
|
||
```java
|
||
@Component
|
||
@Slf4j
|
||
@RequiredArgsConstructor
|
||
public class FeedbackEventListener {
|
||
|
||
private final AiReviewService aiReviewService;
|
||
|
||
@Async
|
||
@EventListener
|
||
public void handleFeedbackSubmittedForAiReview(FeedbackSubmittedForAiReviewEvent event) {
|
||
log.info("Received feedback submission event for AI review. Feedback ID: {}",
|
||
event.getFeedback().getId());
|
||
aiReviewService.reviewFeedback(event.getFeedback());
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 处理特点
|
||
1. **异步执行**: 不阻塞主业务流程
|
||
2. **事件响应**: 响应反馈提交事件
|
||
3. **AI调用**: 调用AI服务进行内容分析
|
||
4. **日志追踪**: 记录处理过程便于监控
|
||
|
||
#### 使用场景
|
||
1. **内容审核**: 自动审核用户提交的反馈内容
|
||
2. **智能分类**: AI分析反馈的类型和优先级
|
||
3. **质量评估**: 评估反馈的有效性和重要性
|
||
4. **自动处理**: 根据AI分析结果自动处理简单反馈
|
||
|
||
## 监听器架构优势
|
||
|
||
### 1. 事件驱动架构
|
||
- **解耦设计**: 事件发布者和监听者之间松耦合
|
||
- **扩展性强**: 可以轻松添加新的事件监听器
|
||
- **职责分离**: 每个监听器专注于特定的业务逻辑
|
||
- **异步支持**: 支持异步事件处理提升性能
|
||
|
||
### 2. 安全机制增强
|
||
- **登录保护**: 自动记录和处理登录失败
|
||
- **状态管理**: 智能管理用户账户状态
|
||
- **攻击防护**: 防止暴力破解和恶意登录
|
||
- **恢复机制**: 自动恢复正常用户的访问权限
|
||
|
||
### 3. 业务流程自动化
|
||
- **智能审核**: 自动触发AI内容审核
|
||
- **状态同步**: 自动同步用户和业务状态
|
||
- **流程优化**: 减少手动干预提升效率
|
||
- **响应及时**: 实时响应业务事件
|
||
|
||
### 4. 系统可观测性
|
||
- **日志记录**: 详细记录事件处理过程
|
||
- **状态追踪**: 跟踪用户和业务状态变化
|
||
- **性能监控**: 监控事件处理性能
|
||
- **问题定位**: 便于排查和解决问题
|
||
|
||
## 事件处理流程
|
||
|
||
### 认证失败处理流程
|
||
```
|
||
1. 用户登录失败 → Spring Security触发AuthenticationFailureBadCredentialsEvent
|
||
2. AuthenticationFailureEventListener接收事件
|
||
3. 获取客户端真实IP地址
|
||
4. 调用LoginAttemptService记录失败尝试
|
||
5. 更新IP失败计数和锁定状态
|
||
```
|
||
|
||
### 认证成功处理流程
|
||
```
|
||
1. 用户登录成功 → Spring Security触发AuthenticationSuccessEvent
|
||
2. AuthenticationSuccessEventListener接收事件
|
||
3. 获取用户身份信息
|
||
4. 查找用户账户记录
|
||
5. 重置失败计数和解除锁定
|
||
6. 保存用户状态更新
|
||
```
|
||
|
||
### 反馈AI审核流程
|
||
```
|
||
1. 反馈提交 → 发布FeedbackSubmittedForAiReviewEvent
|
||
2. FeedbackEventListener异步接收事件
|
||
3. 记录处理日志
|
||
4. 调用AiReviewService进行内容审核
|
||
5. 更新反馈状态和审核结果
|
||
```
|
||
|
||
## 技术实现细节
|
||
|
||
### 1. Spring事件监听机制
|
||
- **ApplicationListener**: 传统的事件监听接口
|
||
- **@EventListener**: 基于注解的事件监听方法
|
||
- **@Async**: 异步事件处理注解
|
||
- **事件发布**: 通过ApplicationEventPublisher发布事件
|
||
|
||
### 2. 依赖注入和管理
|
||
- **@Component**: 将监听器注册为Spring组件
|
||
- **@Autowired**: 自动注入依赖服务
|
||
- **@RequiredArgsConstructor**: Lombok构造器注入
|
||
- **生命周期管理**: Spring容器管理监听器生命周期
|
||
|
||
### 3. 异步处理配置
|
||
```java
|
||
@EnableAsync
|
||
@Configuration
|
||
public class AsyncConfig {
|
||
@Bean
|
||
public TaskExecutor taskExecutor() {
|
||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||
executor.setCorePoolSize(2);
|
||
executor.setMaxPoolSize(10);
|
||
executor.setQueueCapacity(500);
|
||
executor.setThreadNamePrefix("EventListener-");
|
||
executor.initialize();
|
||
return executor;
|
||
}
|
||
}
|
||
```
|
||
|
||
## Listener模块最佳实践
|
||
|
||
1. **事件监听**: 使用合适的监听方式(接口或注解)
|
||
2. **异步处理**: 对于耗时操作使用@Async注解
|
||
3. **异常处理**: 在监听器中添加完善的异常处理
|
||
4. **日志记录**: 记录关键事件的处理过程
|
||
5. **性能考虑**: 避免在监听器中执行阻塞操作
|
||
6. **事务管理**: 注意事件监听器的事务边界
|
||
7. **测试覆盖**: 为监听器编写单元测试和集成测试
|
||
|
||
## Listener模块总结
|
||
|
||
Listener模块为EMS系统提供了强大的事件响应能力:
|
||
|
||
1. **安全保障**: 通过认证事件监听实现登录安全控制
|
||
2. **状态管理**: 自动管理用户账户和业务状态
|
||
3. **业务自动化**: 实现反馈AI审核等业务流程自动化
|
||
4. **异步处理**: 支持异步事件处理提升系统性能
|
||
5. **扩展性强**: 易于添加新的事件监听器扩展功能
|
||
6. **可观测性**: 提供详细的事件处理日志和监控
|
||
7. **Spring集成**: 充分利用Spring框架的事件机制
|
||
|
||
通过这套监听器机制,EMS系统能够及时响应各种系统事件和业务事件,实现了安全控制、状态管理和业务流程的自动化,为系统的稳定运行和用户体验提供了重要保障。
|
||
|
||
---
|
||
|
||
# Model模块详细解析
|
||
|
||
## 概述
|
||
|
||
Model(数据模型)模块位于 `com.dne.ems.model` 包下,包含14个核心实体类和10个枚举类,构成了EMS系统的完整数据模型。该模块使用JPA/Hibernate进行对象关系映射,定义了系统中所有业务实体的结构、关系和约束,为整个系统提供了坚实的数据基础。
|
||
|
||
## 核心实体类
|
||
|
||
### 1. 用户管理相关实体
|
||
|
||
#### UserAccount(用户账户)
|
||
|
||
**功能概述**
|
||
- **用途**: 系统用户账户的核心实体
|
||
- **映射表**: `user_account`
|
||
- **业务价值**: 管理所有用户的基本信息、角色权限和状态
|
||
|
||
**核心特性**
|
||
- **身份验证**: 存储用户登录凭据(邮箱、手机、密码)
|
||
- **角色管理**: 支持多种用户角色(管理员、主管、网格员等)
|
||
- **地理关联**: 支持网格坐标和区域信息
|
||
- **技能管理**: JSON格式存储用户技能列表
|
||
- **状态控制**: 支持账户激活、停用、请假等状态
|
||
|
||
**关键字段**
|
||
```java
|
||
@Entity
|
||
@Table(name = "user_account")
|
||
public class UserAccount {
|
||
@Id
|
||
private Long id;
|
||
|
||
@NotEmpty
|
||
@Column(unique = true)
|
||
private String email;
|
||
|
||
@NotEmpty
|
||
@Column(unique = true)
|
||
private String phone;
|
||
|
||
@Enumerated(EnumType.STRING)
|
||
private Role role;
|
||
|
||
@Enumerated(EnumType.STRING)
|
||
private UserStatus status;
|
||
|
||
@Convert(converter = StringListConverter.class)
|
||
private List<String> skills;
|
||
|
||
private Integer gridX;
|
||
private Integer gridY;
|
||
}
|
||
```
|
||
|
||
#### PasswordResetToken(密码重置令牌)
|
||
|
||
**功能概述**
|
||
- **用途**: 管理用户密码重置流程的安全令牌
|
||
- **映射表**: `password_reset_token`
|
||
- **业务价值**: 提供安全的密码重置机制
|
||
|
||
**核心特性**
|
||
- **安全性**: 唯一令牌字符串,防止伪造
|
||
- **时效性**: 60分钟有效期,自动过期
|
||
- **一对一关联**: 每个令牌关联特定用户
|
||
- **状态检查**: 提供过期检查方法
|
||
|
||
### 2. 反馈管理相关实体
|
||
|
||
#### Feedback(环境问题反馈)
|
||
|
||
**功能概述**
|
||
- **用途**: 系统核心实体,记录用户提交的环境问题报告
|
||
- **映射表**: `feedback`
|
||
- **业务价值**: 收集、管理和跟踪环境问题反馈
|
||
|
||
**核心特性**
|
||
- **唯一标识**: 人类可读的事件ID
|
||
- **分类管理**: 污染类型和严重程度分类
|
||
- **状态跟踪**: 完整的处理状态生命周期
|
||
- **地理定位**: 支持网格坐标和文本地址
|
||
- **关联管理**: 与用户、任务、附件的关联关系
|
||
|
||
**关键字段**
|
||
```java
|
||
@Entity
|
||
@Table(name = "feedback")
|
||
public class Feedback {
|
||
@Id
|
||
private Long id;
|
||
|
||
@Column(unique = true)
|
||
private String eventId;
|
||
|
||
@Enumerated(EnumType.STRING)
|
||
private PollutionType pollutionType;
|
||
|
||
@Enumerated(EnumType.STRING)
|
||
private SeverityLevel severityLevel;
|
||
|
||
@Enumerated(EnumType.STRING)
|
||
private FeedbackStatus status;
|
||
|
||
private Integer gridX;
|
||
private Integer gridY;
|
||
|
||
@OneToMany(mappedBy = "feedback")
|
||
private List<Attachment> attachments;
|
||
}
|
||
```
|
||
|
||
#### Attachment(附件)
|
||
|
||
**功能概述**
|
||
- **用途**: 管理反馈和任务提交的文件附件
|
||
- **映射表**: `attachment`
|
||
- **业务价值**: 支持多媒体证据收集和存储
|
||
|
||
**核心特性**
|
||
- **文件元数据**: 存储文件名、类型、大小等信息
|
||
- **存储管理**: 生成唯一存储文件名防止冲突
|
||
- **多重关联**: 可关联反馈或任务提交
|
||
- **时间戳**: 自动记录上传时间
|
||
|
||
### 3. 任务管理相关实体
|
||
|
||
#### Task(网格任务)
|
||
|
||
**功能概述**
|
||
- **用途**: 表示网格工作人员执行的任务
|
||
- **映射表**: `tasks`
|
||
- **业务价值**: 管理从反馈到任务的完整生命周期
|
||
|
||
**核心特性**
|
||
- **反馈关联**: 可从反馈自动生成或手动创建
|
||
- **分配管理**: 支持任务分配和重新分配
|
||
- **状态跟踪**: 完整的任务状态生命周期
|
||
- **时间管理**: 创建、分配、完成时间戳
|
||
- **地理信息**: 继承反馈的位置信息
|
||
|
||
**关键字段**
|
||
```java
|
||
@Entity
|
||
@Table(name = "tasks")
|
||
public class Task {
|
||
@Id
|
||
private Long id;
|
||
|
||
@OneToOne
|
||
private Feedback feedback;
|
||
|
||
@ManyToOne
|
||
private UserAccount assignee;
|
||
|
||
@Enumerated(EnumType.STRING)
|
||
private TaskStatus status;
|
||
|
||
@CreationTimestamp
|
||
private LocalDateTime createdAt;
|
||
|
||
private LocalDateTime assignedAt;
|
||
private LocalDateTime completedAt;
|
||
}
|
||
```
|
||
|
||
#### TaskSubmission(任务提交)
|
||
|
||
**功能概述**
|
||
- **用途**: 记录网格员完成任务后的提交信息
|
||
- **映射表**: `task_submissions`
|
||
- **业务价值**: 收集任务执行结果和工作记录
|
||
|
||
**核心特性**
|
||
- **任务关联**: 与特定任务一对多关系
|
||
- **详细记录**: 大文本字段存储工作笔记
|
||
- **时间戳**: 自动记录提交时间
|
||
- **附件支持**: 可关联多个附件文件
|
||
|
||
#### TaskHistory(任务历史)
|
||
|
||
**功能概述**
|
||
- **用途**: 记录任务状态变更的审计日志
|
||
- **映射表**: `task_history`
|
||
- **业务价值**: 提供完整的任务处理轨迹
|
||
|
||
**核心特性**
|
||
- **状态变更**: 记录旧状态和新状态
|
||
- **操作人员**: 记录状态变更的执行者
|
||
- **变更时间**: 自动记录变更时间戳
|
||
- **备注信息**: 支持变更原因说明
|
||
|
||
### 4. 分配管理相关实体
|
||
|
||
#### Assignment(任务分配)
|
||
|
||
**功能概述**
|
||
- **用途**: 管理反馈任务到网格员的分配关系
|
||
- **映射表**: `assignments`
|
||
- **业务价值**: 跟踪任务分配的生命周期
|
||
|
||
**核心特性**
|
||
- **一对一关联**: 每个分配对应一个任务
|
||
- **分配者记录**: 记录执行分配的用户
|
||
- **截止日期**: 支持任务截止时间设置
|
||
- **状态管理**: 跟踪分配状态变化
|
||
- **备注信息**: 支持分配说明和要求
|
||
|
||
#### AssignmentRecord(分配记录)
|
||
|
||
**功能概述**
|
||
- **用途**: 详细记录反馈分配的元数据
|
||
- **映射表**: `assignment_record`
|
||
- **业务价值**: 提供丰富的分配分析数据
|
||
|
||
**核心特性**
|
||
- **关系映射**: 直接关联反馈、网格员、管理员
|
||
- **分配方法**: 记录手动或智能分配方式
|
||
- **算法详情**: JSON格式存储智能分配算法细节
|
||
- **创建时间**: 自动记录分配创建时间
|
||
|
||
### 5. 空气质量数据相关实体
|
||
|
||
#### AqiData(空气质量指数数据)
|
||
|
||
**功能概述**
|
||
- **用途**: 存储网格员提交的详细空气质量数据
|
||
- **映射表**: `aqi_data`
|
||
- **业务价值**: 收集实时环境监测数据
|
||
|
||
**核心特性**
|
||
- **多污染物**: 支持PM2.5、PM10、SO2、NO2、CO、O3等
|
||
- **网格关联**: 与具体网格位置关联
|
||
- **报告者**: 记录数据提交的网格员
|
||
- **反馈关联**: 可选关联相关反馈
|
||
|
||
**关键字段**
|
||
```java
|
||
@Entity
|
||
@Table(name = "aqi_data")
|
||
public class AqiData {
|
||
@Id
|
||
private Long id;
|
||
|
||
@ManyToOne
|
||
private Grid grid;
|
||
|
||
private Long reporterId;
|
||
private Integer aqiValue;
|
||
private Double pm25;
|
||
private Double pm10;
|
||
private Double so2;
|
||
private Double no2;
|
||
private Double co;
|
||
private Double o3;
|
||
}
|
||
```
|
||
|
||
#### AqiRecord(空气质量历史记录)
|
||
|
||
**功能概述**
|
||
- **用途**: 存储历史空气质量数据用于趋势分析
|
||
- **映射表**: `aqi_records`
|
||
- **业务价值**: 支持热力图生成和趋势分析
|
||
|
||
**核心特性**
|
||
- **城市级别**: 按城市名称组织数据
|
||
- **完整污染物**: 包含所有主要污染物浓度
|
||
- **地理坐标**: 支持经纬度和网格坐标
|
||
- **时间戳**: 自动记录数据时间
|
||
|
||
### 6. 地理网格相关实体
|
||
|
||
#### Grid(地理网格)
|
||
|
||
**功能概述**
|
||
- **用途**: 表示地理网格单元用于区域管理
|
||
- **映射表**: `grid`
|
||
- **业务价值**: 支持基于网格的数据聚合和管理
|
||
|
||
**核心特性**
|
||
- **坐标系统**: X、Y坐标定位
|
||
- **行政区划**: 城市和区县信息
|
||
- **障碍标记**: 支持限制区域标记
|
||
- **描述信息**: 网格详细描述
|
||
|
||
#### MapGrid(地图网格)
|
||
|
||
**功能概述**
|
||
- **用途**: 专门用于A*寻路算法的网格单元
|
||
- **映射表**: `map_grid`
|
||
- **业务价值**: 支持智能路径规划和导航
|
||
|
||
**核心特性**
|
||
- **唯一约束**: (x,y)坐标唯一性约束
|
||
- **障碍检测**: 支持障碍物标记
|
||
- **地形类型**: 可选的地形类型信息
|
||
- **城市约束**: 限制寻路范围
|
||
|
||
### 7. 系统配置相关实体
|
||
|
||
#### PollutantThreshold(污染物阈值)
|
||
|
||
**功能概述**
|
||
- **用途**: 存储不同污染物的超标阈值设置
|
||
- **映射表**: `pollutant_thresholds`
|
||
- **业务价值**: 支持污染物超标检测和预警
|
||
|
||
**核心特性**
|
||
- **类型关联**: 与污染物类型枚举关联
|
||
- **阈值设置**: 可配置的超标阈值
|
||
- **单位管理**: 支持不同计量单位
|
||
- **描述信息**: 阈值设置说明
|
||
|
||
## 枚举类型系统
|
||
|
||
### 1. 用户相关枚举
|
||
|
||
#### Role(用户角色)
|
||
```java
|
||
public enum Role {
|
||
PUBLIC_SUPERVISOR, // 公众监督员
|
||
SUPERVISOR, // 主管
|
||
GRID_WORKER, // 网格员
|
||
ADMIN, // 系统管理员
|
||
DECISION_MAKER // 决策者
|
||
}
|
||
```
|
||
|
||
#### UserStatus(用户状态)
|
||
```java
|
||
public enum UserStatus {
|
||
ACTIVE, // 活跃
|
||
INACTIVE, // 停用
|
||
ON_LEAVE // 请假
|
||
}
|
||
```
|
||
|
||
#### Gender(性别)
|
||
```java
|
||
public enum Gender {
|
||
MALE, // 男性
|
||
FEMALE, // 女性
|
||
UNKNOWN // 未知
|
||
}
|
||
```
|
||
|
||
#### Level(级别)
|
||
```java
|
||
public enum Level {
|
||
PROVINCE, // 省级
|
||
CITY // 市级
|
||
}
|
||
```
|
||
|
||
### 2. 业务流程相关枚举
|
||
|
||
#### FeedbackStatus(反馈状态)
|
||
```java
|
||
public enum FeedbackStatus {
|
||
PENDING_REVIEW, // 待审核
|
||
AI_REVIEWING, // AI审核中
|
||
AI_REVIEW_FAILED, // AI审核失败
|
||
AI_PROCESSING, // AI处理中
|
||
PENDING_ASSIGNMENT, // 待分配
|
||
ASSIGNED, // 已分配
|
||
CONFIRMED, // 已确认
|
||
RESOLVED, // 已解决
|
||
CLOSED // 已关闭
|
||
}
|
||
```
|
||
|
||
#### TaskStatus(任务状态)
|
||
```java
|
||
public enum TaskStatus {
|
||
PENDING_ASSIGNMENT, // 待分配
|
||
ASSIGNED, // 已分配
|
||
IN_PROGRESS, // 进行中
|
||
SUBMITTED, // 已提交
|
||
COMPLETED, // 已完成
|
||
CANCELLED // 已取消
|
||
}
|
||
```
|
||
|
||
#### AssignmentStatus(分配状态)
|
||
```java
|
||
public enum AssignmentStatus {
|
||
PENDING, // 待处理
|
||
IN_PROGRESS, // 进行中
|
||
COMPLETED, // 已完成
|
||
OVERDUE // 已逾期
|
||
}
|
||
```
|
||
|
||
#### AssignmentMethod(分配方法)
|
||
```java
|
||
public enum AssignmentMethod {
|
||
MANUAL, // 手动分配
|
||
INTELLIGENT // 智能分配
|
||
}
|
||
```
|
||
|
||
### 3. 环境数据相关枚举
|
||
|
||
#### PollutionType(污染类型)
|
||
```java
|
||
public enum PollutionType {
|
||
PM25, // 细颗粒物
|
||
O3, // 臭氧
|
||
NO2, // 二氧化氮
|
||
SO2, // 二氧化硫
|
||
OTHER // 其他
|
||
}
|
||
```
|
||
|
||
#### SeverityLevel(严重程度)
|
||
```java
|
||
public enum SeverityLevel {
|
||
HIGH, // 高
|
||
MEDIUM, // 中
|
||
LOW // 低
|
||
}
|
||
```
|
||
|
||
## 数据模型设计特点
|
||
|
||
### 1. 关系设计
|
||
- **一对一关系**: Task-Feedback, Assignment-Task
|
||
- **一对多关系**: UserAccount-Task, Feedback-Attachment
|
||
- **多对一关系**: AqiData-Grid, Task-UserAccount
|
||
- **自引用关系**: TaskHistory-Task(审计日志)
|
||
|
||
### 2. 约束和验证
|
||
- **唯一性约束**: 用户邮箱、手机号、反馈事件ID
|
||
- **非空约束**: 关键业务字段强制非空
|
||
- **枚举约束**: 使用枚举类型确保数据一致性
|
||
- **长度约束**: 字符串字段长度限制
|
||
|
||
### 3. 时间戳管理
|
||
- **创建时间**: @CreationTimestamp自动设置
|
||
- **更新时间**: @UpdateTimestamp自动更新
|
||
- **业务时间**: 分配时间、完成时间等业务节点
|
||
- **过期检查**: 密码重置令牌过期验证
|
||
|
||
### 4. JSON数据支持
|
||
- **技能列表**: 用户技能JSON存储
|
||
- **算法详情**: 智能分配算法详情JSON存储
|
||
- **大文本**: 使用@Lob注解支持长文本
|
||
|
||
## 数据模型架构优势
|
||
|
||
### 1. 业务完整性
|
||
- **全生命周期**: 覆盖从反馈提交到任务完成的完整流程
|
||
- **状态管理**: 详细的状态枚举和状态转换
|
||
- **审计追踪**: 完整的操作历史记录
|
||
- **数据关联**: 清晰的实体关系和数据流向
|
||
|
||
### 2. 扩展性设计
|
||
- **枚举扩展**: 易于添加新的状态和类型
|
||
- **实体扩展**: 支持新增字段和关系
|
||
- **JSON存储**: 灵活的半结构化数据支持
|
||
- **继承支持**: 预留实体继承扩展空间
|
||
|
||
### 3. 性能优化
|
||
- **懒加载**: 使用FetchType.LAZY优化查询性能
|
||
- **索引设计**: 唯一约束自动创建索引
|
||
- **批量操作**: 支持CascadeType级联操作
|
||
- **查询优化**: 合理的关系设计减少N+1问题
|
||
|
||
### 4. 数据安全
|
||
- **密码加密**: 密码字段加密存储
|
||
- **令牌安全**: 密码重置令牌时效性控制
|
||
- **状态控制**: 用户状态控制访问权限
|
||
- **数据完整性**: 外键约束保证数据一致性
|
||
|
||
## 技术实现细节
|
||
|
||
### 1. JPA注解使用
|
||
```java
|
||
// 实体映射
|
||
@Entity
|
||
@Table(name = "table_name")
|
||
|
||
// 主键生成
|
||
@Id
|
||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||
|
||
// 关系映射
|
||
@OneToOne
|
||
@OneToMany
|
||
@ManyToOne
|
||
@ManyToMany
|
||
|
||
// 枚举映射
|
||
@Enumerated(EnumType.STRING)
|
||
|
||
// 时间戳管理
|
||
@CreationTimestamp
|
||
@UpdateTimestamp
|
||
```
|
||
|
||
### 2. Lombok注解
|
||
```java
|
||
@Data // getter/setter/toString/equals/hashCode
|
||
@Builder // 建造者模式
|
||
@NoArgsConstructor // 无参构造器
|
||
@AllArgsConstructor // 全参构造器
|
||
```
|
||
|
||
### 3. 验证注解
|
||
```java
|
||
@NotEmpty // 非空验证
|
||
@NotNull // 非null验证
|
||
@Email // 邮箱格式验证
|
||
@Size(min = 8) // 长度验证
|
||
```
|
||
|
||
### 4. 自定义转换器
|
||
```java
|
||
@Convert(converter = StringListConverter.class)
|
||
private List<String> skills;
|
||
```
|
||
|
||
## Model模块最佳实践
|
||
|
||
1. **实体设计**: 遵循单一职责原则,每个实体专注特定业务领域
|
||
2. **关系映射**: 合理使用懒加载,避免性能问题
|
||
3. **枚举使用**: 优先使用枚举类型确保数据一致性
|
||
4. **时间戳**: 统一使用Hibernate注解管理时间字段
|
||
5. **验证约束**: 在实体层添加基础验证约束
|
||
6. **命名规范**: 遵循Java命名规范和数据库命名规范
|
||
7. **文档注释**: 为每个实体和字段添加详细注释
|
||
|
||
## Model模块总结
|
||
|
||
Model模块为EMS系统提供了完整、规范的数据模型基础:
|
||
|
||
1. **业务完整性**: 覆盖用户管理、反馈处理、任务分配、数据收集等全业务流程
|
||
2. **数据一致性**: 通过枚举类型、约束和验证确保数据质量
|
||
3. **关系清晰**: 合理的实体关系设计支持复杂业务场景
|
||
4. **扩展性强**: 灵活的设计支持业务需求变化和功能扩展
|
||
5. **性能优化**: 合理的加载策略和索引设计保证系统性能
|
||
6. **安全可靠**: 完善的约束和验证机制保证数据安全
|
||
7. **标准规范**: 遵循JPA规范和最佳实践,便于维护和扩展
|
||
|
||
通过这套完整的数据模型,EMS系统建立了坚实的数据基础,为上层业务逻辑提供了可靠的数据支撑,确保了系统的稳定性、可扩展性和可维护性。
|
||
|
||
---
|
||
|
||
# Repository模块详细解析
|
||
|
||
## 概述
|
||
|
||
Repository(数据访问层)模块位于 `com.dne.ems.repository` 包下,包含14个Repository接口,构成了EMS系统完整的数据访问层。该模块基于Spring Data JPA框架,提供了标准化的数据访问接口,支持基础CRUD操作、自定义查询方法、复杂查询和分页查询,为业务层提供了高效、可靠的数据访问服务。
|
||
|
||
## 核心Repository接口
|
||
|
||
### 1. 用户管理相关Repository
|
||
|
||
#### UserAccountRepository(用户账户数据访问)
|
||
|
||
**功能概述**
|
||
- **继承接口**: `JpaRepository<UserAccount, Long>`, `JpaSpecificationExecutor<UserAccount>`
|
||
- **业务价值**: 提供用户账户的完整数据访问功能
|
||
- **核心特性**: 支持复杂查询、分页查询、规格查询
|
||
|
||
**核心查询方法**
|
||
```java
|
||
@Repository
|
||
public interface UserAccountRepository extends JpaRepository<UserAccount, Long>, JpaSpecificationExecutor<UserAccount> {
|
||
// 基础查询
|
||
Optional<UserAccount> findByEmail(String email);
|
||
Optional<UserAccount> findByPhone(String phone);
|
||
Optional<UserAccount> findByGridXAndGridY(Integer gridX, Integer gridY);
|
||
|
||
// 角色相关查询
|
||
List<UserAccount> findByRole(Role role);
|
||
long countByRole(Role role);
|
||
Page<UserAccount> findByRole(Role role, Pageable pageable);
|
||
List<UserAccount> findByRoleAndStatus(Role role, UserStatus status);
|
||
|
||
// 网格员专用查询
|
||
@Query("SELECT u FROM UserAccount u WHERE u.role = 'GRID_WORKER' AND u.status = 'ACTIVE' AND u.gridX BETWEEN ?1 AND ?2 AND u.gridY BETWEEN ?3 AND ?4")
|
||
List<UserAccount> findActiveWorkersInArea(int minX, int maxX, int minY, int maxY);
|
||
|
||
@Query("SELECT u FROM UserAccount u WHERE u.role = 'GRID_WORKER' AND u.gridX = ?1 AND u.gridY = ?2")
|
||
List<UserAccount> findGridWorkersByCoordinates(int gridX, int gridY);
|
||
|
||
@Query("SELECT u FROM UserAccount u WHERE u.role = 'GRID_WORKER' AND u.gridX IS NOT NULL AND u.gridY IS NOT NULL AND u.gridX >= 0 AND u.gridY >= 0")
|
||
List<UserAccount> findAllAssignedGridWorkers();
|
||
|
||
@Query("SELECT u FROM UserAccount u WHERE u.role = 'GRID_WORKER' AND (u.gridX IS NULL OR u.gridY IS NULL OR u.gridX < 0 OR u.gridY < 0)")
|
||
List<UserAccount> findAllUnassignedGridWorkers();
|
||
|
||
@Query("SELECT u FROM UserAccount u WHERE u.role = 'GRID_WORKER' AND u.region LIKE %?1%")
|
||
Page<UserAccount> findGridWorkersByCity(String cityName, Pageable pageable);
|
||
}
|
||
```
|
||
|
||
**使用场景**
|
||
- **身份验证**: 通过邮箱或手机号查找用户
|
||
- **角色管理**: 按角色查询和统计用户
|
||
- **网格管理**: 查找特定区域或坐标的网格员
|
||
- **用户分配**: 查找已分配和未分配网格的网格员
|
||
- **区域查询**: 按城市查找网格员
|
||
|
||
#### PasswordResetTokenRepository(密码重置令牌数据访问)
|
||
|
||
**功能概述**
|
||
- **继承接口**: `JpaRepository<PasswordResetToken, Long>`
|
||
- **业务价值**: 管理密码重置令牌的数据访问
|
||
- **核心特性**: 支持令牌查找和用户关联查询
|
||
|
||
**核心查询方法**
|
||
```java
|
||
@Repository
|
||
public interface PasswordResetTokenRepository extends JpaRepository<PasswordResetToken, Long> {
|
||
Optional<PasswordResetToken> findByToken(String token);
|
||
Optional<PasswordResetToken> findByUser(UserAccount user);
|
||
}
|
||
```
|
||
|
||
### 2. 反馈管理相关Repository
|
||
|
||
#### FeedbackRepository(反馈数据访问)
|
||
|
||
**功能概述**
|
||
- **继承接口**: `JpaRepository<Feedback, Long>`, `JpaSpecificationExecutor<Feedback>`
|
||
- **业务价值**: 提供反馈数据的完整访问功能
|
||
- **核心特性**: 支持复杂查询、分页查询、统计查询、热力图数据
|
||
|
||
**核心查询方法**
|
||
```java
|
||
@Repository
|
||
public interface FeedbackRepository extends JpaRepository<Feedback, Long>, JpaSpecificationExecutor<Feedback> {
|
||
// 基础查询
|
||
Optional<Feedback> findByEventId(String eventId);
|
||
List<Feedback> findByStatus(FeedbackStatus status);
|
||
long countByStatus(FeedbackStatus status);
|
||
|
||
// 用户相关查询
|
||
Page<Feedback> findBySubmitterId(Long submitterId, Pageable pageable);
|
||
List<Feedback> findBySubmitterId(Long submitterId);
|
||
long countBySubmitterId(Long submitterId);
|
||
long countByStatusAndSubmitterId(FeedbackStatus status, Long submitterId);
|
||
|
||
// 统计查询
|
||
@Query("SELECT new com.dne.ems.dto.HeatmapPointDTO(f.gridX, f.gridY, COUNT(f.id)) FROM Feedback f WHERE f.status = 'CONFIRMED' AND f.gridX IS NOT NULL AND f.gridY IS NOT NULL GROUP BY f.gridX, f.gridY")
|
||
List<HeatmapPointDTO> getHeatmapData();
|
||
|
||
@Query("SELECT new com.dne.ems.dto.PollutionStatsDTO(f.pollutionType, COUNT(f)) FROM Feedback f GROUP BY f.pollutionType")
|
||
List<PollutionStatsDTO> countByPollutionType();
|
||
|
||
// 性能优化查询
|
||
@Override
|
||
@EntityGraph(attributePaths = {"user", "attachments"})
|
||
Page<Feedback> findAll(Specification<Feedback> spec, Pageable pageable);
|
||
}
|
||
```
|
||
|
||
**使用场景**
|
||
- **反馈管理**: 按状态、用户、事件ID查询反馈
|
||
- **数据统计**: 统计各状态反馈数量和污染类型分布
|
||
- **热力图**: 生成反馈分布热力图数据
|
||
- **性能优化**: 使用EntityGraph避免N+1查询问题
|
||
|
||
#### AttachmentRepository(附件数据访问)
|
||
|
||
**功能概述**
|
||
- **继承接口**: `JpaRepository<Attachment, Long>`
|
||
- **业务价值**: 管理反馈和任务提交的附件数据
|
||
- **核心特性**: 支持文件名查找和关联实体查询
|
||
|
||
**核心查询方法**
|
||
```java
|
||
@Repository
|
||
public interface AttachmentRepository extends JpaRepository<Attachment, Long> {
|
||
Optional<Attachment> findByStoredFileName(String storedFileName);
|
||
List<Attachment> findByTaskSubmission(TaskSubmission taskSubmission);
|
||
List<Attachment> findByFeedback(Feedback feedback);
|
||
}
|
||
```
|
||
|
||
### 3. 任务管理相关Repository
|
||
|
||
#### TaskRepository(任务数据访问)
|
||
|
||
**功能概述**
|
||
- **继承接口**: `JpaRepository<Task, Long>`, `JpaSpecificationExecutor<Task>`
|
||
- **业务价值**: 提供任务数据的完整访问功能
|
||
- **核心特性**: 支持复杂查询、状态统计、分配查询
|
||
|
||
**核心查询方法**
|
||
```java
|
||
@Repository
|
||
public interface TaskRepository extends JpaRepository<Task, Long>, JpaSpecificationExecutor<Task> {
|
||
Optional<Task> findByFeedback(Feedback feedback);
|
||
long countByStatus(TaskStatus status);
|
||
long countByAssigneeIdAndStatusIn(Long assigneeId, Collection<TaskStatus> statuses);
|
||
List<Task> findByAssigneeIdAndStatus(Long assigneeId, TaskStatus status);
|
||
Page<Task> findByStatusAndAssignee(AssignmentStatus status, UserAccount assignee, Pageable pageable);
|
||
}
|
||
```
|
||
|
||
#### TaskSubmissionRepository(任务提交数据访问)
|
||
|
||
**功能概述**
|
||
- **继承接口**: `JpaRepository<TaskSubmission, Long>`
|
||
- **业务价值**: 管理任务提交记录的数据访问
|
||
- **核心特性**: 支持按任务查找最新提交
|
||
|
||
**核心查询方法**
|
||
```java
|
||
@Repository
|
||
public interface TaskSubmissionRepository extends JpaRepository<TaskSubmission, Long> {
|
||
TaskSubmission findFirstByTaskOrderBySubmittedAtDesc(Task task);
|
||
}
|
||
```
|
||
|
||
#### TaskHistoryRepository(任务历史数据访问)
|
||
|
||
**功能概述**
|
||
- **继承接口**: `JpaRepository<TaskHistory, Long>`
|
||
- **业务价值**: 管理任务状态变更历史记录
|
||
- **核心特性**: 支持按任务ID查询历史记录
|
||
|
||
**核心查询方法**
|
||
```java
|
||
@Repository
|
||
public interface TaskHistoryRepository extends JpaRepository<TaskHistory, Long> {
|
||
List<TaskHistory> findByTaskIdOrderByChangedAtDesc(Long taskId);
|
||
}
|
||
```
|
||
|
||
### 4. 分配管理相关Repository
|
||
|
||
#### AssignmentRepository(任务分配数据访问)
|
||
|
||
**功能概述**
|
||
- **继承接口**: `JpaRepository<Assignment, Long>`
|
||
- **业务价值**: 管理任务分配关系的数据访问
|
||
- **核心特性**: 支持按分配者查询分配记录
|
||
|
||
**核心查询方法**
|
||
```java
|
||
@Repository
|
||
public interface AssignmentRepository extends JpaRepository<Assignment, Long> {
|
||
List<Assignment> findByTaskAssigneeId(Long assigneeId);
|
||
}
|
||
```
|
||
|
||
#### AssignmentRecordRepository(分配记录数据访问)
|
||
|
||
**功能概述**
|
||
- **继承接口**: `JpaRepository<AssignmentRecord, Long>`
|
||
- **业务价值**: 管理分配记录的详细数据
|
||
- **核心特性**: 基础CRUD操作,预留扩展空间
|
||
|
||
### 5. 空气质量数据相关Repository
|
||
|
||
#### AqiDataRepository(空气质量数据访问)
|
||
|
||
**功能概述**
|
||
- **继承接口**: `JpaRepository<AqiData, Long>`
|
||
- **业务价值**: 管理实时空气质量数据的访问
|
||
- **核心特性**: 支持统计查询、趋势分析、热力图数据
|
||
|
||
**核心查询方法**
|
||
```java
|
||
@Repository
|
||
public interface AqiDataRepository extends JpaRepository<AqiData, Long> {
|
||
// AQI分布统计
|
||
@Query("SELECT new com.dne.ems.dto.AqiDistributionDTO(CASE WHEN a.aqiValue <= 50 THEN 'Good' WHEN a.aqiValue <= 100 THEN 'Moderate' WHEN a.aqiValue <= 150 THEN 'Unhealthy for Sensitive Groups' WHEN a.aqiValue <= 200 THEN 'Unhealthy' WHEN a.aqiValue <= 300 THEN 'Very Unhealthy' ELSE 'Hazardous' END, COUNT(a.id)) FROM AqiData a GROUP BY 1")
|
||
List<AqiDistributionDTO> getAqiDistribution();
|
||
|
||
// 月度超标趋势(原生SQL)
|
||
@Query(value = "SELECT DATE_FORMAT(record_time, '%Y-%m') as yearMonth, COUNT(id) as count FROM aqi_data WHERE aqi_value > 100 AND record_time >= :startDate GROUP BY DATE_FORMAT(record_time, '%Y-%m') ORDER BY 1", nativeQuery = true)
|
||
List<Object[]> getMonthlyExceedanceTrendRaw(@Param("startDate") LocalDateTime startDate);
|
||
|
||
// 默认方法转换原生查询结果
|
||
default List<TrendDataPointDTO> getMonthlyExceedanceTrend(LocalDateTime startDate) {
|
||
List<Object[]> rawResults = getMonthlyExceedanceTrendRaw(startDate);
|
||
return rawResults.stream()
|
||
.map(row -> new TrendDataPointDTO((String) row[0], ((Number) row[1]).longValue()))
|
||
.toList();
|
||
}
|
||
|
||
// 热力图数据
|
||
@Query("SELECT new com.dne.ems.dto.AqiHeatmapPointDTO(g.gridX, g.gridY, AVG(d.aqiValue)) FROM AqiData d JOIN d.grid g WHERE g.gridX IS NOT NULL AND g.gridY IS NOT NULL GROUP BY g.gridX, g.gridY")
|
||
List<AqiHeatmapPointDTO> getAqiHeatmapData();
|
||
}
|
||
```
|
||
|
||
#### AqiRecordRepository(空气质量历史记录数据访问)
|
||
|
||
**功能概述**
|
||
- **继承接口**: `JpaRepository<AqiRecord, Long>`
|
||
- **业务价值**: 管理历史空气质量数据的访问
|
||
- **核心特性**: 支持复杂阈值查询、时间范围查询、热力图数据
|
||
|
||
**核心查询方法**
|
||
```java
|
||
@Repository
|
||
public interface AqiRecordRepository extends JpaRepository<AqiRecord, Long> {
|
||
// AQI分布统计
|
||
@Query("SELECT new com.dne.ems.dto.AqiDistributionDTO(CASE WHEN ar.aqiValue <= 50 THEN 'Good' WHEN ar.aqiValue <= 100 THEN 'Moderate' WHEN ar.aqiValue <= 150 THEN 'Unhealthy for Sensitive Groups' WHEN ar.aqiValue <= 200 THEN 'Unhealthy' WHEN ar.aqiValue <= 300 THEN 'Very Unhealthy' ELSE 'Hazardous' END, COUNT(ar.id)) FROM AqiRecord ar GROUP BY CASE WHEN ar.aqiValue <= 50 THEN 'Good' WHEN ar.aqiValue <= 100 THEN 'Moderate' WHEN ar.aqiValue <= 150 THEN 'Unhealthy for Sensitive Groups' WHEN ar.aqiValue <= 200 THEN 'Unhealthy' WHEN ar.aqiValue <= 300 THEN 'Very Unhealthy' ELSE 'Hazardous' END")
|
||
List<AqiDistributionDTO> getAqiDistribution();
|
||
|
||
// 超标记录查询
|
||
@Query("SELECT ar FROM AqiRecord ar WHERE ar.aqiValue > 100 AND ar.recordTime >= :startDate")
|
||
List<AqiRecord> findExceedanceRecordsSince(@Param("startDate") LocalDateTime startDate);
|
||
|
||
// 多阈值超标查询
|
||
@Query("SELECT ar FROM AqiRecord ar WHERE ar.recordTime >= :startDate AND (ar.aqiValue > :aqiThreshold OR ar.pm25 > :pm25Threshold OR ar.o3 > :o3Threshold OR ar.no2 > :no2Threshold OR ar.so2 > :so2Threshold)")
|
||
List<AqiRecord> findExceedanceRecordsWithThresholds(@Param("startDate") LocalDateTime startDate, @Param("aqiThreshold") Double aqiThreshold, @Param("pm25Threshold") Double pm25Threshold, @Param("o3Threshold") Double o3Threshold, @Param("no2Threshold") Double no2Threshold, @Param("so2Threshold") Double so2Threshold);
|
||
|
||
// 时间范围查询
|
||
List<AqiRecord> findByRecordTimeAfter(LocalDateTime startDate);
|
||
|
||
// 热力图数据(JPQL)
|
||
@Query("SELECT new com.dne.ems.dto.AqiHeatmapPointDTO(ar.gridX, ar.gridY, AVG(ar.aqiValue)) FROM AqiRecord ar WHERE ar.gridX IS NOT NULL AND ar.gridY IS NOT NULL GROUP BY ar.gridX, ar.gridY")
|
||
List<AqiHeatmapPointDTO> getAqiHeatmapData();
|
||
|
||
// 热力图数据(原生SQL)
|
||
@Query(value = "SELECT grid_x, grid_y, AVG(aqi_value) as avg_aqi FROM aqi_records WHERE grid_x IS NOT NULL AND grid_y IS NOT NULL GROUP BY grid_x, grid_y", nativeQuery = true)
|
||
List<Object[]> getAqiHeatmapDataRaw();
|
||
}
|
||
```
|
||
|
||
### 6. 地理网格相关Repository
|
||
|
||
#### GridRepository(地理网格数据访问)
|
||
|
||
**功能概述**
|
||
- **继承接口**: `JpaRepository<Grid, Long>`, `JpaSpecificationExecutor<Grid>`
|
||
- **业务价值**: 管理地理网格数据的访问
|
||
- **核心特性**: 支持坐标查询、城市统计、障碍物查询
|
||
|
||
**核心查询方法**
|
||
```java
|
||
@Repository
|
||
public interface GridRepository extends JpaRepository<Grid, Long>, JpaSpecificationExecutor<Grid> {
|
||
Optional<Grid> findByGridXAndGridY(Integer gridX, Integer gridY);
|
||
List<Grid> findByIsObstacle(boolean isObstacle);
|
||
|
||
@Query("SELECT new com.dne.ems.dto.GridCoverageDTO(g.cityName, COUNT(g.id)) FROM Grid g WHERE g.cityName IS NOT NULL AND g.cityName != '' GROUP BY g.cityName ORDER BY COUNT(g.id) DESC")
|
||
List<GridCoverageDTO> getGridCoverageByCity();
|
||
}
|
||
```
|
||
|
||
#### MapGridRepository(地图网格数据访问)
|
||
|
||
**功能概述**
|
||
- **继承接口**: `JpaRepository<MapGrid, Long>`
|
||
- **业务价值**: 管理A*寻路算法的网格数据
|
||
- **核心特性**: 支持坐标查询和有序遍历
|
||
|
||
**核心查询方法**
|
||
```java
|
||
@Repository
|
||
public interface MapGridRepository extends JpaRepository<MapGrid, Long> {
|
||
Optional<MapGrid> findByXAndY(int x, int y);
|
||
List<MapGrid> findAllByOrderByYAscXAsc();
|
||
}
|
||
```
|
||
|
||
### 7. 系统配置相关Repository
|
||
|
||
#### PollutantThresholdRepository(污染物阈值数据访问)
|
||
|
||
**功能概述**
|
||
- **继承接口**: `JpaRepository<PollutantThreshold, Long>`
|
||
- **业务价值**: 管理污染物阈值配置数据
|
||
- **核心特性**: 支持按污染物名称查询阈值
|
||
|
||
**核心查询方法**
|
||
```java
|
||
@Repository
|
||
public interface PollutantThresholdRepository extends JpaRepository<PollutantThreshold, Long> {
|
||
Optional<PollutantThreshold> findByPollutantName(String pollutantName);
|
||
}
|
||
```
|
||
|
||
## Repository设计特点
|
||
|
||
### 1. 继承体系设计
|
||
- **JpaRepository**: 提供基础CRUD操作
|
||
- **JpaSpecificationExecutor**: 支持动态查询和复杂条件
|
||
- **双重继承**: 核心实体同时继承两个接口,提供完整功能
|
||
|
||
### 2. 查询方法类型
|
||
- **方法名查询**: 基于Spring Data JPA命名规范的自动查询
|
||
- **@Query注解**: 自定义JPQL查询语句
|
||
- **原生SQL**: 使用nativeQuery进行复杂数据库操作
|
||
- **默认方法**: 在接口中提供数据转换逻辑
|
||
|
||
### 3. 性能优化策略
|
||
- **EntityGraph**: 解决N+1查询问题
|
||
- **分页查询**: 支持大数据量的分页处理
|
||
- **索引利用**: 查询方法充分利用数据库索引
|
||
- **批量操作**: 继承JpaRepository的批量操作能力
|
||
|
||
### 4. 查询复杂度分层
|
||
- **简单查询**: 单字段或少量字段的精确匹配
|
||
- **条件查询**: 多条件组合和范围查询
|
||
- **统计查询**: 聚合函数和分组统计
|
||
- **关联查询**: 跨表JOIN查询和子查询
|
||
|
||
## Repository架构优势
|
||
|
||
### 1. 标准化设计
|
||
- **Spring Data JPA**: 遵循Spring生态标准
|
||
- **命名规范**: 统一的方法命名和参数规范
|
||
- **注解驱动**: 使用标准注解进行配置
|
||
- **接口导向**: 面向接口编程,便于测试和扩展
|
||
|
||
### 2. 功能完整性
|
||
- **CRUD操作**: 完整的增删改查功能
|
||
- **复杂查询**: 支持各种业务查询需求
|
||
- **统计分析**: 提供数据统计和分析功能
|
||
- **性能优化**: 内置性能优化机制
|
||
|
||
### 3. 扩展性强
|
||
- **自定义查询**: 支持添加新的查询方法
|
||
- **规格查询**: 支持动态条件构建
|
||
- **原生SQL**: 支持复杂的数据库操作
|
||
- **默认方法**: 支持在接口中添加业务逻辑
|
||
|
||
### 4. 维护性好
|
||
- **类型安全**: 编译时类型检查
|
||
- **自动实现**: Spring自动生成实现类
|
||
- **测试友好**: 易于进行单元测试和集成测试
|
||
- **文档清晰**: 方法名自解释,注释完整
|
||
|
||
## 技术实现细节
|
||
|
||
### 1. Spring Data JPA注解
|
||
```java
|
||
@Repository // 标识为Repository组件
|
||
@Query("JPQL语句") // 自定义JPQL查询
|
||
@Query(value="SQL", nativeQuery=true) // 原生SQL查询
|
||
@Param("参数名") // 命名参数绑定
|
||
@EntityGraph(attributePaths={"关联属性"}) // 性能优化
|
||
```
|
||
|
||
### 2. 查询方法命名规范
|
||
```java
|
||
findBy... // 查询方法前缀
|
||
countBy... // 统计方法前缀
|
||
existsBy... // 存在性检查前缀
|
||
deleteBy... // 删除方法前缀
|
||
|
||
// 条件关键字
|
||
And, Or // 逻辑连接
|
||
Between, LessThan, GreaterThan // 范围条件
|
||
Like, NotLike // 模糊匹配
|
||
In, NotIn // 集合条件
|
||
IsNull, IsNotNull // 空值检查
|
||
OrderBy...Asc/Desc // 排序
|
||
```
|
||
|
||
### 3. 分页和排序
|
||
```java
|
||
Page<Entity> findBy...(Pageable pageable); // 分页查询
|
||
List<Entity> findBy...OrderBy...(); // 排序查询
|
||
Slice<Entity> findBy...(Pageable pageable); // 切片查询
|
||
```
|
||
|
||
### 4. 规格查询(Specification)
|
||
```java
|
||
public interface EntityRepository extends JpaRepository<Entity, Long>, JpaSpecificationExecutor<Entity> {
|
||
// 支持动态条件查询
|
||
Page<Entity> findAll(Specification<Entity> spec, Pageable pageable);
|
||
}
|
||
```
|
||
|
||
## Repository模块最佳实践
|
||
|
||
1. **接口设计**: 保持接口简洁,职责单一
|
||
2. **查询优化**: 合理使用EntityGraph避免N+1问题
|
||
3. **命名规范**: 遵循Spring Data JPA命名约定
|
||
4. **参数验证**: 在Service层进行参数验证,Repository专注数据访问
|
||
5. **事务管理**: 在Service层管理事务,Repository保持无状态
|
||
6. **异常处理**: 让Spring Data JPA的异常向上传播
|
||
7. **测试覆盖**: 为自定义查询方法编写集成测试
|
||
8. **性能监控**: 监控慢查询并进行优化
|
||
|
||
## Repository模块总结
|
||
|
||
Repository模块为EMS系统提供了完整、高效的数据访问层:
|
||
|
||
1. **标准化架构**: 基于Spring Data JPA的标准化设计
|
||
2. **功能完整**: 覆盖所有业务实体的数据访问需求
|
||
3. **查询丰富**: 支持简单查询、复杂查询、统计查询、分页查询
|
||
4. **性能优化**: 内置多种性能优化机制
|
||
5. **扩展性强**: 支持自定义查询和动态条件构建
|
||
6. **维护性好**: 类型安全、自动实现、测试友好
|
||
7. **业务适配**: 针对EMS业务特点设计的专用查询方法
|
||
|
||
通过这套完整的Repository层,EMS系统建立了高效、可靠的数据访问基础,为业务层提供了丰富的数据操作能力,确保了系统的数据访问性能和开发效率。
|
||
|
||
---
|
||
|
||
# Service模块详细解析
|
||
|
||
## 概述
|
||
|
||
Service(业务服务层)模块位于 `com.dne.ems.service` 包下,包含17个服务接口和对应的实现类,构成了EMS系统完整的业务逻辑层。该模块基于Spring框架的服务层架构,提供了标准化的业务服务接口,实现了复杂的业务逻辑处理、事务管理、安全控制和系统集成,为控制器层提供了高质量的业务服务。
|
||
|
||
## 核心Service接口
|
||
|
||
### 1. 认证与安全相关Service
|
||
|
||
#### AuthService(认证服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `AuthService`
|
||
- **实现类**: `AuthServiceImpl`
|
||
- **业务价值**: 提供用户认证和安全相关的核心功能
|
||
- **核心特性**: 用户注册、登录认证、密码重置、JWT令牌管理
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface AuthService {
|
||
// 密码重置流程
|
||
void requestPasswordReset(String email);
|
||
void resetPasswordWithToken(String token, String newPassword);
|
||
void resetPasswordWithCode(String email, String code, String newPassword);
|
||
|
||
// 用户注册与登录
|
||
void registerUser(SignUpRequest signUpRequest);
|
||
JwtAuthenticationResponse signIn(LoginRequest loginRequest);
|
||
|
||
// 验证码相关
|
||
void sendRegistrationCode(String email);
|
||
boolean validateRegistrationCode(String email, String code);
|
||
}
|
||
```
|
||
|
||
**业务流程**
|
||
- **用户注册**: 验证码校验 → 用户信息验证 → 创建用户账户 → 返回注册结果
|
||
- **用户登录**: 身份验证 → 生成JWT令牌 → 记录登录状态 → 返回认证响应
|
||
- **密码重置**: 发送验证码 → 验证码校验 → 密码更新 → 清理令牌
|
||
|
||
#### JwtService(JWT令牌服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `JwtService`
|
||
- **实现类**: `JwtServiceImpl`
|
||
- **业务价值**: 处理JWT令牌的生成、解析和验证
|
||
- **核心特性**: HS256算法签名、24小时有效期、用户角色权限声明
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface JwtService {
|
||
String extractUserName(String token);
|
||
Claims extractAllClaims(String token);
|
||
String generateToken(UserDetails userDetails);
|
||
boolean isTokenValid(String token, UserDetails userDetails);
|
||
}
|
||
```
|
||
|
||
#### LoginAttemptService(登录尝试服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `LoginAttemptService`
|
||
- **实现类**: `LoginAttemptServiceImpl`
|
||
- **业务价值**: 防止暴力破解攻击,管理登录失败次数
|
||
- **核心特性**: 失败次数统计、账户锁定机制、自动解锁
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface LoginAttemptService {
|
||
void loginSucceeded(String key);
|
||
void loginFailed(String key);
|
||
boolean isBlocked(String key);
|
||
}
|
||
```
|
||
|
||
#### VerificationCodeService(验证码服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `VerificationCodeService`
|
||
- **实现类**: `VerificationCodeServiceImpl`
|
||
- **业务价值**: 生成、发送和校验验证码
|
||
- **核心特性**: 6位数字验证码、5分钟有效期、60秒发送间隔
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface VerificationCodeService {
|
||
void sendVerificationCode(String email);
|
||
boolean verifyCode(String email, String code);
|
||
}
|
||
```
|
||
|
||
### 2. 用户管理相关Service
|
||
|
||
#### UserAccountService(用户账户服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `UserAccountService`
|
||
- **实现类**: `UserAccountServiceImpl`
|
||
- **业务价值**: 管理用户账户的完整生命周期
|
||
- **核心特性**: 用户注册、角色管理、位置更新、用户查询
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface UserAccountService {
|
||
UserAccount registerUser(UserRegistrationRequest registrationRequest);
|
||
UserAccount updateUserRole(Long userId, UserRoleUpdateRequest request);
|
||
UserAccount getUserById(Long userId);
|
||
Page<UserAccount> getUsersByRole(Role role, Pageable pageable);
|
||
void updateUserLocation(Long userId, Double latitude, Double longitude);
|
||
Page<UserSummaryDTO> getAllUsers(Role role, UserStatus status, Pageable pageable);
|
||
}
|
||
```
|
||
|
||
#### PersonnelService(人员管理服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `PersonnelService`
|
||
- **实现类**: `PersonnelServiceImpl`
|
||
- **业务价值**: 提供人员管理的高级功能
|
||
- **核心特性**: 用户创建、信息更新、删除管理、个人资料更新
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface PersonnelService {
|
||
Page<UserAccount> getUsers(Role role, String name, Pageable pageable);
|
||
UserAccount createUser(UserCreationRequest request);
|
||
UserAccount updateUser(Long userId, UserUpdateRequest request);
|
||
void deleteUser(Long userId);
|
||
UserAccount updateOwnProfile(String currentUserEmail, UserUpdateRequest request);
|
||
}
|
||
```
|
||
|
||
### 3. 反馈管理相关Service
|
||
|
||
#### FeedbackService(反馈服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `FeedbackService`
|
||
- **实现类**: `FeedbackServiceImpl`
|
||
- **业务价值**: 处理环境问题反馈的提交和管理
|
||
- **核心特性**: 用户反馈提交、匿名反馈、反馈查询、数据统计
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface FeedbackService {
|
||
// 反馈提交
|
||
Feedback submitFeedback(FeedbackSubmissionRequest request, MultipartFile[] files);
|
||
Feedback submitPublicFeedback(PublicFeedbackRequest request, MultipartFile[] files);
|
||
|
||
// 反馈查询
|
||
Page<FeedbackResponseDTO> getFeedbacks(FeedbackStatus status, PollutionType pollutionType,
|
||
SeverityLevel severityLevel, LocalDate startDate,
|
||
LocalDate endDate, Pageable pageable);
|
||
FeedbackResponseDTO getFeedbackById(Long feedbackId);
|
||
|
||
// 反馈处理
|
||
void processFeedback(Long feedbackId, ProcessFeedbackRequest request);
|
||
|
||
// 统计分析
|
||
FeedbackStatsResponse getFeedbackStats();
|
||
}
|
||
```
|
||
|
||
**业务流程**
|
||
- **反馈提交**: 用户信息验证 → 反馈数据创建 → 文件上传处理 → AI审核触发 → 返回反馈实体
|
||
- **反馈处理**: 状态验证 → 业务逻辑处理 → 状态更新 → 事件发布
|
||
|
||
#### UserFeedbackService(用户反馈服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `UserFeedbackService`
|
||
- **实现类**: `UserFeedbackServiceImpl`
|
||
- **业务价值**: 提供用户特定的反馈操作
|
||
- **核心特性**: 用户反馈历史查询
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface UserFeedbackService {
|
||
Page<UserFeedbackSummaryDTO> getFeedbackHistoryByUserId(Long userId, Pageable pageable);
|
||
}
|
||
```
|
||
|
||
#### AiReviewService(AI审核服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `AiReviewService`
|
||
- **实现类**: `AiReviewServiceImpl`
|
||
- **业务价值**: 使用AI模型审核反馈内容
|
||
- **核心特性**: 外部AI服务调用、反馈状态更新
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface AiReviewService {
|
||
void reviewFeedback(Feedback feedback);
|
||
}
|
||
```
|
||
|
||
### 4. 任务管理相关Service
|
||
|
||
#### TaskManagementService(任务管理服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `TaskManagementService`
|
||
- **实现类**: `TaskManagementServiceImpl`
|
||
- **业务价值**: 处理任务全生命周期管理
|
||
- **核心特性**: 任务创建、分配、状态管理、审核审批
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface TaskManagementService {
|
||
// 任务查询
|
||
Page<TaskSummaryDTO> getTasks(TaskStatus status, Long assigneeId, SeverityLevel severity,
|
||
PollutionType pollutionType, LocalDate startDate,
|
||
LocalDate endDate, Pageable pageable);
|
||
TaskDetailDTO getTaskById(Long taskId);
|
||
|
||
// 任务创建
|
||
TaskDetailDTO createTask(TaskCreationRequest request);
|
||
TaskDetailDTO createTaskFromFeedback(TaskFromFeedbackRequest request);
|
||
|
||
// 任务分配
|
||
void assignTask(Long taskId, TaskAssignmentRequest request);
|
||
|
||
// 任务审批
|
||
void approveTask(Long taskId, TaskApprovalRequest request);
|
||
void rejectTask(Long taskId, TaskApprovalRequest request);
|
||
|
||
// 任务历史
|
||
List<TaskHistoryDTO> getTaskHistory(Long taskId);
|
||
}
|
||
```
|
||
|
||
**业务流程**
|
||
- **任务创建**: 请求验证 → 任务实体创建 → 历史记录 → 事件发布
|
||
- **任务分配**: 分配者验证 → 被分配者验证 → 分配关系创建 → 状态更新
|
||
- **任务审批**: 权限验证 → 状态流转 → 审批记录 → 通知发送
|
||
|
||
#### TaskAssignmentService(任务分配服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `TaskAssignmentService`
|
||
- **实现类**: `TaskAssignmentServiceImpl`
|
||
- **业务价值**: 处理任务分配操作
|
||
- **核心特性**: 未分配任务查询、网格员查询、任务分配
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface TaskAssignmentService {
|
||
List<Feedback> getUnassignedFeedback();
|
||
List<UserAccount> getAvailableGridWorkers();
|
||
Assignment assignTask(Long feedbackId, Long assigneeId, Long assignerId);
|
||
}
|
||
```
|
||
|
||
#### GridWorkerTaskService(网格员任务服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `GridWorkerTaskService`
|
||
- **实现类**: `GridWorkerTaskServiceImpl`
|
||
- **业务价值**: 处理网格员的任务管理操作
|
||
- **核心特性**: 任务查询、状态流转、任务提交
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface GridWorkerTaskService {
|
||
Page<TaskSummaryDTO> getAssignedTasks(Long workerId, TaskStatus status, Pageable pageable);
|
||
TaskDetailDTO getTaskDetail(Long taskId, Long workerId);
|
||
void acceptTask(Long taskId, Long workerId);
|
||
void startTask(Long taskId, Long workerId);
|
||
void submitTask(Long taskId, TaskSubmissionRequest request, MultipartFile[] files);
|
||
List<TaskSummaryDTO> getTasksNearLocation(Double latitude, Double longitude, Double radiusKm);
|
||
}
|
||
```
|
||
|
||
### 5. 监管审核相关Service
|
||
|
||
#### SupervisorService(主管服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `SupervisorService`
|
||
- **实现类**: `SupervisorServiceImpl`
|
||
- **业务价值**: 处理主管的审核操作
|
||
- **核心特性**: 反馈审核、审批决策
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface SupervisorService {
|
||
List<Feedback> getFeedbackForReview();
|
||
void approveFeedback(Long feedbackId);
|
||
void rejectFeedback(Long feedbackId, RejectFeedbackRequest request);
|
||
}
|
||
```
|
||
|
||
### 6. 数据分析相关Service
|
||
|
||
#### DashboardService(仪表盘服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `DashboardService`
|
||
- **实现类**: `DashboardServiceImpl`
|
||
- **业务价值**: 提供各类环境监测数据的统计和可视化数据
|
||
- **核心特性**: 环境质量指标统计、AQI数据分布、热力图数据、趋势分析
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface DashboardService {
|
||
// 统计数据
|
||
List<PollutionStatsDTO> getPollutionStats();
|
||
TaskStatsDTO getTaskStats();
|
||
DashboardStatsDTO getDashboardStats();
|
||
|
||
// AQI数据
|
||
List<AqiDistributionDTO> getAqiDistribution();
|
||
List<AqiHeatmapPointDTO> getAqiHeatmapData();
|
||
List<TrendDataPointDTO> getMonthlyExceedanceTrend();
|
||
|
||
// 热力图数据
|
||
List<HeatmapPointDTO> getFeedbackHeatmapData();
|
||
|
||
// 网格覆盖
|
||
List<GridCoverageDTO> getGridCoverageByCity();
|
||
|
||
// 阈值管理
|
||
List<PollutantThresholdDTO> getAllThresholds();
|
||
PollutantThresholdDTO updateThreshold(String pollutantName, Double threshold);
|
||
}
|
||
```
|
||
|
||
### 7. 基础设施相关Service
|
||
|
||
#### FileStorageService(文件存储服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `FileStorageService`
|
||
- **实现类**: `FileStorageServiceImpl`
|
||
- **业务价值**: 处理文件上传和下载
|
||
- **核心特性**: 安全文件存储、文件关联管理、下载服务
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface FileStorageService {
|
||
@Deprecated
|
||
Attachment storeFileAndCreateAttachment(MultipartFile file, Feedback feedback);
|
||
String storeFile(MultipartFile file);
|
||
Resource loadFileAsResource(String fileName);
|
||
void deleteFile(String fileName);
|
||
}
|
||
```
|
||
|
||
#### MailService(邮件服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `MailService`
|
||
- **实现类**: `MailServiceImpl`
|
||
- **业务价值**: 发送各类邮件
|
||
- **核心特性**: 密码重置邮件、注册验证码邮件
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface MailService {
|
||
void sendPasswordResetEmail(String to, String code);
|
||
void sendVerificationCodeEmail(String to, String code);
|
||
}
|
||
```
|
||
|
||
#### GridService(网格服务)
|
||
|
||
**功能概述**
|
||
- **接口定义**: `GridService`
|
||
- **实现类**: `GridServiceImpl`
|
||
- **业务价值**: 处理网格数据的查询和更新
|
||
- **核心特性**: 网格查询、网格员分配、网格更新
|
||
|
||
**核心方法**
|
||
```java
|
||
public interface GridService {
|
||
List<Grid> getAllGrids();
|
||
Optional<Grid> getGridById(Long gridId);
|
||
UserAccount assignWorkerToGrid(Long gridId, Long userId);
|
||
void unassignWorkerFromGrid(Long gridId);
|
||
Page<Grid> getGridsWithFilters(String cityName, Boolean isObstacle, Pageable pageable);
|
||
Grid updateGrid(Long gridId, GridUpdateRequest request);
|
||
}
|
||
```
|
||
|
||
### 8. 算法服务
|
||
|
||
#### AStarService(A*寻路服务)
|
||
|
||
**功能概述**
|
||
- **服务类**: `AStarService`(直接实现,无接口)
|
||
- **业务价值**: 提供A*寻路算法实现
|
||
- **核心特性**: 最短路径计算、障碍物避让、网格导航
|
||
|
||
**核心方法**
|
||
```java
|
||
@Service
|
||
public class AStarService {
|
||
public List<Point> findPath(Point start, Point end);
|
||
private int calculateHeuristic(Point a, Point b);
|
||
private List<Point> getNeighbors(Point point);
|
||
private List<Point> reconstructPath(Node endNode);
|
||
}
|
||
```
|
||
|
||
## Service设计特点
|
||
|
||
### 1. 分层架构设计
|
||
- **接口与实现分离**: 所有业务服务都定义了接口,便于测试和扩展
|
||
- **职责单一原则**: 每个服务专注于特定的业务领域
|
||
- **依赖注入**: 使用Spring的依赖注入管理服务间依赖
|
||
- **事务管理**: 在服务层统一管理事务边界
|
||
|
||
### 2. 业务逻辑封装
|
||
- **复杂业务流程**: 将复杂的业务逻辑封装在服务方法中
|
||
- **数据转换**: 在服务层完成实体与DTO之间的转换
|
||
- **业务验证**: 在服务层进行业务规则验证
|
||
- **异常处理**: 统一的业务异常处理机制
|
||
|
||
### 3. 安全控制
|
||
- **权限验证**: 在服务层进行细粒度的权限控制
|
||
- **数据安全**: 敏感数据的加密和脱敏处理
|
||
- **审计日志**: 关键操作的审计日志记录
|
||
- **防护机制**: 防止各种安全攻击的保护措施
|
||
|
||
### 4. 性能优化
|
||
- **缓存策略**: 合理使用缓存提升性能
|
||
- **批量处理**: 支持批量操作减少数据库访问
|
||
- **异步处理**: 使用异步机制处理耗时操作
|
||
- **分页查询**: 大数据量查询的分页处理
|
||
|
||
## Service架构优势
|
||
|
||
### 1. 业务完整性
|
||
- **端到端流程**: 完整的业务流程实现
|
||
- **状态管理**: 复杂的业务状态流转管理
|
||
- **数据一致性**: 确保业务数据的一致性
|
||
- **业务规则**: 完整的业务规则实现
|
||
|
||
### 2. 系统集成
|
||
- **外部服务**: 与外部系统的集成接口
|
||
- **事件驱动**: 基于事件的松耦合架构
|
||
- **消息传递**: 系统间的消息传递机制
|
||
- **API网关**: 统一的API访问入口
|
||
|
||
### 3. 扩展性强
|
||
- **插件化**: 支持插件化的功能扩展
|
||
- **配置驱动**: 基于配置的功能开关
|
||
- **版本兼容**: 向后兼容的版本升级
|
||
- **模块化**: 模块化的服务设计
|
||
|
||
### 4. 维护性好
|
||
- **代码规范**: 统一的代码规范和风格
|
||
- **文档完整**: 完整的接口文档和注释
|
||
- **测试覆盖**: 高覆盖率的单元测试和集成测试
|
||
- **监控告警**: 完善的监控和告警机制
|
||
|
||
## 技术实现细节
|
||
|
||
### 1. Spring注解
|
||
```java
|
||
@Service // 标识为服务组件
|
||
@Transactional // 事务管理
|
||
@RequiredArgsConstructor // Lombok构造器注入
|
||
@Slf4j // 日志记录
|
||
@Async // 异步处理
|
||
@EventListener // 事件监听
|
||
@Cacheable // 缓存支持
|
||
```
|
||
|
||
### 2. 事务管理
|
||
```java
|
||
@Transactional(readOnly = true) // 只读事务
|
||
@Transactional(rollbackFor = Exception.class) // 异常回滚
|
||
@Transactional(propagation = Propagation.REQUIRES_NEW) // 事务传播
|
||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) // 事务事件
|
||
```
|
||
|
||
### 3. 安全控制
|
||
```java
|
||
@PreAuthorize("hasRole('ADMIN')") // 方法级权限控制
|
||
@PostAuthorize("returnObject.owner == authentication.name") // 返回值权限控制
|
||
SecurityContextHolder.getContext().getAuthentication() // 获取当前用户
|
||
```
|
||
|
||
### 4. 异常处理
|
||
```java
|
||
@ControllerAdvice // 全局异常处理
|
||
@ExceptionHandler // 特定异常处理
|
||
try-catch-finally // 局部异常处理
|
||
throw new BusinessException() // 业务异常抛出
|
||
```
|
||
|
||
## Service模块最佳实践
|
||
|
||
1. **接口设计**: 保持接口简洁,方法职责单一
|
||
2. **事务边界**: 合理设置事务边界,避免长事务
|
||
3. **异常处理**: 统一的异常处理策略,提供有意义的错误信息
|
||
4. **参数验证**: 在服务入口进行参数验证
|
||
5. **日志记录**: 记录关键业务操作和异常信息
|
||
6. **性能监控**: 监控服务性能,及时发现瓶颈
|
||
7. **单元测试**: 为每个服务方法编写单元测试
|
||
8. **文档维护**: 保持接口文档和代码注释的同步更新
|
||
|
||
## Service模块总结
|
||
|
||
Service模块为EMS系统提供了完整、高质量的业务服务层:
|
||
|
||
1. **业务完整**: 覆盖环境监管系统的所有核心业务功能
|
||
2. **架构清晰**: 分层明确、职责单一、依赖合理
|
||
3. **安全可靠**: 完善的安全控制和异常处理机制
|
||
4. **性能优化**: 多种性能优化策略和监控机制
|
||
5. **扩展性强**: 支持业务功能的灵活扩展和配置
|
||
6. **维护性好**: 代码规范、文档完整、测试覆盖率高
|
||
7. **技术先进**: 采用Spring生态的最佳实践和现代化技术
|
||
|
||
通过这套完整的Service层,EMS系统建立了稳定、高效的业务处理能力,为前端应用提供了可靠的业务支撑,确保了系统的业务完整性和技术先进性。
|
||
|
||
---
|
||
|
||
# Security模块详细解析
|
||
|
||
## 概述
|
||
|
||
Security(安全模块)位于 `com.dne.ems.security` 包下,包含4个核心安全组件,构成了EMS系统完整的安全防护体系。该模块基于Spring Security框架,实现了JWT无状态认证、细粒度权限控制、CORS跨域支持和安全过滤机制,为整个系统提供了企业级的安全保障。
|
||
|
||
## 核心Security组件
|
||
|
||
### 1. CustomUserDetails(自定义用户详情)
|
||
|
||
**功能概述**
|
||
- **类定义**: `CustomUserDetails`
|
||
- **实现接口**: `UserDetails`
|
||
- **业务价值**: 封装用户认证和授权信息,桥接业务用户实体与Spring Security
|
||
- **核心特性**: 用户信息封装、权限映射、账户状态管理、角色权限控制
|
||
|
||
**核心方法**
|
||
```java
|
||
public class CustomUserDetails implements UserDetails {
|
||
private final UserAccount userAccount;
|
||
|
||
// 业务信息获取
|
||
public Long getId();
|
||
public String getName();
|
||
public Role getRole();
|
||
public String getPhone();
|
||
public UserStatus getStatus();
|
||
public String getRegion();
|
||
public List<String> getSkills();
|
||
|
||
// Spring Security标准接口实现
|
||
public Collection<? extends GrantedAuthority> getAuthorities();
|
||
public String getPassword();
|
||
public String getUsername(); // 使用邮箱作为用户名
|
||
public boolean isAccountNonExpired();
|
||
public boolean isAccountNonLocked(); // 基于lockoutEndTime实现锁定逻辑
|
||
public boolean isCredentialsNonExpired();
|
||
public boolean isEnabled(); // 基于enabled和status字段
|
||
}
|
||
```
|
||
|
||
**设计特点**
|
||
- **权限映射**: 自动添加"ROLE_"前缀,确保与Spring Security的hasRole()方法兼容
|
||
- **账户锁定**: 基于lockoutEndTime字段实现动态账户锁定机制
|
||
- **状态检查**: 综合enabled字段和UserStatus枚举进行账户状态验证
|
||
- **信息暴露**: 提供完整的用户业务信息访问接口
|
||
|
||
### 2. JwtAuthenticationFilter(JWT认证过滤器)
|
||
|
||
**功能概述**
|
||
- **类定义**: `JwtAuthenticationFilter`
|
||
- **继承关系**: `OncePerRequestFilter`
|
||
- **业务价值**: 处理每个HTTP请求的JWT认证,实现无状态认证机制
|
||
- **核心特性**: JWT令牌提取、令牌验证、用户加载、安全上下文设置
|
||
|
||
**核心方法**
|
||
```java
|
||
@Component
|
||
@RequiredArgsConstructor
|
||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||
private final JwtService jwtService;
|
||
private final UserDetailsService userDetailsService;
|
||
|
||
@Override
|
||
protected void doFilterInternal(HttpServletRequest request,
|
||
HttpServletResponse response,
|
||
FilterChain filterChain) throws ServletException, IOException;
|
||
}
|
||
```
|
||
|
||
**工作流程**
|
||
1. **路径检查**: 跳过认证相关路径(/api/auth)
|
||
2. **令牌提取**: 从Authorization头提取Bearer令牌
|
||
3. **令牌验证**: 验证JWT签名和有效期
|
||
4. **用户加载**: 根据令牌中的用户名加载用户详情
|
||
5. **上下文设置**: 创建认证对象并设置到SecurityContext
|
||
|
||
**安全特性**
|
||
- **一次性过滤**: 继承OncePerRequestFilter确保每个请求只过滤一次
|
||
- **路径跳过**: 智能跳过不需要认证的路径
|
||
- **令牌格式**: 严格验证Bearer令牌格式
|
||
- **上下文管理**: 正确设置Spring Security上下文
|
||
|
||
### 3. SecurityConfig(安全配置)
|
||
|
||
**功能概述**
|
||
- **类定义**: `SecurityConfig`
|
||
- **配置注解**: `@Configuration`, `@EnableWebSecurity`, `@EnableMethodSecurity`
|
||
- **业务价值**: 定义系统整体安全策略和访问控制规则
|
||
- **核心特性**: HTTP安全配置、CORS配置、认证管理、密码编码
|
||
|
||
**核心配置方法**
|
||
```java
|
||
@Configuration
|
||
@EnableWebSecurity
|
||
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
|
||
@RequiredArgsConstructor
|
||
public class SecurityConfig {
|
||
|
||
@Bean
|
||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception;
|
||
|
||
@Bean
|
||
CorsConfigurationSource corsConfigurationSource();
|
||
|
||
@Bean
|
||
public AuthenticationManager authenticationManager(AuthenticationConfiguration config);
|
||
|
||
@Bean
|
||
public PasswordEncoder passwordEncoder();
|
||
}
|
||
```
|
||
|
||
**安全规则配置**
|
||
```java
|
||
// HTTP安全配置
|
||
http
|
||
.csrf(AbstractHttpConfigurer::disable) // 禁用CSRF(使用JWT)
|
||
.cors(cors -> cors.configurationSource(corsConfigurationSource())) // 启用CORS
|
||
.authorizeHttpRequests(authorize -> authorize
|
||
// 允许OPTIONS预检请求
|
||
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
|
||
// 认证相关端点
|
||
.requestMatchers("/api/auth/**").permitAll()
|
||
// 公共访问端点
|
||
.requestMatchers("/api/public/**", "/api/map/**", "/api/pathfinding/**").permitAll()
|
||
// 文件访问端点
|
||
.requestMatchers("/api/files/**").permitAll()
|
||
// Dashboard端点权限控制
|
||
.requestMatchers("/api/dashboard/**").hasAnyRole("ADMIN", "DECISION_MAKER")
|
||
// 其他请求需要认证
|
||
.anyRequest().authenticated()
|
||
)
|
||
.sessionManagement(session -> session
|
||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 无状态会话
|
||
)
|
||
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||
```
|
||
|
||
**CORS配置**
|
||
```java
|
||
@Bean
|
||
CorsConfigurationSource corsConfigurationSource() {
|
||
CorsConfiguration configuration = new CorsConfiguration();
|
||
configuration.addAllowedOrigin("*"); // 允许所有来源
|
||
configuration.addAllowedMethod("*"); // 允许所有HTTP方法
|
||
configuration.addAllowedHeader("*"); // 允许所有HTTP头
|
||
// 注册到所有路径
|
||
source.registerCorsConfiguration("/**", configuration);
|
||
return source;
|
||
}
|
||
```
|
||
|
||
**安全特性**
|
||
- **无状态认证**: 基于JWT的无状态会话管理
|
||
- **CSRF防护**: 禁用CSRF(因使用JWT令牌)
|
||
- **方法级权限**: 启用@PreAuthorize、@Secured、@RolesAllowed注解
|
||
- **细粒度控制**: 基于URL模式的访问控制
|
||
- **密码安全**: 使用BCrypt强密码编码
|
||
|
||
### 4. UserDetailsServiceImpl(用户详情服务实现)
|
||
|
||
**功能概述**
|
||
- **类定义**: `UserDetailsServiceImpl`
|
||
- **实现接口**: `UserDetailsService`
|
||
- **业务价值**: 为Spring Security提供用户加载服务
|
||
- **核心特性**: 用户查找、账户锁定检查、异常处理
|
||
|
||
**核心方法**
|
||
```java
|
||
@Service
|
||
@RequiredArgsConstructor
|
||
public class UserDetailsServiceImpl implements UserDetailsService {
|
||
private final UserAccountRepository userAccountRepository;
|
||
|
||
@Override
|
||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||
// 根据邮箱查找用户
|
||
UserAccount userAccount = userAccountRepository.findByEmail(username)
|
||
.orElseThrow(() -> new UsernameNotFoundException("未找到邮箱为 " + username + " 的用户。"));
|
||
|
||
// 检查账户锁定状态
|
||
if (userAccount.getLockoutEndTime() != null &&
|
||
userAccount.getLockoutEndTime().isAfter(LocalDateTime.now())) {
|
||
throw new LockedException("该账户已被锁定,请稍后再试。");
|
||
}
|
||
|
||
return new CustomUserDetails(userAccount);
|
||
}
|
||
}
|
||
```
|
||
|
||
**业务逻辑**
|
||
- **用户查找**: 使用邮箱作为用户名进行查找
|
||
- **锁定检查**: 检查lockoutEndTime字段判断账户是否被锁定
|
||
- **异常处理**: 提供清晰的中文错误信息
|
||
- **对象封装**: 返回CustomUserDetails包装的用户信息
|
||
|
||
## Security模块设计特点
|
||
|
||
### 1. 无状态认证架构
|
||
- **JWT令牌**: 使用JWT实现无状态认证,避免服务器端会话存储
|
||
- **令牌验证**: 每个请求都进行令牌验证,确保安全性
|
||
- **过期管理**: 自动处理令牌过期和刷新
|
||
- **跨服务**: 支持微服务架构下的认证共享
|
||
|
||
### 2. 细粒度权限控制
|
||
- **角色映射**: 自动处理Spring Security角色前缀
|
||
- **方法级权限**: 支持@PreAuthorize等注解进行方法级控制
|
||
- **URL级权限**: 基于URL模式的访问控制
|
||
- **动态权限**: 支持运行时权限检查
|
||
|
||
### 3. 安全防护机制
|
||
- **CSRF防护**: 针对JWT场景禁用CSRF
|
||
- **CORS支持**: 完整的跨域资源共享配置
|
||
- **账户锁定**: 基于时间的账户锁定机制
|
||
- **密码安全**: BCrypt强密码编码
|
||
|
||
### 4. 异常处理
|
||
- **用户友好**: 提供中文错误信息
|
||
- **安全考虑**: 不泄露敏感信息
|
||
- **分类处理**: 区分不同类型的认证异常
|
||
- **日志记录**: 记录安全相关事件
|
||
|
||
## Security架构优势
|
||
|
||
### 1. 安全性强
|
||
- **多层防护**: 从过滤器到方法级的多层安全防护
|
||
- **标准实现**: 基于Spring Security标准实现
|
||
- **最佳实践**: 采用业界安全最佳实践
|
||
- **持续更新**: 跟随Spring Security版本更新
|
||
|
||
### 2. 性能优化
|
||
- **无状态设计**: 避免服务器端会话存储开销
|
||
- **一次性过滤**: 每个请求只进行一次安全检查
|
||
- **缓存友好**: 支持用户信息缓存
|
||
- **轻量级**: 最小化安全检查开销
|
||
|
||
### 3. 扩展性好
|
||
- **插件化**: 支持自定义安全组件
|
||
- **配置驱动**: 基于配置的安全策略
|
||
- **接口标准**: 遵循Spring Security接口标准
|
||
- **模块化**: 各安全组件职责清晰
|
||
|
||
### 4. 维护性强
|
||
- **代码清晰**: 结构清晰,职责明确
|
||
- **文档完整**: 详细的注释和文档
|
||
- **测试友好**: 易于进行单元测试和集成测试
|
||
- **监控支持**: 支持安全事件监控
|
||
|
||
## 技术实现细节
|
||
|
||
### 1. Spring Security注解
|
||
```java
|
||
@EnableWebSecurity // 启用Web安全
|
||
@EnableMethodSecurity // 启用方法级安全
|
||
@Component // 组件注册
|
||
@Service // 服务注册
|
||
@RequiredArgsConstructor // 构造器注入
|
||
```
|
||
|
||
### 2. JWT处理
|
||
```java
|
||
// JWT令牌提取
|
||
String jwt = authHeader.substring(7); // 移除"Bearer "前缀
|
||
String userEmail = jwtService.extractUserName(jwt);
|
||
|
||
// JWT令牌验证
|
||
if (jwtService.isTokenValid(jwt, userDetails)) {
|
||
// 设置认证上下文
|
||
}
|
||
```
|
||
|
||
### 3. 权限控制
|
||
```java
|
||
// 角色权限映射
|
||
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_" + getRole().name());
|
||
|
||
// URL权限控制
|
||
.requestMatchers("/api/dashboard/**").hasAnyRole("ADMIN", "DECISION_MAKER")
|
||
|
||
// 方法级权限控制
|
||
@PreAuthorize("hasRole('ADMIN')")
|
||
public void adminOnlyMethod() { }
|
||
```
|
||
|
||
### 4. 异常处理
|
||
```java
|
||
// 用户不存在异常
|
||
throw new UsernameNotFoundException("未找到邮箱为 " + username + " 的用户。");
|
||
|
||
// 账户锁定异常
|
||
throw new LockedException("该账户已被锁定,请稍后再试。");
|
||
```
|
||
|
||
## Security模块最佳实践
|
||
|
||
1. **令牌管理**: 合理设置JWT过期时间,实现令牌刷新机制
|
||
2. **权限设计**: 采用最小权限原则,细粒度控制访问权限
|
||
3. **异常处理**: 提供用户友好的错误信息,避免泄露敏感信息
|
||
4. **日志记录**: 记录关键安全事件,便于审计和监控
|
||
5. **配置管理**: 将安全配置外部化,支持不同环境配置
|
||
6. **测试覆盖**: 编写完整的安全测试用例
|
||
7. **定期更新**: 及时更新安全依赖,修复安全漏洞
|
||
8. **监控告警**: 建立安全事件监控和告警机制
|
||
|
||
## Security模块总结
|
||
|
||
Security模块为EMS系统提供了完整、可靠的安全防护体系:
|
||
|
||
1. **认证完整**: 基于JWT的无状态认证机制,支持多种认证场景
|
||
2. **授权精确**: 细粒度的权限控制,从URL到方法级的全面覆盖
|
||
3. **防护全面**: CSRF、CORS、账户锁定等多重安全防护
|
||
4. **性能优秀**: 无状态设计,最小化安全检查开销
|
||
5. **扩展性强**: 模块化设计,支持自定义安全组件
|
||
6. **维护性好**: 代码清晰,文档完整,易于维护和扩展
|
||
7. **标准兼容**: 完全基于Spring Security标准,确保兼容性
|
||
|
||
通过这套完整的Security模块,EMS系统建立了企业级的安全防护能力,确保了用户数据安全、系统访问控制和业务操作的安全性,为整个系统提供了坚实的安全基础。 |