1
This commit is contained in:
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"java.compile.nullAnalysis.mode": "automatic"
|
||||
}
|
||||
48296
PROJECT_DOCS.md
48296
PROJECT_DOCS.md
File diff suppressed because it is too large
Load Diff
192
README.md
192
README.md
@@ -1,47 +1,47 @@
|
||||
# 环境监督系统 (EMS)
|
||||
|
||||
## 📖 项目简介
|
||||
|
||||
环境监督系统 (EMS) 是一个全栈的Web应用程序,旨在提供一个全面的平台,用于环境事件的报告、监控、管理和分析。系统支持多角色访问,包括管理员、决策者、网格员、监督员和公众监督员,每个角色都有特定的权限和定制化的用户界面。
|
||||
|
||||
## ✨ 主要功能
|
||||
|
||||
- **仪表盘**: 为决策者和管理员提供关键指标和数据的可视化概览。
|
||||
- **网格化地图**: 在地图上直观地展示和管理环境监督网格。
|
||||
- **任务管理**: 网格员可以接收、处理和汇报环境监督任务。
|
||||
- **反馈系统**: 公众监督员和监督员可以提交、查看和管理环境问题反馈。
|
||||
- **系统管理**: 管理员可以管理用户信息和查看系统操作日志。
|
||||
- **个性化设置**: 所有用户都可以查看个人信息和操作日志。
|
||||
- **动态主题**: 系统界面颜色会根据用户角色动态变化,提供更好的用户体验。
|
||||
|
||||
## 🛠️ 技术栈
|
||||
|
||||
本项目采用前后端分离的架构。
|
||||
|
||||
### 后端 (ems-backend)
|
||||
|
||||
- **框架**: [Spring Boot 3](https://spring.io/projects/spring-boot)
|
||||
- **语言**: Java 17
|
||||
- **文件存储**: json
|
||||
- **API文档**: SpringDoc (Swagger UI)
|
||||
- **身份认证**: Spring Security with JWT
|
||||
- **构建工具**: Maven
|
||||
|
||||
### 前端 (ems-frontend)
|
||||
|
||||
- **框架**: [Vue 3](https://vuejs.org/)
|
||||
- **构建工具**: [Vite](https://vitejs.dev/)
|
||||
- **语言**: TypeScript
|
||||
- **UI 组件库**: [Element Plus](https://element-plus.org/)
|
||||
- **状态管理**: [Pinia](https://pinia.vuejs.org/)
|
||||
- **路由**: [Vue Router](https://router.vuejs.org/)
|
||||
- **HTTP客户端**: Axios
|
||||
- **代码风格**: Prettier
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
```
|
||||
.
|
||||
# 环境监督系统 (EMS)
|
||||
|
||||
## 📖 项目简介
|
||||
|
||||
环境监督系统 (EMS) 是一个全栈的Web应用程序,旨在提供一个全面的平台,用于环境事件的报告、监控、管理和分析。系统支持多角色访问,包括管理员、决策者、网格员、监督员和公众监督员,每个角色都有特定的权限和定制化的用户界面。
|
||||
|
||||
## ✨ 主要功能
|
||||
|
||||
- **仪表盘**: 为决策者和管理员提供关键指标和数据的可视化概览。
|
||||
- **网格化地图**: 在地图上直观地展示和管理环境监督网格。
|
||||
- **任务管理**: 网格员可以接收、处理和汇报环境监督任务。
|
||||
- **反馈系统**: 公众监督员和监督员可以提交、查看和管理环境问题反馈。
|
||||
- **系统管理**: 管理员可以管理用户信息和查看系统操作日志。
|
||||
- **个性化设置**: 所有用户都可以查看个人信息和操作日志。
|
||||
- **动态主题**: 系统界面颜色会根据用户角色动态变化,提供更好的用户体验。
|
||||
|
||||
## 🛠️ 技术栈
|
||||
|
||||
本项目采用前后端分离的架构。
|
||||
|
||||
### 后端 (ems-backend)
|
||||
|
||||
- **框架**: [Spring Boot 3](https://spring.io/projects/spring-boot)
|
||||
- **语言**: Java 17
|
||||
- **文件存储**: json
|
||||
- **API文档**: SpringDoc (Swagger UI)
|
||||
- **身份认证**: Spring Security with JWT
|
||||
- **构建工具**: Maven
|
||||
|
||||
### 前端 (ems-frontend)
|
||||
|
||||
- **框架**: [Vue 3](https://vuejs.org/)
|
||||
- **构建工具**: [Vite](https://vitejs.dev/)
|
||||
- **语言**: TypeScript
|
||||
- **UI 组件库**: [Element Plus](https://element-plus.org/)
|
||||
- **状态管理**: [Pinia](https://pinia.vuejs.org/)
|
||||
- **路由**: [Vue Router](https://router.vuejs.org/)
|
||||
- **HTTP客户端**: Axios
|
||||
- **代码风格**: Prettier
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
```
|
||||
.
|
||||
├── ems-backend/ # 后端 Spring Boot 项目
|
||||
│ ├── src/
|
||||
│ └── pom.xml
|
||||
@@ -51,59 +51,59 @@
|
||||
│ └── package.json
|
||||
└── README.md # 项目说明文件
|
||||
```
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 环境准备
|
||||
|
||||
- [JDK 17](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) 或更高版本
|
||||
- [Maven 3.8](https://maven.apache.org/download.cgi) 或更高版本
|
||||
- [Node.js 22.x](https://nodejs.org/) 或更高版本
|
||||
|
||||
### 后端启动
|
||||
|
||||
1. **进入后端目录**:
|
||||
```bash
|
||||
cd ems-backend
|
||||
```
|
||||
|
||||
2. **安装依赖**:
|
||||
```bash
|
||||
mvn install
|
||||
```
|
||||
|
||||
3. **运行项目**:
|
||||
```bash
|
||||
mvn spring-boot:run
|
||||
```
|
||||
后端服务将启动在默认端口 (通常是 `8080`)。
|
||||
|
||||
### 前端启动
|
||||
|
||||
1. **进入前端目录**:
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 环境准备
|
||||
|
||||
- [JDK 17](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) 或更高版本
|
||||
- [Maven 3.8](https://maven.apache.org/download.cgi) 或更高版本
|
||||
- [Node.js 22.x](https://nodejs.org/) 或更高版本
|
||||
|
||||
### 后端启动
|
||||
|
||||
1. **进入后端目录**:
|
||||
```bash
|
||||
cd ems-backend
|
||||
```
|
||||
|
||||
2. **安装依赖**:
|
||||
```bash
|
||||
mvn install
|
||||
```
|
||||
|
||||
3. **运行项目**:
|
||||
```bash
|
||||
mvn spring-boot:run
|
||||
```
|
||||
后端服务将启动在默认端口 (通常是 `8080`)。
|
||||
|
||||
### 前端启动
|
||||
|
||||
1. **进入前端目录**:
|
||||
```bash
|
||||
cd ems-frontend
|
||||
```
|
||||
|
||||
2. **安装依赖**:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
3. **启动开发服务器**:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
前端应用将启动在 `http://localhost:5173` (或其他Vite指定的端口)。
|
||||
|
||||
## 👥 用户角色
|
||||
|
||||
系统预设了以下五种角色,拥有不同的职责和访问权限:
|
||||
|
||||
1. **ADMIN (管理员)**: 拥有最高权限,可以访问所有功能,包括用户管理和系统设置。
|
||||
2. **DECISION_MAKER (决策者)**: 关注宏观数据,主要访问仪表盘和地图。
|
||||
3. **GRID_WORKER (网格员)**: 负责执行具体任务,主要使用任务管理和地图功能。
|
||||
4. **SUPERVISOR (监督员)**: 负责管理和审查收到的反馈信息。
|
||||
5. **PUBLIC_SUPERVISOR (公众监督员)**: 可以提交环境问题的反馈。
|
||||
|
||||
|
||||
2. **安装依赖**:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
3. **启动开发服务器**:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
前端应用将启动在 `http://localhost:5173` (或其他Vite指定的端口)。
|
||||
|
||||
## 👥 用户角色
|
||||
|
||||
系统预设了以下五种角色,拥有不同的职责和访问权限:
|
||||
|
||||
1. **ADMIN (管理员)**: 拥有最高权限,可以访问所有功能,包括用户管理和系统设置。
|
||||
2. **DECISION_MAKER (决策者)**: 关注宏观数据,主要访问仪表盘和地图。
|
||||
3. **GRID_WORKER (网格员)**: 负责执行具体任务,主要使用任务管理和地图功能。
|
||||
4. **SUPERVISOR (监督员)**: 负责管理和审查收到的反馈信息。
|
||||
5. **PUBLIC_SUPERVISOR (公众监督员)**: 可以提交环境问题的反馈。
|
||||
|
||||
登录后,系统会根据您的角色,展示不同的菜单和界面主题。
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
services:
|
||||
backend:
|
||||
image: chuxunyu/ems-backend:latest
|
||||
ports:
|
||||
- "8080:8080"
|
||||
environment:
|
||||
APP_BASE_URL: ${APP_BASE_URL:-http://localhost:5173}
|
||||
TOKEN_SIGNING_KEY: ${TOKEN_SIGNING_KEY:-}
|
||||
JWT_SECRET: ${JWT_SECRET:-}
|
||||
VOLCANO_API_KEY: ${VOLCANO_API_KEY:-}
|
||||
SPRING_MAIL_USERNAME: ${SPRING_MAIL_USERNAME:-}
|
||||
SPRING_MAIL_PASSWORD: ${SPRING_MAIL_PASSWORD:-}
|
||||
volumes:
|
||||
- ems_json_db:/app/json-db
|
||||
- ems_uploads:/app/uploads
|
||||
|
||||
frontend:
|
||||
image: chuxunyu/ems-frontend:latest
|
||||
ports:
|
||||
- "5173:80"
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
volumes:
|
||||
ems_json_db:
|
||||
ems_uploads:
|
||||
89
docs/DOCKER_COMPOSE_USAGE.md
Normal file
89
docs/DOCKER_COMPOSE_USAGE.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Docker Compose 使用说明
|
||||
|
||||
本项目在根目录提供三个 Docker Compose 配置文件,分别面向不同的运行场景。下面说明各自用途与使用方式。
|
||||
|
||||
## 公共说明
|
||||
|
||||
- 端口映射:
|
||||
- 后端:`8080:8080`
|
||||
- 前端:`5173:80`
|
||||
- 运行前建议在根目录准备 `.env`(已提供示例),用于配置以下环境变量:
|
||||
- `APP_BASE_URL`
|
||||
- `TOKEN_SIGNING_KEY`
|
||||
- `JWT_SECRET`
|
||||
- `VOLCANO_API_KEY`
|
||||
- `SPRING_MAIL_USERNAME`
|
||||
- `SPRING_MAIL_PASSWORD`
|
||||
|
||||
> 说明:三个 Compose 文件都依赖这些环境变量;其中 `TOKEN_SIGNING_KEY`/`JWT_SECRET` 等不设置会使用空值默认。
|
||||
|
||||
## 1) `docker-compose.yml`(本地构建)
|
||||
|
||||
用途:本地开发或需要基于源码构建镜像时使用。
|
||||
|
||||
特点:
|
||||
- `backend` 和 `frontend` 使用 `build` 从本地目录构建镜像。
|
||||
- 挂载 `./ems-backend/json-db` 与 `./ems-backend/uploads` 到容器内,便于持久化数据。
|
||||
|
||||
使用方式(PowerShell):
|
||||
```bash
|
||||
docker compose -f docker-compose.yml up -d --build
|
||||
```
|
||||
|
||||
停止并清理(保留挂载目录):
|
||||
```bash
|
||||
docker compose -f docker-compose.yml down
|
||||
```
|
||||
|
||||
## 2) `docker-compose.hub.yml`(Docker Hub 镜像)
|
||||
|
||||
用途:不想本地构建,直接使用 Docker Hub 公共镜像。
|
||||
|
||||
特点:
|
||||
- `backend`/`frontend` 使用 `chuxunyu/ems-backend:latest`、`chuxunyu/ems-frontend:latest`。
|
||||
- 使用 Docker 卷 `ems_json_db`、`ems_uploads` 持久化数据。
|
||||
|
||||
使用方式(PowerShell):
|
||||
```bash
|
||||
docker compose -f docker-compose.hub.yml up -d
|
||||
```
|
||||
|
||||
停止并清理(保留卷):
|
||||
```bash
|
||||
docker compose -f docker-compose.hub.yml down
|
||||
```
|
||||
|
||||
如需删除卷(会清空数据):
|
||||
```bash
|
||||
docker compose -f docker-compose.hub.yml down -v
|
||||
```
|
||||
|
||||
## 3) `docker-compose.private.yml`(私有镜像仓库)
|
||||
|
||||
用途:使用私有镜像仓库 `docker.aizhangz.top` 的镜像部署。
|
||||
|
||||
特点:
|
||||
- `backend`/`frontend` 使用 `docker.aizhangz.top/ems-backend:latest`、`docker.aizhangz.top/ems-frontend:latest`。
|
||||
- 使用 Docker 卷 `ems_json_db`、`ems_uploads` 持久化数据。
|
||||
- 通常需要先登录私有仓库。
|
||||
|
||||
使用方式(PowerShell):
|
||||
```bash
|
||||
docker login docker.aizhangz.top
|
||||
docker compose -f docker-compose.private.yml up -d
|
||||
```
|
||||
|
||||
停止并清理(保留卷):
|
||||
```bash
|
||||
docker compose -f docker-compose.private.yml down
|
||||
```
|
||||
|
||||
如需删除卷(会清空数据):
|
||||
```bash
|
||||
docker compose -f docker-compose.private.yml down -v
|
||||
```
|
||||
|
||||
## 常见检查
|
||||
|
||||
- 前端:访问 `http://localhost:5173`
|
||||
- 后端:访问 `http://localhost:8080`(可结合后端 Swagger UI)
|
||||
@@ -1,19 +1,19 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
wrapperVersion=3.3.2
|
||||
distributionType=only-script
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.10/apache-maven-3.9.10-bin.zip
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
wrapperVersion=3.3.2
|
||||
distributionType=only-script
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.10/apache-maven-3.9.10-bin.zip
|
||||
|
||||
@@ -1,444 +1,444 @@
|
||||
# EMS 后端 API 测试文档
|
||||
|
||||
本文档基于项目源代码生成,包含了所有后端 API 接口的详细信息,用于开发和测试。
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
1. [认证模块 (Auth)](#1-认证模块-auth)
|
||||
2. [仪表盘模块 (Dashboard)](#2-仪表盘模块-dashboard)
|
||||
3. [反馈模块 (Feedback)](#3-反馈模块-feedback)
|
||||
4. [文件模块 (File)](#4-文件模块-file)
|
||||
5. [网格模块 (Grid)](#5-网格模块-grid)
|
||||
6. [网格员任务模块 (Worker)](#6-网格员任务模块-worker)
|
||||
7. [地图模块 (Map)](#7-地图模块-map)
|
||||
8. [路径规划模块 (Pathfinding)](#8-路径规划模块-pathfinding)
|
||||
9. [人员管理模块 (Personnel)](#9-人员管理模块-personnel)
|
||||
10. [个人资料模块 (Me)](#10-个人资料模块-me)
|
||||
11. [公共接口模块 (Public)](#11-公共接口模块-public)
|
||||
12. [主管模块 (Supervisor)](#12-主管模块-supervisor)
|
||||
13. [任务分配模块 (Tasks)](#13-任务分配模块-tasks)
|
||||
14. [任务管理模块 (Management)](#14-任务管理模块-management)
|
||||
|
||||
---
|
||||
|
||||
## 1. 认证模块 (Auth)
|
||||
**基础路径**: `/api/auth`
|
||||
|
||||
### 1.1 用户注册
|
||||
- **功能描述**: 注册一个新用户。
|
||||
- **请求路径**: `/api/auth/signup`
|
||||
- **请求方法**: `POST`
|
||||
- **所需权限**: 无
|
||||
- **请求体**: `SignUpRequest`
|
||||
- **示例请求**:
|
||||
```json
|
||||
{
|
||||
"name": "测试用户",
|
||||
"email": "test@example.com",
|
||||
"phone": "13800138000",
|
||||
"password": "password123",
|
||||
"verificationCode": "123456",
|
||||
"role": "GRID_WORKER"
|
||||
}
|
||||
```
|
||||
|
||||
### 1.2 用户登录
|
||||
- **功能描述**: 用户登录并获取 JWT Token。
|
||||
- **请求路径**: `/api/auth/login`
|
||||
- **请求方法**: `POST`
|
||||
- **所需权限**: 无
|
||||
- **请求体**: `LoginRequest`
|
||||
- **示例请求**:
|
||||
```json
|
||||
{
|
||||
"email": "admin@aizhangz.top",
|
||||
"password": "Admin@123"
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 发送验证码
|
||||
- **功能描述**: 请求发送验证码到指定邮箱。
|
||||
- **请求路径**: `/api/auth/send-verification-code`
|
||||
- **请求方法**: `POST`
|
||||
- **所需权限**: 无
|
||||
- **查询参数**: `email` (string, 必需)
|
||||
- **示例请求**: `POST /api/auth/send-verification-code?email=test@example.com`
|
||||
|
||||
### 1.4 请求重置密码
|
||||
- **功能描述**: 请求发送重置密码的链接或令牌。
|
||||
- **请求路径**: `/api/auth/request-password-reset`
|
||||
- **请求方法**: `POST`
|
||||
- **所需权限**: 无
|
||||
- **请求体**: `PasswordResetRequest`
|
||||
- **示例请求**:
|
||||
```json
|
||||
{
|
||||
"email": "test@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
### 1.5 重置密码
|
||||
- **功能描述**: 使用令牌重置密码。
|
||||
- **请求路径**: `/api/auth/reset-password`
|
||||
- **请求方法**: `POST`
|
||||
- **所需权限**: 无
|
||||
- **请求体**: `PasswordResetDto`
|
||||
- **示例请求**:
|
||||
```json
|
||||
{
|
||||
"token": "some-reset-token",
|
||||
"newPassword": "newPassword123"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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`
|
||||
- `feedback` (part): `FeedbackSubmissionRequest` (JSON)
|
||||
- `files` (part, optional): `MultipartFile[]`
|
||||
|
||||
### 3.3 获取所有反馈
|
||||
- **功能描述**: 获取所有反馈(分页)。
|
||||
- **请求路径**: `/api/feedback`
|
||||
- **请求方法**: `GET`
|
||||
- **所需权限**: `ADMIN`
|
||||
- **查询参数**: `page`, `size`, `sort`
|
||||
|
||||
---
|
||||
|
||||
## 4. 文件模块 (File)
|
||||
**基础路径**: `/api/files`
|
||||
**所需权限**: 公开访问
|
||||
|
||||
### 4.1 下载文件
|
||||
- **请求路径**: `/api/files/download/{fileName}`
|
||||
- **请求方法**: `GET`
|
||||
- **路径参数**: `fileName` (string, 必需)
|
||||
|
||||
### 4.2 查看文件
|
||||
- **请求路径**: `/api/files/view/{fileName}`
|
||||
- **请求方法**: `GET`
|
||||
- **路径参数**: `fileName` (string, 必需)
|
||||
|
||||
---
|
||||
|
||||
## 5. 网格模块 (Grid)
|
||||
**基础路径**: `/api/grids`
|
||||
**所需权限**: `ADMIN` 或 `DECISION_MAKER`
|
||||
|
||||
### 5.1 获取网格列表
|
||||
- **请求路径**: `/api/grids`
|
||||
- **请求方法**: `GET`
|
||||
- **查询参数**:
|
||||
- `cityName` (string, 可选)
|
||||
- `districtName` (string, 可选)
|
||||
- `page`, `size`, `sort`
|
||||
|
||||
### 5.2 更新网格信息
|
||||
- **请求路径**: `/api/grids/{gridId}`
|
||||
- **请求方法**: `PATCH`
|
||||
- **所需权限**: `ADMIN`
|
||||
- **路径参数**: `gridId` (long, 必需)
|
||||
- **请求体**: `GridUpdateRequest`
|
||||
|
||||
---
|
||||
|
||||
## 6. 网格员任务模块 (Worker)
|
||||
**基础路径**: `/api/worker`
|
||||
**所需权限**: `GRID_WORKER`
|
||||
|
||||
### 6.1 获取我的任务
|
||||
- **请求路径**: `/api/worker`
|
||||
- **请求方法**: `GET`
|
||||
- **查询参数**:
|
||||
- `status` (enum: `TaskStatus`, 可选)
|
||||
- `page`, `size`, `sort`
|
||||
|
||||
### 6.2 获取任务详情
|
||||
- **请求路径**: `/api/worker/{taskId}`
|
||||
- **请求方法**: `GET`
|
||||
- **路径参数**: `taskId` (long, 必需)
|
||||
|
||||
### 6.3 接受任务
|
||||
- **请求路径**: `/api/worker/{taskId}/accept`
|
||||
- **请求方法**: `POST`
|
||||
- **路径参数**: `taskId` (long, 必需)
|
||||
|
||||
### 6.4 提交任务
|
||||
- **请求路径**: `/api/worker/{taskId}/submit`
|
||||
- **请求方法**: `POST`
|
||||
- **路径参数**: `taskId` (long, 必需)
|
||||
- **请求体**: `TaskSubmissionRequest`
|
||||
|
||||
---
|
||||
|
||||
## 7. 地图模块 (Map)
|
||||
**基础路径**: `/api/map`
|
||||
**所需权限**: 公开访问
|
||||
|
||||
### 7.1 获取完整地图网格
|
||||
- **请求路径**: `/api/map/grid`
|
||||
- **请求方法**: `GET`
|
||||
|
||||
### 7.2 创建或更新地图单元格
|
||||
- **请求路径**: `/api/map/grid`
|
||||
- **请求方法**: `POST`
|
||||
- **请求体**: `MapGrid`
|
||||
|
||||
### 7.3 初始化地图
|
||||
- **请求路径**: `/api/map/initialize`
|
||||
- **请求方法**: `POST`
|
||||
- **查询参数**:
|
||||
- `width` (int, 可选, 默认 20)
|
||||
- `height` (int, 可选, 默认 20)
|
||||
|
||||
---
|
||||
|
||||
## 8. 路径规划模块 (Pathfinding)
|
||||
**基础路径**: `/api/pathfinding`
|
||||
**所需权限**: 公开访问
|
||||
|
||||
### 8.1 寻找路径
|
||||
- **功能描述**: 使用 A* 算法在两点之间寻找最短路径。
|
||||
- **请求路径**: `/api/pathfinding/find`
|
||||
- **请求方法**: `POST`
|
||||
- **请求体**: `PathfindingRequest`
|
||||
- **示例请求**:
|
||||
```json
|
||||
{
|
||||
"startX": 0,
|
||||
"startY": 0,
|
||||
"endX": 19,
|
||||
"endY": 19
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 人员管理模块 (Personnel)
|
||||
**基础路径**: `/api/personnel`
|
||||
**所需权限**: `ADMIN`
|
||||
|
||||
### 9.1 创建用户
|
||||
- **请求路径**: `/api/personnel/users`
|
||||
- **请求方法**: `POST`
|
||||
- **请求体**: `UserCreationRequest`
|
||||
|
||||
### 9.2 获取用户列表
|
||||
- **请求路径**: `/api/personnel/users`
|
||||
- **请求方法**: `GET`
|
||||
- **查询参数**:
|
||||
- `role` (enum: `Role`, 可选)
|
||||
- `name` (string, 可选)
|
||||
- `page`, `size`, `sort`
|
||||
|
||||
### 9.3 获取单个用户
|
||||
- **请求路径**: `/api/personnel/users/{userId}`
|
||||
- **请求方法**: `GET`
|
||||
- **路径参数**: `userId` (long, 必需)
|
||||
|
||||
### 9.4 更新用户信息
|
||||
- **请求路径**: `/api/personnel/users/{userId}`
|
||||
- **请求方法**: `PATCH`
|
||||
- **路径参数**: `userId` (long, 必需)
|
||||
- **请求体**: `UserUpdateRequest`
|
||||
|
||||
### 9.5 更新用户角色
|
||||
- **请求路径**: `/api/personnel/users/{userId}/role`
|
||||
- **请求方法**: `PUT`
|
||||
- **路径参数**: `userId` (long, 必需)
|
||||
- **请求体**: `UserRoleUpdateRequest`
|
||||
|
||||
### 9.6 删除用户
|
||||
- **请求路径**: `/api/personnel/users/{userId}`
|
||||
- **请求方法**: `DELETE`
|
||||
- **路径参数**: `userId` (long, 必需)
|
||||
|
||||
---
|
||||
|
||||
## 10. 个人资料模块 (Me)
|
||||
**基础路径**: `/api/me`
|
||||
**所需权限**: 已认证用户
|
||||
|
||||
### 10.1 更新个人资料
|
||||
- **请求路径**: `/api/me`
|
||||
- **请求方法**: `PATCH`
|
||||
- **请求体**: `UserUpdateRequest`
|
||||
|
||||
### 10.2 更新我的位置
|
||||
- **请求路径**: `/api/me/location`
|
||||
- **请求方法**: `POST`
|
||||
- **请求体**: `LocationUpdateRequest`
|
||||
|
||||
### 10.3 获取我的反馈历史
|
||||
- **请求路径**: `/api/me/feedback`
|
||||
- **请求方法**: `GET`
|
||||
- **查询参数**: `page`, `size`, `sort`
|
||||
|
||||
---
|
||||
|
||||
## 11. 公共接口模块 (Public)
|
||||
**基础路径**: `/api/public`
|
||||
**所需权限**: 公开访问
|
||||
|
||||
### 11.1 公众提交反馈
|
||||
- **请求路径**: `/api/public/feedback`
|
||||
- **请求方法**: `POST`
|
||||
- **请求体**: `multipart/form-data`
|
||||
- `feedback` (part): `PublicFeedbackRequest` (JSON)
|
||||
- `files` (part, optional): `MultipartFile[]`
|
||||
|
||||
---
|
||||
|
||||
## 12. 主管模块 (Supervisor)
|
||||
**基础路径**: `/api/supervisor`
|
||||
**所需权限**: `SUPERVISOR` 或 `ADMIN`
|
||||
|
||||
### 12.1 获取待审核的反馈列表
|
||||
- **请求路径**: `/api/supervisor/reviews`
|
||||
- **请求方法**: `GET`
|
||||
|
||||
### 12.2 批准反馈
|
||||
- **请求路径**: `/api/supervisor/reviews/{feedbackId}/approve`
|
||||
- **请求方法**: `POST`
|
||||
- **路径参数**: `feedbackId` (long, 必需)
|
||||
|
||||
### 12.3 拒绝反馈
|
||||
- **请求路径**: `/api/supervisor/reviews/{feedbackId}/reject`
|
||||
- **请求方法**: `POST`
|
||||
- **路径参数**: `feedbackId` (long, 必需)
|
||||
|
||||
---
|
||||
|
||||
## 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`
|
||||
- **请求体**:
|
||||
```json
|
||||
{
|
||||
"feedbackId": 1,
|
||||
"assigneeId": 2
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. 任务管理模块 (Management)
|
||||
**基础路径**: `/api/management/tasks`
|
||||
**所需权限**: `SUPERVISOR`
|
||||
|
||||
### 14.1 获取任务列表
|
||||
- **请求路径**: `/api/management/tasks`
|
||||
- **请求方法**: `GET`
|
||||
- **查询参数**:
|
||||
- `status` (enum: `TaskStatus`, 可选)
|
||||
- `assigneeId` (long, 可选)
|
||||
- `severity` (enum: `SeverityLevel`, 可选)
|
||||
- `pollutionType` (enum: `PollutionType`, 可选)
|
||||
- `startDate` (date, 可选, 格式: YYYY-MM-DD)
|
||||
- `endDate` (date, 可选, 格式: YYYY-MM-DD)
|
||||
- `page`, `size`, `sort`
|
||||
|
||||
### 14.2 分配任务
|
||||
- **请求路径**: `/api/management/tasks/{taskId}/assign`
|
||||
- **请求方法**: `POST`
|
||||
- **路径参数**: `taskId` (long, 必需)
|
||||
- **请求体**: `TaskAssignmentRequest`
|
||||
|
||||
### 14.3 获取任务详情
|
||||
- **请求路径**: `/api/management/tasks/{taskId}`
|
||||
- **请求方法**: `GET`
|
||||
- **路径参数**: `taskId` (long, 必需)
|
||||
|
||||
### 14.4 审核任务
|
||||
- **请求路径**: `/api/management/tasks/{taskId}/review`
|
||||
- **请求方法**: `POST`
|
||||
- **路径参数**: `taskId` (long, 必需)
|
||||
- **请求体**: `TaskApprovalRequest`
|
||||
|
||||
### 14.5 取消任务
|
||||
- **请求路径**: `/api/management/tasks/{taskId}/cancel`
|
||||
- **请求方法**: `POST`
|
||||
- **路径参数**: `taskId` (long, 必需)
|
||||
|
||||
### 14.6 直接创建任务
|
||||
- **请求路径**: `/api/management/tasks`
|
||||
- **请求方法**: `POST`
|
||||
- **请求体**: `TaskCreationRequest`
|
||||
|
||||
### 14.7 获取待处理的反馈
|
||||
- **请求路径**: `/api/management/tasks/feedback`
|
||||
- **请求方法**: `GET`
|
||||
- **所需权限**: `SUPERVISOR` 或 `ADMIN`
|
||||
|
||||
### 14.8 从反馈创建任务
|
||||
- **请求路径**: `/api/management/tasks/feedback/{feedbackId}/create-task`
|
||||
- **请求方法**: `POST`
|
||||
- **路径参数**: `feedbackId` (long, 必需)
|
||||
# EMS 后端 API 测试文档
|
||||
|
||||
本文档基于项目源代码生成,包含了所有后端 API 接口的详细信息,用于开发和测试。
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
1. [认证模块 (Auth)](#1-认证模块-auth)
|
||||
2. [仪表盘模块 (Dashboard)](#2-仪表盘模块-dashboard)
|
||||
3. [反馈模块 (Feedback)](#3-反馈模块-feedback)
|
||||
4. [文件模块 (File)](#4-文件模块-file)
|
||||
5. [网格模块 (Grid)](#5-网格模块-grid)
|
||||
6. [网格员任务模块 (Worker)](#6-网格员任务模块-worker)
|
||||
7. [地图模块 (Map)](#7-地图模块-map)
|
||||
8. [路径规划模块 (Pathfinding)](#8-路径规划模块-pathfinding)
|
||||
9. [人员管理模块 (Personnel)](#9-人员管理模块-personnel)
|
||||
10. [个人资料模块 (Me)](#10-个人资料模块-me)
|
||||
11. [公共接口模块 (Public)](#11-公共接口模块-public)
|
||||
12. [主管模块 (Supervisor)](#12-主管模块-supervisor)
|
||||
13. [任务分配模块 (Tasks)](#13-任务分配模块-tasks)
|
||||
14. [任务管理模块 (Management)](#14-任务管理模块-management)
|
||||
|
||||
---
|
||||
|
||||
## 1. 认证模块 (Auth)
|
||||
**基础路径**: `/api/auth`
|
||||
|
||||
### 1.1 用户注册
|
||||
- **功能描述**: 注册一个新用户。
|
||||
- **请求路径**: `/api/auth/signup`
|
||||
- **请求方法**: `POST`
|
||||
- **所需权限**: 无
|
||||
- **请求体**: `SignUpRequest`
|
||||
- **示例请求**:
|
||||
```json
|
||||
{
|
||||
"name": "测试用户",
|
||||
"email": "test@example.com",
|
||||
"phone": "13800138000",
|
||||
"password": "password123",
|
||||
"verificationCode": "123456",
|
||||
"role": "GRID_WORKER"
|
||||
}
|
||||
```
|
||||
|
||||
### 1.2 用户登录
|
||||
- **功能描述**: 用户登录并获取 JWT Token。
|
||||
- **请求路径**: `/api/auth/login`
|
||||
- **请求方法**: `POST`
|
||||
- **所需权限**: 无
|
||||
- **请求体**: `LoginRequest`
|
||||
- **示例请求**:
|
||||
```json
|
||||
{
|
||||
"email": "admin@aizhangz.top",
|
||||
"password": "Admin@123"
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 发送验证码
|
||||
- **功能描述**: 请求发送验证码到指定邮箱。
|
||||
- **请求路径**: `/api/auth/send-verification-code`
|
||||
- **请求方法**: `POST`
|
||||
- **所需权限**: 无
|
||||
- **查询参数**: `email` (string, 必需)
|
||||
- **示例请求**: `POST /api/auth/send-verification-code?email=test@example.com`
|
||||
|
||||
### 1.4 请求重置密码
|
||||
- **功能描述**: 请求发送重置密码的链接或令牌。
|
||||
- **请求路径**: `/api/auth/request-password-reset`
|
||||
- **请求方法**: `POST`
|
||||
- **所需权限**: 无
|
||||
- **请求体**: `PasswordResetRequest`
|
||||
- **示例请求**:
|
||||
```json
|
||||
{
|
||||
"email": "test@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
### 1.5 重置密码
|
||||
- **功能描述**: 使用令牌重置密码。
|
||||
- **请求路径**: `/api/auth/reset-password`
|
||||
- **请求方法**: `POST`
|
||||
- **所需权限**: 无
|
||||
- **请求体**: `PasswordResetDto`
|
||||
- **示例请求**:
|
||||
```json
|
||||
{
|
||||
"token": "some-reset-token",
|
||||
"newPassword": "newPassword123"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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`
|
||||
- `feedback` (part): `FeedbackSubmissionRequest` (JSON)
|
||||
- `files` (part, optional): `MultipartFile[]`
|
||||
|
||||
### 3.3 获取所有反馈
|
||||
- **功能描述**: 获取所有反馈(分页)。
|
||||
- **请求路径**: `/api/feedback`
|
||||
- **请求方法**: `GET`
|
||||
- **所需权限**: `ADMIN`
|
||||
- **查询参数**: `page`, `size`, `sort`
|
||||
|
||||
---
|
||||
|
||||
## 4. 文件模块 (File)
|
||||
**基础路径**: `/api/files`
|
||||
**所需权限**: 公开访问
|
||||
|
||||
### 4.1 下载文件
|
||||
- **请求路径**: `/api/files/download/{fileName}`
|
||||
- **请求方法**: `GET`
|
||||
- **路径参数**: `fileName` (string, 必需)
|
||||
|
||||
### 4.2 查看文件
|
||||
- **请求路径**: `/api/files/view/{fileName}`
|
||||
- **请求方法**: `GET`
|
||||
- **路径参数**: `fileName` (string, 必需)
|
||||
|
||||
---
|
||||
|
||||
## 5. 网格模块 (Grid)
|
||||
**基础路径**: `/api/grids`
|
||||
**所需权限**: `ADMIN` 或 `DECISION_MAKER`
|
||||
|
||||
### 5.1 获取网格列表
|
||||
- **请求路径**: `/api/grids`
|
||||
- **请求方法**: `GET`
|
||||
- **查询参数**:
|
||||
- `cityName` (string, 可选)
|
||||
- `districtName` (string, 可选)
|
||||
- `page`, `size`, `sort`
|
||||
|
||||
### 5.2 更新网格信息
|
||||
- **请求路径**: `/api/grids/{gridId}`
|
||||
- **请求方法**: `PATCH`
|
||||
- **所需权限**: `ADMIN`
|
||||
- **路径参数**: `gridId` (long, 必需)
|
||||
- **请求体**: `GridUpdateRequest`
|
||||
|
||||
---
|
||||
|
||||
## 6. 网格员任务模块 (Worker)
|
||||
**基础路径**: `/api/worker`
|
||||
**所需权限**: `GRID_WORKER`
|
||||
|
||||
### 6.1 获取我的任务
|
||||
- **请求路径**: `/api/worker`
|
||||
- **请求方法**: `GET`
|
||||
- **查询参数**:
|
||||
- `status` (enum: `TaskStatus`, 可选)
|
||||
- `page`, `size`, `sort`
|
||||
|
||||
### 6.2 获取任务详情
|
||||
- **请求路径**: `/api/worker/{taskId}`
|
||||
- **请求方法**: `GET`
|
||||
- **路径参数**: `taskId` (long, 必需)
|
||||
|
||||
### 6.3 接受任务
|
||||
- **请求路径**: `/api/worker/{taskId}/accept`
|
||||
- **请求方法**: `POST`
|
||||
- **路径参数**: `taskId` (long, 必需)
|
||||
|
||||
### 6.4 提交任务
|
||||
- **请求路径**: `/api/worker/{taskId}/submit`
|
||||
- **请求方法**: `POST`
|
||||
- **路径参数**: `taskId` (long, 必需)
|
||||
- **请求体**: `TaskSubmissionRequest`
|
||||
|
||||
---
|
||||
|
||||
## 7. 地图模块 (Map)
|
||||
**基础路径**: `/api/map`
|
||||
**所需权限**: 公开访问
|
||||
|
||||
### 7.1 获取完整地图网格
|
||||
- **请求路径**: `/api/map/grid`
|
||||
- **请求方法**: `GET`
|
||||
|
||||
### 7.2 创建或更新地图单元格
|
||||
- **请求路径**: `/api/map/grid`
|
||||
- **请求方法**: `POST`
|
||||
- **请求体**: `MapGrid`
|
||||
|
||||
### 7.3 初始化地图
|
||||
- **请求路径**: `/api/map/initialize`
|
||||
- **请求方法**: `POST`
|
||||
- **查询参数**:
|
||||
- `width` (int, 可选, 默认 20)
|
||||
- `height` (int, 可选, 默认 20)
|
||||
|
||||
---
|
||||
|
||||
## 8. 路径规划模块 (Pathfinding)
|
||||
**基础路径**: `/api/pathfinding`
|
||||
**所需权限**: 公开访问
|
||||
|
||||
### 8.1 寻找路径
|
||||
- **功能描述**: 使用 A* 算法在两点之间寻找最短路径。
|
||||
- **请求路径**: `/api/pathfinding/find`
|
||||
- **请求方法**: `POST`
|
||||
- **请求体**: `PathfindingRequest`
|
||||
- **示例请求**:
|
||||
```json
|
||||
{
|
||||
"startX": 0,
|
||||
"startY": 0,
|
||||
"endX": 19,
|
||||
"endY": 19
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 人员管理模块 (Personnel)
|
||||
**基础路径**: `/api/personnel`
|
||||
**所需权限**: `ADMIN`
|
||||
|
||||
### 9.1 创建用户
|
||||
- **请求路径**: `/api/personnel/users`
|
||||
- **请求方法**: `POST`
|
||||
- **请求体**: `UserCreationRequest`
|
||||
|
||||
### 9.2 获取用户列表
|
||||
- **请求路径**: `/api/personnel/users`
|
||||
- **请求方法**: `GET`
|
||||
- **查询参数**:
|
||||
- `role` (enum: `Role`, 可选)
|
||||
- `name` (string, 可选)
|
||||
- `page`, `size`, `sort`
|
||||
|
||||
### 9.3 获取单个用户
|
||||
- **请求路径**: `/api/personnel/users/{userId}`
|
||||
- **请求方法**: `GET`
|
||||
- **路径参数**: `userId` (long, 必需)
|
||||
|
||||
### 9.4 更新用户信息
|
||||
- **请求路径**: `/api/personnel/users/{userId}`
|
||||
- **请求方法**: `PATCH`
|
||||
- **路径参数**: `userId` (long, 必需)
|
||||
- **请求体**: `UserUpdateRequest`
|
||||
|
||||
### 9.5 更新用户角色
|
||||
- **请求路径**: `/api/personnel/users/{userId}/role`
|
||||
- **请求方法**: `PUT`
|
||||
- **路径参数**: `userId` (long, 必需)
|
||||
- **请求体**: `UserRoleUpdateRequest`
|
||||
|
||||
### 9.6 删除用户
|
||||
- **请求路径**: `/api/personnel/users/{userId}`
|
||||
- **请求方法**: `DELETE`
|
||||
- **路径参数**: `userId` (long, 必需)
|
||||
|
||||
---
|
||||
|
||||
## 10. 个人资料模块 (Me)
|
||||
**基础路径**: `/api/me`
|
||||
**所需权限**: 已认证用户
|
||||
|
||||
### 10.1 更新个人资料
|
||||
- **请求路径**: `/api/me`
|
||||
- **请求方法**: `PATCH`
|
||||
- **请求体**: `UserUpdateRequest`
|
||||
|
||||
### 10.2 更新我的位置
|
||||
- **请求路径**: `/api/me/location`
|
||||
- **请求方法**: `POST`
|
||||
- **请求体**: `LocationUpdateRequest`
|
||||
|
||||
### 10.3 获取我的反馈历史
|
||||
- **请求路径**: `/api/me/feedback`
|
||||
- **请求方法**: `GET`
|
||||
- **查询参数**: `page`, `size`, `sort`
|
||||
|
||||
---
|
||||
|
||||
## 11. 公共接口模块 (Public)
|
||||
**基础路径**: `/api/public`
|
||||
**所需权限**: 公开访问
|
||||
|
||||
### 11.1 公众提交反馈
|
||||
- **请求路径**: `/api/public/feedback`
|
||||
- **请求方法**: `POST`
|
||||
- **请求体**: `multipart/form-data`
|
||||
- `feedback` (part): `PublicFeedbackRequest` (JSON)
|
||||
- `files` (part, optional): `MultipartFile[]`
|
||||
|
||||
---
|
||||
|
||||
## 12. 主管模块 (Supervisor)
|
||||
**基础路径**: `/api/supervisor`
|
||||
**所需权限**: `SUPERVISOR` 或 `ADMIN`
|
||||
|
||||
### 12.1 获取待审核的反馈列表
|
||||
- **请求路径**: `/api/supervisor/reviews`
|
||||
- **请求方法**: `GET`
|
||||
|
||||
### 12.2 批准反馈
|
||||
- **请求路径**: `/api/supervisor/reviews/{feedbackId}/approve`
|
||||
- **请求方法**: `POST`
|
||||
- **路径参数**: `feedbackId` (long, 必需)
|
||||
|
||||
### 12.3 拒绝反馈
|
||||
- **请求路径**: `/api/supervisor/reviews/{feedbackId}/reject`
|
||||
- **请求方法**: `POST`
|
||||
- **路径参数**: `feedbackId` (long, 必需)
|
||||
|
||||
---
|
||||
|
||||
## 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`
|
||||
- **请求体**:
|
||||
```json
|
||||
{
|
||||
"feedbackId": 1,
|
||||
"assigneeId": 2
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. 任务管理模块 (Management)
|
||||
**基础路径**: `/api/management/tasks`
|
||||
**所需权限**: `SUPERVISOR`
|
||||
|
||||
### 14.1 获取任务列表
|
||||
- **请求路径**: `/api/management/tasks`
|
||||
- **请求方法**: `GET`
|
||||
- **查询参数**:
|
||||
- `status` (enum: `TaskStatus`, 可选)
|
||||
- `assigneeId` (long, 可选)
|
||||
- `severity` (enum: `SeverityLevel`, 可选)
|
||||
- `pollutionType` (enum: `PollutionType`, 可选)
|
||||
- `startDate` (date, 可选, 格式: YYYY-MM-DD)
|
||||
- `endDate` (date, 可选, 格式: YYYY-MM-DD)
|
||||
- `page`, `size`, `sort`
|
||||
|
||||
### 14.2 分配任务
|
||||
- **请求路径**: `/api/management/tasks/{taskId}/assign`
|
||||
- **请求方法**: `POST`
|
||||
- **路径参数**: `taskId` (long, 必需)
|
||||
- **请求体**: `TaskAssignmentRequest`
|
||||
|
||||
### 14.3 获取任务详情
|
||||
- **请求路径**: `/api/management/tasks/{taskId}`
|
||||
- **请求方法**: `GET`
|
||||
- **路径参数**: `taskId` (long, 必需)
|
||||
|
||||
### 14.4 审核任务
|
||||
- **请求路径**: `/api/management/tasks/{taskId}/review`
|
||||
- **请求方法**: `POST`
|
||||
- **路径参数**: `taskId` (long, 必需)
|
||||
- **请求体**: `TaskApprovalRequest`
|
||||
|
||||
### 14.5 取消任务
|
||||
- **请求路径**: `/api/management/tasks/{taskId}/cancel`
|
||||
- **请求方法**: `POST`
|
||||
- **路径参数**: `taskId` (long, 必需)
|
||||
|
||||
### 14.6 直接创建任务
|
||||
- **请求路径**: `/api/management/tasks`
|
||||
- **请求方法**: `POST`
|
||||
- **请求体**: `TaskCreationRequest`
|
||||
|
||||
### 14.7 获取待处理的反馈
|
||||
- **请求路径**: `/api/management/tasks/feedback`
|
||||
- **请求方法**: `GET`
|
||||
- **所需权限**: `SUPERVISOR` 或 `ADMIN`
|
||||
|
||||
### 14.8 从反馈创建任务
|
||||
- **请求路径**: `/api/management/tasks/feedback/{feedbackId}/create-task`
|
||||
- **请求方法**: `POST`
|
||||
- **路径参数**: `feedbackId` (long, 必需)
|
||||
- **请求体**: `TaskFromFeedbackRequest`
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,134 +1,134 @@
|
||||
[ {
|
||||
"id" : 1,
|
||||
"task" : {
|
||||
"id" : 4,
|
||||
"feedback" : {
|
||||
"id" : 4,
|
||||
"eventId" : "6b800765-b0aa-457b-bde7-af60bace45f9",
|
||||
"title" : "SO2",
|
||||
"description" : "SO2",
|
||||
"pollutionType" : "SO2",
|
||||
"severityLevel" : "MEDIUM",
|
||||
"status" : "PENDING_ASSIGNMENT",
|
||||
"textAddress" : "东北大学195号",
|
||||
"gridX" : 0,
|
||||
"gridY" : 1,
|
||||
"submitterId" : 6,
|
||||
"createdAt" : "2025-06-24T23:35:05.9704809",
|
||||
"updatedAt" : "2025-06-24T23:39:54.0988378",
|
||||
"user" : {
|
||||
"id" : 6,
|
||||
"name" : "公众监督员",
|
||||
"phone" : "13700137004",
|
||||
"email" : "public.supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$tg2qMBxN/grBjChEMOzGI.lPFjBD5H7nwwnIQX59DHJ81AfRUjUI.",
|
||||
"gender" : null,
|
||||
"role" : "PUBLIC_SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.7646754",
|
||||
"updatedAt" : "2025-06-24T23:33:58.7646754",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"latitude" : 1.0E-6,
|
||||
"longitude" : 1.0E-6,
|
||||
"attachments" : [ ],
|
||||
"task" : null
|
||||
},
|
||||
"assignee" : {
|
||||
"id" : 57,
|
||||
"name" : "特定网格员",
|
||||
"phone" : "14700009999",
|
||||
"email" : "worker@aizhangz.top",
|
||||
"password" : "$2a$10$BESSBI.5BicU8NbFp.HG8envauXACHo/sDe20XvFFezGJWqbI7v1i",
|
||||
"gender" : "MALE",
|
||||
"role" : "GRID_WORKER",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : 10,
|
||||
"gridY" : 10,
|
||||
"region" : "苏州市",
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:34:02.2703065",
|
||||
"updatedAt" : "2025-06-24T23:34:02.2703065",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"createdBy" : {
|
||||
"id" : 1,
|
||||
"name" : "系统管理员",
|
||||
"phone" : "13800138000",
|
||||
"email" : "admin@example.com",
|
||||
"password" : "$2a$10$PjqWk7oBYmN2ecjNd6njFuS125JSGyb9A8v7HAWJjTzTH5fvLDgLK",
|
||||
"gender" : null,
|
||||
"role" : "ADMIN",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.3224414",
|
||||
"updatedAt" : "2025-06-24T23:33:58.323441",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"status" : "ASSIGNED",
|
||||
"assignedAt" : "2025-06-24T23:40:35.4504136",
|
||||
"completedAt" : null,
|
||||
"createdAt" : null,
|
||||
"updatedAt" : null,
|
||||
"title" : "SO2",
|
||||
"description" : "SO2",
|
||||
"pollutionType" : "SO2",
|
||||
"severityLevel" : "MEDIUM",
|
||||
"textAddress" : "东北大学195号",
|
||||
"gridX" : 0,
|
||||
"gridY" : 1,
|
||||
"latitude" : 1.0E-6,
|
||||
"longitude" : 1.0E-6,
|
||||
"history" : [ ],
|
||||
"assignment" : null,
|
||||
"submissions" : [ ]
|
||||
},
|
||||
"assigner" : {
|
||||
"id" : 1,
|
||||
"name" : "系统管理员",
|
||||
"phone" : "13800138000",
|
||||
"email" : "admin@example.com",
|
||||
"password" : "$2a$10$PjqWk7oBYmN2ecjNd6njFuS125JSGyb9A8v7HAWJjTzTH5fvLDgLK",
|
||||
"gender" : null,
|
||||
"role" : "ADMIN",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.3224414",
|
||||
"updatedAt" : "2025-06-24T23:33:58.323441",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"assignmentTime" : "2025-06-24T23:40:35.4534373",
|
||||
"deadline" : null,
|
||||
"status" : "PENDING",
|
||||
"remarks" : null
|
||||
[ {
|
||||
"id" : 1,
|
||||
"task" : {
|
||||
"id" : 4,
|
||||
"feedback" : {
|
||||
"id" : 4,
|
||||
"eventId" : "6b800765-b0aa-457b-bde7-af60bace45f9",
|
||||
"title" : "SO2",
|
||||
"description" : "SO2",
|
||||
"pollutionType" : "SO2",
|
||||
"severityLevel" : "MEDIUM",
|
||||
"status" : "PENDING_ASSIGNMENT",
|
||||
"textAddress" : "东北大学195号",
|
||||
"gridX" : 0,
|
||||
"gridY" : 1,
|
||||
"submitterId" : 6,
|
||||
"createdAt" : "2025-06-24T23:35:05.9704809",
|
||||
"updatedAt" : "2025-06-24T23:39:54.0988378",
|
||||
"user" : {
|
||||
"id" : 6,
|
||||
"name" : "公众监督员",
|
||||
"phone" : "13700137004",
|
||||
"email" : "public.supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$tg2qMBxN/grBjChEMOzGI.lPFjBD5H7nwwnIQX59DHJ81AfRUjUI.",
|
||||
"gender" : null,
|
||||
"role" : "PUBLIC_SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.7646754",
|
||||
"updatedAt" : "2025-06-24T23:33:58.7646754",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"latitude" : 1.0E-6,
|
||||
"longitude" : 1.0E-6,
|
||||
"attachments" : [ ],
|
||||
"task" : null
|
||||
},
|
||||
"assignee" : {
|
||||
"id" : 57,
|
||||
"name" : "特定网格员",
|
||||
"phone" : "14700009999",
|
||||
"email" : "worker@aizhangz.top",
|
||||
"password" : "$2a$10$BESSBI.5BicU8NbFp.HG8envauXACHo/sDe20XvFFezGJWqbI7v1i",
|
||||
"gender" : "MALE",
|
||||
"role" : "GRID_WORKER",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : 10,
|
||||
"gridY" : 10,
|
||||
"region" : "苏州市",
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:34:02.2703065",
|
||||
"updatedAt" : "2025-06-24T23:34:02.2703065",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"createdBy" : {
|
||||
"id" : 1,
|
||||
"name" : "系统管理员",
|
||||
"phone" : "13800138000",
|
||||
"email" : "admin@example.com",
|
||||
"password" : "$2a$10$PjqWk7oBYmN2ecjNd6njFuS125JSGyb9A8v7HAWJjTzTH5fvLDgLK",
|
||||
"gender" : null,
|
||||
"role" : "ADMIN",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.3224414",
|
||||
"updatedAt" : "2025-06-24T23:33:58.323441",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"status" : "ASSIGNED",
|
||||
"assignedAt" : "2025-06-24T23:40:35.4504136",
|
||||
"completedAt" : null,
|
||||
"createdAt" : null,
|
||||
"updatedAt" : null,
|
||||
"title" : "SO2",
|
||||
"description" : "SO2",
|
||||
"pollutionType" : "SO2",
|
||||
"severityLevel" : "MEDIUM",
|
||||
"textAddress" : "东北大学195号",
|
||||
"gridX" : 0,
|
||||
"gridY" : 1,
|
||||
"latitude" : 1.0E-6,
|
||||
"longitude" : 1.0E-6,
|
||||
"history" : [ ],
|
||||
"assignment" : null,
|
||||
"submissions" : [ ]
|
||||
},
|
||||
"assigner" : {
|
||||
"id" : 1,
|
||||
"name" : "系统管理员",
|
||||
"phone" : "13800138000",
|
||||
"email" : "admin@example.com",
|
||||
"password" : "$2a$10$PjqWk7oBYmN2ecjNd6njFuS125JSGyb9A8v7HAWJjTzTH5fvLDgLK",
|
||||
"gender" : null,
|
||||
"role" : "ADMIN",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.3224414",
|
||||
"updatedAt" : "2025-06-24T23:33:58.323441",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"assignmentTime" : "2025-06-24T23:40:35.4534373",
|
||||
"deadline" : null,
|
||||
"status" : "PENDING",
|
||||
"remarks" : null
|
||||
} ]
|
||||
@@ -1,50 +1,50 @@
|
||||
[ {
|
||||
"id" : 1,
|
||||
"fileName" : "屏幕截图 2025-05-06 153004.png",
|
||||
"fileType" : "image/png",
|
||||
"storedFileName" : "4af7ae76-6dbd-4ab4-b103-e04b1ee15ea2.png",
|
||||
"fileSize" : 8645,
|
||||
"uploadDate" : null,
|
||||
"feedback" : {
|
||||
"id" : 4,
|
||||
"eventId" : "6b800765-b0aa-457b-bde7-af60bace45f9",
|
||||
"title" : "SO2",
|
||||
"description" : "SO2",
|
||||
"pollutionType" : "SO2",
|
||||
"severityLevel" : "MEDIUM",
|
||||
"status" : "AI_REVIEWING",
|
||||
"textAddress" : "东北大学195号",
|
||||
"gridX" : 0,
|
||||
"gridY" : 1,
|
||||
"submitterId" : 6,
|
||||
"createdAt" : "2025-06-24T23:35:05.9704809",
|
||||
"updatedAt" : "2025-06-24T23:35:05.9704809",
|
||||
"user" : {
|
||||
"id" : 6,
|
||||
"name" : "公众监督员",
|
||||
"phone" : "13700137004",
|
||||
"email" : "public.supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$tg2qMBxN/grBjChEMOzGI.lPFjBD5H7nwwnIQX59DHJ81AfRUjUI.",
|
||||
"gender" : null,
|
||||
"role" : "PUBLIC_SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.7646754",
|
||||
"updatedAt" : "2025-06-24T23:33:58.7646754",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"latitude" : 1.0E-6,
|
||||
"longitude" : 1.0E-6,
|
||||
"attachments" : [ ],
|
||||
"task" : null
|
||||
},
|
||||
"taskSubmission" : null
|
||||
[ {
|
||||
"id" : 1,
|
||||
"fileName" : "屏幕截图 2025-05-06 153004.png",
|
||||
"fileType" : "image/png",
|
||||
"storedFileName" : "4af7ae76-6dbd-4ab4-b103-e04b1ee15ea2.png",
|
||||
"fileSize" : 8645,
|
||||
"uploadDate" : null,
|
||||
"feedback" : {
|
||||
"id" : 4,
|
||||
"eventId" : "6b800765-b0aa-457b-bde7-af60bace45f9",
|
||||
"title" : "SO2",
|
||||
"description" : "SO2",
|
||||
"pollutionType" : "SO2",
|
||||
"severityLevel" : "MEDIUM",
|
||||
"status" : "AI_REVIEWING",
|
||||
"textAddress" : "东北大学195号",
|
||||
"gridX" : 0,
|
||||
"gridY" : 1,
|
||||
"submitterId" : 6,
|
||||
"createdAt" : "2025-06-24T23:35:05.9704809",
|
||||
"updatedAt" : "2025-06-24T23:35:05.9704809",
|
||||
"user" : {
|
||||
"id" : 6,
|
||||
"name" : "公众监督员",
|
||||
"phone" : "13700137004",
|
||||
"email" : "public.supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$tg2qMBxN/grBjChEMOzGI.lPFjBD5H7nwwnIQX59DHJ81AfRUjUI.",
|
||||
"gender" : null,
|
||||
"role" : "PUBLIC_SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.7646754",
|
||||
"updatedAt" : "2025-06-24T23:33:58.7646754",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"latitude" : 1.0E-6,
|
||||
"longitude" : 1.0E-6,
|
||||
"attachments" : [ ],
|
||||
"task" : null
|
||||
},
|
||||
"taskSubmission" : null
|
||||
} ]
|
||||
@@ -1,161 +1,161 @@
|
||||
[ {
|
||||
"id" : 1,
|
||||
"eventId" : "FB-202301",
|
||||
"title" : "工业区PM2.5超标",
|
||||
"description" : "市中心化工厂在夜间排放黄色烟雾,气味刺鼻。",
|
||||
"pollutionType" : "PM25",
|
||||
"severityLevel" : "HIGH",
|
||||
"status" : "PENDING_ASSIGNMENT",
|
||||
"textAddress" : null,
|
||||
"gridX" : 5,
|
||||
"gridY" : 5,
|
||||
"submitterId" : 6,
|
||||
"createdAt" : "2025-06-24T23:34:02.4935754",
|
||||
"updatedAt" : "2025-06-24T23:34:02.4935754",
|
||||
"user" : {
|
||||
"id" : 6,
|
||||
"name" : "公众监督员",
|
||||
"phone" : "13700137004",
|
||||
"email" : "public.supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$tg2qMBxN/grBjChEMOzGI.lPFjBD5H7nwwnIQX59DHJ81AfRUjUI.",
|
||||
"gender" : null,
|
||||
"role" : "PUBLIC_SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.7646754",
|
||||
"updatedAt" : "2025-06-24T23:33:58.7646754",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"latitude" : 39.9042,
|
||||
"longitude" : 116.4074,
|
||||
"attachments" : [ ],
|
||||
"task" : null
|
||||
}, {
|
||||
"id" : 2,
|
||||
"eventId" : "FB-202302",
|
||||
"title" : "交通要道NO2浓度过高",
|
||||
"description" : "主干道车辆拥堵,空气质量差。",
|
||||
"pollutionType" : "NO2",
|
||||
"severityLevel" : "MEDIUM",
|
||||
"status" : "ASSIGNED",
|
||||
"textAddress" : null,
|
||||
"gridX" : 6,
|
||||
"gridY" : 6,
|
||||
"submitterId" : 6,
|
||||
"createdAt" : "2025-06-24T23:34:02.4945751",
|
||||
"updatedAt" : "2025-06-24T23:34:02.4945751",
|
||||
"user" : {
|
||||
"id" : 6,
|
||||
"name" : "公众监督员",
|
||||
"phone" : "13700137004",
|
||||
"email" : "public.supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$tg2qMBxN/grBjChEMOzGI.lPFjBD5H7nwwnIQX59DHJ81AfRUjUI.",
|
||||
"gender" : null,
|
||||
"role" : "PUBLIC_SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.7646754",
|
||||
"updatedAt" : "2025-06-24T23:33:58.7646754",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"latitude" : 39.915,
|
||||
"longitude" : 116.4,
|
||||
"attachments" : [ ],
|
||||
"task" : null
|
||||
}, {
|
||||
"id" : 3,
|
||||
"eventId" : "FB-202303",
|
||||
"title" : "未知来源的SO2异味",
|
||||
"description" : "居民区闻到类似烧煤的刺鼻气味。",
|
||||
"pollutionType" : "SO2",
|
||||
"severityLevel" : "LOW",
|
||||
"status" : "PROCESSED",
|
||||
"textAddress" : null,
|
||||
"gridX" : 7,
|
||||
"gridY" : 7,
|
||||
"submitterId" : 6,
|
||||
"createdAt" : "2025-06-24T23:34:02.4945751",
|
||||
"updatedAt" : "2025-06-24T23:34:02.4945751",
|
||||
"user" : {
|
||||
"id" : 6,
|
||||
"name" : "公众监督员",
|
||||
"phone" : "13700137004",
|
||||
"email" : "public.supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$tg2qMBxN/grBjChEMOzGI.lPFjBD5H7nwwnIQX59DHJ81AfRUjUI.",
|
||||
"gender" : null,
|
||||
"role" : "PUBLIC_SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.7646754",
|
||||
"updatedAt" : "2025-06-24T23:33:58.7646754",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"latitude" : 39.888,
|
||||
"longitude" : 116.356,
|
||||
"attachments" : [ ],
|
||||
"task" : null
|
||||
}, {
|
||||
"id" : 4,
|
||||
"eventId" : "6b800765-b0aa-457b-bde7-af60bace45f9",
|
||||
"title" : "SO2",
|
||||
"description" : "SO2",
|
||||
"pollutionType" : "SO2",
|
||||
"severityLevel" : "MEDIUM",
|
||||
"status" : "ASSIGNED",
|
||||
"textAddress" : "东北大学195号",
|
||||
"gridX" : 0,
|
||||
"gridY" : 1,
|
||||
"submitterId" : 6,
|
||||
"createdAt" : "2025-06-24T23:35:05.9704809",
|
||||
"updatedAt" : "2025-06-24T23:39:54.0988378",
|
||||
"user" : {
|
||||
"id" : 6,
|
||||
"name" : "公众监督员",
|
||||
"phone" : "13700137004",
|
||||
"email" : "public.supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$tg2qMBxN/grBjChEMOzGI.lPFjBD5H7nwwnIQX59DHJ81AfRUjUI.",
|
||||
"gender" : null,
|
||||
"role" : "PUBLIC_SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.7646754",
|
||||
"updatedAt" : "2025-06-24T23:33:58.7646754",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"latitude" : 1.0E-6,
|
||||
"longitude" : 1.0E-6,
|
||||
"attachments" : [ ],
|
||||
"task" : null
|
||||
[ {
|
||||
"id" : 1,
|
||||
"eventId" : "FB-202301",
|
||||
"title" : "工业区PM2.5超标",
|
||||
"description" : "市中心化工厂在夜间排放黄色烟雾,气味刺鼻。",
|
||||
"pollutionType" : "PM25",
|
||||
"severityLevel" : "HIGH",
|
||||
"status" : "PENDING_ASSIGNMENT",
|
||||
"textAddress" : null,
|
||||
"gridX" : 5,
|
||||
"gridY" : 5,
|
||||
"submitterId" : 6,
|
||||
"createdAt" : "2025-06-24T23:34:02.4935754",
|
||||
"updatedAt" : "2025-06-24T23:34:02.4935754",
|
||||
"user" : {
|
||||
"id" : 6,
|
||||
"name" : "公众监督员",
|
||||
"phone" : "13700137004",
|
||||
"email" : "public.supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$tg2qMBxN/grBjChEMOzGI.lPFjBD5H7nwwnIQX59DHJ81AfRUjUI.",
|
||||
"gender" : null,
|
||||
"role" : "PUBLIC_SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.7646754",
|
||||
"updatedAt" : "2025-06-24T23:33:58.7646754",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"latitude" : 39.9042,
|
||||
"longitude" : 116.4074,
|
||||
"attachments" : [ ],
|
||||
"task" : null
|
||||
}, {
|
||||
"id" : 2,
|
||||
"eventId" : "FB-202302",
|
||||
"title" : "交通要道NO2浓度过高",
|
||||
"description" : "主干道车辆拥堵,空气质量差。",
|
||||
"pollutionType" : "NO2",
|
||||
"severityLevel" : "MEDIUM",
|
||||
"status" : "ASSIGNED",
|
||||
"textAddress" : null,
|
||||
"gridX" : 6,
|
||||
"gridY" : 6,
|
||||
"submitterId" : 6,
|
||||
"createdAt" : "2025-06-24T23:34:02.4945751",
|
||||
"updatedAt" : "2025-06-24T23:34:02.4945751",
|
||||
"user" : {
|
||||
"id" : 6,
|
||||
"name" : "公众监督员",
|
||||
"phone" : "13700137004",
|
||||
"email" : "public.supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$tg2qMBxN/grBjChEMOzGI.lPFjBD5H7nwwnIQX59DHJ81AfRUjUI.",
|
||||
"gender" : null,
|
||||
"role" : "PUBLIC_SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.7646754",
|
||||
"updatedAt" : "2025-06-24T23:33:58.7646754",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"latitude" : 39.915,
|
||||
"longitude" : 116.4,
|
||||
"attachments" : [ ],
|
||||
"task" : null
|
||||
}, {
|
||||
"id" : 3,
|
||||
"eventId" : "FB-202303",
|
||||
"title" : "未知来源的SO2异味",
|
||||
"description" : "居民区闻到类似烧煤的刺鼻气味。",
|
||||
"pollutionType" : "SO2",
|
||||
"severityLevel" : "LOW",
|
||||
"status" : "PROCESSED",
|
||||
"textAddress" : null,
|
||||
"gridX" : 7,
|
||||
"gridY" : 7,
|
||||
"submitterId" : 6,
|
||||
"createdAt" : "2025-06-24T23:34:02.4945751",
|
||||
"updatedAt" : "2025-06-24T23:34:02.4945751",
|
||||
"user" : {
|
||||
"id" : 6,
|
||||
"name" : "公众监督员",
|
||||
"phone" : "13700137004",
|
||||
"email" : "public.supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$tg2qMBxN/grBjChEMOzGI.lPFjBD5H7nwwnIQX59DHJ81AfRUjUI.",
|
||||
"gender" : null,
|
||||
"role" : "PUBLIC_SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.7646754",
|
||||
"updatedAt" : "2025-06-24T23:33:58.7646754",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"latitude" : 39.888,
|
||||
"longitude" : 116.356,
|
||||
"attachments" : [ ],
|
||||
"task" : null
|
||||
}, {
|
||||
"id" : 4,
|
||||
"eventId" : "6b800765-b0aa-457b-bde7-af60bace45f9",
|
||||
"title" : "SO2",
|
||||
"description" : "SO2",
|
||||
"pollutionType" : "SO2",
|
||||
"severityLevel" : "MEDIUM",
|
||||
"status" : "ASSIGNED",
|
||||
"textAddress" : "东北大学195号",
|
||||
"gridX" : 0,
|
||||
"gridY" : 1,
|
||||
"submitterId" : 6,
|
||||
"createdAt" : "2025-06-24T23:35:05.9704809",
|
||||
"updatedAt" : "2025-06-24T23:39:54.0988378",
|
||||
"user" : {
|
||||
"id" : 6,
|
||||
"name" : "公众监督员",
|
||||
"phone" : "13700137004",
|
||||
"email" : "public.supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$tg2qMBxN/grBjChEMOzGI.lPFjBD5H7nwwnIQX59DHJ81AfRUjUI.",
|
||||
"gender" : null,
|
||||
"role" : "PUBLIC_SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.7646754",
|
||||
"updatedAt" : "2025-06-24T23:33:58.7646754",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"latitude" : 1.0E-6,
|
||||
"longitude" : 1.0E-6,
|
||||
"attachments" : [ ],
|
||||
"task" : null
|
||||
} ]
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,36 +1,36 @@
|
||||
[ {
|
||||
"id" : 1,
|
||||
"pollutionType" : "PM25",
|
||||
"pollutantName" : "PM25",
|
||||
"threshold" : 75.0,
|
||||
"unit" : "µg/m³",
|
||||
"description" : "细颗粒物"
|
||||
}, {
|
||||
"id" : 2,
|
||||
"pollutionType" : "O3",
|
||||
"pollutantName" : "O3",
|
||||
"threshold" : 160.0,
|
||||
"unit" : "µg/m³",
|
||||
"description" : "臭氧"
|
||||
}, {
|
||||
"id" : 3,
|
||||
"pollutionType" : "NO2",
|
||||
"pollutantName" : "NO2",
|
||||
"threshold" : 100.0,
|
||||
"unit" : "µg/m³",
|
||||
"description" : "二氧化氮"
|
||||
}, {
|
||||
"id" : 4,
|
||||
"pollutionType" : "SO2",
|
||||
"pollutantName" : "SO2",
|
||||
"threshold" : 150.0,
|
||||
"unit" : "µg/m³",
|
||||
"description" : "二氧化硫"
|
||||
}, {
|
||||
"id" : 5,
|
||||
"pollutionType" : "OTHER",
|
||||
"pollutantName" : "OTHER",
|
||||
"threshold" : 100.0,
|
||||
"unit" : "unit",
|
||||
"description" : "其他污染物"
|
||||
[ {
|
||||
"id" : 1,
|
||||
"pollutionType" : "PM25",
|
||||
"pollutantName" : "PM25",
|
||||
"threshold" : 75.0,
|
||||
"unit" : "µg/m³",
|
||||
"description" : "细颗粒物"
|
||||
}, {
|
||||
"id" : 2,
|
||||
"pollutionType" : "O3",
|
||||
"pollutantName" : "O3",
|
||||
"threshold" : 160.0,
|
||||
"unit" : "µg/m³",
|
||||
"description" : "臭氧"
|
||||
}, {
|
||||
"id" : 3,
|
||||
"pollutionType" : "NO2",
|
||||
"pollutantName" : "NO2",
|
||||
"threshold" : 100.0,
|
||||
"unit" : "µg/m³",
|
||||
"description" : "二氧化氮"
|
||||
}, {
|
||||
"id" : 4,
|
||||
"pollutionType" : "SO2",
|
||||
"pollutantName" : "SO2",
|
||||
"threshold" : 150.0,
|
||||
"unit" : "µg/m³",
|
||||
"description" : "二氧化硫"
|
||||
}, {
|
||||
"id" : 5,
|
||||
"pollutionType" : "OTHER",
|
||||
"pollutantName" : "OTHER",
|
||||
"threshold" : 100.0,
|
||||
"unit" : "unit",
|
||||
"description" : "其他污染物"
|
||||
} ]
|
||||
@@ -1,297 +1,297 @@
|
||||
[ {
|
||||
"id" : 1,
|
||||
"feedback" : null,
|
||||
"assignee" : {
|
||||
"id" : 57,
|
||||
"name" : "特定网格员",
|
||||
"phone" : "14700009999",
|
||||
"email" : "worker@aizhangz.top",
|
||||
"password" : "$2a$10$BESSBI.5BicU8NbFp.HG8envauXACHo/sDe20XvFFezGJWqbI7v1i",
|
||||
"gender" : "MALE",
|
||||
"role" : "GRID_WORKER",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : 10,
|
||||
"gridY" : 10,
|
||||
"region" : "苏州市",
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:34:02.2703065",
|
||||
"updatedAt" : "2025-06-24T23:34:02.2703065",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"createdBy" : {
|
||||
"id" : 5,
|
||||
"name" : "特定主管",
|
||||
"phone" : "13700137003",
|
||||
"email" : "supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$t4pY9dOVge5xJygEasVI/enJPqWZwTVOE9wTSKs6VN8DQ4Dd8D8Zu",
|
||||
"gender" : null,
|
||||
"role" : "SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : "苏州市",
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.6886435",
|
||||
"updatedAt" : "2025-06-24T23:33:58.6886435",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"status" : "IN_PROGRESS",
|
||||
"assignedAt" : "2025-06-22T23:34:02.5085803",
|
||||
"completedAt" : null,
|
||||
"createdAt" : null,
|
||||
"updatedAt" : null,
|
||||
"title" : "调查PM2.5超标问题",
|
||||
"description" : "前往工业区,使用便携式检测仪检测空气质量,并记录排放情况。",
|
||||
"pollutionType" : null,
|
||||
"severityLevel" : "HIGH",
|
||||
"textAddress" : null,
|
||||
"gridX" : 2,
|
||||
"gridY" : 3,
|
||||
"latitude" : 39.9042,
|
||||
"longitude" : 116.4074,
|
||||
"history" : [ ],
|
||||
"assignment" : null,
|
||||
"submissions" : [ ]
|
||||
}, {
|
||||
"id" : 2,
|
||||
"feedback" : null,
|
||||
"assignee" : {
|
||||
"id" : 57,
|
||||
"name" : "特定网格员",
|
||||
"phone" : "14700009999",
|
||||
"email" : "worker@aizhangz.top",
|
||||
"password" : "$2a$10$BESSBI.5BicU8NbFp.HG8envauXACHo/sDe20XvFFezGJWqbI7v1i",
|
||||
"gender" : "MALE",
|
||||
"role" : "GRID_WORKER",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : 10,
|
||||
"gridY" : 10,
|
||||
"region" : "苏州市",
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:34:02.2703065",
|
||||
"updatedAt" : "2025-06-24T23:34:02.2703065",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"createdBy" : {
|
||||
"id" : 5,
|
||||
"name" : "特定主管",
|
||||
"phone" : "13700137003",
|
||||
"email" : "supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$t4pY9dOVge5xJygEasVI/enJPqWZwTVOE9wTSKs6VN8DQ4Dd8D8Zu",
|
||||
"gender" : null,
|
||||
"role" : "SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : "苏州市",
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.6886435",
|
||||
"updatedAt" : "2025-06-24T23:33:58.6886435",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"status" : "COMPLETED",
|
||||
"assignedAt" : "2025-06-20T23:34:02.5085803",
|
||||
"completedAt" : "2025-06-23T23:34:02.5085803",
|
||||
"createdAt" : null,
|
||||
"updatedAt" : null,
|
||||
"title" : "疏导交通并监测NO2",
|
||||
"description" : "在交通要道关键节点进行监测,并与交管部门协调。",
|
||||
"pollutionType" : null,
|
||||
"severityLevel" : "MEDIUM",
|
||||
"textAddress" : null,
|
||||
"gridX" : 4,
|
||||
"gridY" : 5,
|
||||
"latitude" : 39.915,
|
||||
"longitude" : 116.4,
|
||||
"history" : [ ],
|
||||
"assignment" : null,
|
||||
"submissions" : [ ]
|
||||
}, {
|
||||
"id" : 3,
|
||||
"feedback" : null,
|
||||
"assignee" : {
|
||||
"id" : 57,
|
||||
"name" : "特定网格员",
|
||||
"phone" : "14700009999",
|
||||
"email" : "worker@aizhangz.top",
|
||||
"password" : "$2a$10$BESSBI.5BicU8NbFp.HG8envauXACHo/sDe20XvFFezGJWqbI7v1i",
|
||||
"gender" : "MALE",
|
||||
"role" : "GRID_WORKER",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : 10,
|
||||
"gridY" : 10,
|
||||
"region" : "苏州市",
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:34:02.2703065",
|
||||
"updatedAt" : "2025-06-24T23:34:02.2703065",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"createdBy" : {
|
||||
"id" : 5,
|
||||
"name" : "特定主管",
|
||||
"phone" : "13700137003",
|
||||
"email" : "supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$t4pY9dOVge5xJygEasVI/enJPqWZwTVOE9wTSKs6VN8DQ4Dd8D8Zu",
|
||||
"gender" : null,
|
||||
"role" : "SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : "苏州市",
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.6886435",
|
||||
"updatedAt" : "2025-06-24T23:33:58.6886435",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"status" : "ASSIGNED",
|
||||
"assignedAt" : "2025-06-24T23:34:02.5085803",
|
||||
"completedAt" : null,
|
||||
"createdAt" : null,
|
||||
"updatedAt" : null,
|
||||
"title" : "排查SO2异味源",
|
||||
"description" : "在相关居民区进行走访和气味溯源。",
|
||||
"pollutionType" : null,
|
||||
"severityLevel" : "LOW",
|
||||
"textAddress" : null,
|
||||
"gridX" : 6,
|
||||
"gridY" : 7,
|
||||
"latitude" : 39.888,
|
||||
"longitude" : 116.356,
|
||||
"history" : [ ],
|
||||
"assignment" : null,
|
||||
"submissions" : [ ]
|
||||
}, {
|
||||
"id" : 4,
|
||||
"feedback" : {
|
||||
"id" : 4,
|
||||
"eventId" : "6b800765-b0aa-457b-bde7-af60bace45f9",
|
||||
"title" : "SO2",
|
||||
"description" : "SO2",
|
||||
"pollutionType" : "SO2",
|
||||
"severityLevel" : "MEDIUM",
|
||||
"status" : "PENDING_ASSIGNMENT",
|
||||
"textAddress" : "东北大学195号",
|
||||
"gridX" : 0,
|
||||
"gridY" : 1,
|
||||
"submitterId" : 6,
|
||||
"createdAt" : "2025-06-24T23:35:05.9704809",
|
||||
"updatedAt" : "2025-06-24T23:39:54.0988378",
|
||||
"user" : {
|
||||
"id" : 6,
|
||||
"name" : "公众监督员",
|
||||
"phone" : "13700137004",
|
||||
"email" : "public.supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$tg2qMBxN/grBjChEMOzGI.lPFjBD5H7nwwnIQX59DHJ81AfRUjUI.",
|
||||
"gender" : null,
|
||||
"role" : "PUBLIC_SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.7646754",
|
||||
"updatedAt" : "2025-06-24T23:33:58.7646754",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"latitude" : 1.0E-6,
|
||||
"longitude" : 1.0E-6,
|
||||
"attachments" : [ ],
|
||||
"task" : null
|
||||
},
|
||||
"assignee" : {
|
||||
"id" : 57,
|
||||
"name" : "特定网格员",
|
||||
"phone" : "14700009999",
|
||||
"email" : "worker@aizhangz.top",
|
||||
"password" : "$2a$10$BESSBI.5BicU8NbFp.HG8envauXACHo/sDe20XvFFezGJWqbI7v1i",
|
||||
"gender" : "MALE",
|
||||
"role" : "GRID_WORKER",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : 10,
|
||||
"gridY" : 10,
|
||||
"region" : "苏州市",
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:34:02.2703065",
|
||||
"updatedAt" : "2025-06-24T23:34:02.2703065",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"createdBy" : {
|
||||
"id" : 1,
|
||||
"name" : "系统管理员",
|
||||
"phone" : "13800138000",
|
||||
"email" : "admin@example.com",
|
||||
"password" : "$2a$10$PjqWk7oBYmN2ecjNd6njFuS125JSGyb9A8v7HAWJjTzTH5fvLDgLK",
|
||||
"gender" : null,
|
||||
"role" : "ADMIN",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.3224414",
|
||||
"updatedAt" : "2025-06-24T23:33:58.323441",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"status" : "ASSIGNED",
|
||||
"assignedAt" : "2025-06-24T23:40:35.4504136",
|
||||
"completedAt" : null,
|
||||
"createdAt" : null,
|
||||
"updatedAt" : null,
|
||||
"title" : "SO2",
|
||||
"description" : "SO2",
|
||||
"pollutionType" : "SO2",
|
||||
"severityLevel" : "MEDIUM",
|
||||
"textAddress" : "东北大学195号",
|
||||
"gridX" : 0,
|
||||
"gridY" : 1,
|
||||
"latitude" : 1.0E-6,
|
||||
"longitude" : 1.0E-6,
|
||||
"history" : [ ],
|
||||
"assignment" : null,
|
||||
"submissions" : [ ]
|
||||
[ {
|
||||
"id" : 1,
|
||||
"feedback" : null,
|
||||
"assignee" : {
|
||||
"id" : 57,
|
||||
"name" : "特定网格员",
|
||||
"phone" : "14700009999",
|
||||
"email" : "worker@aizhangz.top",
|
||||
"password" : "$2a$10$BESSBI.5BicU8NbFp.HG8envauXACHo/sDe20XvFFezGJWqbI7v1i",
|
||||
"gender" : "MALE",
|
||||
"role" : "GRID_WORKER",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : 10,
|
||||
"gridY" : 10,
|
||||
"region" : "苏州市",
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:34:02.2703065",
|
||||
"updatedAt" : "2025-06-24T23:34:02.2703065",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"createdBy" : {
|
||||
"id" : 5,
|
||||
"name" : "特定主管",
|
||||
"phone" : "13700137003",
|
||||
"email" : "supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$t4pY9dOVge5xJygEasVI/enJPqWZwTVOE9wTSKs6VN8DQ4Dd8D8Zu",
|
||||
"gender" : null,
|
||||
"role" : "SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : "苏州市",
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.6886435",
|
||||
"updatedAt" : "2025-06-24T23:33:58.6886435",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"status" : "IN_PROGRESS",
|
||||
"assignedAt" : "2025-06-22T23:34:02.5085803",
|
||||
"completedAt" : null,
|
||||
"createdAt" : null,
|
||||
"updatedAt" : null,
|
||||
"title" : "调查PM2.5超标问题",
|
||||
"description" : "前往工业区,使用便携式检测仪检测空气质量,并记录排放情况。",
|
||||
"pollutionType" : null,
|
||||
"severityLevel" : "HIGH",
|
||||
"textAddress" : null,
|
||||
"gridX" : 2,
|
||||
"gridY" : 3,
|
||||
"latitude" : 39.9042,
|
||||
"longitude" : 116.4074,
|
||||
"history" : [ ],
|
||||
"assignment" : null,
|
||||
"submissions" : [ ]
|
||||
}, {
|
||||
"id" : 2,
|
||||
"feedback" : null,
|
||||
"assignee" : {
|
||||
"id" : 57,
|
||||
"name" : "特定网格员",
|
||||
"phone" : "14700009999",
|
||||
"email" : "worker@aizhangz.top",
|
||||
"password" : "$2a$10$BESSBI.5BicU8NbFp.HG8envauXACHo/sDe20XvFFezGJWqbI7v1i",
|
||||
"gender" : "MALE",
|
||||
"role" : "GRID_WORKER",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : 10,
|
||||
"gridY" : 10,
|
||||
"region" : "苏州市",
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:34:02.2703065",
|
||||
"updatedAt" : "2025-06-24T23:34:02.2703065",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"createdBy" : {
|
||||
"id" : 5,
|
||||
"name" : "特定主管",
|
||||
"phone" : "13700137003",
|
||||
"email" : "supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$t4pY9dOVge5xJygEasVI/enJPqWZwTVOE9wTSKs6VN8DQ4Dd8D8Zu",
|
||||
"gender" : null,
|
||||
"role" : "SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : "苏州市",
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.6886435",
|
||||
"updatedAt" : "2025-06-24T23:33:58.6886435",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"status" : "COMPLETED",
|
||||
"assignedAt" : "2025-06-20T23:34:02.5085803",
|
||||
"completedAt" : "2025-06-23T23:34:02.5085803",
|
||||
"createdAt" : null,
|
||||
"updatedAt" : null,
|
||||
"title" : "疏导交通并监测NO2",
|
||||
"description" : "在交通要道关键节点进行监测,并与交管部门协调。",
|
||||
"pollutionType" : null,
|
||||
"severityLevel" : "MEDIUM",
|
||||
"textAddress" : null,
|
||||
"gridX" : 4,
|
||||
"gridY" : 5,
|
||||
"latitude" : 39.915,
|
||||
"longitude" : 116.4,
|
||||
"history" : [ ],
|
||||
"assignment" : null,
|
||||
"submissions" : [ ]
|
||||
}, {
|
||||
"id" : 3,
|
||||
"feedback" : null,
|
||||
"assignee" : {
|
||||
"id" : 57,
|
||||
"name" : "特定网格员",
|
||||
"phone" : "14700009999",
|
||||
"email" : "worker@aizhangz.top",
|
||||
"password" : "$2a$10$BESSBI.5BicU8NbFp.HG8envauXACHo/sDe20XvFFezGJWqbI7v1i",
|
||||
"gender" : "MALE",
|
||||
"role" : "GRID_WORKER",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : 10,
|
||||
"gridY" : 10,
|
||||
"region" : "苏州市",
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:34:02.2703065",
|
||||
"updatedAt" : "2025-06-24T23:34:02.2703065",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"createdBy" : {
|
||||
"id" : 5,
|
||||
"name" : "特定主管",
|
||||
"phone" : "13700137003",
|
||||
"email" : "supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$t4pY9dOVge5xJygEasVI/enJPqWZwTVOE9wTSKs6VN8DQ4Dd8D8Zu",
|
||||
"gender" : null,
|
||||
"role" : "SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : "苏州市",
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.6886435",
|
||||
"updatedAt" : "2025-06-24T23:33:58.6886435",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"status" : "ASSIGNED",
|
||||
"assignedAt" : "2025-06-24T23:34:02.5085803",
|
||||
"completedAt" : null,
|
||||
"createdAt" : null,
|
||||
"updatedAt" : null,
|
||||
"title" : "排查SO2异味源",
|
||||
"description" : "在相关居民区进行走访和气味溯源。",
|
||||
"pollutionType" : null,
|
||||
"severityLevel" : "LOW",
|
||||
"textAddress" : null,
|
||||
"gridX" : 6,
|
||||
"gridY" : 7,
|
||||
"latitude" : 39.888,
|
||||
"longitude" : 116.356,
|
||||
"history" : [ ],
|
||||
"assignment" : null,
|
||||
"submissions" : [ ]
|
||||
}, {
|
||||
"id" : 4,
|
||||
"feedback" : {
|
||||
"id" : 4,
|
||||
"eventId" : "6b800765-b0aa-457b-bde7-af60bace45f9",
|
||||
"title" : "SO2",
|
||||
"description" : "SO2",
|
||||
"pollutionType" : "SO2",
|
||||
"severityLevel" : "MEDIUM",
|
||||
"status" : "PENDING_ASSIGNMENT",
|
||||
"textAddress" : "东北大学195号",
|
||||
"gridX" : 0,
|
||||
"gridY" : 1,
|
||||
"submitterId" : 6,
|
||||
"createdAt" : "2025-06-24T23:35:05.9704809",
|
||||
"updatedAt" : "2025-06-24T23:39:54.0988378",
|
||||
"user" : {
|
||||
"id" : 6,
|
||||
"name" : "公众监督员",
|
||||
"phone" : "13700137004",
|
||||
"email" : "public.supervisor@aizhangz.top",
|
||||
"password" : "$2a$10$tg2qMBxN/grBjChEMOzGI.lPFjBD5H7nwwnIQX59DHJ81AfRUjUI.",
|
||||
"gender" : null,
|
||||
"role" : "PUBLIC_SUPERVISOR",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.7646754",
|
||||
"updatedAt" : "2025-06-24T23:33:58.7646754",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"latitude" : 1.0E-6,
|
||||
"longitude" : 1.0E-6,
|
||||
"attachments" : [ ],
|
||||
"task" : null
|
||||
},
|
||||
"assignee" : {
|
||||
"id" : 57,
|
||||
"name" : "特定网格员",
|
||||
"phone" : "14700009999",
|
||||
"email" : "worker@aizhangz.top",
|
||||
"password" : "$2a$10$BESSBI.5BicU8NbFp.HG8envauXACHo/sDe20XvFFezGJWqbI7v1i",
|
||||
"gender" : "MALE",
|
||||
"role" : "GRID_WORKER",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : 10,
|
||||
"gridY" : 10,
|
||||
"region" : "苏州市",
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:34:02.2703065",
|
||||
"updatedAt" : "2025-06-24T23:34:02.2703065",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"createdBy" : {
|
||||
"id" : 1,
|
||||
"name" : "系统管理员",
|
||||
"phone" : "13800138000",
|
||||
"email" : "admin@example.com",
|
||||
"password" : "$2a$10$PjqWk7oBYmN2ecjNd6njFuS125JSGyb9A8v7HAWJjTzTH5fvLDgLK",
|
||||
"gender" : null,
|
||||
"role" : "ADMIN",
|
||||
"status" : "ACTIVE",
|
||||
"gridX" : null,
|
||||
"gridY" : null,
|
||||
"region" : null,
|
||||
"level" : null,
|
||||
"skills" : null,
|
||||
"createdAt" : "2025-06-24T23:33:58.3224414",
|
||||
"updatedAt" : "2025-06-24T23:33:58.323441",
|
||||
"enabled" : true,
|
||||
"currentLatitude" : null,
|
||||
"currentLongitude" : null,
|
||||
"failedLoginAttempts" : 0,
|
||||
"lockoutEndTime" : null
|
||||
},
|
||||
"status" : "ASSIGNED",
|
||||
"assignedAt" : "2025-06-24T23:40:35.4504136",
|
||||
"completedAt" : null,
|
||||
"createdAt" : null,
|
||||
"updatedAt" : null,
|
||||
"title" : "SO2",
|
||||
"description" : "SO2",
|
||||
"pollutionType" : "SO2",
|
||||
"severityLevel" : "MEDIUM",
|
||||
"textAddress" : "东北大学195号",
|
||||
"gridX" : 0,
|
||||
"gridY" : 1,
|
||||
"latitude" : 1.0E-6,
|
||||
"longitude" : 1.0E-6,
|
||||
"history" : [ ],
|
||||
"assignment" : null,
|
||||
"submissions" : [ ]
|
||||
} ]
|
||||
File diff suppressed because it is too large
Load Diff
298
ems-backend/mvnw.cmd
vendored
298
ems-backend/mvnw.cmd
vendored
@@ -1,149 +1,149 @@
|
||||
<# : batch portion
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Apache Maven Wrapper startup batch script, version 3.3.2
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
||||
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
||||
@SET __MVNW_CMD__=
|
||||
@SET __MVNW_ERROR__=
|
||||
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||
@SET PSModulePath=
|
||||
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
||||
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
||||
)
|
||||
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
||||
@SET __MVNW_PSMODULEP_SAVE=
|
||||
@SET __MVNW_ARG0_NAME__=
|
||||
@SET MVNW_USERNAME=
|
||||
@SET MVNW_PASSWORD=
|
||||
@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
|
||||
@echo Cannot start maven from wrapper >&2 && exit /b 1
|
||||
@GOTO :EOF
|
||||
: end batch / begin powershell #>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
if ($env:MVNW_VERBOSE -eq "true") {
|
||||
$VerbosePreference = "Continue"
|
||||
}
|
||||
|
||||
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||
if (!$distributionUrl) {
|
||||
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||
}
|
||||
|
||||
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
||||
"maven-mvnd-*" {
|
||||
$USE_MVND = $true
|
||||
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
||||
$MVN_CMD = "mvnd.cmd"
|
||||
break
|
||||
}
|
||||
default {
|
||||
$USE_MVND = $false
|
||||
$MVN_CMD = $script -replace '^mvnw','mvn'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
if ($env:MVNW_REPOURL) {
|
||||
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
|
||||
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
|
||||
}
|
||||
$distributionUrlName = $distributionUrl -replace '^.*/',''
|
||||
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
|
||||
$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
|
||||
if ($env:MAVEN_USER_HOME) {
|
||||
$MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
|
||||
}
|
||||
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
||||
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
||||
|
||||
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
||||
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
exit $?
|
||||
}
|
||||
|
||||
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
||||
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
||||
}
|
||||
|
||||
# prepare tmp dir
|
||||
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
|
||||
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
|
||||
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
|
||||
trap {
|
||||
if ($TMP_DOWNLOAD_DIR.Exists) {
|
||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||
}
|
||||
}
|
||||
|
||||
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
||||
|
||||
# Download and Install Apache Maven
|
||||
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||
Write-Verbose "Downloading from: $distributionUrl"
|
||||
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
|
||||
$webclient = New-Object System.Net.WebClient
|
||||
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
||||
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
|
||||
}
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
||||
if ($distributionSha256Sum) {
|
||||
if ($USE_MVND) {
|
||||
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
||||
}
|
||||
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
|
||||
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
|
||||
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
|
||||
}
|
||||
}
|
||||
|
||||
# unzip and move
|
||||
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
|
||||
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
|
||||
try {
|
||||
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
|
||||
} catch {
|
||||
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
|
||||
Write-Error "fail to move MAVEN_HOME"
|
||||
}
|
||||
} finally {
|
||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||
}
|
||||
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
<# : batch portion
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Apache Maven Wrapper startup batch script, version 3.3.2
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
||||
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
||||
@SET __MVNW_CMD__=
|
||||
@SET __MVNW_ERROR__=
|
||||
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||
@SET PSModulePath=
|
||||
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
||||
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
||||
)
|
||||
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
||||
@SET __MVNW_PSMODULEP_SAVE=
|
||||
@SET __MVNW_ARG0_NAME__=
|
||||
@SET MVNW_USERNAME=
|
||||
@SET MVNW_PASSWORD=
|
||||
@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
|
||||
@echo Cannot start maven from wrapper >&2 && exit /b 1
|
||||
@GOTO :EOF
|
||||
: end batch / begin powershell #>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
if ($env:MVNW_VERBOSE -eq "true") {
|
||||
$VerbosePreference = "Continue"
|
||||
}
|
||||
|
||||
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||
if (!$distributionUrl) {
|
||||
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||
}
|
||||
|
||||
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
||||
"maven-mvnd-*" {
|
||||
$USE_MVND = $true
|
||||
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
||||
$MVN_CMD = "mvnd.cmd"
|
||||
break
|
||||
}
|
||||
default {
|
||||
$USE_MVND = $false
|
||||
$MVN_CMD = $script -replace '^mvnw','mvn'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
if ($env:MVNW_REPOURL) {
|
||||
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
|
||||
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
|
||||
}
|
||||
$distributionUrlName = $distributionUrl -replace '^.*/',''
|
||||
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
|
||||
$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
|
||||
if ($env:MAVEN_USER_HOME) {
|
||||
$MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
|
||||
}
|
||||
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
||||
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
||||
|
||||
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
||||
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
exit $?
|
||||
}
|
||||
|
||||
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
||||
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
||||
}
|
||||
|
||||
# prepare tmp dir
|
||||
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
|
||||
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
|
||||
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
|
||||
trap {
|
||||
if ($TMP_DOWNLOAD_DIR.Exists) {
|
||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||
}
|
||||
}
|
||||
|
||||
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
||||
|
||||
# Download and Install Apache Maven
|
||||
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||
Write-Verbose "Downloading from: $distributionUrl"
|
||||
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
|
||||
$webclient = New-Object System.Net.WebClient
|
||||
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
||||
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
|
||||
}
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
||||
if ($distributionSha256Sum) {
|
||||
if ($USE_MVND) {
|
||||
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
||||
}
|
||||
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
|
||||
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
|
||||
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
|
||||
}
|
||||
}
|
||||
|
||||
# unzip and move
|
||||
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
|
||||
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
|
||||
try {
|
||||
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
|
||||
} catch {
|
||||
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
|
||||
Write-Error "fail to move MAVEN_HOME"
|
||||
}
|
||||
} finally {
|
||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||
}
|
||||
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
|
||||
@@ -1,141 +1,141 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.dne</groupId>
|
||||
<artifactId>ems-backend</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>ems-backend</name>
|
||||
<description>ems-backend</description>
|
||||
<url/>
|
||||
<licenses>
|
||||
<license/>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer/>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection/>
|
||||
<developerConnection/>
|
||||
<tag/>
|
||||
<url/>
|
||||
</scm>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>0.11.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Swagger UI -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Google Guava for Caching -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>32.1.2-jre</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.dne</groupId>
|
||||
<artifactId>ems-backend</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>ems-backend</name>
|
||||
<description>ems-backend</description>
|
||||
<url/>
|
||||
<licenses>
|
||||
<license/>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer/>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection/>
|
||||
<developerConnection/>
|
||||
<tag/>
|
||||
<url/>
|
||||
</scm>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>0.11.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Swagger UI -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Google Guava for Caching -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>32.1.2-jre</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
package com.dne.ems;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
/**
|
||||
* EMS系统主启动类
|
||||
*
|
||||
* <p>核心配置:
|
||||
* <ul>
|
||||
* <li>@SpringBootApplication - 启用Spring Boot自动配置,排除数据库相关配置</li>
|
||||
* <li>@ComponentScan - 扫描com.dne.ems包下的组件</li>
|
||||
* <li>@EnableAsync - 启用异步处理</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2025-06
|
||||
*/
|
||||
@SpringBootApplication(exclude = {
|
||||
DataSourceAutoConfiguration.class,
|
||||
HibernateJpaAutoConfiguration.class
|
||||
})
|
||||
@ComponentScan(basePackages = "com.dne.ems")
|
||||
@EnableAsync
|
||||
public class EmsBackendApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(EmsBackendApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
package com.dne.ems;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
/**
|
||||
* EMS系统主启动类
|
||||
*
|
||||
* <p>核心配置:
|
||||
* <ul>
|
||||
* <li>@SpringBootApplication - 启用Spring Boot自动配置,排除数据库相关配置</li>
|
||||
* <li>@ComponentScan - 扫描com.dne.ems包下的组件</li>
|
||||
* <li>@EnableAsync - 启用异步处理</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2025-06
|
||||
*/
|
||||
@SpringBootApplication(exclude = {
|
||||
DataSourceAutoConfiguration.class,
|
||||
HibernateJpaAutoConfiguration.class
|
||||
})
|
||||
@ComponentScan(basePackages = "com.dne.ems")
|
||||
@EnableAsync
|
||||
public class EmsBackendApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(EmsBackendApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,134 +1,134 @@
|
||||
package com.dne.ems.config;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 这个类提供了空的JPA注解实现,用于在移除JPA依赖后保持代码兼容性。
|
||||
* 这些注解不会有任何实际效果,仅用于编译通过。
|
||||
*/
|
||||
public class EmptyJpaAnnotations {
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface Entity {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface Table {
|
||||
String name() default "";
|
||||
String schema() default "";
|
||||
String catalog() default "";
|
||||
UniqueConstraint[] uniqueConstraints() default {};
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface Column {
|
||||
String name() default "";
|
||||
boolean unique() default false;
|
||||
boolean nullable() default true;
|
||||
boolean insertable() default true;
|
||||
boolean updatable() default true;
|
||||
String columnDefinition() default "";
|
||||
String table() default "";
|
||||
int length() default 255;
|
||||
int precision() default 0;
|
||||
int scale() default 0;
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface Id {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface GeneratedValue {
|
||||
GenerationType strategy() default GenerationType.AUTO;
|
||||
String generator() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface ManyToOne {
|
||||
Class<?> targetEntity() default void.class;
|
||||
FetchType fetch() default FetchType.EAGER;
|
||||
boolean optional() default true;
|
||||
String mappedBy() default "";
|
||||
CascadeType[] cascade() default {};
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface OneToMany {
|
||||
Class<?> targetEntity() default void.class;
|
||||
String mappedBy() default "";
|
||||
CascadeType[] cascade() default {};
|
||||
FetchType fetch() default FetchType.LAZY;
|
||||
boolean orphanRemoval() default false;
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface OneToOne {
|
||||
Class<?> targetEntity() default void.class;
|
||||
String mappedBy() default "";
|
||||
CascadeType[] cascade() default {};
|
||||
FetchType fetch() default FetchType.EAGER;
|
||||
boolean optional() default true;
|
||||
boolean orphanRemoval() default false;
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface ManyToMany {
|
||||
Class<?> targetEntity() default void.class;
|
||||
String mappedBy() default "";
|
||||
CascadeType[] cascade() default {};
|
||||
FetchType fetch() default FetchType.LAZY;
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface JoinColumn {
|
||||
String name() default "";
|
||||
String referencedColumnName() default "";
|
||||
boolean unique() default false;
|
||||
boolean nullable() default true;
|
||||
boolean insertable() default true;
|
||||
boolean updatable() default true;
|
||||
String columnDefinition() default "";
|
||||
String table() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface UniqueConstraint {
|
||||
String name() default "";
|
||||
String[] columnNames();
|
||||
}
|
||||
|
||||
public enum GenerationType {
|
||||
TABLE,
|
||||
SEQUENCE,
|
||||
IDENTITY,
|
||||
AUTO
|
||||
}
|
||||
|
||||
public enum FetchType {
|
||||
LAZY,
|
||||
EAGER
|
||||
}
|
||||
|
||||
public enum CascadeType {
|
||||
ALL,
|
||||
PERSIST,
|
||||
MERGE,
|
||||
REMOVE,
|
||||
REFRESH,
|
||||
DETACH
|
||||
}
|
||||
package com.dne.ems.config;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 这个类提供了空的JPA注解实现,用于在移除JPA依赖后保持代码兼容性。
|
||||
* 这些注解不会有任何实际效果,仅用于编译通过。
|
||||
*/
|
||||
public class EmptyJpaAnnotations {
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface Entity {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface Table {
|
||||
String name() default "";
|
||||
String schema() default "";
|
||||
String catalog() default "";
|
||||
UniqueConstraint[] uniqueConstraints() default {};
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface Column {
|
||||
String name() default "";
|
||||
boolean unique() default false;
|
||||
boolean nullable() default true;
|
||||
boolean insertable() default true;
|
||||
boolean updatable() default true;
|
||||
String columnDefinition() default "";
|
||||
String table() default "";
|
||||
int length() default 255;
|
||||
int precision() default 0;
|
||||
int scale() default 0;
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface Id {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface GeneratedValue {
|
||||
GenerationType strategy() default GenerationType.AUTO;
|
||||
String generator() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface ManyToOne {
|
||||
Class<?> targetEntity() default void.class;
|
||||
FetchType fetch() default FetchType.EAGER;
|
||||
boolean optional() default true;
|
||||
String mappedBy() default "";
|
||||
CascadeType[] cascade() default {};
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface OneToMany {
|
||||
Class<?> targetEntity() default void.class;
|
||||
String mappedBy() default "";
|
||||
CascadeType[] cascade() default {};
|
||||
FetchType fetch() default FetchType.LAZY;
|
||||
boolean orphanRemoval() default false;
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface OneToOne {
|
||||
Class<?> targetEntity() default void.class;
|
||||
String mappedBy() default "";
|
||||
CascadeType[] cascade() default {};
|
||||
FetchType fetch() default FetchType.EAGER;
|
||||
boolean optional() default true;
|
||||
boolean orphanRemoval() default false;
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface ManyToMany {
|
||||
Class<?> targetEntity() default void.class;
|
||||
String mappedBy() default "";
|
||||
CascadeType[] cascade() default {};
|
||||
FetchType fetch() default FetchType.LAZY;
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
public @interface JoinColumn {
|
||||
String name() default "";
|
||||
String referencedColumnName() default "";
|
||||
boolean unique() default false;
|
||||
boolean nullable() default true;
|
||||
boolean insertable() default true;
|
||||
boolean updatable() default true;
|
||||
String columnDefinition() default "";
|
||||
String table() default "";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface UniqueConstraint {
|
||||
String name() default "";
|
||||
String[] columnNames();
|
||||
}
|
||||
|
||||
public enum GenerationType {
|
||||
TABLE,
|
||||
SEQUENCE,
|
||||
IDENTITY,
|
||||
AUTO
|
||||
}
|
||||
|
||||
public enum FetchType {
|
||||
LAZY,
|
||||
EAGER
|
||||
}
|
||||
|
||||
public enum CascadeType {
|
||||
ALL,
|
||||
PERSIST,
|
||||
MERGE,
|
||||
REMOVE,
|
||||
REFRESH,
|
||||
DETACH
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,37 @@
|
||||
package com.dne.ems.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 文件存储配置属性类,用于管理文件上传存储的相关配置
|
||||
*
|
||||
* <p>该配置类通过Spring Boot的@ConfigurationProperties注解,
|
||||
* 自动绑定application配置文件中以"file"为前缀的属性。
|
||||
* 主要用于指定上传文件的存储目录。
|
||||
*
|
||||
* <p>使用示例:在application.yml中配置
|
||||
* <pre>
|
||||
* file:
|
||||
* upload-dir: /path/to/uploads
|
||||
* </pre>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "file")
|
||||
@Data
|
||||
public class FileStorageProperties {
|
||||
|
||||
/**
|
||||
* The directory where uploaded files will be stored.
|
||||
* This path should be configured in the application.yml file.
|
||||
* Example: file.upload-dir=/path/to/your/upload-dir
|
||||
*/
|
||||
private String uploadDir;
|
||||
|
||||
package com.dne.ems.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 文件存储配置属性类,用于管理文件上传存储的相关配置
|
||||
*
|
||||
* <p>该配置类通过Spring Boot的@ConfigurationProperties注解,
|
||||
* 自动绑定application配置文件中以"file"为前缀的属性。
|
||||
* 主要用于指定上传文件的存储目录。
|
||||
*
|
||||
* <p>使用示例:在application.yml中配置
|
||||
* <pre>
|
||||
* file:
|
||||
* upload-dir: /path/to/uploads
|
||||
* </pre>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "file")
|
||||
@Data
|
||||
public class FileStorageProperties {
|
||||
|
||||
/**
|
||||
* The directory where uploaded files will be stored.
|
||||
* This path should be configured in the application.yml file.
|
||||
* Example: file.upload-dir=/path/to/your/upload-dir
|
||||
*/
|
||||
private String uploadDir;
|
||||
|
||||
}
|
||||
@@ -1,40 +1,40 @@
|
||||
package com.dne.ems.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
|
||||
import io.swagger.v3.oas.annotations.info.Info;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityScheme;
|
||||
|
||||
/**
|
||||
* OpenAPI配置类,用于配置Swagger API文档
|
||||
*
|
||||
* <p>该配置类使用SpringDoc OpenAPI实现,提供以下功能:
|
||||
* <ul>
|
||||
* <li>配置API文档的基本信息(标题、版本等)</li>
|
||||
* <li>配置JWT Bearer Token认证方案</li>
|
||||
* <li>为所有API端点添加安全要求</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>通过该配置,可以在/swagger-ui.html访问API文档界面
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Configuration
|
||||
@OpenAPIDefinition(
|
||||
info = @Info(title = "EMS API", version = "v1"),
|
||||
security = @SecurityRequirement(name = "bearerAuth")
|
||||
)
|
||||
@SecurityScheme(
|
||||
name = "bearerAuth",
|
||||
type = SecuritySchemeType.HTTP,
|
||||
scheme = "bearer",
|
||||
bearerFormat = "JWT"
|
||||
)
|
||||
public class OpenApiConfig {
|
||||
// 配置类,无需实现内容
|
||||
package com.dne.ems.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
|
||||
import io.swagger.v3.oas.annotations.info.Info;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityScheme;
|
||||
|
||||
/**
|
||||
* OpenAPI配置类,用于配置Swagger API文档
|
||||
*
|
||||
* <p>该配置类使用SpringDoc OpenAPI实现,提供以下功能:
|
||||
* <ul>
|
||||
* <li>配置API文档的基本信息(标题、版本等)</li>
|
||||
* <li>配置JWT Bearer Token认证方案</li>
|
||||
* <li>为所有API端点添加安全要求</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>通过该配置,可以在/swagger-ui.html访问API文档界面
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Configuration
|
||||
@OpenAPIDefinition(
|
||||
info = @Info(title = "EMS API", version = "v1"),
|
||||
security = @SecurityRequirement(name = "bearerAuth")
|
||||
)
|
||||
@SecurityScheme(
|
||||
name = "bearerAuth",
|
||||
type = SecuritySchemeType.HTTP,
|
||||
scheme = "bearer",
|
||||
bearerFormat = "JWT"
|
||||
)
|
||||
public class OpenApiConfig {
|
||||
// 配置类,无需实现内容
|
||||
}
|
||||
@@ -1,36 +1,36 @@
|
||||
package com.dne.ems.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* RestTemplate配置类,提供HTTP客户端功能
|
||||
*
|
||||
* <p>该配置类创建并配置RestTemplate Bean,用于执行HTTP请求。
|
||||
* RestTemplate是Spring提供的用于与RESTful服务交互的核心类,
|
||||
* 支持各种HTTP方法(GET、POST、PUT、DELETE等)。
|
||||
*
|
||||
* <p>主要用途:
|
||||
* <ul>
|
||||
* <li>与外部API进行集成</li>
|
||||
* <li>执行微服务间的通信</li>
|
||||
* <li>获取外部数据源的信息</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
/**
|
||||
* 创建并配置 RestTemplate Bean
|
||||
* @return 配置好的 RestTemplate 实例
|
||||
*/
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
package com.dne.ems.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* RestTemplate配置类,提供HTTP客户端功能
|
||||
*
|
||||
* <p>该配置类创建并配置RestTemplate Bean,用于执行HTTP请求。
|
||||
* RestTemplate是Spring提供的用于与RESTful服务交互的核心类,
|
||||
* 支持各种HTTP方法(GET、POST、PUT、DELETE等)。
|
||||
*
|
||||
* <p>主要用途:
|
||||
* <ul>
|
||||
* <li>与外部API进行集成</li>
|
||||
* <li>执行微服务间的通信</li>
|
||||
* <li>获取外部数据源的信息</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
/**
|
||||
* 创建并配置 RestTemplate Bean
|
||||
* @return 配置好的 RestTemplate 实例
|
||||
*/
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,33 @@
|
||||
package com.dne.ems.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* WebClient配置类,提供响应式HTTP客户端功能
|
||||
*
|
||||
* <p>该配置类创建并配置WebClient Bean,用于执行响应式HTTP请求。
|
||||
* WebClient是Spring WebFlux提供的非阻塞、响应式的HTTP客户端,
|
||||
* 支持异步请求处理和响应式流。
|
||||
*
|
||||
* <p>主要优势:
|
||||
* <ul>
|
||||
* <li>非阻塞I/O,提高系统吞吐量</li>
|
||||
* <li>支持响应式编程模型</li>
|
||||
* <li>提供流式处理能力</li>
|
||||
* <li>适用于高并发场景</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Configuration
|
||||
public class WebClientConfig {
|
||||
|
||||
@Bean
|
||||
public WebClient webClient() {
|
||||
return WebClient.builder().build();
|
||||
}
|
||||
package com.dne.ems.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* WebClient配置类,提供响应式HTTP客户端功能
|
||||
*
|
||||
* <p>该配置类创建并配置WebClient Bean,用于执行响应式HTTP请求。
|
||||
* WebClient是Spring WebFlux提供的非阻塞、响应式的HTTP客户端,
|
||||
* 支持异步请求处理和响应式流。
|
||||
*
|
||||
* <p>主要优势:
|
||||
* <ul>
|
||||
* <li>非阻塞I/O,提高系统吞吐量</li>
|
||||
* <li>支持响应式编程模型</li>
|
||||
* <li>提供流式处理能力</li>
|
||||
* <li>适用于高并发场景</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Configuration
|
||||
public class WebClientConfig {
|
||||
|
||||
@Bean
|
||||
public WebClient webClient() {
|
||||
return WebClient.builder().build();
|
||||
}
|
||||
}
|
||||
@@ -1,51 +1,51 @@
|
||||
package com.dne.ems.config.converter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
import jakarta.persistence.Converter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Converter
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class StringListConverter implements AttributeConverter<List<String>, String> {
|
||||
|
||||
private final ObjectMapper 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("Could not convert list to JSON string", e);
|
||||
throw new IllegalArgumentException("Error converting list to JSON", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> convertToEntityAttribute(String dbData) {
|
||||
if (!StringUtils.hasText(dbData)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(dbData, new TypeReference<>() {});
|
||||
} catch (JsonProcessingException e) {
|
||||
log.warn("Could not deserialize JSON string to list: '{}'. Returning empty list.", dbData, e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
package com.dne.ems.config.converter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
import jakarta.persistence.Converter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Converter
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class StringListConverter implements AttributeConverter<List<String>, String> {
|
||||
|
||||
private final ObjectMapper 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("Could not convert list to JSON string", e);
|
||||
throw new IllegalArgumentException("Error converting list to JSON", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> convertToEntityAttribute(String dbData) {
|
||||
if (!StringUtils.hasText(dbData)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(dbData, new TypeReference<>() {});
|
||||
} catch (JsonProcessingException e) {
|
||||
log.warn("Could not deserialize JSON string to list: '{}'. Returning empty list.", dbData, e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,134 +1,134 @@
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.JwtAuthenticationResponse;
|
||||
import com.dne.ems.dto.LoginRequest;
|
||||
import com.dne.ems.dto.PasswordResetDto;
|
||||
import com.dne.ems.dto.PasswordResetRequest;
|
||||
import com.dne.ems.dto.PasswordResetWithCodeDto;
|
||||
import com.dne.ems.dto.SignUpRequest;
|
||||
import com.dne.ems.service.AuthService;
|
||||
import com.dne.ems.service.OperationLogService;
|
||||
import com.dne.ems.service.VerificationCodeService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/auth")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class AuthController {
|
||||
|
||||
private final AuthService authService;
|
||||
private final VerificationCodeService verificationCodeService;
|
||||
@SuppressWarnings("unused")
|
||||
private final OperationLogService operationLogService;
|
||||
|
||||
/**
|
||||
* 用户注册接口
|
||||
* @param request 包含用户注册信息的请求体
|
||||
* @return 成功创建返回201状态码
|
||||
*/
|
||||
@PostMapping("/signup")
|
||||
public ResponseEntity<Void> signUp(@Valid @RequestBody SignUpRequest request) {
|
||||
authService.registerUser(request);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录认证接口
|
||||
* @param request 包含用户凭证的登录请求体
|
||||
* @return 包含JWT令牌的认证响应实体
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<JwtAuthenticationResponse> signIn(@Valid @RequestBody LoginRequest request) {
|
||||
return ResponseEntity.ok(authService.signIn(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登出接口
|
||||
* @return 成功则返回200 OK
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
public ResponseEntity<Void> logout() {
|
||||
authService.logout();
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求发送账号注册验证码到指定邮箱。
|
||||
*
|
||||
* @param email 接收验证码的邮箱地址
|
||||
* @return 成功则返回200 OK
|
||||
*/
|
||||
@PostMapping("/send-verification-code")
|
||||
public ResponseEntity<Void> sendVerificationCode(@RequestParam @NotBlank @Email String email) {
|
||||
verificationCodeService.sendVerificationCode(email);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求发送密码重置验证码到指定邮箱。
|
||||
*
|
||||
* @param email 接收验证码的邮箱地址
|
||||
* @return 成功则返回200 OK
|
||||
*/
|
||||
@PostMapping("/send-password-reset-code")
|
||||
public ResponseEntity<Void> sendPasswordResetCode(@RequestParam @NotBlank @Email String email) {
|
||||
authService.requestPasswordReset(email);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求密码重置(将发送包含验证码的邮件)
|
||||
*
|
||||
* @param request 包含邮箱的请求
|
||||
* @return 成功则返回200 OK
|
||||
* @deprecated 使用 {@link #sendPasswordResetCode(String)} 代替
|
||||
*/
|
||||
@PostMapping("/request-password-reset")
|
||||
@Deprecated
|
||||
public ResponseEntity<Void> requestPasswordReset(@Valid @RequestBody PasswordResetRequest request) {
|
||||
authService.requestPasswordReset(request.email());
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用验证码重置密码
|
||||
*
|
||||
* @param request 包含邮箱、验证码和新密码的请求
|
||||
* @return 成功则返回200 OK
|
||||
*/
|
||||
@PostMapping("/reset-password-with-code")
|
||||
public ResponseEntity<Void> resetPasswordWithCode(@Valid @RequestBody PasswordResetWithCodeDto request) {
|
||||
authService.resetPasswordWithCode(request.email(), request.code(), request.newPassword());
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用令牌重置密码(旧版API,保留以兼容)
|
||||
* 这将使用令牌和新密码进行重置。
|
||||
* @param request 包含令牌和新密码的请求
|
||||
* @return 成功则返回200 OK
|
||||
* @deprecated 使用基于验证码的 {@link #resetPasswordWithCode(PasswordResetWithCodeDto)} 替代
|
||||
*/
|
||||
@PostMapping("/reset-password")
|
||||
@Deprecated
|
||||
public ResponseEntity<Void> resetPassword(@Valid @RequestBody PasswordResetDto request) {
|
||||
authService.resetPassword(request.token(), request.newPassword());
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
// Internal DTOs have been moved to the com.dne.ems.dto package
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.JwtAuthenticationResponse;
|
||||
import com.dne.ems.dto.LoginRequest;
|
||||
import com.dne.ems.dto.PasswordResetDto;
|
||||
import com.dne.ems.dto.PasswordResetRequest;
|
||||
import com.dne.ems.dto.PasswordResetWithCodeDto;
|
||||
import com.dne.ems.dto.SignUpRequest;
|
||||
import com.dne.ems.service.AuthService;
|
||||
import com.dne.ems.service.OperationLogService;
|
||||
import com.dne.ems.service.VerificationCodeService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/auth")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class AuthController {
|
||||
|
||||
private final AuthService authService;
|
||||
private final VerificationCodeService verificationCodeService;
|
||||
@SuppressWarnings("unused")
|
||||
private final OperationLogService operationLogService;
|
||||
|
||||
/**
|
||||
* 用户注册接口
|
||||
* @param request 包含用户注册信息的请求体
|
||||
* @return 成功创建返回201状态码
|
||||
*/
|
||||
@PostMapping("/signup")
|
||||
public ResponseEntity<Void> signUp(@Valid @RequestBody SignUpRequest request) {
|
||||
authService.registerUser(request);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录认证接口
|
||||
* @param request 包含用户凭证的登录请求体
|
||||
* @return 包含JWT令牌的认证响应实体
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<JwtAuthenticationResponse> signIn(@Valid @RequestBody LoginRequest request) {
|
||||
return ResponseEntity.ok(authService.signIn(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登出接口
|
||||
* @return 成功则返回200 OK
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
public ResponseEntity<Void> logout() {
|
||||
authService.logout();
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求发送账号注册验证码到指定邮箱。
|
||||
*
|
||||
* @param email 接收验证码的邮箱地址
|
||||
* @return 成功则返回200 OK
|
||||
*/
|
||||
@PostMapping("/send-verification-code")
|
||||
public ResponseEntity<Void> sendVerificationCode(@RequestParam @NotBlank @Email String email) {
|
||||
verificationCodeService.sendVerificationCode(email);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求发送密码重置验证码到指定邮箱。
|
||||
*
|
||||
* @param email 接收验证码的邮箱地址
|
||||
* @return 成功则返回200 OK
|
||||
*/
|
||||
@PostMapping("/send-password-reset-code")
|
||||
public ResponseEntity<Void> sendPasswordResetCode(@RequestParam @NotBlank @Email String email) {
|
||||
authService.requestPasswordReset(email);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求密码重置(将发送包含验证码的邮件)
|
||||
*
|
||||
* @param request 包含邮箱的请求
|
||||
* @return 成功则返回200 OK
|
||||
* @deprecated 使用 {@link #sendPasswordResetCode(String)} 代替
|
||||
*/
|
||||
@PostMapping("/request-password-reset")
|
||||
@Deprecated
|
||||
public ResponseEntity<Void> requestPasswordReset(@Valid @RequestBody PasswordResetRequest request) {
|
||||
authService.requestPasswordReset(request.email());
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用验证码重置密码
|
||||
*
|
||||
* @param request 包含邮箱、验证码和新密码的请求
|
||||
* @return 成功则返回200 OK
|
||||
*/
|
||||
@PostMapping("/reset-password-with-code")
|
||||
public ResponseEntity<Void> resetPasswordWithCode(@Valid @RequestBody PasswordResetWithCodeDto request) {
|
||||
authService.resetPasswordWithCode(request.email(), request.code(), request.newPassword());
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用令牌重置密码(旧版API,保留以兼容)
|
||||
* 这将使用令牌和新密码进行重置。
|
||||
* @param request 包含令牌和新密码的请求
|
||||
* @return 成功则返回200 OK
|
||||
* @deprecated 使用基于验证码的 {@link #resetPasswordWithCode(PasswordResetWithCodeDto)} 替代
|
||||
*/
|
||||
@PostMapping("/reset-password")
|
||||
@Deprecated
|
||||
public ResponseEntity<Void> resetPassword(@Valid @RequestBody PasswordResetDto request) {
|
||||
authService.resetPassword(request.token(), request.newPassword());
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
// Internal DTOs have been moved to the com.dne.ems.dto package
|
||||
}
|
||||
@@ -1,237 +1,237 @@
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.AqiDistributionDTO;
|
||||
import com.dne.ems.dto.AqiHeatmapPointDTO;
|
||||
import com.dne.ems.dto.DashboardStatsDTO;
|
||||
import com.dne.ems.dto.GridCoverageDTO;
|
||||
import com.dne.ems.dto.HeatmapPointDTO;
|
||||
import com.dne.ems.dto.PollutantThresholdDTO;
|
||||
import com.dne.ems.dto.PollutionStatsDTO;
|
||||
import com.dne.ems.dto.TaskStatsDTO;
|
||||
import com.dne.ems.dto.TrendDataPointDTO;
|
||||
import com.dne.ems.service.DashboardService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 仪表盘数据控制器,提供各类环境监测数据的统计和可视化接口
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>环境质量指标统计</li>
|
||||
* <li>AQI数据分布和热力图</li>
|
||||
* <li>污染物阈值管理</li>
|
||||
* <li>任务完成情况统计</li>
|
||||
* <li>各类趋势分析数据</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/dashboard")
|
||||
@RequiredArgsConstructor
|
||||
public class DashboardController {
|
||||
|
||||
private final DashboardService dashboardService;
|
||||
private static final Logger log = LoggerFactory.getLogger(DashboardController.class);
|
||||
|
||||
/**
|
||||
* 获取仪表盘核心统计数据
|
||||
*
|
||||
* @return 包含各类核心统计数据的响应实体
|
||||
* @see DashboardStatsDTO
|
||||
*/
|
||||
@GetMapping("/stats")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<DashboardStatsDTO> getDashboardStats() {
|
||||
DashboardStatsDTO stats = dashboardService.getDashboardStats();
|
||||
return ResponseEntity.ok(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取AQI等级分布数据
|
||||
*
|
||||
* @return 包含各AQI等级分布数据的响应实体
|
||||
* @see AqiDistributionDTO
|
||||
*/
|
||||
@GetMapping("/reports/aqi-distribution")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<List<AqiDistributionDTO>> getAqiDistribution() {
|
||||
List<AqiDistributionDTO> distribution = dashboardService.getAqiDistribution();
|
||||
return ResponseEntity.ok(distribution);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取月度超标趋势数据
|
||||
*
|
||||
* @return 包含月度超标趋势数据的响应实体
|
||||
* @see TrendDataPointDTO
|
||||
*/
|
||||
@GetMapping("/reports/monthly-exceedance-trend")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<List<TrendDataPointDTO>> getMonthlyExceedanceTrend() {
|
||||
List<TrendDataPointDTO> trend = dashboardService.getMonthlyExceedanceTrend();
|
||||
return ResponseEntity.ok(trend);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网格覆盖情况数据
|
||||
*
|
||||
* @return 包含各城市网格覆盖情况的响应实体
|
||||
* @see GridCoverageDTO
|
||||
*/
|
||||
@GetMapping("/reports/grid-coverage")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<List<GridCoverageDTO>> getGridCoverage() {
|
||||
List<GridCoverageDTO> coverage = dashboardService.getGridCoverageByCity();
|
||||
return ResponseEntity.ok(coverage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取反馈热力图数据
|
||||
*
|
||||
* @return 包含热力图坐标点数据的响应实体
|
||||
* @throws ResourceNotFoundException 如果未找到任何热力图数据
|
||||
* @see HeatmapPointDTO
|
||||
*/
|
||||
@GetMapping("/map/heatmap")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<List<HeatmapPointDTO>> getHeatmapData() {
|
||||
List<HeatmapPointDTO> heatmapData = dashboardService.getHeatmapData();
|
||||
if (heatmapData == null || heatmapData.isEmpty()) {
|
||||
log.warn("反馈热力图数据为空");
|
||||
} else {
|
||||
log.info("成功获取反馈热力图数据,共 {} 个数据点", heatmapData.size());
|
||||
}
|
||||
return ResponseEntity.ok(heatmapData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取污染物统计报告数据
|
||||
*
|
||||
* @return 包含各类污染物统计数据的响应实体
|
||||
* @see PollutionStatsDTO
|
||||
*/
|
||||
@GetMapping("/reports/pollution-stats")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<List<PollutionStatsDTO>> getPollutionStats() {
|
||||
List<PollutionStatsDTO> stats = dashboardService.getPollutionStats();
|
||||
return ResponseEntity.ok(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务完成情况统计数据
|
||||
*
|
||||
* @return 包含任务完成率等统计数据的响应实体
|
||||
* @see TaskStatsDTO
|
||||
*/
|
||||
@GetMapping("/reports/task-completion-stats")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<TaskStatsDTO> getTaskCompletionStats() {
|
||||
TaskStatsDTO stats = dashboardService.getTaskCompletionStats();
|
||||
return ResponseEntity.ok(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取AQI热力图数据
|
||||
*
|
||||
* @return 包含AQI热力图坐标点数据的响应实体
|
||||
* @throws ResourceNotFoundException 如果未找到任何AQI数据
|
||||
* @see AqiHeatmapPointDTO
|
||||
*/
|
||||
@GetMapping("/map/aqi-heatmap")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<List<AqiHeatmapPointDTO>> getAqiHeatmapData() {
|
||||
List<AqiHeatmapPointDTO> heatmapData = dashboardService.getAqiHeatmapData();
|
||||
if (heatmapData == null || heatmapData.isEmpty()) {
|
||||
log.warn("AQI热力图数据为空");
|
||||
} else {
|
||||
log.info("成功获取AQI热力图数据,共 {} 个数据点", heatmapData.size());
|
||||
}
|
||||
return ResponseEntity.ok(heatmapData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有污染物阈值设置
|
||||
*
|
||||
* @return 包含所有污染物阈值设置的响应实体
|
||||
* @see PollutantThresholdDTO
|
||||
*/
|
||||
@GetMapping("/thresholds")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<List<PollutantThresholdDTO>> getPollutantThresholds() {
|
||||
log.info("接收到获取所有污染物阈值设置的请求");
|
||||
List<PollutantThresholdDTO> thresholds = dashboardService.getPollutantThresholds();
|
||||
log.info("返回 {} 个污染物阈值设置", thresholds.size());
|
||||
return ResponseEntity.ok(thresholds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定污染物的阈值设置
|
||||
*
|
||||
* @param pollutantName 污染物名称
|
||||
* @return 包含指定污染物阈值设置的响应实体
|
||||
* @throws ResourceNotFoundException 如果未找到该污染物的阈值设置
|
||||
* @see PollutantThresholdDTO
|
||||
*/
|
||||
@GetMapping("/thresholds/{pollutantName}")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<PollutantThresholdDTO> getPollutantThreshold(@PathVariable String pollutantName) {
|
||||
log.info("接收到获取污染物 {} 阈值设置的请求", pollutantName);
|
||||
PollutantThresholdDTO threshold = dashboardService.getPollutantThreshold(pollutantName);
|
||||
if (threshold == null) {
|
||||
log.warn("未找到污染物 {} 的阈值设置", pollutantName);
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
log.info("返回污染物 {} 的阈值设置: {}", pollutantName, threshold);
|
||||
return ResponseEntity.ok(threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存污染物阈值设置
|
||||
*
|
||||
* @param thresholdDTO 包含阈值设置的数据传输对象
|
||||
* @return 包含已保存阈值设置的响应实体
|
||||
* @throws IllegalArgumentException 如果阈值设置无效
|
||||
* @see PollutantThresholdDTO
|
||||
*/
|
||||
@PostMapping("/thresholds")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<PollutantThresholdDTO> savePollutantThreshold(@RequestBody PollutantThresholdDTO thresholdDTO) {
|
||||
log.info("接收到保存污染物阈值设置的请求: {}", thresholdDTO);
|
||||
PollutantThresholdDTO saved = dashboardService.savePollutantThreshold(thresholdDTO);
|
||||
log.info("污染物阈值设置已保存: {}", saved);
|
||||
return ResponseEntity.ok(saved);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取各类污染物的月度趋势数据
|
||||
*
|
||||
* @return 包含各类污染物月度趋势数据的响应实体
|
||||
* @see TrendDataPointDTO
|
||||
*/
|
||||
@GetMapping("/reports/pollutant-monthly-trends")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<Map<String, List<TrendDataPointDTO>>> getPollutantMonthlyTrends() {
|
||||
log.info("接收到获取每种污染物月度趋势数据的请求");
|
||||
Map<String, List<TrendDataPointDTO>> trends = dashboardService.getPollutantMonthlyTrends();
|
||||
log.info("返回 {} 种污染物的趋势数据", trends.size());
|
||||
return ResponseEntity.ok(trends);
|
||||
}
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.AqiDistributionDTO;
|
||||
import com.dne.ems.dto.AqiHeatmapPointDTO;
|
||||
import com.dne.ems.dto.DashboardStatsDTO;
|
||||
import com.dne.ems.dto.GridCoverageDTO;
|
||||
import com.dne.ems.dto.HeatmapPointDTO;
|
||||
import com.dne.ems.dto.PollutantThresholdDTO;
|
||||
import com.dne.ems.dto.PollutionStatsDTO;
|
||||
import com.dne.ems.dto.TaskStatsDTO;
|
||||
import com.dne.ems.dto.TrendDataPointDTO;
|
||||
import com.dne.ems.service.DashboardService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 仪表盘数据控制器,提供各类环境监测数据的统计和可视化接口
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>环境质量指标统计</li>
|
||||
* <li>AQI数据分布和热力图</li>
|
||||
* <li>污染物阈值管理</li>
|
||||
* <li>任务完成情况统计</li>
|
||||
* <li>各类趋势分析数据</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/dashboard")
|
||||
@RequiredArgsConstructor
|
||||
public class DashboardController {
|
||||
|
||||
private final DashboardService dashboardService;
|
||||
private static final Logger log = LoggerFactory.getLogger(DashboardController.class);
|
||||
|
||||
/**
|
||||
* 获取仪表盘核心统计数据
|
||||
*
|
||||
* @return 包含各类核心统计数据的响应实体
|
||||
* @see DashboardStatsDTO
|
||||
*/
|
||||
@GetMapping("/stats")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<DashboardStatsDTO> getDashboardStats() {
|
||||
DashboardStatsDTO stats = dashboardService.getDashboardStats();
|
||||
return ResponseEntity.ok(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取AQI等级分布数据
|
||||
*
|
||||
* @return 包含各AQI等级分布数据的响应实体
|
||||
* @see AqiDistributionDTO
|
||||
*/
|
||||
@GetMapping("/reports/aqi-distribution")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<List<AqiDistributionDTO>> getAqiDistribution() {
|
||||
List<AqiDistributionDTO> distribution = dashboardService.getAqiDistribution();
|
||||
return ResponseEntity.ok(distribution);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取月度超标趋势数据
|
||||
*
|
||||
* @return 包含月度超标趋势数据的响应实体
|
||||
* @see TrendDataPointDTO
|
||||
*/
|
||||
@GetMapping("/reports/monthly-exceedance-trend")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<List<TrendDataPointDTO>> getMonthlyExceedanceTrend() {
|
||||
List<TrendDataPointDTO> trend = dashboardService.getMonthlyExceedanceTrend();
|
||||
return ResponseEntity.ok(trend);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网格覆盖情况数据
|
||||
*
|
||||
* @return 包含各城市网格覆盖情况的响应实体
|
||||
* @see GridCoverageDTO
|
||||
*/
|
||||
@GetMapping("/reports/grid-coverage")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<List<GridCoverageDTO>> getGridCoverage() {
|
||||
List<GridCoverageDTO> coverage = dashboardService.getGridCoverageByCity();
|
||||
return ResponseEntity.ok(coverage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取反馈热力图数据
|
||||
*
|
||||
* @return 包含热力图坐标点数据的响应实体
|
||||
* @throws ResourceNotFoundException 如果未找到任何热力图数据
|
||||
* @see HeatmapPointDTO
|
||||
*/
|
||||
@GetMapping("/map/heatmap")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<List<HeatmapPointDTO>> getHeatmapData() {
|
||||
List<HeatmapPointDTO> heatmapData = dashboardService.getHeatmapData();
|
||||
if (heatmapData == null || heatmapData.isEmpty()) {
|
||||
log.warn("反馈热力图数据为空");
|
||||
} else {
|
||||
log.info("成功获取反馈热力图数据,共 {} 个数据点", heatmapData.size());
|
||||
}
|
||||
return ResponseEntity.ok(heatmapData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取污染物统计报告数据
|
||||
*
|
||||
* @return 包含各类污染物统计数据的响应实体
|
||||
* @see PollutionStatsDTO
|
||||
*/
|
||||
@GetMapping("/reports/pollution-stats")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<List<PollutionStatsDTO>> getPollutionStats() {
|
||||
List<PollutionStatsDTO> stats = dashboardService.getPollutionStats();
|
||||
return ResponseEntity.ok(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务完成情况统计数据
|
||||
*
|
||||
* @return 包含任务完成率等统计数据的响应实体
|
||||
* @see TaskStatsDTO
|
||||
*/
|
||||
@GetMapping("/reports/task-completion-stats")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<TaskStatsDTO> getTaskCompletionStats() {
|
||||
TaskStatsDTO stats = dashboardService.getTaskCompletionStats();
|
||||
return ResponseEntity.ok(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取AQI热力图数据
|
||||
*
|
||||
* @return 包含AQI热力图坐标点数据的响应实体
|
||||
* @throws ResourceNotFoundException 如果未找到任何AQI数据
|
||||
* @see AqiHeatmapPointDTO
|
||||
*/
|
||||
@GetMapping("/map/aqi-heatmap")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<List<AqiHeatmapPointDTO>> getAqiHeatmapData() {
|
||||
List<AqiHeatmapPointDTO> heatmapData = dashboardService.getAqiHeatmapData();
|
||||
if (heatmapData == null || heatmapData.isEmpty()) {
|
||||
log.warn("AQI热力图数据为空");
|
||||
} else {
|
||||
log.info("成功获取AQI热力图数据,共 {} 个数据点", heatmapData.size());
|
||||
}
|
||||
return ResponseEntity.ok(heatmapData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有污染物阈值设置
|
||||
*
|
||||
* @return 包含所有污染物阈值设置的响应实体
|
||||
* @see PollutantThresholdDTO
|
||||
*/
|
||||
@GetMapping("/thresholds")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<List<PollutantThresholdDTO>> getPollutantThresholds() {
|
||||
log.info("接收到获取所有污染物阈值设置的请求");
|
||||
List<PollutantThresholdDTO> thresholds = dashboardService.getPollutantThresholds();
|
||||
log.info("返回 {} 个污染物阈值设置", thresholds.size());
|
||||
return ResponseEntity.ok(thresholds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定污染物的阈值设置
|
||||
*
|
||||
* @param pollutantName 污染物名称
|
||||
* @return 包含指定污染物阈值设置的响应实体
|
||||
* @throws ResourceNotFoundException 如果未找到该污染物的阈值设置
|
||||
* @see PollutantThresholdDTO
|
||||
*/
|
||||
@GetMapping("/thresholds/{pollutantName}")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<PollutantThresholdDTO> getPollutantThreshold(@PathVariable String pollutantName) {
|
||||
log.info("接收到获取污染物 {} 阈值设置的请求", pollutantName);
|
||||
PollutantThresholdDTO threshold = dashboardService.getPollutantThreshold(pollutantName);
|
||||
if (threshold == null) {
|
||||
log.warn("未找到污染物 {} 的阈值设置", pollutantName);
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
log.info("返回污染物 {} 的阈值设置: {}", pollutantName, threshold);
|
||||
return ResponseEntity.ok(threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存污染物阈值设置
|
||||
*
|
||||
* @param thresholdDTO 包含阈值设置的数据传输对象
|
||||
* @return 包含已保存阈值设置的响应实体
|
||||
* @throws IllegalArgumentException 如果阈值设置无效
|
||||
* @see PollutantThresholdDTO
|
||||
*/
|
||||
@PostMapping("/thresholds")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<PollutantThresholdDTO> savePollutantThreshold(@RequestBody PollutantThresholdDTO thresholdDTO) {
|
||||
log.info("接收到保存污染物阈值设置的请求: {}", thresholdDTO);
|
||||
PollutantThresholdDTO saved = dashboardService.savePollutantThreshold(thresholdDTO);
|
||||
log.info("污染物阈值设置已保存: {}", saved);
|
||||
return ResponseEntity.ok(saved);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取各类污染物的月度趋势数据
|
||||
*
|
||||
* @return 包含各类污染物月度趋势数据的响应实体
|
||||
* @see TrendDataPointDTO
|
||||
*/
|
||||
@GetMapping("/reports/pollutant-monthly-trends")
|
||||
@PreAuthorize("hasAnyRole('DECISION_MAKER', 'ADMIN')")
|
||||
public ResponseEntity<Map<String, List<TrendDataPointDTO>>> getPollutantMonthlyTrends() {
|
||||
log.info("接收到获取每种污染物月度趋势数据的请求");
|
||||
Map<String, List<TrendDataPointDTO>> trends = dashboardService.getPollutantMonthlyTrends();
|
||||
log.info("返回 {} 种污染物的趋势数据", trends.size());
|
||||
return ResponseEntity.ok(trends);
|
||||
}
|
||||
}
|
||||
@@ -1,213 +1,213 @@
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.dne.ems.dto.FeedbackResponseDTO;
|
||||
import com.dne.ems.dto.FeedbackStatsResponse;
|
||||
import com.dne.ems.dto.FeedbackSubmissionRequest;
|
||||
import com.dne.ems.dto.ProcessFeedbackRequest;
|
||||
import com.dne.ems.model.Feedback;
|
||||
import com.dne.ems.model.enums.FeedbackStatus;
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
import com.dne.ems.security.CustomUserDetails;
|
||||
import com.dne.ems.service.FeedbackService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 公众反馈控制器,处理环境问题反馈的提交、查询和统计
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>公众环境问题反馈提交(支持认证用户和匿名用户)</li>
|
||||
* <li>反馈信息查询与多条件过滤</li>
|
||||
* <li>反馈数据统计分析</li>
|
||||
* <li>反馈状态流转管理</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>权限控制:
|
||||
* <ul>
|
||||
* <li>提交接口:所有认证用户</li>
|
||||
* <li>查询接口:管理员、主管、决策者</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024-03
|
||||
* @see com.dne.ems.dto.FeedbackSubmissionRequest 反馈提交请求DTO
|
||||
* @see com.dne.ems.model.enums.FeedbackStatus 反馈状态枚举
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/feedback")
|
||||
@RequiredArgsConstructor
|
||||
public class FeedbackController {
|
||||
|
||||
private final FeedbackService feedbackService;
|
||||
|
||||
/**
|
||||
* 提交反馈(JSON格式,仅用于测试)
|
||||
*
|
||||
* <p>此接口用于测试目的,使用application/json格式提交反馈数据,
|
||||
* 比multipart/form-data格式更便于使用curl等工具测试。
|
||||
* 正式接口请使用 {@link #submitFeedback(FeedbackSubmissionRequest, MultipartFile[], CustomUserDetails)}
|
||||
*
|
||||
* @param request 包含反馈信息的请求体
|
||||
* @param userDetails 当前认证用户信息
|
||||
* @return 创建成功的反馈实体和201状态码
|
||||
* @throws IllegalArgumentException 如果请求参数无效
|
||||
* @see FeedbackSubmissionRequest
|
||||
*/
|
||||
@PostMapping(value = "/submit-json")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
public ResponseEntity<Feedback> submitFeedbackJson(
|
||||
@Valid @RequestBody FeedbackSubmissionRequest request,
|
||||
@AuthenticationPrincipal CustomUserDetails userDetails) {
|
||||
|
||||
Feedback createdFeedback = feedbackService.submitFeedback(request, null); // No files for this endpoint
|
||||
return new ResponseEntity<>(createdFeedback, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交反馈(正式接口)
|
||||
*
|
||||
* <p>使用multipart/form-data格式提交反馈,支持附件上传
|
||||
*
|
||||
* @param request 包含反馈信息的请求体
|
||||
* @param files 可选的上传附件
|
||||
* @param userDetails 当前认证用户信息
|
||||
* @return 创建成功的反馈实体和201状态码
|
||||
* @throws IllegalArgumentException 如果请求参数无效
|
||||
* @throws FileStorageException 如果文件上传失败
|
||||
* @see FeedbackSubmissionRequest
|
||||
*/
|
||||
@PostMapping(value = "/submit", consumes = {"multipart/form-data"})
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
public ResponseEntity<Feedback> submitFeedback(
|
||||
@Valid @RequestPart("feedback") FeedbackSubmissionRequest request,
|
||||
@RequestPart(value = "files", required = false) MultipartFile[] files,
|
||||
@AuthenticationPrincipal CustomUserDetails userDetails) {
|
||||
|
||||
Feedback createdFeedback = feedbackService.submitFeedback(request, files);
|
||||
return new ResponseEntity<>(createdFeedback, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有反馈(分页+多条件过滤)
|
||||
*
|
||||
* <p>支持以下过滤条件组合查询:
|
||||
* <ul>
|
||||
* <li>状态:PENDING_REVIEW(待审核)/PROCESSED(已处理)/REJECTED(已拒绝)</li>
|
||||
* <li>污染类型:AIR/WATER/SOIL/NOISE等</li>
|
||||
* <li>严重程度:LOW/MEDIUM/HIGH/CRITICAL</li>
|
||||
* <li>地理位置:城市/区县</li>
|
||||
* <li>时间范围:开始日期至结束日期</li>
|
||||
* <li>关键词:标题/描述模糊匹配</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param userDetails 当前认证用户信息
|
||||
* @param status 反馈状态过滤条件
|
||||
* @param pollutionType 污染类型过滤条件
|
||||
* @param severityLevel 严重程度过滤条件
|
||||
* @param cityName 城市名称过滤条件
|
||||
* @param districtName 区县名称过滤条件
|
||||
* @param startDate 开始日期过滤条件(ISO格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期过滤条件(ISO格式:yyyy-MM-dd)
|
||||
* @param keyword 关键词过滤条件(模糊匹配)
|
||||
* @param pageable 分页参数(页号从0开始)
|
||||
* @return 符合条件的反馈列表(分页)
|
||||
* @see com.dne.ems.model.Feedback 反馈实体
|
||||
* @see com.dne.ems.model.enums.FeedbackStatus 反馈状态枚举
|
||||
* @see com.dne.ems.model.enums.PollutionType 污染类型枚举
|
||||
* @see com.dne.ems.model.enums.SeverityLevel 严重程度枚举
|
||||
*/
|
||||
@GetMapping
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@Operation(summary = "Get all feedback", description = "Retrieves a paginated list of all feedback submissions with filtering options.")
|
||||
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) {
|
||||
|
||||
Page<FeedbackResponseDTO> feedbackPage = feedbackService.getFeedback(
|
||||
userDetails, status, pollutionType, severityLevel, cityName, districtName,
|
||||
startDate, endDate, keyword, pageable);
|
||||
|
||||
return ResponseEntity.ok(feedbackPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取反馈详情
|
||||
*
|
||||
* @param id 反馈ID
|
||||
* @return 反馈详情实体
|
||||
* @throws ResourceNotFoundException 如果反馈不存在
|
||||
* @see Feedback
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPERVISOR', 'DECISION_MAKER')")
|
||||
@Operation(summary = "Get feedback by ID", description = "Retrieves feedback details by ID.")
|
||||
public ResponseEntity<FeedbackResponseDTO> getFeedbackById(@PathVariable Long id) {
|
||||
FeedbackResponseDTO feedback = feedbackService.getFeedbackDetail(id);
|
||||
return ResponseEntity.ok(feedback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取反馈统计数据
|
||||
*
|
||||
* @param userDetails 当前认证用户信息
|
||||
* @return 包含各类反馈统计数据的响应实体
|
||||
* @see FeedbackStatsResponse
|
||||
*/
|
||||
@GetMapping("/stats")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@Operation(summary = "Get feedback statistics", description = "Retrieves statistics about feedback status counts.")
|
||||
public ResponseEntity<FeedbackStatsResponse> getFeedbackStats(@AuthenticationPrincipal CustomUserDetails userDetails) {
|
||||
FeedbackStatsResponse stats = feedbackService.getFeedbackStats(userDetails);
|
||||
return ResponseEntity.ok(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理反馈 (例如, 批准)
|
||||
*
|
||||
* @param id 反馈ID
|
||||
* @param request 包含新状态和备注的请求体
|
||||
* @return 更新后的反馈详情
|
||||
*/
|
||||
@PostMapping("/{id}/process")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPERVISOR')")
|
||||
@Operation(summary = "Process a feedback entry", description = "Processes a feedback, e.g., approving it to be pending assignment.")
|
||||
public ResponseEntity<FeedbackResponseDTO> processFeedback(
|
||||
@PathVariable Long id,
|
||||
@Valid @RequestBody ProcessFeedbackRequest request) {
|
||||
FeedbackResponseDTO updatedFeedback = feedbackService.processFeedback(id, request);
|
||||
return ResponseEntity.ok(updatedFeedback);
|
||||
}
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.dne.ems.dto.FeedbackResponseDTO;
|
||||
import com.dne.ems.dto.FeedbackStatsResponse;
|
||||
import com.dne.ems.dto.FeedbackSubmissionRequest;
|
||||
import com.dne.ems.dto.ProcessFeedbackRequest;
|
||||
import com.dne.ems.model.Feedback;
|
||||
import com.dne.ems.model.enums.FeedbackStatus;
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
import com.dne.ems.security.CustomUserDetails;
|
||||
import com.dne.ems.service.FeedbackService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 公众反馈控制器,处理环境问题反馈的提交、查询和统计
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>公众环境问题反馈提交(支持认证用户和匿名用户)</li>
|
||||
* <li>反馈信息查询与多条件过滤</li>
|
||||
* <li>反馈数据统计分析</li>
|
||||
* <li>反馈状态流转管理</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>权限控制:
|
||||
* <ul>
|
||||
* <li>提交接口:所有认证用户</li>
|
||||
* <li>查询接口:管理员、主管、决策者</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024-03
|
||||
* @see com.dne.ems.dto.FeedbackSubmissionRequest 反馈提交请求DTO
|
||||
* @see com.dne.ems.model.enums.FeedbackStatus 反馈状态枚举
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/feedback")
|
||||
@RequiredArgsConstructor
|
||||
public class FeedbackController {
|
||||
|
||||
private final FeedbackService feedbackService;
|
||||
|
||||
/**
|
||||
* 提交反馈(JSON格式,仅用于测试)
|
||||
*
|
||||
* <p>此接口用于测试目的,使用application/json格式提交反馈数据,
|
||||
* 比multipart/form-data格式更便于使用curl等工具测试。
|
||||
* 正式接口请使用 {@link #submitFeedback(FeedbackSubmissionRequest, MultipartFile[], CustomUserDetails)}
|
||||
*
|
||||
* @param request 包含反馈信息的请求体
|
||||
* @param userDetails 当前认证用户信息
|
||||
* @return 创建成功的反馈实体和201状态码
|
||||
* @throws IllegalArgumentException 如果请求参数无效
|
||||
* @see FeedbackSubmissionRequest
|
||||
*/
|
||||
@PostMapping(value = "/submit-json")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
public ResponseEntity<Feedback> submitFeedbackJson(
|
||||
@Valid @RequestBody FeedbackSubmissionRequest request,
|
||||
@AuthenticationPrincipal CustomUserDetails userDetails) {
|
||||
|
||||
Feedback createdFeedback = feedbackService.submitFeedback(request, null); // No files for this endpoint
|
||||
return new ResponseEntity<>(createdFeedback, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交反馈(正式接口)
|
||||
*
|
||||
* <p>使用multipart/form-data格式提交反馈,支持附件上传
|
||||
*
|
||||
* @param request 包含反馈信息的请求体
|
||||
* @param files 可选的上传附件
|
||||
* @param userDetails 当前认证用户信息
|
||||
* @return 创建成功的反馈实体和201状态码
|
||||
* @throws IllegalArgumentException 如果请求参数无效
|
||||
* @throws FileStorageException 如果文件上传失败
|
||||
* @see FeedbackSubmissionRequest
|
||||
*/
|
||||
@PostMapping(value = "/submit", consumes = {"multipart/form-data"})
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
public ResponseEntity<Feedback> submitFeedback(
|
||||
@Valid @RequestPart("feedback") FeedbackSubmissionRequest request,
|
||||
@RequestPart(value = "files", required = false) MultipartFile[] files,
|
||||
@AuthenticationPrincipal CustomUserDetails userDetails) {
|
||||
|
||||
Feedback createdFeedback = feedbackService.submitFeedback(request, files);
|
||||
return new ResponseEntity<>(createdFeedback, HttpStatus.CREATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有反馈(分页+多条件过滤)
|
||||
*
|
||||
* <p>支持以下过滤条件组合查询:
|
||||
* <ul>
|
||||
* <li>状态:PENDING_REVIEW(待审核)/PROCESSED(已处理)/REJECTED(已拒绝)</li>
|
||||
* <li>污染类型:AIR/WATER/SOIL/NOISE等</li>
|
||||
* <li>严重程度:LOW/MEDIUM/HIGH/CRITICAL</li>
|
||||
* <li>地理位置:城市/区县</li>
|
||||
* <li>时间范围:开始日期至结束日期</li>
|
||||
* <li>关键词:标题/描述模糊匹配</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param userDetails 当前认证用户信息
|
||||
* @param status 反馈状态过滤条件
|
||||
* @param pollutionType 污染类型过滤条件
|
||||
* @param severityLevel 严重程度过滤条件
|
||||
* @param cityName 城市名称过滤条件
|
||||
* @param districtName 区县名称过滤条件
|
||||
* @param startDate 开始日期过滤条件(ISO格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期过滤条件(ISO格式:yyyy-MM-dd)
|
||||
* @param keyword 关键词过滤条件(模糊匹配)
|
||||
* @param pageable 分页参数(页号从0开始)
|
||||
* @return 符合条件的反馈列表(分页)
|
||||
* @see com.dne.ems.model.Feedback 反馈实体
|
||||
* @see com.dne.ems.model.enums.FeedbackStatus 反馈状态枚举
|
||||
* @see com.dne.ems.model.enums.PollutionType 污染类型枚举
|
||||
* @see com.dne.ems.model.enums.SeverityLevel 严重程度枚举
|
||||
*/
|
||||
@GetMapping
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@Operation(summary = "Get all feedback", description = "Retrieves a paginated list of all feedback submissions with filtering options.")
|
||||
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) {
|
||||
|
||||
Page<FeedbackResponseDTO> feedbackPage = feedbackService.getFeedback(
|
||||
userDetails, status, pollutionType, severityLevel, cityName, districtName,
|
||||
startDate, endDate, keyword, pageable);
|
||||
|
||||
return ResponseEntity.ok(feedbackPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取反馈详情
|
||||
*
|
||||
* @param id 反馈ID
|
||||
* @return 反馈详情实体
|
||||
* @throws ResourceNotFoundException 如果反馈不存在
|
||||
* @see Feedback
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPERVISOR', 'DECISION_MAKER')")
|
||||
@Operation(summary = "Get feedback by ID", description = "Retrieves feedback details by ID.")
|
||||
public ResponseEntity<FeedbackResponseDTO> getFeedbackById(@PathVariable Long id) {
|
||||
FeedbackResponseDTO feedback = feedbackService.getFeedbackDetail(id);
|
||||
return ResponseEntity.ok(feedback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取反馈统计数据
|
||||
*
|
||||
* @param userDetails 当前认证用户信息
|
||||
* @return 包含各类反馈统计数据的响应实体
|
||||
* @see FeedbackStatsResponse
|
||||
*/
|
||||
@GetMapping("/stats")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
@Operation(summary = "Get feedback statistics", description = "Retrieves statistics about feedback status counts.")
|
||||
public ResponseEntity<FeedbackStatsResponse> getFeedbackStats(@AuthenticationPrincipal CustomUserDetails userDetails) {
|
||||
FeedbackStatsResponse stats = feedbackService.getFeedbackStats(userDetails);
|
||||
return ResponseEntity.ok(stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理反馈 (例如, 批准)
|
||||
*
|
||||
* @param id 反馈ID
|
||||
* @param request 包含新状态和备注的请求体
|
||||
* @return 更新后的反馈详情
|
||||
*/
|
||||
@PostMapping("/{id}/process")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPERVISOR')")
|
||||
@Operation(summary = "Process a feedback entry", description = "Processes a feedback, e.g., approving it to be pending assignment.")
|
||||
public ResponseEntity<FeedbackResponseDTO> processFeedback(
|
||||
@PathVariable Long id,
|
||||
@Valid @RequestBody ProcessFeedbackRequest request) {
|
||||
FeedbackResponseDTO updatedFeedback = feedbackService.processFeedback(id, request);
|
||||
return ResponseEntity.ok(updatedFeedback);
|
||||
}
|
||||
}
|
||||
@@ -1,103 +1,103 @@
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.service.FileStorageService;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 文件控制器,处理文件下载和预览请求
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>文件下载</li>
|
||||
* <li>文件在线预览</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
public class FileController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FileController.class);
|
||||
|
||||
@Autowired
|
||||
private FileStorageService fileStorageService;
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
*
|
||||
* @param filename 文件名(包含扩展名)
|
||||
* @param request HTTP请求对象
|
||||
* @return 包含文件资源的响应实体
|
||||
* @throws FileNotFoundException 如果文件不存在
|
||||
* @throws FileStorageException 如果文件读取失败
|
||||
*/
|
||||
@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.");
|
||||
}
|
||||
|
||||
// Fallback to the default content type if type could not be determined
|
||||
if (contentType == null) {
|
||||
contentType = "application/octet-stream";
|
||||
}
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType(contentType))
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + resource.getFilename() + "\"")
|
||||
.body(resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在线预览文件
|
||||
*
|
||||
* @param fileName 文件名(包含扩展名)
|
||||
* @param request HTTP请求对象
|
||||
* @return 包含文件资源的响应实体
|
||||
* @throws FileNotFoundException 如果文件不存在
|
||||
* @throws FileStorageException 如果文件读取失败
|
||||
*/
|
||||
@GetMapping("/view/{fileName:.+}")
|
||||
public ResponseEntity<Resource> viewFile(@PathVariable String fileName, HttpServletRequest request) {
|
||||
Resource resource = fileStorageService.loadFileAsResource(fileName);
|
||||
|
||||
String contentType = null;
|
||||
try {
|
||||
contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
|
||||
} catch (IOException ex) {
|
||||
// log error
|
||||
}
|
||||
|
||||
if(contentType == null) {
|
||||
contentType = "application/octet-stream";
|
||||
}
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType(contentType))
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + resource.getFilename() + "\"")
|
||||
.body(resource);
|
||||
}
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.service.FileStorageService;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 文件控制器,处理文件下载和预览请求
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>文件下载</li>
|
||||
* <li>文件在线预览</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
public class FileController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FileController.class);
|
||||
|
||||
@Autowired
|
||||
private FileStorageService fileStorageService;
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
*
|
||||
* @param filename 文件名(包含扩展名)
|
||||
* @param request HTTP请求对象
|
||||
* @return 包含文件资源的响应实体
|
||||
* @throws FileNotFoundException 如果文件不存在
|
||||
* @throws FileStorageException 如果文件读取失败
|
||||
*/
|
||||
@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.");
|
||||
}
|
||||
|
||||
// Fallback to the default content type if type could not be determined
|
||||
if (contentType == null) {
|
||||
contentType = "application/octet-stream";
|
||||
}
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType(contentType))
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + resource.getFilename() + "\"")
|
||||
.body(resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在线预览文件
|
||||
*
|
||||
* @param fileName 文件名(包含扩展名)
|
||||
* @param request HTTP请求对象
|
||||
* @return 包含文件资源的响应实体
|
||||
* @throws FileNotFoundException 如果文件不存在
|
||||
* @throws FileStorageException 如果文件读取失败
|
||||
*/
|
||||
@GetMapping("/view/{fileName:.+}")
|
||||
public ResponseEntity<Resource> viewFile(@PathVariable String fileName, HttpServletRequest request) {
|
||||
Resource resource = fileStorageService.loadFileAsResource(fileName);
|
||||
|
||||
String contentType = null;
|
||||
try {
|
||||
contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
|
||||
} catch (IOException ex) {
|
||||
// log error
|
||||
}
|
||||
|
||||
if(contentType == null) {
|
||||
contentType = "application/octet-stream";
|
||||
}
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType(contentType))
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + resource.getFilename() + "\"")
|
||||
.body(resource);
|
||||
}
|
||||
}
|
||||
@@ -1,343 +1,343 @@
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PatchMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.GridCoverageDTO;
|
||||
import com.dne.ems.dto.GridUpdateRequest;
|
||||
import com.dne.ems.model.Grid;
|
||||
import com.dne.ems.model.UserAccount;
|
||||
import com.dne.ems.model.enums.Role;
|
||||
import com.dne.ems.repository.GridRepository;
|
||||
import com.dne.ems.repository.UserAccountRepository;
|
||||
import com.dne.ems.service.GridService;
|
||||
import com.dne.ems.service.OperationLogService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 网格管理控制器,负责网格数据的查询、更新和网格员分配管理
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>网格数据查询与分页</li>
|
||||
* <li>网格信息更新</li>
|
||||
* <li>网格员分配与解除分配</li>
|
||||
* <li>网格覆盖率统计</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/grids")
|
||||
@RequiredArgsConstructor
|
||||
public class GridController {
|
||||
|
||||
private final GridService gridService;
|
||||
private final GridRepository gridRepository;
|
||||
private final UserAccountRepository userAccountRepository;
|
||||
private final OperationLogService operationLogService;
|
||||
|
||||
// 添加日志记录器
|
||||
private static final Logger logger = LoggerFactory.getLogger(GridController.class);
|
||||
|
||||
/**
|
||||
* 获取网格列表(可分页和过滤)
|
||||
*
|
||||
* @return 符合条件的网格分页列表
|
||||
* @see Grid
|
||||
*/
|
||||
@GetMapping
|
||||
@PreAuthorize("hasRole('ADMIN') or hasRole('DECISION_MAKER') or hasRole('GRID_WORKER')")
|
||||
public ResponseEntity<List<Grid>> getGrids() {
|
||||
List<Grid> grids = gridRepository.findAll();
|
||||
return ResponseEntity.ok(grids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新网格信息
|
||||
*
|
||||
* @param id 要更新的网格ID
|
||||
* @param request 包含更新数据的请求体
|
||||
* @return 更新后的网格实体
|
||||
* @throws ResourceNotFoundException 如果网格不存在
|
||||
* @see GridUpdateRequest
|
||||
*/
|
||||
@Operation(summary = "Update a grid by ID")
|
||||
@PatchMapping("/{id}")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<Grid> updateGrid(
|
||||
@Parameter(description = "Grid ID") @PathVariable Long id,
|
||||
@Valid @RequestBody GridUpdateRequest request
|
||||
) {
|
||||
Grid updatedGrid = gridService.updateGrid(id, request);
|
||||
return ResponseEntity.ok(updatedGrid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网格覆盖率统计数据
|
||||
*
|
||||
* @return 各城市网格覆盖率数据列表
|
||||
* @see GridCoverageDTO
|
||||
*/
|
||||
@GetMapping("/coverage")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<List<GridCoverageDTO>> getGridCoverage() {
|
||||
List<GridCoverageDTO> coverage = gridRepository.getGridCoverageByCity();
|
||||
return ResponseEntity.ok(coverage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配网格员到指定网格
|
||||
*
|
||||
* @param gridId 目标网格ID
|
||||
* @param request 包含用户ID的请求体
|
||||
* @return 操作结果
|
||||
* @throws ResourceNotFoundException 如果网格或用户不存在
|
||||
* @throws IllegalArgumentException 如果用户不是网格员角色
|
||||
*/
|
||||
@PostMapping("/{gridId}/assign")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<?> assignGridWorker(
|
||||
@PathVariable Long gridId,
|
||||
@RequestBody Map<String, Long> request) {
|
||||
Long userId = request.get("userId");
|
||||
if (userId == null) {
|
||||
return ResponseEntity.badRequest().body("用户ID不能为空");
|
||||
}
|
||||
|
||||
Optional<Grid> gridOpt = gridRepository.findById(gridId);
|
||||
Optional<UserAccount> userOpt = userAccountRepository.findById(userId);
|
||||
|
||||
if (gridOpt.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
if (userOpt.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body("用户不存在");
|
||||
}
|
||||
|
||||
Grid grid = gridOpt.get();
|
||||
UserAccount user = userOpt.get();
|
||||
|
||||
if (user.getRole() != Role.GRID_WORKER) {
|
||||
return ResponseEntity.badRequest().body("只能分配网格员角色的用户");
|
||||
}
|
||||
|
||||
user.setGridX(grid.getGridX());
|
||||
user.setGridY(grid.getGridY());
|
||||
userAccountRepository.save(user);
|
||||
|
||||
// 记录操作日志
|
||||
operationLogService.recordOperation(
|
||||
com.dne.ems.model.enums.OperationType.UPDATE,
|
||||
"将网格员 " + user.getName() + " (ID: " + userId + ") 分配到网格 (ID: " + gridId + ")",
|
||||
userId.toString(),
|
||||
"UserAccount"
|
||||
);
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从网格中移除网格员
|
||||
*
|
||||
* @param gridId 目标网格ID
|
||||
* @return 操作结果
|
||||
* @throws ResourceNotFoundException 如果网格不存在
|
||||
*/
|
||||
@PostMapping("/{gridId}/unassign")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<?> removeGridWorker(@PathVariable Long gridId) {
|
||||
Optional<Grid> gridOpt = gridRepository.findById(gridId);
|
||||
|
||||
if (gridOpt.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
Grid grid = gridOpt.get();
|
||||
|
||||
// 使用新添加的方法查找指定网格坐标的网格员
|
||||
List<UserAccount> workers = userAccountRepository.findGridWorkersByCoordinates(
|
||||
grid.getGridX(), grid.getGridY());
|
||||
|
||||
for (UserAccount worker : workers) {
|
||||
worker.setGridX(-1);
|
||||
worker.setGridY(-1);
|
||||
userAccountRepository.save(worker);
|
||||
}
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过网格坐标分配网格员,仅限ADMIN角色使用。
|
||||
*
|
||||
* @param gridX 网格X坐标
|
||||
* @param gridY 网格Y坐标
|
||||
* @param request 包含用户ID的请求体
|
||||
* @return 操作结果响应
|
||||
*/
|
||||
@PostMapping("/coordinates/{gridX}/{gridY}/assign")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'GRID_WORKER')")
|
||||
public ResponseEntity<?> assignGridWorkerByCoordinates(
|
||||
@PathVariable Integer gridX,
|
||||
@PathVariable Integer gridY,
|
||||
@RequestBody Map<String, Long> request) {
|
||||
logger.info("开始通过坐标分配网格员: gridX={}, gridY={}, request={}", gridX, gridY, request);
|
||||
|
||||
Long userId = request.get("userId");
|
||||
if (userId == null) {
|
||||
logger.warn("分配网格员失败: 用户ID为空");
|
||||
return ResponseEntity.badRequest().body("用户ID不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
// 通过坐标查找网格
|
||||
logger.debug("查询网格坐标: gridX={}, gridY={}", gridX, gridY);
|
||||
Optional<Grid> gridOpt = gridRepository.findByGridXAndGridY(gridX, gridY);
|
||||
|
||||
if (gridOpt.isEmpty()) {
|
||||
logger.warn("分配网格员失败: 未找到网格, gridX={}, gridY={}", gridX, gridY);
|
||||
|
||||
// 尝试查询所有网格,看看是否有数据
|
||||
List<Grid> allGrids = gridRepository.findAll();
|
||||
logger.debug("数据库中共有 {} 个网格", allGrids.size());
|
||||
if (!allGrids.isEmpty()) {
|
||||
Grid firstGrid = allGrids.get(0);
|
||||
logger.debug("第一个网格: id={}, gridX={}, gridY={}, cityName={}",
|
||||
firstGrid.getId(), firstGrid.getGridX(), firstGrid.getGridY(), firstGrid.getCityName());
|
||||
}
|
||||
|
||||
return ResponseEntity.status(404).body("未找到指定坐标的网格");
|
||||
}
|
||||
|
||||
logger.debug("查询用户: userId={}", userId);
|
||||
Optional<UserAccount> userOpt = userAccountRepository.findById(userId);
|
||||
|
||||
if (userOpt.isEmpty()) {
|
||||
logger.warn("分配网格员失败: 未找到用户, userId={}", userId);
|
||||
return ResponseEntity.badRequest().body("用户不存在");
|
||||
}
|
||||
|
||||
Grid grid = gridOpt.get();
|
||||
UserAccount user = userOpt.get();
|
||||
|
||||
logger.debug("网格信息: id={}, gridX={}, gridY={}", grid.getId(), grid.getGridX(), grid.getGridY());
|
||||
logger.debug("用户信息: id={}, name={}, role={}", user.getId(), user.getName(), user.getRole());
|
||||
|
||||
if (user.getRole() != Role.GRID_WORKER) {
|
||||
logger.warn("分配网格员失败: 用户角色不是网格员, userId={}, role={}", userId, user.getRole());
|
||||
return ResponseEntity.badRequest().body("只能分配网格员角色的用户");
|
||||
}
|
||||
|
||||
// 先检查是否有其他网格员已分配到此网格
|
||||
logger.debug("检查是否有其他网格员已分配到此网格: gridX={}, gridY={}", gridX, gridY);
|
||||
List<UserAccount> existingWorkers = userAccountRepository.findGridWorkersByCoordinates(gridX, gridY);
|
||||
logger.debug("已分配的网格员数量: {}", existingWorkers.size());
|
||||
|
||||
for (UserAccount existingWorker : existingWorkers) {
|
||||
if (!existingWorker.getId().equals(userId)) {
|
||||
logger.info("移除已存在的网格员: workerId={}, name={}", existingWorker.getId(), existingWorker.getName());
|
||||
existingWorker.setGridX(-1);
|
||||
existingWorker.setGridY(-1);
|
||||
userAccountRepository.save(existingWorker);
|
||||
}
|
||||
}
|
||||
|
||||
// 分配新网格员
|
||||
logger.info("分配网格员: userId={}, name={}, gridX={}, gridY={}", user.getId(), user.getName(), grid.getGridX(), grid.getGridY());
|
||||
user.setGridX(grid.getGridX());
|
||||
user.setGridY(grid.getGridY());
|
||||
userAccountRepository.save(user);
|
||||
|
||||
// 记录操作日志
|
||||
operationLogService.recordOperation(
|
||||
com.dne.ems.model.enums.OperationType.UPDATE,
|
||||
"将网格员 " + user.getName() + " (ID: " + userId + ") 分配到网格 (X: " + gridX + ", Y: " + gridY + ")",
|
||||
userId.toString(),
|
||||
"UserAccount"
|
||||
);
|
||||
|
||||
logger.info("成功将用户 {} 分配到网格 (X: {}, Y: {})", user.getName(), gridX, gridY);
|
||||
return ResponseEntity.ok().build();
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("通过坐标分配网格员时发生异常", e);
|
||||
return ResponseEntity.status(500).body("服务器内部错误");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过网格坐标移除网格员,仅限ADMIN角色使用。
|
||||
*
|
||||
* @param gridX 网格X坐标
|
||||
* @param gridY 网格Y坐标
|
||||
* @return 操作结果响应
|
||||
*/
|
||||
@PostMapping("/coordinates/{gridX}/{gridY}/unassign")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'GRID_WORKER')")
|
||||
public ResponseEntity<?> removeGridWorkerByCoordinates(
|
||||
@PathVariable Integer gridX,
|
||||
@PathVariable Integer gridY) {
|
||||
logger.info("开始通过坐标移除网格员: gridX={}, gridY={}", gridX, gridY);
|
||||
|
||||
try {
|
||||
// 查找指定坐标的网格员
|
||||
logger.debug("查询指定坐标的网格员: gridX={}, gridY={}", gridX, gridY);
|
||||
List<UserAccount> workers = userAccountRepository.findGridWorkersByCoordinates(gridX, gridY);
|
||||
logger.debug("找到网格员数量: {}", workers.size());
|
||||
|
||||
if (workers.isEmpty()) {
|
||||
logger.warn("移除网格员失败: 未找到网格员, gridX={}, gridY={}", gridX, gridY);
|
||||
return ResponseEntity.status(404).body("未找到指定坐标的网格员");
|
||||
}
|
||||
|
||||
// 移除所有网格员的分配
|
||||
for (UserAccount worker : workers) {
|
||||
logger.info("移除网格员分配: workerId={}, name={}, 原坐标: gridX={}, gridY={}",
|
||||
worker.getId(), worker.getName(), worker.getGridX(), worker.getGridY());
|
||||
|
||||
try {
|
||||
worker.setGridX(-1);
|
||||
worker.setGridY(-1);
|
||||
UserAccount savedWorker = userAccountRepository.save(worker);
|
||||
logger.info("网格员移除成功: workerId={}, 新坐标: gridX={}, gridY={}",
|
||||
savedWorker.getId(), savedWorker.getGridX(), savedWorker.getGridY());
|
||||
|
||||
// 验证保存是否成功
|
||||
UserAccount verifiedWorker = userAccountRepository.findById(worker.getId()).orElse(null);
|
||||
if (verifiedWorker != null) {
|
||||
logger.debug("验证移除结果: gridX={}, gridY={}", verifiedWorker.getGridX(), verifiedWorker.getGridY());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("保存用户数据时发生异常", e);
|
||||
return ResponseEntity.status(500).body("保存用户数据失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
} catch (Exception e) {
|
||||
logger.error("移除网格员时发生异常", e);
|
||||
return ResponseEntity.status(500).body("移除网格员失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PatchMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.GridCoverageDTO;
|
||||
import com.dne.ems.dto.GridUpdateRequest;
|
||||
import com.dne.ems.model.Grid;
|
||||
import com.dne.ems.model.UserAccount;
|
||||
import com.dne.ems.model.enums.Role;
|
||||
import com.dne.ems.repository.GridRepository;
|
||||
import com.dne.ems.repository.UserAccountRepository;
|
||||
import com.dne.ems.service.GridService;
|
||||
import com.dne.ems.service.OperationLogService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 网格管理控制器,负责网格数据的查询、更新和网格员分配管理
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>网格数据查询与分页</li>
|
||||
* <li>网格信息更新</li>
|
||||
* <li>网格员分配与解除分配</li>
|
||||
* <li>网格覆盖率统计</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/grids")
|
||||
@RequiredArgsConstructor
|
||||
public class GridController {
|
||||
|
||||
private final GridService gridService;
|
||||
private final GridRepository gridRepository;
|
||||
private final UserAccountRepository userAccountRepository;
|
||||
private final OperationLogService operationLogService;
|
||||
|
||||
// 添加日志记录器
|
||||
private static final Logger logger = LoggerFactory.getLogger(GridController.class);
|
||||
|
||||
/**
|
||||
* 获取网格列表(可分页和过滤)
|
||||
*
|
||||
* @return 符合条件的网格分页列表
|
||||
* @see Grid
|
||||
*/
|
||||
@GetMapping
|
||||
@PreAuthorize("hasRole('ADMIN') or hasRole('DECISION_MAKER') or hasRole('GRID_WORKER')")
|
||||
public ResponseEntity<List<Grid>> getGrids() {
|
||||
List<Grid> grids = gridRepository.findAll();
|
||||
return ResponseEntity.ok(grids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新网格信息
|
||||
*
|
||||
* @param id 要更新的网格ID
|
||||
* @param request 包含更新数据的请求体
|
||||
* @return 更新后的网格实体
|
||||
* @throws ResourceNotFoundException 如果网格不存在
|
||||
* @see GridUpdateRequest
|
||||
*/
|
||||
@Operation(summary = "Update a grid by ID")
|
||||
@PatchMapping("/{id}")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<Grid> updateGrid(
|
||||
@Parameter(description = "Grid ID") @PathVariable Long id,
|
||||
@Valid @RequestBody GridUpdateRequest request
|
||||
) {
|
||||
Grid updatedGrid = gridService.updateGrid(id, request);
|
||||
return ResponseEntity.ok(updatedGrid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网格覆盖率统计数据
|
||||
*
|
||||
* @return 各城市网格覆盖率数据列表
|
||||
* @see GridCoverageDTO
|
||||
*/
|
||||
@GetMapping("/coverage")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<List<GridCoverageDTO>> getGridCoverage() {
|
||||
List<GridCoverageDTO> coverage = gridRepository.getGridCoverageByCity();
|
||||
return ResponseEntity.ok(coverage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配网格员到指定网格
|
||||
*
|
||||
* @param gridId 目标网格ID
|
||||
* @param request 包含用户ID的请求体
|
||||
* @return 操作结果
|
||||
* @throws ResourceNotFoundException 如果网格或用户不存在
|
||||
* @throws IllegalArgumentException 如果用户不是网格员角色
|
||||
*/
|
||||
@PostMapping("/{gridId}/assign")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<?> assignGridWorker(
|
||||
@PathVariable Long gridId,
|
||||
@RequestBody Map<String, Long> request) {
|
||||
Long userId = request.get("userId");
|
||||
if (userId == null) {
|
||||
return ResponseEntity.badRequest().body("用户ID不能为空");
|
||||
}
|
||||
|
||||
Optional<Grid> gridOpt = gridRepository.findById(gridId);
|
||||
Optional<UserAccount> userOpt = userAccountRepository.findById(userId);
|
||||
|
||||
if (gridOpt.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
if (userOpt.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body("用户不存在");
|
||||
}
|
||||
|
||||
Grid grid = gridOpt.get();
|
||||
UserAccount user = userOpt.get();
|
||||
|
||||
if (user.getRole() != Role.GRID_WORKER) {
|
||||
return ResponseEntity.badRequest().body("只能分配网格员角色的用户");
|
||||
}
|
||||
|
||||
user.setGridX(grid.getGridX());
|
||||
user.setGridY(grid.getGridY());
|
||||
userAccountRepository.save(user);
|
||||
|
||||
// 记录操作日志
|
||||
operationLogService.recordOperation(
|
||||
com.dne.ems.model.enums.OperationType.UPDATE,
|
||||
"将网格员 " + user.getName() + " (ID: " + userId + ") 分配到网格 (ID: " + gridId + ")",
|
||||
userId.toString(),
|
||||
"UserAccount"
|
||||
);
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从网格中移除网格员
|
||||
*
|
||||
* @param gridId 目标网格ID
|
||||
* @return 操作结果
|
||||
* @throws ResourceNotFoundException 如果网格不存在
|
||||
*/
|
||||
@PostMapping("/{gridId}/unassign")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<?> removeGridWorker(@PathVariable Long gridId) {
|
||||
Optional<Grid> gridOpt = gridRepository.findById(gridId);
|
||||
|
||||
if (gridOpt.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
Grid grid = gridOpt.get();
|
||||
|
||||
// 使用新添加的方法查找指定网格坐标的网格员
|
||||
List<UserAccount> workers = userAccountRepository.findGridWorkersByCoordinates(
|
||||
grid.getGridX(), grid.getGridY());
|
||||
|
||||
for (UserAccount worker : workers) {
|
||||
worker.setGridX(-1);
|
||||
worker.setGridY(-1);
|
||||
userAccountRepository.save(worker);
|
||||
}
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过网格坐标分配网格员,仅限ADMIN角色使用。
|
||||
*
|
||||
* @param gridX 网格X坐标
|
||||
* @param gridY 网格Y坐标
|
||||
* @param request 包含用户ID的请求体
|
||||
* @return 操作结果响应
|
||||
*/
|
||||
@PostMapping("/coordinates/{gridX}/{gridY}/assign")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'GRID_WORKER')")
|
||||
public ResponseEntity<?> assignGridWorkerByCoordinates(
|
||||
@PathVariable Integer gridX,
|
||||
@PathVariable Integer gridY,
|
||||
@RequestBody Map<String, Long> request) {
|
||||
logger.info("开始通过坐标分配网格员: gridX={}, gridY={}, request={}", gridX, gridY, request);
|
||||
|
||||
Long userId = request.get("userId");
|
||||
if (userId == null) {
|
||||
logger.warn("分配网格员失败: 用户ID为空");
|
||||
return ResponseEntity.badRequest().body("用户ID不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
// 通过坐标查找网格
|
||||
logger.debug("查询网格坐标: gridX={}, gridY={}", gridX, gridY);
|
||||
Optional<Grid> gridOpt = gridRepository.findByGridXAndGridY(gridX, gridY);
|
||||
|
||||
if (gridOpt.isEmpty()) {
|
||||
logger.warn("分配网格员失败: 未找到网格, gridX={}, gridY={}", gridX, gridY);
|
||||
|
||||
// 尝试查询所有网格,看看是否有数据
|
||||
List<Grid> allGrids = gridRepository.findAll();
|
||||
logger.debug("数据库中共有 {} 个网格", allGrids.size());
|
||||
if (!allGrids.isEmpty()) {
|
||||
Grid firstGrid = allGrids.get(0);
|
||||
logger.debug("第一个网格: id={}, gridX={}, gridY={}, cityName={}",
|
||||
firstGrid.getId(), firstGrid.getGridX(), firstGrid.getGridY(), firstGrid.getCityName());
|
||||
}
|
||||
|
||||
return ResponseEntity.status(404).body("未找到指定坐标的网格");
|
||||
}
|
||||
|
||||
logger.debug("查询用户: userId={}", userId);
|
||||
Optional<UserAccount> userOpt = userAccountRepository.findById(userId);
|
||||
|
||||
if (userOpt.isEmpty()) {
|
||||
logger.warn("分配网格员失败: 未找到用户, userId={}", userId);
|
||||
return ResponseEntity.badRequest().body("用户不存在");
|
||||
}
|
||||
|
||||
Grid grid = gridOpt.get();
|
||||
UserAccount user = userOpt.get();
|
||||
|
||||
logger.debug("网格信息: id={}, gridX={}, gridY={}", grid.getId(), grid.getGridX(), grid.getGridY());
|
||||
logger.debug("用户信息: id={}, name={}, role={}", user.getId(), user.getName(), user.getRole());
|
||||
|
||||
if (user.getRole() != Role.GRID_WORKER) {
|
||||
logger.warn("分配网格员失败: 用户角色不是网格员, userId={}, role={}", userId, user.getRole());
|
||||
return ResponseEntity.badRequest().body("只能分配网格员角色的用户");
|
||||
}
|
||||
|
||||
// 先检查是否有其他网格员已分配到此网格
|
||||
logger.debug("检查是否有其他网格员已分配到此网格: gridX={}, gridY={}", gridX, gridY);
|
||||
List<UserAccount> existingWorkers = userAccountRepository.findGridWorkersByCoordinates(gridX, gridY);
|
||||
logger.debug("已分配的网格员数量: {}", existingWorkers.size());
|
||||
|
||||
for (UserAccount existingWorker : existingWorkers) {
|
||||
if (!existingWorker.getId().equals(userId)) {
|
||||
logger.info("移除已存在的网格员: workerId={}, name={}", existingWorker.getId(), existingWorker.getName());
|
||||
existingWorker.setGridX(-1);
|
||||
existingWorker.setGridY(-1);
|
||||
userAccountRepository.save(existingWorker);
|
||||
}
|
||||
}
|
||||
|
||||
// 分配新网格员
|
||||
logger.info("分配网格员: userId={}, name={}, gridX={}, gridY={}", user.getId(), user.getName(), grid.getGridX(), grid.getGridY());
|
||||
user.setGridX(grid.getGridX());
|
||||
user.setGridY(grid.getGridY());
|
||||
userAccountRepository.save(user);
|
||||
|
||||
// 记录操作日志
|
||||
operationLogService.recordOperation(
|
||||
com.dne.ems.model.enums.OperationType.UPDATE,
|
||||
"将网格员 " + user.getName() + " (ID: " + userId + ") 分配到网格 (X: " + gridX + ", Y: " + gridY + ")",
|
||||
userId.toString(),
|
||||
"UserAccount"
|
||||
);
|
||||
|
||||
logger.info("成功将用户 {} 分配到网格 (X: {}, Y: {})", user.getName(), gridX, gridY);
|
||||
return ResponseEntity.ok().build();
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("通过坐标分配网格员时发生异常", e);
|
||||
return ResponseEntity.status(500).body("服务器内部错误");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过网格坐标移除网格员,仅限ADMIN角色使用。
|
||||
*
|
||||
* @param gridX 网格X坐标
|
||||
* @param gridY 网格Y坐标
|
||||
* @return 操作结果响应
|
||||
*/
|
||||
@PostMapping("/coordinates/{gridX}/{gridY}/unassign")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'GRID_WORKER')")
|
||||
public ResponseEntity<?> removeGridWorkerByCoordinates(
|
||||
@PathVariable Integer gridX,
|
||||
@PathVariable Integer gridY) {
|
||||
logger.info("开始通过坐标移除网格员: gridX={}, gridY={}", gridX, gridY);
|
||||
|
||||
try {
|
||||
// 查找指定坐标的网格员
|
||||
logger.debug("查询指定坐标的网格员: gridX={}, gridY={}", gridX, gridY);
|
||||
List<UserAccount> workers = userAccountRepository.findGridWorkersByCoordinates(gridX, gridY);
|
||||
logger.debug("找到网格员数量: {}", workers.size());
|
||||
|
||||
if (workers.isEmpty()) {
|
||||
logger.warn("移除网格员失败: 未找到网格员, gridX={}, gridY={}", gridX, gridY);
|
||||
return ResponseEntity.status(404).body("未找到指定坐标的网格员");
|
||||
}
|
||||
|
||||
// 移除所有网格员的分配
|
||||
for (UserAccount worker : workers) {
|
||||
logger.info("移除网格员分配: workerId={}, name={}, 原坐标: gridX={}, gridY={}",
|
||||
worker.getId(), worker.getName(), worker.getGridX(), worker.getGridY());
|
||||
|
||||
try {
|
||||
worker.setGridX(-1);
|
||||
worker.setGridY(-1);
|
||||
UserAccount savedWorker = userAccountRepository.save(worker);
|
||||
logger.info("网格员移除成功: workerId={}, 新坐标: gridX={}, gridY={}",
|
||||
savedWorker.getId(), savedWorker.getGridX(), savedWorker.getGridY());
|
||||
|
||||
// 验证保存是否成功
|
||||
UserAccount verifiedWorker = userAccountRepository.findById(worker.getId()).orElse(null);
|
||||
if (verifiedWorker != null) {
|
||||
logger.debug("验证移除结果: gridX={}, gridY={}", verifiedWorker.getGridX(), verifiedWorker.getGridY());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("保存用户数据时发生异常", e);
|
||||
return ResponseEntity.status(500).body("保存用户数据失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
} catch (Exception e) {
|
||||
logger.error("移除网格员时发生异常", e);
|
||||
return ResponseEntity.status(500).body("移除网格员失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,136 +1,136 @@
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.dne.ems.dto.TaskDetailDTO;
|
||||
import com.dne.ems.dto.TaskSubmissionRequest;
|
||||
import com.dne.ems.dto.TaskSummaryDTO;
|
||||
import com.dne.ems.model.enums.TaskStatus;
|
||||
import com.dne.ems.security.CustomUserDetails;
|
||||
import com.dne.ems.service.GridWorkerTaskService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
/**
|
||||
* 网格员任务控制器,处理网格员的任务管理操作
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>获取分配的任务列表</li>
|
||||
* <li>接受任务</li>
|
||||
* <li>提交任务完成情况</li>
|
||||
* <li>查看任务详情</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/worker")
|
||||
@RequiredArgsConstructor
|
||||
@PreAuthorize("hasRole('GRID_WORKER')")
|
||||
@Tag(name = "Grid Worker Tasks", description = "Endpoints for grid workers to manage their assigned tasks")
|
||||
public class GridWorkerTaskController {
|
||||
|
||||
private final GridWorkerTaskService gridWorkerTaskService;
|
||||
|
||||
/**
|
||||
* 获取当前网格员分配的任务列表
|
||||
*
|
||||
* @param userDetails 当前认证用户信息
|
||||
* @param status 任务状态过滤条件(可选)
|
||||
* @param pageable 分页参数
|
||||
* @return 任务摘要列表
|
||||
* @see TaskSummaryDTO
|
||||
*/
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAuthority('ROLE_GRID_WORKER')")
|
||||
@Operation(summary = "Get my assigned tasks", description = "Retrieves a paginated list of tasks assigned to the currently authenticated grid worker.")
|
||||
public ResponseEntity<Page<TaskSummaryDTO>> getMyTasks(
|
||||
@AuthenticationPrincipal CustomUserDetails userDetails,
|
||||
@Parameter(description = "Filter tasks by status") @RequestParam(required = false) TaskStatus status,
|
||||
Pageable pageable
|
||||
) {
|
||||
Page<TaskSummaryDTO> tasks = gridWorkerTaskService.getAssignedTasks(userDetails.getId(), status, pageable);
|
||||
return ResponseEntity.ok(tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* 接受指定任务
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @param userDetails 当前认证用户信息
|
||||
* @return 更新后的任务摘要
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
* @throws InvalidOperationException 如果任务无法被接受
|
||||
* @see TaskSummaryDTO
|
||||
*/
|
||||
@PostMapping("/{taskId}/accept")
|
||||
public ResponseEntity<TaskSummaryDTO> acceptTask(
|
||||
@PathVariable Long taskId,
|
||||
@AuthenticationPrincipal CustomUserDetails userDetails) {
|
||||
TaskSummaryDTO updatedTask = gridWorkerTaskService.acceptTask(taskId, userDetails.getId());
|
||||
return ResponseEntity.ok(updatedTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交任务完成情况
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @param comments 任务完成评论
|
||||
* @param files 任务完成附件
|
||||
* @param userDetails 当前认证用户信息
|
||||
* @return 更新后的任务摘要
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
* @throws InvalidOperationException 如果任务无法被提交
|
||||
* @see TaskSubmissionRequest
|
||||
* @see TaskSummaryDTO
|
||||
*/
|
||||
@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) {
|
||||
TaskSubmissionRequest request = new TaskSubmissionRequest(comments);
|
||||
TaskSummaryDTO updatedTask = gridWorkerTaskService.submitTaskCompletion(taskId, userDetails.getId(), request, files);
|
||||
return ResponseEntity.ok(updatedTask);
|
||||
}
|
||||
/**
|
||||
* 获取任务详情
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @param userDetails 当前认证用户信息
|
||||
* @return 任务详情
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
* @see TaskDetailDTO
|
||||
*/
|
||||
@GetMapping("/{taskId}")
|
||||
public ResponseEntity<TaskDetailDTO> getTaskDetails(
|
||||
@PathVariable Long taskId,
|
||||
@AuthenticationPrincipal CustomUserDetails userDetails) {
|
||||
TaskDetailDTO taskDetails = gridWorkerTaskService.getTaskDetails(taskId, userDetails.getId());
|
||||
return ResponseEntity.ok(taskDetails);
|
||||
}
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.dne.ems.dto.TaskDetailDTO;
|
||||
import com.dne.ems.dto.TaskSubmissionRequest;
|
||||
import com.dne.ems.dto.TaskSummaryDTO;
|
||||
import com.dne.ems.model.enums.TaskStatus;
|
||||
import com.dne.ems.security.CustomUserDetails;
|
||||
import com.dne.ems.service.GridWorkerTaskService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
/**
|
||||
* 网格员任务控制器,处理网格员的任务管理操作
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>获取分配的任务列表</li>
|
||||
* <li>接受任务</li>
|
||||
* <li>提交任务完成情况</li>
|
||||
* <li>查看任务详情</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/worker")
|
||||
@RequiredArgsConstructor
|
||||
@PreAuthorize("hasRole('GRID_WORKER')")
|
||||
@Tag(name = "Grid Worker Tasks", description = "Endpoints for grid workers to manage their assigned tasks")
|
||||
public class GridWorkerTaskController {
|
||||
|
||||
private final GridWorkerTaskService gridWorkerTaskService;
|
||||
|
||||
/**
|
||||
* 获取当前网格员分配的任务列表
|
||||
*
|
||||
* @param userDetails 当前认证用户信息
|
||||
* @param status 任务状态过滤条件(可选)
|
||||
* @param pageable 分页参数
|
||||
* @return 任务摘要列表
|
||||
* @see TaskSummaryDTO
|
||||
*/
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAuthority('ROLE_GRID_WORKER')")
|
||||
@Operation(summary = "Get my assigned tasks", description = "Retrieves a paginated list of tasks assigned to the currently authenticated grid worker.")
|
||||
public ResponseEntity<Page<TaskSummaryDTO>> getMyTasks(
|
||||
@AuthenticationPrincipal CustomUserDetails userDetails,
|
||||
@Parameter(description = "Filter tasks by status") @RequestParam(required = false) TaskStatus status,
|
||||
Pageable pageable
|
||||
) {
|
||||
Page<TaskSummaryDTO> tasks = gridWorkerTaskService.getAssignedTasks(userDetails.getId(), status, pageable);
|
||||
return ResponseEntity.ok(tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* 接受指定任务
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @param userDetails 当前认证用户信息
|
||||
* @return 更新后的任务摘要
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
* @throws InvalidOperationException 如果任务无法被接受
|
||||
* @see TaskSummaryDTO
|
||||
*/
|
||||
@PostMapping("/{taskId}/accept")
|
||||
public ResponseEntity<TaskSummaryDTO> acceptTask(
|
||||
@PathVariable Long taskId,
|
||||
@AuthenticationPrincipal CustomUserDetails userDetails) {
|
||||
TaskSummaryDTO updatedTask = gridWorkerTaskService.acceptTask(taskId, userDetails.getId());
|
||||
return ResponseEntity.ok(updatedTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交任务完成情况
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @param comments 任务完成评论
|
||||
* @param files 任务完成附件
|
||||
* @param userDetails 当前认证用户信息
|
||||
* @return 更新后的任务摘要
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
* @throws InvalidOperationException 如果任务无法被提交
|
||||
* @see TaskSubmissionRequest
|
||||
* @see TaskSummaryDTO
|
||||
*/
|
||||
@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) {
|
||||
TaskSubmissionRequest request = new TaskSubmissionRequest(comments);
|
||||
TaskSummaryDTO updatedTask = gridWorkerTaskService.submitTaskCompletion(taskId, userDetails.getId(), request, files);
|
||||
return ResponseEntity.ok(updatedTask);
|
||||
}
|
||||
/**
|
||||
* 获取任务详情
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @param userDetails 当前认证用户信息
|
||||
* @return 任务详情
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
* @see TaskDetailDTO
|
||||
*/
|
||||
@GetMapping("/{taskId}")
|
||||
public ResponseEntity<TaskDetailDTO> getTaskDetails(
|
||||
@PathVariable Long taskId,
|
||||
@AuthenticationPrincipal CustomUserDetails userDetails) {
|
||||
TaskDetailDTO taskDetails = gridWorkerTaskService.getTaskDetails(taskId, userDetails.getId());
|
||||
return ResponseEntity.ok(taskDetails);
|
||||
}
|
||||
}
|
||||
@@ -1,106 +1,106 @@
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.model.MapGrid;
|
||||
import com.dne.ems.repository.MapGridRepository;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 地图控制器,处理地图网格数据的操作
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>获取完整地图网格数据</li>
|
||||
* <li>创建或更新网格单元</li>
|
||||
* <li>初始化地图</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/map")
|
||||
@RequiredArgsConstructor
|
||||
public class MapController {
|
||||
|
||||
private final MapGridRepository mapGridRepository;
|
||||
|
||||
/**
|
||||
* 获取完整地图网格数据
|
||||
*
|
||||
* @return 包含所有地图网格数据的响应实体
|
||||
*/
|
||||
@GetMapping("/grid")
|
||||
public ResponseEntity<List<MapGrid>> getFullMap() {
|
||||
List<MapGrid> mapGrids = mapGridRepository.findAllByOrderByYAscXAsc();
|
||||
return ResponseEntity.ok(mapGrids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建或更新网格单元
|
||||
*
|
||||
* @param gridCellRequest 包含网格单元数据的请求体
|
||||
* @return 包含保存后的网格单元的响应实体
|
||||
* @throws IllegalArgumentException 如果坐标值为负数
|
||||
*/
|
||||
@PostMapping("/grid")
|
||||
@Transactional
|
||||
public ResponseEntity<MapGrid> createOrUpdateGridCell(@RequestBody MapGrid gridCellRequest) {
|
||||
// Simple validation
|
||||
if (gridCellRequest.getX() < 0 || gridCellRequest.getY() < 0) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
MapGrid gridCell = mapGridRepository.findByXAndY(gridCellRequest.getX(), gridCellRequest.getY())
|
||||
.orElse(new MapGrid());
|
||||
|
||||
gridCell.setX(gridCellRequest.getX());
|
||||
gridCell.setY(gridCellRequest.getY());
|
||||
gridCell.setObstacle(gridCellRequest.isObstacle());
|
||||
gridCell.setTerrainType(gridCellRequest.getTerrainType());
|
||||
|
||||
MapGrid savedCell = mapGridRepository.save(gridCell);
|
||||
return ResponseEntity.ok(savedCell);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化地图
|
||||
*
|
||||
* @param width 地图宽度(默认20)
|
||||
* @param height 地图高度(默认20)
|
||||
* @return 包含初始化结果的响应实体
|
||||
*/
|
||||
@PostMapping("/initialize")
|
||||
@Transactional
|
||||
public ResponseEntity<String> initializeMap(
|
||||
@RequestParam(defaultValue = "20") int width,
|
||||
@RequestParam(defaultValue = "20") int height) {
|
||||
|
||||
mapGridRepository.deleteAll(); // Clear existing map
|
||||
|
||||
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.");
|
||||
}
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.model.MapGrid;
|
||||
import com.dne.ems.repository.MapGridRepository;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 地图控制器,处理地图网格数据的操作
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>获取完整地图网格数据</li>
|
||||
* <li>创建或更新网格单元</li>
|
||||
* <li>初始化地图</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/map")
|
||||
@RequiredArgsConstructor
|
||||
public class MapController {
|
||||
|
||||
private final MapGridRepository mapGridRepository;
|
||||
|
||||
/**
|
||||
* 获取完整地图网格数据
|
||||
*
|
||||
* @return 包含所有地图网格数据的响应实体
|
||||
*/
|
||||
@GetMapping("/grid")
|
||||
public ResponseEntity<List<MapGrid>> getFullMap() {
|
||||
List<MapGrid> mapGrids = mapGridRepository.findAllByOrderByYAscXAsc();
|
||||
return ResponseEntity.ok(mapGrids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建或更新网格单元
|
||||
*
|
||||
* @param gridCellRequest 包含网格单元数据的请求体
|
||||
* @return 包含保存后的网格单元的响应实体
|
||||
* @throws IllegalArgumentException 如果坐标值为负数
|
||||
*/
|
||||
@PostMapping("/grid")
|
||||
@Transactional
|
||||
public ResponseEntity<MapGrid> createOrUpdateGridCell(@RequestBody MapGrid gridCellRequest) {
|
||||
// Simple validation
|
||||
if (gridCellRequest.getX() < 0 || gridCellRequest.getY() < 0) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
MapGrid gridCell = mapGridRepository.findByXAndY(gridCellRequest.getX(), gridCellRequest.getY())
|
||||
.orElse(new MapGrid());
|
||||
|
||||
gridCell.setX(gridCellRequest.getX());
|
||||
gridCell.setY(gridCellRequest.getY());
|
||||
gridCell.setObstacle(gridCellRequest.isObstacle());
|
||||
gridCell.setTerrainType(gridCellRequest.getTerrainType());
|
||||
|
||||
MapGrid savedCell = mapGridRepository.save(gridCell);
|
||||
return ResponseEntity.ok(savedCell);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化地图
|
||||
*
|
||||
* @param width 地图宽度(默认20)
|
||||
* @param height 地图高度(默认20)
|
||||
* @return 包含初始化结果的响应实体
|
||||
*/
|
||||
@PostMapping("/initialize")
|
||||
@Transactional
|
||||
public ResponseEntity<String> initializeMap(
|
||||
@RequestParam(defaultValue = "20") int width,
|
||||
@RequestParam(defaultValue = "20") int height) {
|
||||
|
||||
mapGridRepository.deleteAll(); // Clear existing map
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
@@ -1,89 +1,89 @@
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.OperationLogDTO;
|
||||
import com.dne.ems.model.enums.OperationType;
|
||||
import com.dne.ems.security.CustomUserDetails;
|
||||
import com.dne.ems.service.OperationLogService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
/**
|
||||
* 操作日志控制器,提供查询用户操作日志的API。
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025-06-20
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/logs")
|
||||
@Tag(name = "操作日志管理", description = "提供操作日志查询功能")
|
||||
public class OperationLogController {
|
||||
|
||||
@Autowired
|
||||
private OperationLogService operationLogService;
|
||||
|
||||
/**
|
||||
* 获取当前用户的操作日志
|
||||
*
|
||||
* @param userDetails 当前用户详情
|
||||
* @return 操作日志列表
|
||||
*/
|
||||
@GetMapping("/my-logs")
|
||||
@Operation(summary = "获取当前用户的操作日志", description = "返回当前登录用户的所有操作记录")
|
||||
public ResponseEntity<List<OperationLogDTO>> getMyOperationLogs(
|
||||
@AuthenticationPrincipal CustomUserDetails userDetails) {
|
||||
|
||||
Long userId = userDetails.getUserAccount().getId();
|
||||
List<OperationLogDTO> logs = operationLogService.getUserOperationLogs(userId);
|
||||
return ResponseEntity.ok(logs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有用户的操作日志(仅限管理员)
|
||||
*
|
||||
* @param operationType 操作类型(可选)
|
||||
* @param userId 用户ID(可选)
|
||||
* @param startTime 开始时间(可选)
|
||||
* @param endTime 结束时间(可选)
|
||||
* @return 操作日志列表
|
||||
*/
|
||||
@GetMapping
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@Operation(summary = "获取所有操作日志", description = "管理员可以查询所有用户的操作日志,支持多种过滤条件")
|
||||
public ResponseEntity<List<OperationLogDTO>> getAllOperationLogs(
|
||||
@RequestParam(required = false)
|
||||
@Parameter(description = "操作类型")
|
||||
OperationType operationType,
|
||||
|
||||
@RequestParam(required = false)
|
||||
@Parameter(description = "用户ID")
|
||||
Long userId,
|
||||
|
||||
@RequestParam(required = false)
|
||||
@Parameter(description = "开始时间,格式:yyyy-MM-dd'T'HH:mm:ss")
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
|
||||
LocalDateTime startTime,
|
||||
|
||||
@RequestParam(required = false)
|
||||
@Parameter(description = "结束时间,格式:yyyy-MM-dd'T'HH:mm:ss")
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
|
||||
LocalDateTime endTime) {
|
||||
|
||||
List<OperationLogDTO> logs = operationLogService.queryOperationLogs(userId, operationType, startTime, endTime);
|
||||
return ResponseEntity.ok(logs);
|
||||
}
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.OperationLogDTO;
|
||||
import com.dne.ems.model.enums.OperationType;
|
||||
import com.dne.ems.security.CustomUserDetails;
|
||||
import com.dne.ems.service.OperationLogService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
/**
|
||||
* 操作日志控制器,提供查询用户操作日志的API。
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025-06-20
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/logs")
|
||||
@Tag(name = "操作日志管理", description = "提供操作日志查询功能")
|
||||
public class OperationLogController {
|
||||
|
||||
@Autowired
|
||||
private OperationLogService operationLogService;
|
||||
|
||||
/**
|
||||
* 获取当前用户的操作日志
|
||||
*
|
||||
* @param userDetails 当前用户详情
|
||||
* @return 操作日志列表
|
||||
*/
|
||||
@GetMapping("/my-logs")
|
||||
@Operation(summary = "获取当前用户的操作日志", description = "返回当前登录用户的所有操作记录")
|
||||
public ResponseEntity<List<OperationLogDTO>> getMyOperationLogs(
|
||||
@AuthenticationPrincipal CustomUserDetails userDetails) {
|
||||
|
||||
Long userId = userDetails.getUserAccount().getId();
|
||||
List<OperationLogDTO> logs = operationLogService.getUserOperationLogs(userId);
|
||||
return ResponseEntity.ok(logs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有用户的操作日志(仅限管理员)
|
||||
*
|
||||
* @param operationType 操作类型(可选)
|
||||
* @param userId 用户ID(可选)
|
||||
* @param startTime 开始时间(可选)
|
||||
* @param endTime 结束时间(可选)
|
||||
* @return 操作日志列表
|
||||
*/
|
||||
@GetMapping
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@Operation(summary = "获取所有操作日志", description = "管理员可以查询所有用户的操作日志,支持多种过滤条件")
|
||||
public ResponseEntity<List<OperationLogDTO>> getAllOperationLogs(
|
||||
@RequestParam(required = false)
|
||||
@Parameter(description = "操作类型")
|
||||
OperationType operationType,
|
||||
|
||||
@RequestParam(required = false)
|
||||
@Parameter(description = "用户ID")
|
||||
Long userId,
|
||||
|
||||
@RequestParam(required = false)
|
||||
@Parameter(description = "开始时间,格式:yyyy-MM-dd'T'HH:mm:ss")
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
|
||||
LocalDateTime startTime,
|
||||
|
||||
@RequestParam(required = false)
|
||||
@Parameter(description = "结束时间,格式:yyyy-MM-dd'T'HH:mm:ss")
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
|
||||
LocalDateTime endTime) {
|
||||
|
||||
List<OperationLogDTO> logs = operationLogService.queryOperationLogs(userId, operationType, startTime, endTime);
|
||||
return ResponseEntity.ok(logs);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +1,62 @@
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.PathfindingRequest;
|
||||
import com.dne.ems.dto.Point;
|
||||
import com.dne.ems.service.pathfinding.AStarService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 路径规划控制器,提供A*寻路算法功能
|
||||
*
|
||||
* <p>主要功能:
|
||||
* <ul>
|
||||
* <li>计算两点之间的最优路径</li>
|
||||
* <li>考虑地图障碍物</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/pathfinding")
|
||||
@RequiredArgsConstructor
|
||||
public class PathfindingController {
|
||||
|
||||
private final AStarService aStarService;
|
||||
|
||||
/**
|
||||
* 使用A*算法查找两点之间的路径
|
||||
*
|
||||
* <p>实现细节:
|
||||
* <ol>
|
||||
* <li>验证请求参数</li>
|
||||
* <li>调用A*算法服务计算路径</li>
|
||||
* <li>返回路径点列表</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param request 包含起点和终点坐标的请求体
|
||||
* @return 包含路径点列表的响应实体,若无路径则返回空列表
|
||||
* @throws IllegalArgumentException 如果请求参数无效
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.PathfindingRequest;
|
||||
import com.dne.ems.dto.Point;
|
||||
import com.dne.ems.service.pathfinding.AStarService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 路径规划控制器,提供A*寻路算法功能
|
||||
*
|
||||
* <p>主要功能:
|
||||
* <ul>
|
||||
* <li>计算两点之间的最优路径</li>
|
||||
* <li>考虑地图障碍物</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/pathfinding")
|
||||
@RequiredArgsConstructor
|
||||
public class PathfindingController {
|
||||
|
||||
private final AStarService aStarService;
|
||||
|
||||
/**
|
||||
* 使用A*算法查找两点之间的路径
|
||||
*
|
||||
* <p>实现细节:
|
||||
* <ol>
|
||||
* <li>验证请求参数</li>
|
||||
* <li>调用A*算法服务计算路径</li>
|
||||
* <li>返回路径点列表</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param request 包含起点和终点坐标的请求体
|
||||
* @return 包含路径点列表的响应实体,若无路径则返回空列表
|
||||
* @throws IllegalArgumentException 如果请求参数无效
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
}
|
||||
@@ -1,157 +1,157 @@
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PatchMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.PageDTO;
|
||||
import com.dne.ems.dto.UserCreationRequest;
|
||||
import com.dne.ems.dto.UserRoleUpdateRequest;
|
||||
import com.dne.ems.dto.UserUpdateRequest;
|
||||
import com.dne.ems.model.UserAccount;
|
||||
import com.dne.ems.model.enums.Role;
|
||||
import com.dne.ems.service.PersonnelService;
|
||||
import com.dne.ems.service.UserAccountService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 人员管理控制器,处理用户账号的CRUD操作
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>用户账号创建</li>
|
||||
* <li>用户信息查询与更新</li>
|
||||
* <li>用户角色管理</li>
|
||||
* <li>用户删除</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/personnel")
|
||||
@RequiredArgsConstructor
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public class PersonnelController {
|
||||
|
||||
private final PersonnelService personnelService;
|
||||
private final UserAccountService userAccountService;
|
||||
|
||||
/**
|
||||
* 创建新用户
|
||||
*
|
||||
* @param request 包含用户信息的请求体
|
||||
* @return 创建成功的用户实体
|
||||
* @throws UserAlreadyExistsException 如果用户名或邮箱已存在
|
||||
*/
|
||||
@PostMapping("/users")
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
public UserAccount createUser(@RequestBody @Valid UserCreationRequest request) {
|
||||
return personnelService.createUser(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户分页列表
|
||||
*
|
||||
* @param role 角色过滤条件(可选)
|
||||
* @param name 姓名过滤条件(可选,不区分大小写)
|
||||
* @param pageable 分页参数
|
||||
* @return 用户分页列表
|
||||
*/
|
||||
@Operation(summary = "Get a paginated list of users", description = "Retrieves a list of users, with optional filtering by role and name.")
|
||||
@GetMapping("/users")
|
||||
@PreAuthorize("hasRole('ADMIN') or hasRole('GRID_WORKER')")
|
||||
public ResponseEntity<PageDTO<UserAccount>> getUsers(
|
||||
@Parameter(description = "Filter by user role") @RequestParam(required = false) Role role,
|
||||
@Parameter(description = "Filter by user name (case-insensitive search)") @RequestParam(required = false) String name,
|
||||
Pageable pageable) {
|
||||
Page<UserAccount> usersPage = personnelService.getUsers(role, name, pageable);
|
||||
PageDTO<UserAccount> userPageDTO = new PageDTO<>(
|
||||
usersPage.getContent(),
|
||||
usersPage.getTotalElements(),
|
||||
usersPage.getTotalPages(),
|
||||
usersPage.getNumber(),
|
||||
usersPage.getSize()
|
||||
);
|
||||
return ResponseEntity.ok(userPageDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取用户详情
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户详情
|
||||
* @throws ResourceNotFoundException 如果用户不存在
|
||||
*/
|
||||
@Operation(summary = "Get user by ID", description = "Fetches the details of a specific user by their ID.")
|
||||
@GetMapping("/users/{userId}")
|
||||
public ResponseEntity<UserAccount> getUserById(@PathVariable Long userId) {
|
||||
UserAccount user = userAccountService.getUserById(userId);
|
||||
user.setPassword(null); // Do not expose password
|
||||
return ResponseEntity.ok(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param request 包含更新信息的请求体
|
||||
* @return 更新后的用户实体
|
||||
* @throws ResourceNotFoundException 如果用户不存在
|
||||
*/
|
||||
@PatchMapping("/users/{userId}")
|
||||
public ResponseEntity<UserAccount> updateUser(
|
||||
@PathVariable Long userId,
|
||||
@RequestBody UserUpdateRequest request) {
|
||||
UserAccount updatedUser = personnelService.updateUser(userId, request);
|
||||
return ResponseEntity.ok(updatedUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户角色
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param request 包含角色信息的请求体
|
||||
* @return 更新后的用户实体
|
||||
* @throws ResourceNotFoundException 如果用户不存在
|
||||
* @throws InvalidOperationException 如果角色更新无效
|
||||
*/
|
||||
@PutMapping("/users/{userId}/role")
|
||||
public ResponseEntity<UserAccount> updateUserRole(
|
||||
@PathVariable Long userId,
|
||||
@RequestBody @Valid UserRoleUpdateRequest request) {
|
||||
UserAccount updatedUser = userAccountService.updateUserRole(userId, request);
|
||||
updatedUser.setPassword(null); // Do not expose password
|
||||
return ResponseEntity.ok(updatedUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @throws ResourceNotFoundException 如果用户不存在
|
||||
*/
|
||||
@DeleteMapping("/users/{userId}")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void deleteUser(@PathVariable Long userId) {
|
||||
personnelService.deleteUser(userId);
|
||||
}
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PatchMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.PageDTO;
|
||||
import com.dne.ems.dto.UserCreationRequest;
|
||||
import com.dne.ems.dto.UserRoleUpdateRequest;
|
||||
import com.dne.ems.dto.UserUpdateRequest;
|
||||
import com.dne.ems.model.UserAccount;
|
||||
import com.dne.ems.model.enums.Role;
|
||||
import com.dne.ems.service.PersonnelService;
|
||||
import com.dne.ems.service.UserAccountService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 人员管理控制器,处理用户账号的CRUD操作
|
||||
*
|
||||
* <p>主要功能包括:
|
||||
* <ul>
|
||||
* <li>用户账号创建</li>
|
||||
* <li>用户信息查询与更新</li>
|
||||
* <li>用户角色管理</li>
|
||||
* <li>用户删除</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/personnel")
|
||||
@RequiredArgsConstructor
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public class PersonnelController {
|
||||
|
||||
private final PersonnelService personnelService;
|
||||
private final UserAccountService userAccountService;
|
||||
|
||||
/**
|
||||
* 创建新用户
|
||||
*
|
||||
* @param request 包含用户信息的请求体
|
||||
* @return 创建成功的用户实体
|
||||
* @throws UserAlreadyExistsException 如果用户名或邮箱已存在
|
||||
*/
|
||||
@PostMapping("/users")
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
public UserAccount createUser(@RequestBody @Valid UserCreationRequest request) {
|
||||
return personnelService.createUser(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户分页列表
|
||||
*
|
||||
* @param role 角色过滤条件(可选)
|
||||
* @param name 姓名过滤条件(可选,不区分大小写)
|
||||
* @param pageable 分页参数
|
||||
* @return 用户分页列表
|
||||
*/
|
||||
@Operation(summary = "Get a paginated list of users", description = "Retrieves a list of users, with optional filtering by role and name.")
|
||||
@GetMapping("/users")
|
||||
@PreAuthorize("hasRole('ADMIN') or hasRole('GRID_WORKER')")
|
||||
public ResponseEntity<PageDTO<UserAccount>> getUsers(
|
||||
@Parameter(description = "Filter by user role") @RequestParam(required = false) Role role,
|
||||
@Parameter(description = "Filter by user name (case-insensitive search)") @RequestParam(required = false) String name,
|
||||
Pageable pageable) {
|
||||
Page<UserAccount> usersPage = personnelService.getUsers(role, name, pageable);
|
||||
PageDTO<UserAccount> userPageDTO = new PageDTO<>(
|
||||
usersPage.getContent(),
|
||||
usersPage.getTotalElements(),
|
||||
usersPage.getTotalPages(),
|
||||
usersPage.getNumber(),
|
||||
usersPage.getSize()
|
||||
);
|
||||
return ResponseEntity.ok(userPageDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取用户详情
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户详情
|
||||
* @throws ResourceNotFoundException 如果用户不存在
|
||||
*/
|
||||
@Operation(summary = "Get user by ID", description = "Fetches the details of a specific user by their ID.")
|
||||
@GetMapping("/users/{userId}")
|
||||
public ResponseEntity<UserAccount> getUserById(@PathVariable Long userId) {
|
||||
UserAccount user = userAccountService.getUserById(userId);
|
||||
user.setPassword(null); // Do not expose password
|
||||
return ResponseEntity.ok(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param request 包含更新信息的请求体
|
||||
* @return 更新后的用户实体
|
||||
* @throws ResourceNotFoundException 如果用户不存在
|
||||
*/
|
||||
@PatchMapping("/users/{userId}")
|
||||
public ResponseEntity<UserAccount> updateUser(
|
||||
@PathVariable Long userId,
|
||||
@RequestBody UserUpdateRequest request) {
|
||||
UserAccount updatedUser = personnelService.updateUser(userId, request);
|
||||
return ResponseEntity.ok(updatedUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户角色
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param request 包含角色信息的请求体
|
||||
* @return 更新后的用户实体
|
||||
* @throws ResourceNotFoundException 如果用户不存在
|
||||
* @throws InvalidOperationException 如果角色更新无效
|
||||
*/
|
||||
@PutMapping("/users/{userId}/role")
|
||||
public ResponseEntity<UserAccount> updateUserRole(
|
||||
@PathVariable Long userId,
|
||||
@RequestBody @Valid UserRoleUpdateRequest request) {
|
||||
UserAccount updatedUser = userAccountService.updateUserRole(userId, request);
|
||||
updatedUser.setPassword(null); // Do not expose password
|
||||
return ResponseEntity.ok(updatedUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @throws ResourceNotFoundException 如果用户不存在
|
||||
*/
|
||||
@DeleteMapping("/users/{userId}")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void deleteUser(@PathVariable Long userId) {
|
||||
personnelService.deleteUser(userId);
|
||||
}
|
||||
}
|
||||
@@ -1,69 +1,69 @@
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.UserFeedbackSummaryDTO;
|
||||
import com.dne.ems.security.CustomUserDetails;
|
||||
import com.dne.ems.service.UserFeedbackService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 用户个人资料控制器,处理认证用户相关数据
|
||||
*
|
||||
* <p>主要功能:
|
||||
* <ul>
|
||||
* <li>获取用户提交的反馈历史</li>
|
||||
* <li>管理个人资料相关数据</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.2
|
||||
* @since 2025-06-16
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/me")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "User Profile", description = "Endpoints related to the authenticated user's own data")
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
public class ProfileController {
|
||||
|
||||
private final UserFeedbackService userFeedbackService;
|
||||
|
||||
/**
|
||||
* 获取当前用户的反馈历史记录
|
||||
*
|
||||
* <p>实现细节:
|
||||
* <ol>
|
||||
* <li>从认证信息获取用户ID</li>
|
||||
* <li>查询该用户提交的所有反馈</li>
|
||||
* <li>返回分页结果</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param userDetails 认证用户详情
|
||||
* @param pageable 分页参数
|
||||
* @return 用户反馈历史的分页列表
|
||||
* @throws AuthenticationException 如果用户未认证
|
||||
*/
|
||||
@Operation(summary = "Get current user's feedback history", description = "Retrieves a paginated list of feedback submitted by the currently authenticated user.")
|
||||
@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());
|
||||
}
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.UserFeedbackSummaryDTO;
|
||||
import com.dne.ems.security.CustomUserDetails;
|
||||
import com.dne.ems.service.UserFeedbackService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 用户个人资料控制器,处理认证用户相关数据
|
||||
*
|
||||
* <p>主要功能:
|
||||
* <ul>
|
||||
* <li>获取用户提交的反馈历史</li>
|
||||
* <li>管理个人资料相关数据</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.2
|
||||
* @since 2025-06-16
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/me")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "User Profile", description = "Endpoints related to the authenticated user's own data")
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
public class ProfileController {
|
||||
|
||||
private final UserFeedbackService userFeedbackService;
|
||||
|
||||
/**
|
||||
* 获取当前用户的反馈历史记录
|
||||
*
|
||||
* <p>实现细节:
|
||||
* <ol>
|
||||
* <li>从认证信息获取用户ID</li>
|
||||
* <li>查询该用户提交的所有反馈</li>
|
||||
* <li>返回分页结果</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param userDetails 认证用户详情
|
||||
* @param pageable 分页参数
|
||||
* @return 用户反馈历史的分页列表
|
||||
* @throws AuthenticationException 如果用户未认证
|
||||
*/
|
||||
@Operation(summary = "Get current user's feedback history", description = "Retrieves a paginated list of feedback submitted by the currently authenticated user.")
|
||||
@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());
|
||||
}
|
||||
}
|
||||
@@ -1,63 +1,63 @@
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.dne.ems.dto.PublicFeedbackRequest;
|
||||
import com.dne.ems.model.Feedback;
|
||||
import com.dne.ems.service.FeedbackService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 公共API控制器,处理无需认证的公共接口
|
||||
*
|
||||
* <p>主要功能:
|
||||
* <ul>
|
||||
* <li>接收公众反馈提交</li>
|
||||
* <li>处理带图片上传的表单</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/public")
|
||||
@RequiredArgsConstructor
|
||||
public class PublicController {
|
||||
|
||||
private final FeedbackService feedbackService;
|
||||
|
||||
/**
|
||||
* 提交公共反馈(支持图片上传)
|
||||
*
|
||||
* <p>实现细节:
|
||||
* <ol>
|
||||
* <li>验证反馈请求参数</li>
|
||||
* <li>处理上传的图片文件(如果有)</li>
|
||||
* <li>保存反馈到数据库</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param request 包含反馈信息的请求体(JSON格式)
|
||||
* @param files 上传的图片文件数组(可选)
|
||||
* @return 创建成功的反馈实体
|
||||
* @throws InvalidRequestException 如果请求参数无效
|
||||
* @throws FileUploadException 如果文件上传失败
|
||||
*/
|
||||
@PostMapping(value = "/feedback", consumes = "multipart/form-data")
|
||||
public ResponseEntity<Feedback> submitPublicFeedback(
|
||||
@Valid @RequestPart("feedback") PublicFeedbackRequest request,
|
||||
@RequestPart(value = "files", required = false) MultipartFile[] files) {
|
||||
// We will need to update the service method to handle files.
|
||||
// For now, let's pass null and focus on the controller signature.
|
||||
Feedback createdFeedback = feedbackService.createPublicFeedback(request, files);
|
||||
return new ResponseEntity<>(createdFeedback, HttpStatus.CREATED);
|
||||
}
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.dne.ems.dto.PublicFeedbackRequest;
|
||||
import com.dne.ems.model.Feedback;
|
||||
import com.dne.ems.service.FeedbackService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 公共API控制器,处理无需认证的公共接口
|
||||
*
|
||||
* <p>主要功能:
|
||||
* <ul>
|
||||
* <li>接收公众反馈提交</li>
|
||||
* <li>处理带图片上传的表单</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/public")
|
||||
@RequiredArgsConstructor
|
||||
public class PublicController {
|
||||
|
||||
private final FeedbackService feedbackService;
|
||||
|
||||
/**
|
||||
* 提交公共反馈(支持图片上传)
|
||||
*
|
||||
* <p>实现细节:
|
||||
* <ol>
|
||||
* <li>验证反馈请求参数</li>
|
||||
* <li>处理上传的图片文件(如果有)</li>
|
||||
* <li>保存反馈到数据库</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param request 包含反馈信息的请求体(JSON格式)
|
||||
* @param files 上传的图片文件数组(可选)
|
||||
* @return 创建成功的反馈实体
|
||||
* @throws InvalidRequestException 如果请求参数无效
|
||||
* @throws FileUploadException 如果文件上传失败
|
||||
*/
|
||||
@PostMapping(value = "/feedback", consumes = "multipart/form-data")
|
||||
public ResponseEntity<Feedback> submitPublicFeedback(
|
||||
@Valid @RequestPart("feedback") PublicFeedbackRequest request,
|
||||
@RequestPart(value = "files", required = false) MultipartFile[] files) {
|
||||
// We will need to update the service method to handle files.
|
||||
// For now, let's pass null and focus on the controller signature.
|
||||
Feedback createdFeedback = feedbackService.createPublicFeedback(request, files);
|
||||
return new ResponseEntity<>(createdFeedback, HttpStatus.CREATED);
|
||||
}
|
||||
}
|
||||
@@ -1,90 +1,90 @@
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.RejectFeedbackRequest;
|
||||
import com.dne.ems.model.Feedback;
|
||||
import com.dne.ems.service.SupervisorService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 主管控制器,处理反馈审核相关功能
|
||||
*
|
||||
* <p>主要职责:
|
||||
* <ul>
|
||||
* <li>获取待审核反馈列表</li>
|
||||
* <li>审核通过/拒绝反馈</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/supervisor")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "Supervisor", description = "Endpoints for supervisor to review and manage feedback")
|
||||
public class SupervisorController {
|
||||
|
||||
private final SupervisorService supervisorService;
|
||||
|
||||
/**
|
||||
* 获取待审核反馈列表
|
||||
*
|
||||
* @return 待审核反馈列表
|
||||
* @throws AccessDeniedException 如果用户无权限
|
||||
*/
|
||||
@GetMapping("/reviews")
|
||||
@PreAuthorize("hasAnyRole('SUPERVISOR', 'ADMIN')")
|
||||
@Operation(summary = "Get feedback list pending for supervisor review")
|
||||
public ResponseEntity<List<Feedback>> getFeedbackForReview() {
|
||||
List<Feedback> feedbackList = supervisorService.getFeedbackForReview();
|
||||
return ResponseEntity.ok(feedbackList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核通过反馈
|
||||
*
|
||||
* @param feedbackId 反馈ID
|
||||
* @return 空响应
|
||||
* @throws ResourceNotFoundException 如果反馈不存在
|
||||
* @throws InvalidOperationException 如果反馈已审核
|
||||
*/
|
||||
@PostMapping("/reviews/{feedbackId}/approve")
|
||||
@PreAuthorize("hasAnyRole('SUPERVISOR', 'ADMIN')")
|
||||
@Operation(summary = "Approve a feedback")
|
||||
public ResponseEntity<Void> approveFeedback(@PathVariable Long feedbackId) {
|
||||
supervisorService.approveFeedback(feedbackId);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核拒绝反馈
|
||||
*
|
||||
* @param feedbackId 反馈ID
|
||||
* @return 空响应
|
||||
* @throws ResourceNotFoundException 如果反馈不存在
|
||||
* @throws InvalidOperationException 如果反馈已审核
|
||||
*/
|
||||
@PostMapping("/reviews/{feedbackId}/reject")
|
||||
@PreAuthorize("hasAnyRole('SUPERVISOR', 'ADMIN')")
|
||||
@Operation(summary = "Reject a feedback")
|
||||
public ResponseEntity<Void> rejectFeedback(
|
||||
@PathVariable Long feedbackId,
|
||||
@RequestBody RejectFeedbackRequest request) {
|
||||
supervisorService.rejectFeedback(feedbackId, request);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.RejectFeedbackRequest;
|
||||
import com.dne.ems.model.Feedback;
|
||||
import com.dne.ems.service.SupervisorService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 主管控制器,处理反馈审核相关功能
|
||||
*
|
||||
* <p>主要职责:
|
||||
* <ul>
|
||||
* <li>获取待审核反馈列表</li>
|
||||
* <li>审核通过/拒绝反馈</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/supervisor")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "Supervisor", description = "Endpoints for supervisor to review and manage feedback")
|
||||
public class SupervisorController {
|
||||
|
||||
private final SupervisorService supervisorService;
|
||||
|
||||
/**
|
||||
* 获取待审核反馈列表
|
||||
*
|
||||
* @return 待审核反馈列表
|
||||
* @throws AccessDeniedException 如果用户无权限
|
||||
*/
|
||||
@GetMapping("/reviews")
|
||||
@PreAuthorize("hasAnyRole('SUPERVISOR', 'ADMIN')")
|
||||
@Operation(summary = "Get feedback list pending for supervisor review")
|
||||
public ResponseEntity<List<Feedback>> getFeedbackForReview() {
|
||||
List<Feedback> feedbackList = supervisorService.getFeedbackForReview();
|
||||
return ResponseEntity.ok(feedbackList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核通过反馈
|
||||
*
|
||||
* @param feedbackId 反馈ID
|
||||
* @return 空响应
|
||||
* @throws ResourceNotFoundException 如果反馈不存在
|
||||
* @throws InvalidOperationException 如果反馈已审核
|
||||
*/
|
||||
@PostMapping("/reviews/{feedbackId}/approve")
|
||||
@PreAuthorize("hasAnyRole('SUPERVISOR', 'ADMIN')")
|
||||
@Operation(summary = "Approve a feedback")
|
||||
public ResponseEntity<Void> approveFeedback(@PathVariable Long feedbackId) {
|
||||
supervisorService.approveFeedback(feedbackId);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核拒绝反馈
|
||||
*
|
||||
* @param feedbackId 反馈ID
|
||||
* @return 空响应
|
||||
* @throws ResourceNotFoundException 如果反馈不存在
|
||||
* @throws InvalidOperationException 如果反馈已审核
|
||||
*/
|
||||
@PostMapping("/reviews/{feedbackId}/reject")
|
||||
@PreAuthorize("hasAnyRole('SUPERVISOR', 'ADMIN')")
|
||||
@Operation(summary = "Reject a feedback")
|
||||
public ResponseEntity<Void> rejectFeedback(
|
||||
@PathVariable Long feedbackId,
|
||||
@RequestBody RejectFeedbackRequest request) {
|
||||
supervisorService.rejectFeedback(feedbackId, request);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
}
|
||||
@@ -1,105 +1,105 @@
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.model.Assignment;
|
||||
import com.dne.ems.model.Feedback;
|
||||
import com.dne.ems.model.UserAccount;
|
||||
import com.dne.ems.security.CustomUserDetails;
|
||||
import com.dne.ems.service.TaskAssignmentService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 任务分配控制器,处理网格任务分配相关功能
|
||||
*
|
||||
* <p>主要职责:
|
||||
* <ul>
|
||||
* <li>获取未分配任务列表</li>
|
||||
* <li>查询可用网格工作人员</li>
|
||||
* <li>分配任务给工作人员</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/tasks")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class TaskAssignmentController {
|
||||
|
||||
private final TaskAssignmentService taskAssignmentService;
|
||||
|
||||
/**
|
||||
* 获取未分配的任务列表
|
||||
*
|
||||
* @return 未分配的反馈任务列表
|
||||
* @throws AccessDeniedException 如果用户无权限
|
||||
*/
|
||||
@GetMapping("/unassigned")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPERVISOR')")
|
||||
public ResponseEntity<List<Feedback>> getUnassignedFeedback() {
|
||||
List<Feedback> feedbackList = taskAssignmentService.getUnassignedFeedback();
|
||||
return ResponseEntity.ok(feedbackList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可用的网格工作人员列表
|
||||
*
|
||||
* @return 可用工作人员列表
|
||||
* @throws AccessDeniedException 如果用户无权限
|
||||
*/
|
||||
@GetMapping("/grid-workers")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPERVISOR')")
|
||||
public ResponseEntity<List<UserAccount>> getAvailableGridWorkers() {
|
||||
log.info("API接收到获取可用网格员列表的请求");
|
||||
try {
|
||||
List<UserAccount> workers = taskAssignmentService.getAvailableGridWorkers();
|
||||
log.info("成功获取到 {} 名可用的网格员", workers.size());
|
||||
return ResponseEntity.ok(workers);
|
||||
} catch (Exception e) {
|
||||
log.error("获取可用网格员列表时发生未知异常", e);
|
||||
return ResponseEntity.internalServerError().build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配任务给工作人员
|
||||
*
|
||||
* @param request 包含任务ID和分配人ID的请求体
|
||||
* @param adminDetails 当前管理员认证信息
|
||||
* @return 创建的任务分配记录
|
||||
* @throws ResourceNotFoundException 如果任务或工作人员不存在
|
||||
* @throws InvalidOperationException 如果任务已分配
|
||||
* @throws AccessDeniedException 如果用户无权限
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
|
||||
// A simple record to represent the assignment request payload
|
||||
public record AssignmentRequest(Long feedbackId, Long assigneeId) {}
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.model.Assignment;
|
||||
import com.dne.ems.model.Feedback;
|
||||
import com.dne.ems.model.UserAccount;
|
||||
import com.dne.ems.security.CustomUserDetails;
|
||||
import com.dne.ems.service.TaskAssignmentService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 任务分配控制器,处理网格任务分配相关功能
|
||||
*
|
||||
* <p>主要职责:
|
||||
* <ul>
|
||||
* <li>获取未分配任务列表</li>
|
||||
* <li>查询可用网格工作人员</li>
|
||||
* <li>分配任务给工作人员</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/tasks")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class TaskAssignmentController {
|
||||
|
||||
private final TaskAssignmentService taskAssignmentService;
|
||||
|
||||
/**
|
||||
* 获取未分配的任务列表
|
||||
*
|
||||
* @return 未分配的反馈任务列表
|
||||
* @throws AccessDeniedException 如果用户无权限
|
||||
*/
|
||||
@GetMapping("/unassigned")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPERVISOR')")
|
||||
public ResponseEntity<List<Feedback>> getUnassignedFeedback() {
|
||||
List<Feedback> feedbackList = taskAssignmentService.getUnassignedFeedback();
|
||||
return ResponseEntity.ok(feedbackList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可用的网格工作人员列表
|
||||
*
|
||||
* @return 可用工作人员列表
|
||||
* @throws AccessDeniedException 如果用户无权限
|
||||
*/
|
||||
@GetMapping("/grid-workers")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPERVISOR')")
|
||||
public ResponseEntity<List<UserAccount>> getAvailableGridWorkers() {
|
||||
log.info("API接收到获取可用网格员列表的请求");
|
||||
try {
|
||||
List<UserAccount> workers = taskAssignmentService.getAvailableGridWorkers();
|
||||
log.info("成功获取到 {} 名可用的网格员", workers.size());
|
||||
return ResponseEntity.ok(workers);
|
||||
} catch (Exception e) {
|
||||
log.error("获取可用网格员列表时发生未知异常", e);
|
||||
return ResponseEntity.internalServerError().build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配任务给工作人员
|
||||
*
|
||||
* @param request 包含任务ID和分配人ID的请求体
|
||||
* @param adminDetails 当前管理员认证信息
|
||||
* @return 创建的任务分配记录
|
||||
* @throws ResourceNotFoundException 如果任务或工作人员不存在
|
||||
* @throws InvalidOperationException 如果任务已分配
|
||||
* @throws AccessDeniedException 如果用户无权限
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
|
||||
// A simple record to represent the assignment request payload
|
||||
public record AssignmentRequest(Long feedbackId, Long assigneeId) {}
|
||||
}
|
||||
@@ -1,231 +1,231 @@
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.TaskApprovalRequest;
|
||||
import com.dne.ems.dto.TaskAssignmentRequest;
|
||||
import com.dne.ems.dto.TaskCreationRequest;
|
||||
import com.dne.ems.dto.TaskDetailDTO;
|
||||
import com.dne.ems.dto.TaskFromFeedbackRequest;
|
||||
import com.dne.ems.dto.TaskRejectionRequest;
|
||||
import com.dne.ems.dto.TaskSummaryDTO;
|
||||
import com.dne.ems.model.Feedback;
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
import com.dne.ems.model.enums.TaskStatus;
|
||||
import com.dne.ems.service.TaskManagementService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 任务管理控制器,处理任务全生命周期管理
|
||||
*
|
||||
* <p>主要功能:
|
||||
* <ul>
|
||||
* <li>任务创建与分配</li>
|
||||
* <li>任务状态管理</li>
|
||||
* <li>任务审核与审批</li>
|
||||
* <li>从反馈创建任务</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/management/tasks")
|
||||
@RequiredArgsConstructor
|
||||
@PreAuthorize("hasRole('SUPERVISOR')")
|
||||
public class TaskManagementController {
|
||||
|
||||
private final TaskManagementService taskManagementService;
|
||||
|
||||
/**
|
||||
* 获取任务分页列表(支持多条件过滤)
|
||||
*
|
||||
* @param status 任务状态过滤条件
|
||||
* @param assigneeId 分配人ID过滤条件
|
||||
* @param severity 严重程度过滤条件
|
||||
* @param pollutionType 污染类型过滤条件
|
||||
* @param startDate 开始日期过滤条件(yyyy-MM-dd)
|
||||
* @param endDate 结束日期过滤条件(yyyy-MM-dd)
|
||||
* @param pageable 分页参数
|
||||
* @return 任务分页列表
|
||||
* @throws AccessDeniedException 如果用户无权限
|
||||
*/
|
||||
@Operation(summary = "Get a paginated list of tasks", description = "Retrieves tasks with advanced filtering options.")
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAnyAuthority('ADMIN', 'SUPERVISOR', 'DECISION_MAKER')")
|
||||
public ResponseEntity<List<TaskSummaryDTO>> getTasks(
|
||||
@Parameter(description = "Filter by task status") @RequestParam(required = false) TaskStatus status,
|
||||
@Parameter(description = "Filter by assignee ID") @RequestParam(required = false) Long assigneeId,
|
||||
@Parameter(description = "Filter by severity level") @RequestParam(required = false) SeverityLevel severity,
|
||||
@Parameter(description = "Filter by pollution type") @RequestParam(required = false) PollutionType pollutionType,
|
||||
@Parameter(description = "Start date for filtering tasks (format: yyyy-MM-dd)") @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
|
||||
@Parameter(description = "End date for filtering tasks (format: yyyy-MM-dd)") @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate,
|
||||
Pageable pageable
|
||||
) {
|
||||
Page<TaskSummaryDTO> tasks = taskManagementService.getTasks(status, assigneeId, severity, pollutionType, startDate, endDate, pageable);
|
||||
return ResponseEntity.ok(tasks.getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配任务给工作人员
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @param request 包含分配信息的请求体
|
||||
* @return 更新后的任务摘要
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
* @throws InvalidOperationException 如果任务已分配
|
||||
*/
|
||||
@PostMapping("/{taskId}/assign")
|
||||
public ResponseEntity<TaskSummaryDTO> assignTask(
|
||||
@PathVariable Long taskId,
|
||||
@RequestBody @Valid TaskAssignmentRequest request) {
|
||||
TaskSummaryDTO updatedTask = taskManagementService.assignTask(taskId, request);
|
||||
return ResponseEntity.ok(updatedTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务详情
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @return 任务详情DTO
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
*/
|
||||
@GetMapping("/{taskId}")
|
||||
public ResponseEntity<TaskDetailDTO> getTaskDetails(@PathVariable Long taskId) {
|
||||
TaskDetailDTO taskDetails = taskManagementService.getTaskDetails(taskId);
|
||||
return ResponseEntity.ok(taskDetails);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核任务
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @param request 包含审核信息的请求体
|
||||
* @return 更新后的任务详情
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
* @throws InvalidOperationException 如果任务状态不允许审核
|
||||
*/
|
||||
@PostMapping("/{taskId}/review")
|
||||
public ResponseEntity<TaskDetailDTO> reviewTask(
|
||||
@PathVariable Long taskId,
|
||||
@RequestBody @Valid TaskApprovalRequest request) {
|
||||
TaskDetailDTO updatedTask = taskManagementService.reviewTask(taskId, request);
|
||||
return ResponseEntity.ok(updatedTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消任务
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @return 更新后的任务详情
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
* @throws InvalidOperationException 如果任务状态不允许取消
|
||||
*/
|
||||
@PostMapping("/{taskId}/cancel")
|
||||
public ResponseEntity<TaskDetailDTO> cancelTask(@PathVariable Long taskId) {
|
||||
TaskDetailDTO updatedTask = taskManagementService.cancelTask(taskId);
|
||||
return ResponseEntity.ok(updatedTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新任务
|
||||
*
|
||||
* @param request 包含任务信息的请求体
|
||||
* @return 创建的任务详情
|
||||
* @throws InvalidRequestException 如果请求参数无效
|
||||
*/
|
||||
@PostMapping
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
public ResponseEntity<TaskDetailDTO> createTask(@RequestBody @Valid TaskCreationRequest request) {
|
||||
TaskDetailDTO createdTask = taskManagementService.createTask(request);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(createdTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待处理反馈列表
|
||||
*
|
||||
* @return 待处理反馈列表
|
||||
* @throws AccessDeniedException 如果用户无权限
|
||||
*/
|
||||
@GetMapping("/feedback")
|
||||
@PreAuthorize("hasAnyRole('SUPERVISOR', 'ADMIN')")
|
||||
public ResponseEntity<List<Feedback>> getPendingFeedback() {
|
||||
List<Feedback> feedbackList = taskManagementService.getPendingFeedback();
|
||||
return ResponseEntity.ok(feedbackList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从反馈创建任务
|
||||
*
|
||||
* @param feedbackId 反馈ID
|
||||
* @param request 包含任务信息的请求体
|
||||
* @return 创建的任务详情
|
||||
* @throws ResourceNotFoundException 如果反馈不存在
|
||||
* @throws InvalidOperationException 如果反馈已处理
|
||||
*/
|
||||
@PostMapping("/feedback/{feedbackId}/create-task")
|
||||
@PreAuthorize("hasRole('SUPERVISOR')")
|
||||
public ResponseEntity<TaskDetailDTO> createTaskFromFeedback(
|
||||
@PathVariable Long feedbackId,
|
||||
@RequestBody @Valid TaskFromFeedbackRequest request) {
|
||||
TaskDetailDTO createdTask = taskManagementService.createTaskFromFeedback(feedbackId, request);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(createdTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批准任务,将状态更新为"已完成"
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @return 更新后的任务详情
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
* @throws InvalidOperationException 如果任务状态不是"已提交"
|
||||
*/
|
||||
@PostMapping("/{taskId}/approve")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@Operation(summary = "Approve a task", description = "Approves a submitted task, changing its status to COMPLETED.")
|
||||
public ResponseEntity<TaskDetailDTO> approveTask(@PathVariable Long taskId) {
|
||||
TaskDetailDTO updatedTask = taskManagementService.approveTask(taskId);
|
||||
return ResponseEntity.ok(updatedTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拒绝任务,将状态更新为"进行中"
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @param request 包含拒绝理由的请求体
|
||||
* @return 更新后的任务详情
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
* @throws InvalidOperationException 如果任务状态不是"已提交"
|
||||
*/
|
||||
@PostMapping("/{taskId}/reject")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@Operation(summary = "Reject a task", description = "Rejects a submitted task, changing its status back to IN_PROGRESS.")
|
||||
public ResponseEntity<TaskDetailDTO> rejectTask(
|
||||
@PathVariable Long taskId,
|
||||
@RequestBody @Valid TaskRejectionRequest request) {
|
||||
TaskDetailDTO updatedTask = taskManagementService.rejectTask(taskId, request.getReason());
|
||||
return ResponseEntity.ok(updatedTask);
|
||||
}
|
||||
package com.dne.ems.controller;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.dne.ems.dto.TaskApprovalRequest;
|
||||
import com.dne.ems.dto.TaskAssignmentRequest;
|
||||
import com.dne.ems.dto.TaskCreationRequest;
|
||||
import com.dne.ems.dto.TaskDetailDTO;
|
||||
import com.dne.ems.dto.TaskFromFeedbackRequest;
|
||||
import com.dne.ems.dto.TaskRejectionRequest;
|
||||
import com.dne.ems.dto.TaskSummaryDTO;
|
||||
import com.dne.ems.model.Feedback;
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
import com.dne.ems.model.enums.TaskStatus;
|
||||
import com.dne.ems.service.TaskManagementService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 任务管理控制器,处理任务全生命周期管理
|
||||
*
|
||||
* <p>主要功能:
|
||||
* <ul>
|
||||
* <li>任务创建与分配</li>
|
||||
* <li>任务状态管理</li>
|
||||
* <li>任务审核与审批</li>
|
||||
* <li>从反馈创建任务</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/management/tasks")
|
||||
@RequiredArgsConstructor
|
||||
@PreAuthorize("hasRole('SUPERVISOR')")
|
||||
public class TaskManagementController {
|
||||
|
||||
private final TaskManagementService taskManagementService;
|
||||
|
||||
/**
|
||||
* 获取任务分页列表(支持多条件过滤)
|
||||
*
|
||||
* @param status 任务状态过滤条件
|
||||
* @param assigneeId 分配人ID过滤条件
|
||||
* @param severity 严重程度过滤条件
|
||||
* @param pollutionType 污染类型过滤条件
|
||||
* @param startDate 开始日期过滤条件(yyyy-MM-dd)
|
||||
* @param endDate 结束日期过滤条件(yyyy-MM-dd)
|
||||
* @param pageable 分页参数
|
||||
* @return 任务分页列表
|
||||
* @throws AccessDeniedException 如果用户无权限
|
||||
*/
|
||||
@Operation(summary = "Get a paginated list of tasks", description = "Retrieves tasks with advanced filtering options.")
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAnyAuthority('ADMIN', 'SUPERVISOR', 'DECISION_MAKER')")
|
||||
public ResponseEntity<List<TaskSummaryDTO>> getTasks(
|
||||
@Parameter(description = "Filter by task status") @RequestParam(required = false) TaskStatus status,
|
||||
@Parameter(description = "Filter by assignee ID") @RequestParam(required = false) Long assigneeId,
|
||||
@Parameter(description = "Filter by severity level") @RequestParam(required = false) SeverityLevel severity,
|
||||
@Parameter(description = "Filter by pollution type") @RequestParam(required = false) PollutionType pollutionType,
|
||||
@Parameter(description = "Start date for filtering tasks (format: yyyy-MM-dd)") @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
|
||||
@Parameter(description = "End date for filtering tasks (format: yyyy-MM-dd)") @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate,
|
||||
Pageable pageable
|
||||
) {
|
||||
Page<TaskSummaryDTO> tasks = taskManagementService.getTasks(status, assigneeId, severity, pollutionType, startDate, endDate, pageable);
|
||||
return ResponseEntity.ok(tasks.getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配任务给工作人员
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @param request 包含分配信息的请求体
|
||||
* @return 更新后的任务摘要
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
* @throws InvalidOperationException 如果任务已分配
|
||||
*/
|
||||
@PostMapping("/{taskId}/assign")
|
||||
public ResponseEntity<TaskSummaryDTO> assignTask(
|
||||
@PathVariable Long taskId,
|
||||
@RequestBody @Valid TaskAssignmentRequest request) {
|
||||
TaskSummaryDTO updatedTask = taskManagementService.assignTask(taskId, request);
|
||||
return ResponseEntity.ok(updatedTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务详情
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @return 任务详情DTO
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
*/
|
||||
@GetMapping("/{taskId}")
|
||||
public ResponseEntity<TaskDetailDTO> getTaskDetails(@PathVariable Long taskId) {
|
||||
TaskDetailDTO taskDetails = taskManagementService.getTaskDetails(taskId);
|
||||
return ResponseEntity.ok(taskDetails);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核任务
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @param request 包含审核信息的请求体
|
||||
* @return 更新后的任务详情
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
* @throws InvalidOperationException 如果任务状态不允许审核
|
||||
*/
|
||||
@PostMapping("/{taskId}/review")
|
||||
public ResponseEntity<TaskDetailDTO> reviewTask(
|
||||
@PathVariable Long taskId,
|
||||
@RequestBody @Valid TaskApprovalRequest request) {
|
||||
TaskDetailDTO updatedTask = taskManagementService.reviewTask(taskId, request);
|
||||
return ResponseEntity.ok(updatedTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消任务
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @return 更新后的任务详情
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
* @throws InvalidOperationException 如果任务状态不允许取消
|
||||
*/
|
||||
@PostMapping("/{taskId}/cancel")
|
||||
public ResponseEntity<TaskDetailDTO> cancelTask(@PathVariable Long taskId) {
|
||||
TaskDetailDTO updatedTask = taskManagementService.cancelTask(taskId);
|
||||
return ResponseEntity.ok(updatedTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新任务
|
||||
*
|
||||
* @param request 包含任务信息的请求体
|
||||
* @return 创建的任务详情
|
||||
* @throws InvalidRequestException 如果请求参数无效
|
||||
*/
|
||||
@PostMapping
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
public ResponseEntity<TaskDetailDTO> createTask(@RequestBody @Valid TaskCreationRequest request) {
|
||||
TaskDetailDTO createdTask = taskManagementService.createTask(request);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(createdTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待处理反馈列表
|
||||
*
|
||||
* @return 待处理反馈列表
|
||||
* @throws AccessDeniedException 如果用户无权限
|
||||
*/
|
||||
@GetMapping("/feedback")
|
||||
@PreAuthorize("hasAnyRole('SUPERVISOR', 'ADMIN')")
|
||||
public ResponseEntity<List<Feedback>> getPendingFeedback() {
|
||||
List<Feedback> feedbackList = taskManagementService.getPendingFeedback();
|
||||
return ResponseEntity.ok(feedbackList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从反馈创建任务
|
||||
*
|
||||
* @param feedbackId 反馈ID
|
||||
* @param request 包含任务信息的请求体
|
||||
* @return 创建的任务详情
|
||||
* @throws ResourceNotFoundException 如果反馈不存在
|
||||
* @throws InvalidOperationException 如果反馈已处理
|
||||
*/
|
||||
@PostMapping("/feedback/{feedbackId}/create-task")
|
||||
@PreAuthorize("hasRole('SUPERVISOR')")
|
||||
public ResponseEntity<TaskDetailDTO> createTaskFromFeedback(
|
||||
@PathVariable Long feedbackId,
|
||||
@RequestBody @Valid TaskFromFeedbackRequest request) {
|
||||
TaskDetailDTO createdTask = taskManagementService.createTaskFromFeedback(feedbackId, request);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(createdTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批准任务,将状态更新为"已完成"
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @return 更新后的任务详情
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
* @throws InvalidOperationException 如果任务状态不是"已提交"
|
||||
*/
|
||||
@PostMapping("/{taskId}/approve")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@Operation(summary = "Approve a task", description = "Approves a submitted task, changing its status to COMPLETED.")
|
||||
public ResponseEntity<TaskDetailDTO> approveTask(@PathVariable Long taskId) {
|
||||
TaskDetailDTO updatedTask = taskManagementService.approveTask(taskId);
|
||||
return ResponseEntity.ok(updatedTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拒绝任务,将状态更新为"进行中"
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @param request 包含拒绝理由的请求体
|
||||
* @return 更新后的任务详情
|
||||
* @throws ResourceNotFoundException 如果任务不存在
|
||||
* @throws InvalidOperationException 如果任务状态不是"已提交"
|
||||
*/
|
||||
@PostMapping("/{taskId}/reject")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@Operation(summary = "Reject a task", description = "Rejects a submitted task, changing its status back to IN_PROGRESS.")
|
||||
public ResponseEntity<TaskDetailDTO> rejectTask(
|
||||
@PathVariable Long taskId,
|
||||
@RequestBody @Valid TaskRejectionRequest request) {
|
||||
TaskDetailDTO updatedTask = taskManagementService.rejectTask(taskId, request.getReason());
|
||||
return ResponseEntity.ok(updatedTask);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,29 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* DTO for submitting AQI data from a grid worker.
|
||||
*
|
||||
* @param gridId The ID of the grid cell where data was recorded.
|
||||
* @param aqiValue The overall AQI value.
|
||||
* @param pm25 PM2.5 concentration.
|
||||
* @param pm10 PM10 concentration.
|
||||
* @param so2 SO2 concentration.
|
||||
* @param no2 NO2 concentration.
|
||||
* @param co CO concentration.
|
||||
* @param o3 O3 concentration.
|
||||
* @param primaryPollutant The main pollutant identified.
|
||||
*/
|
||||
public record AqiDataSubmissionRequest(
|
||||
@NotNull Long gridId,
|
||||
Integer aqiValue,
|
||||
Double pm25,
|
||||
Double pm10,
|
||||
Double so2,
|
||||
Double no2,
|
||||
Double co,
|
||||
Double o3,
|
||||
String primaryPollutant
|
||||
) {
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* DTO for submitting AQI data from a grid worker.
|
||||
*
|
||||
* @param gridId The ID of the grid cell where data was recorded.
|
||||
* @param aqiValue The overall AQI value.
|
||||
* @param pm25 PM2.5 concentration.
|
||||
* @param pm10 PM10 concentration.
|
||||
* @param so2 SO2 concentration.
|
||||
* @param no2 NO2 concentration.
|
||||
* @param co CO concentration.
|
||||
* @param o3 O3 concentration.
|
||||
* @param primaryPollutant The main pollutant identified.
|
||||
*/
|
||||
public record AqiDataSubmissionRequest(
|
||||
@NotNull Long gridId,
|
||||
Integer aqiValue,
|
||||
Double pm25,
|
||||
Double pm10,
|
||||
Double so2,
|
||||
Double no2,
|
||||
Double co,
|
||||
Double o3,
|
||||
String primaryPollutant
|
||||
) {
|
||||
}
|
||||
@@ -1,53 +1,53 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 空气质量指数分布数据传输对象
|
||||
*
|
||||
* <p>该类用于封装空气质量指数(AQI)的分布统计数据,包括不同等级的AQI及其对应的数量。
|
||||
* 主要用于仪表盘展示和数据分析报告。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Data
|
||||
public class AqiDistributionDTO {
|
||||
/**
|
||||
* AQI等级(如优、良、轻度污染等)
|
||||
*/
|
||||
private String level;
|
||||
|
||||
/**
|
||||
* 该等级的记录数量
|
||||
*/
|
||||
private long count;
|
||||
|
||||
/**
|
||||
* 默认构造函数,用于框架反序列化
|
||||
*/
|
||||
public AqiDistributionDTO() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 适用于Long类型计数的构造函数,通常由JPQL的COUNT查询返回
|
||||
*
|
||||
* @param level AQI等级
|
||||
* @param count 该等级的记录数量
|
||||
*/
|
||||
public AqiDistributionDTO(String level, Long count) {
|
||||
this.level = level;
|
||||
this.count = (count != null) ? count : 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
* 适用于JPQL查询的构造函数,提供对不同数字类型的灵活支持
|
||||
*
|
||||
* @param level AQI等级
|
||||
* @param count 该等级的记录数量(任意Number类型)
|
||||
*/
|
||||
public AqiDistributionDTO(String level, Number count) {
|
||||
this.level = level;
|
||||
this.count = (count != null) ? count.longValue() : 0L;
|
||||
}
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 空气质量指数分布数据传输对象
|
||||
*
|
||||
* <p>该类用于封装空气质量指数(AQI)的分布统计数据,包括不同等级的AQI及其对应的数量。
|
||||
* 主要用于仪表盘展示和数据分析报告。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Data
|
||||
public class AqiDistributionDTO {
|
||||
/**
|
||||
* AQI等级(如优、良、轻度污染等)
|
||||
*/
|
||||
private String level;
|
||||
|
||||
/**
|
||||
* 该等级的记录数量
|
||||
*/
|
||||
private long count;
|
||||
|
||||
/**
|
||||
* 默认构造函数,用于框架反序列化
|
||||
*/
|
||||
public AqiDistributionDTO() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 适用于Long类型计数的构造函数,通常由JPQL的COUNT查询返回
|
||||
*
|
||||
* @param level AQI等级
|
||||
* @param count 该等级的记录数量
|
||||
*/
|
||||
public AqiDistributionDTO(String level, Long count) {
|
||||
this.level = level;
|
||||
this.count = (count != null) ? count : 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
* 适用于JPQL查询的构造函数,提供对不同数字类型的灵活支持
|
||||
*
|
||||
* @param level AQI等级
|
||||
* @param count 该等级的记录数量(任意Number类型)
|
||||
*/
|
||||
public AqiDistributionDTO(String level, Number count) {
|
||||
this.level = level;
|
||||
this.count = (count != null) ? count.longValue() : 0L;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,34 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
/**
|
||||
* 空气质量指数热力图数据点传输对象
|
||||
*
|
||||
* <p>该类用于表示AQI热力图上的单个数据点,包含网格坐标和该网格的平均AQI值。
|
||||
* 主要用于生成热力图可视化展示。</p>
|
||||
*
|
||||
* @param gridX 网格X坐标
|
||||
* @param gridY 网格Y坐标
|
||||
* @param averageAqi 该网格的平均AQI值
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public record AqiHeatmapPointDTO(
|
||||
Integer gridX,
|
||||
Integer gridY,
|
||||
Double averageAqi
|
||||
) {
|
||||
/**
|
||||
* 辅助构造函数,用于处理从数据库查询返回的Number类型数据
|
||||
*
|
||||
* @param gridX 网格X坐标(Number类型)
|
||||
* @param gridY 网格Y坐标(Number类型)
|
||||
* @param averageAqi 平均AQI值(Number类型)
|
||||
*/
|
||||
public AqiHeatmapPointDTO(Number gridX, Number gridY, Number averageAqi) {
|
||||
this(
|
||||
gridX != null ? gridX.intValue() : null,
|
||||
gridY != null ? gridY.intValue() : null,
|
||||
averageAqi != null ? averageAqi.doubleValue() : null
|
||||
);
|
||||
}
|
||||
}
|
||||
package com.dne.ems.dto;
|
||||
|
||||
/**
|
||||
* 空气质量指数热力图数据点传输对象
|
||||
*
|
||||
* <p>该类用于表示AQI热力图上的单个数据点,包含网格坐标和该网格的平均AQI值。
|
||||
* 主要用于生成热力图可视化展示。</p>
|
||||
*
|
||||
* @param gridX 网格X坐标
|
||||
* @param gridY 网格Y坐标
|
||||
* @param averageAqi 该网格的平均AQI值
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public record AqiHeatmapPointDTO(
|
||||
Integer gridX,
|
||||
Integer gridY,
|
||||
Double averageAqi
|
||||
) {
|
||||
/**
|
||||
* 辅助构造函数,用于处理从数据库查询返回的Number类型数据
|
||||
*
|
||||
* @param gridX 网格X坐标(Number类型)
|
||||
* @param gridY 网格Y坐标(Number类型)
|
||||
* @param averageAqi 平均AQI值(Number类型)
|
||||
*/
|
||||
public AqiHeatmapPointDTO(Number gridX, Number gridY, Number averageAqi) {
|
||||
this(
|
||||
gridX != null ? gridX.intValue() : null,
|
||||
gridY != null ? gridY.intValue() : null,
|
||||
averageAqi != null ? averageAqi.doubleValue() : null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 任务受理人信息数据传输对象
|
||||
*
|
||||
* <p>该类用于封装任务受理人的基本信息,包括ID、姓名和联系电话。
|
||||
* 主要用于任务分配和展示受理人信息的场景。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AssigneeInfoDTO {
|
||||
/**
|
||||
* 受理人唯一标识符
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 受理人姓名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 受理人联系电话
|
||||
*/
|
||||
private String phone;
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 任务受理人信息数据传输对象
|
||||
*
|
||||
* <p>该类用于封装任务受理人的基本信息,包括ID、姓名和联系电话。
|
||||
* 主要用于任务分配和展示受理人信息的场景。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AssigneeInfoDTO {
|
||||
/**
|
||||
* 受理人唯一标识符
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 受理人姓名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 受理人联系电话
|
||||
*/
|
||||
private String phone;
|
||||
}
|
||||
@@ -1,34 +1,34 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.Attachment;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AttachmentDTO {
|
||||
private Long id;
|
||||
private String fileName;
|
||||
private String fileType;
|
||||
private String url;
|
||||
private LocalDateTime uploadedAt;
|
||||
|
||||
public static AttachmentDTO fromEntity(Attachment attachment) {
|
||||
if (attachment == null) {
|
||||
return null;
|
||||
}
|
||||
String url = "/api/files/" + attachment.getStoredFileName();
|
||||
|
||||
return new AttachmentDTO(
|
||||
attachment.getId(),
|
||||
attachment.getFileName(),
|
||||
attachment.getFileType(),
|
||||
url,
|
||||
attachment.getUploadDate()
|
||||
);
|
||||
}
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.Attachment;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AttachmentDTO {
|
||||
private Long id;
|
||||
private String fileName;
|
||||
private String fileType;
|
||||
private String url;
|
||||
private LocalDateTime uploadedAt;
|
||||
|
||||
public static AttachmentDTO fromEntity(Attachment attachment) {
|
||||
if (attachment == null) {
|
||||
return null;
|
||||
}
|
||||
String url = "/api/files/" + attachment.getStoredFileName();
|
||||
|
||||
return new AttachmentDTO(
|
||||
attachment.getId(),
|
||||
attachment.getFileName(),
|
||||
attachment.getFileType(),
|
||||
url,
|
||||
attachment.getUploadDate()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,26 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Represents the blueprint for generating a city within the grid.
|
||||
* Used during data initialization.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public class CityBlueprint {
|
||||
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private String cityName;
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private String districtName;
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private int startX;
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private int startY;
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private int width;
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private int height;
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Represents the blueprint for generating a city within the grid.
|
||||
* Used during data initialization.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public class CityBlueprint {
|
||||
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private String cityName;
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private String districtName;
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private int startX;
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private int startY;
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private int width;
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private int height;
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
/**
|
||||
* DTO for carrying key statistics for the main dashboard.
|
||||
*
|
||||
* @param totalFeedbacks Total number of feedback submissions.
|
||||
* @param confirmedFeedbacks Total number of feedback entries that have been confirmed.
|
||||
* @param totalAqiRecords Total number of AQI data records submitted.
|
||||
* @param activeGridWorkers Number of grid workers with an 'ACTIVE' status.
|
||||
*/
|
||||
public record DashboardStatsDTO(
|
||||
Long totalFeedbacks,
|
||||
Long confirmedFeedbacks,
|
||||
Long totalAqiRecords,
|
||||
Long activeGridWorkers
|
||||
) {
|
||||
package com.dne.ems.dto;
|
||||
|
||||
/**
|
||||
* DTO for carrying key statistics for the main dashboard.
|
||||
*
|
||||
* @param totalFeedbacks Total number of feedback submissions.
|
||||
* @param confirmedFeedbacks Total number of feedback entries that have been confirmed.
|
||||
* @param totalAqiRecords Total number of AQI data records submitted.
|
||||
* @param activeGridWorkers Number of grid workers with an 'ACTIVE' status.
|
||||
*/
|
||||
public record DashboardStatsDTO(
|
||||
Long totalFeedbacks,
|
||||
Long confirmedFeedbacks,
|
||||
Long totalAqiRecords,
|
||||
Long activeGridWorkers
|
||||
) {
|
||||
}
|
||||
@@ -1,51 +1,51 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 错误响应数据传输对象
|
||||
*
|
||||
* <p>该类用于封装API错误响应的标准格式,包含错误发生的时间戳、HTTP状态码、
|
||||
* 错误类型、错误消息以及请求路径等信息。主要用于统一的异常处理和客户端错误提示。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025-06
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ErrorResponseDTO {
|
||||
|
||||
/**
|
||||
* 错误发生的时间戳
|
||||
*/
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss")
|
||||
private LocalDateTime timestamp;
|
||||
|
||||
/**
|
||||
* HTTP状态码
|
||||
*/
|
||||
private int status;
|
||||
|
||||
/**
|
||||
* 错误类型
|
||||
*/
|
||||
private String error;
|
||||
|
||||
/**
|
||||
* 错误详细信息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 发生错误的请求路径
|
||||
*/
|
||||
private String path;
|
||||
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 错误响应数据传输对象
|
||||
*
|
||||
* <p>该类用于封装API错误响应的标准格式,包含错误发生的时间戳、HTTP状态码、
|
||||
* 错误类型、错误消息以及请求路径等信息。主要用于统一的异常处理和客户端错误提示。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025-06
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ErrorResponseDTO {
|
||||
|
||||
/**
|
||||
* 错误发生的时间戳
|
||||
*/
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss")
|
||||
private LocalDateTime timestamp;
|
||||
|
||||
/**
|
||||
* HTTP状态码
|
||||
*/
|
||||
private int status;
|
||||
|
||||
/**
|
||||
* 错误类型
|
||||
*/
|
||||
private String error;
|
||||
|
||||
/**
|
||||
* 错误详细信息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 发生错误的请求路径
|
||||
*/
|
||||
private String path;
|
||||
|
||||
}
|
||||
@@ -1,51 +1,51 @@
|
||||
/**
|
||||
* 反馈数据传输对象
|
||||
*
|
||||
* <p>用于在服务层和表示层之间传递反馈信息,
|
||||
* 包含反馈的基本信息和关联数据。
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import com.dne.ems.model.enums.FeedbackStatus;
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 反馈数据传输对象
|
||||
*
|
||||
* <p>包含反馈的完整信息,用于展示和传输
|
||||
*/
|
||||
@Data
|
||||
public class FeedbackDTO {
|
||||
/**
|
||||
* 反馈ID,唯一标识
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 反馈标题
|
||||
*/
|
||||
private String title;
|
||||
private String description;
|
||||
private PollutionType pollutionType;
|
||||
private SeverityLevel severityLevel;
|
||||
private Double latitude;
|
||||
private Double longitude;
|
||||
private String textAddress;
|
||||
private Integer gridX;
|
||||
private Integer gridY;
|
||||
private String eventId;
|
||||
private FeedbackStatus status;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
private Long submitterId;
|
||||
private List<String> attachmentUrls;
|
||||
/**
|
||||
* 反馈数据传输对象
|
||||
*
|
||||
* <p>用于在服务层和表示层之间传递反馈信息,
|
||||
* 包含反馈的基本信息和关联数据。
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import com.dne.ems.model.enums.FeedbackStatus;
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 反馈数据传输对象
|
||||
*
|
||||
* <p>包含反馈的完整信息,用于展示和传输
|
||||
*/
|
||||
@Data
|
||||
public class FeedbackDTO {
|
||||
/**
|
||||
* 反馈ID,唯一标识
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 反馈标题
|
||||
*/
|
||||
private String title;
|
||||
private String description;
|
||||
private PollutionType pollutionType;
|
||||
private SeverityLevel severityLevel;
|
||||
private Double latitude;
|
||||
private Double longitude;
|
||||
private String textAddress;
|
||||
private Integer gridX;
|
||||
private Integer gridY;
|
||||
private String eventId;
|
||||
private FeedbackStatus status;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
private Long submitterId;
|
||||
private List<String> attachmentUrls;
|
||||
}
|
||||
@@ -1,151 +1,151 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.Feedback;
|
||||
import com.dne.ems.model.enums.FeedbackStatus;
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 反馈响应数据传输对象
|
||||
*
|
||||
* <p>该类用于封装反馈信息的响应数据,包含反馈的详细信息、状态、位置信息、
|
||||
* 提交者信息、关联任务以及附件信息等。主要用于前端展示和API响应。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025-06
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FeedbackResponseDTO {
|
||||
|
||||
/**
|
||||
* 反馈记录的唯一标识符
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 事件唯一标识符
|
||||
*/
|
||||
private String eventId;
|
||||
|
||||
/**
|
||||
* 反馈标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 反馈详细描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 污染类型
|
||||
*/
|
||||
private PollutionType pollutionType;
|
||||
|
||||
/**
|
||||
* 严重程度级别
|
||||
*/
|
||||
private SeverityLevel severityLevel;
|
||||
|
||||
/**
|
||||
* 反馈当前状态
|
||||
*/
|
||||
private FeedbackStatus status;
|
||||
|
||||
/**
|
||||
* 文本地址描述
|
||||
*/
|
||||
private String textAddress;
|
||||
|
||||
/**
|
||||
* 网格X坐标
|
||||
*/
|
||||
private Integer gridX;
|
||||
|
||||
/**
|
||||
* 网格Y坐标
|
||||
*/
|
||||
private Integer gridY;
|
||||
|
||||
/**
|
||||
* 地理纬度
|
||||
*/
|
||||
private Double latitude;
|
||||
|
||||
/**
|
||||
* 地理经度
|
||||
*/
|
||||
private Double longitude;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 最后更新时间
|
||||
*/
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 提交者ID
|
||||
*/
|
||||
private Long submitterId;
|
||||
|
||||
/**
|
||||
* 提交用户信息
|
||||
*/
|
||||
private UserInfoDTO user;
|
||||
|
||||
/**
|
||||
* 关联任务详情
|
||||
*/
|
||||
private TaskDetailDTO task;
|
||||
|
||||
/**
|
||||
* 附件列表
|
||||
*/
|
||||
private List<AttachmentDTO> attachments;
|
||||
|
||||
public static FeedbackResponseDTO fromEntity(Feedback feedback, TaskDetailDTO taskDetail) {
|
||||
FeedbackResponseDTO dto = new FeedbackResponseDTO();
|
||||
dto.setId(feedback.getId());
|
||||
dto.setEventId(feedback.getEventId());
|
||||
dto.setTitle(feedback.getTitle());
|
||||
dto.setDescription(feedback.getDescription());
|
||||
dto.setPollutionType(feedback.getPollutionType());
|
||||
dto.setSeverityLevel(feedback.getSeverityLevel());
|
||||
dto.setStatus(feedback.getStatus());
|
||||
dto.setTextAddress(feedback.getTextAddress());
|
||||
dto.setGridX(feedback.getGridX());
|
||||
dto.setGridY(feedback.getGridY());
|
||||
dto.setLatitude(feedback.getLatitude());
|
||||
dto.setLongitude(feedback.getLongitude());
|
||||
dto.setCreatedAt(feedback.getCreatedAt());
|
||||
dto.setUpdatedAt(feedback.getUpdatedAt());
|
||||
dto.setSubmitterId(feedback.getSubmitterId());
|
||||
|
||||
if (feedback.getUser() != null) {
|
||||
dto.setUser(UserInfoDTO.fromEntity(feedback.getUser()));
|
||||
}
|
||||
|
||||
if (feedback.getAttachments() != null) {
|
||||
dto.setAttachments(feedback.getAttachments().stream()
|
||||
.map(AttachmentDTO::fromEntity)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
dto.setTask(taskDetail);
|
||||
|
||||
return dto;
|
||||
}
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.Feedback;
|
||||
import com.dne.ems.model.enums.FeedbackStatus;
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 反馈响应数据传输对象
|
||||
*
|
||||
* <p>该类用于封装反馈信息的响应数据,包含反馈的详细信息、状态、位置信息、
|
||||
* 提交者信息、关联任务以及附件信息等。主要用于前端展示和API响应。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025-06
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FeedbackResponseDTO {
|
||||
|
||||
/**
|
||||
* 反馈记录的唯一标识符
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 事件唯一标识符
|
||||
*/
|
||||
private String eventId;
|
||||
|
||||
/**
|
||||
* 反馈标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 反馈详细描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 污染类型
|
||||
*/
|
||||
private PollutionType pollutionType;
|
||||
|
||||
/**
|
||||
* 严重程度级别
|
||||
*/
|
||||
private SeverityLevel severityLevel;
|
||||
|
||||
/**
|
||||
* 反馈当前状态
|
||||
*/
|
||||
private FeedbackStatus status;
|
||||
|
||||
/**
|
||||
* 文本地址描述
|
||||
*/
|
||||
private String textAddress;
|
||||
|
||||
/**
|
||||
* 网格X坐标
|
||||
*/
|
||||
private Integer gridX;
|
||||
|
||||
/**
|
||||
* 网格Y坐标
|
||||
*/
|
||||
private Integer gridY;
|
||||
|
||||
/**
|
||||
* 地理纬度
|
||||
*/
|
||||
private Double latitude;
|
||||
|
||||
/**
|
||||
* 地理经度
|
||||
*/
|
||||
private Double longitude;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 最后更新时间
|
||||
*/
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
/**
|
||||
* 提交者ID
|
||||
*/
|
||||
private Long submitterId;
|
||||
|
||||
/**
|
||||
* 提交用户信息
|
||||
*/
|
||||
private UserInfoDTO user;
|
||||
|
||||
/**
|
||||
* 关联任务详情
|
||||
*/
|
||||
private TaskDetailDTO task;
|
||||
|
||||
/**
|
||||
* 附件列表
|
||||
*/
|
||||
private List<AttachmentDTO> attachments;
|
||||
|
||||
public static FeedbackResponseDTO fromEntity(Feedback feedback, TaskDetailDTO taskDetail) {
|
||||
FeedbackResponseDTO dto = new FeedbackResponseDTO();
|
||||
dto.setId(feedback.getId());
|
||||
dto.setEventId(feedback.getEventId());
|
||||
dto.setTitle(feedback.getTitle());
|
||||
dto.setDescription(feedback.getDescription());
|
||||
dto.setPollutionType(feedback.getPollutionType());
|
||||
dto.setSeverityLevel(feedback.getSeverityLevel());
|
||||
dto.setStatus(feedback.getStatus());
|
||||
dto.setTextAddress(feedback.getTextAddress());
|
||||
dto.setGridX(feedback.getGridX());
|
||||
dto.setGridY(feedback.getGridY());
|
||||
dto.setLatitude(feedback.getLatitude());
|
||||
dto.setLongitude(feedback.getLongitude());
|
||||
dto.setCreatedAt(feedback.getCreatedAt());
|
||||
dto.setUpdatedAt(feedback.getUpdatedAt());
|
||||
dto.setSubmitterId(feedback.getSubmitterId());
|
||||
|
||||
if (feedback.getUser() != null) {
|
||||
dto.setUser(UserInfoDTO.fromEntity(feedback.getUser()));
|
||||
}
|
||||
|
||||
if (feedback.getAttachments() != null) {
|
||||
dto.setAttachments(feedback.getAttachments().stream()
|
||||
.map(AttachmentDTO::fromEntity)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
dto.setTask(taskDetail);
|
||||
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +1,56 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 反馈统计响应DTO
|
||||
*
|
||||
* <p>包含系统反馈数据的统计信息,用于仪表盘展示
|
||||
*
|
||||
* <p>统计指标说明:
|
||||
* <ul>
|
||||
* <li>total - 总反馈数</li>
|
||||
* <li>pending - 待处理总数</li>
|
||||
* <li>confirmed - 已确认数</li>
|
||||
* <li>assigned - 已分配数</li>
|
||||
* <li>inProgress - 处理中数</li>
|
||||
* <li>resolved - 已解决数</li>
|
||||
* <li>closed - 已关闭数</li>
|
||||
* <li>rejected - 已拒绝数</li>
|
||||
* <li>pendingReview - 待审核数</li>
|
||||
* <li>pendingAssignment - 待分配数</li>
|
||||
* <li>aiReviewing - AI审核中数</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FeedbackStatsResponse {
|
||||
/**
|
||||
* 总反馈数量
|
||||
*/
|
||||
private int total;
|
||||
/**
|
||||
* 待处理反馈总数(包含所有待处理状态)
|
||||
*/
|
||||
private int pending;
|
||||
private int confirmed;
|
||||
private int assigned;
|
||||
private int inProgress;
|
||||
private int resolved;
|
||||
private int closed;
|
||||
private int rejected;
|
||||
|
||||
// 待分配和待审核的特殊计数
|
||||
private int pendingReview;
|
||||
private int pendingAssignment;
|
||||
private int aiReviewing;
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 反馈统计响应DTO
|
||||
*
|
||||
* <p>包含系统反馈数据的统计信息,用于仪表盘展示
|
||||
*
|
||||
* <p>统计指标说明:
|
||||
* <ul>
|
||||
* <li>total - 总反馈数</li>
|
||||
* <li>pending - 待处理总数</li>
|
||||
* <li>confirmed - 已确认数</li>
|
||||
* <li>assigned - 已分配数</li>
|
||||
* <li>inProgress - 处理中数</li>
|
||||
* <li>resolved - 已解决数</li>
|
||||
* <li>closed - 已关闭数</li>
|
||||
* <li>rejected - 已拒绝数</li>
|
||||
* <li>pendingReview - 待审核数</li>
|
||||
* <li>pendingAssignment - 待分配数</li>
|
||||
* <li>aiReviewing - AI审核中数</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FeedbackStatsResponse {
|
||||
/**
|
||||
* 总反馈数量
|
||||
*/
|
||||
private int total;
|
||||
/**
|
||||
* 待处理反馈总数(包含所有待处理状态)
|
||||
*/
|
||||
private int pending;
|
||||
private int confirmed;
|
||||
private int assigned;
|
||||
private int inProgress;
|
||||
private int resolved;
|
||||
private int closed;
|
||||
private int rejected;
|
||||
|
||||
// 待分配和待审核的特殊计数
|
||||
private int pendingReview;
|
||||
private int pendingAssignment;
|
||||
private int aiReviewing;
|
||||
}
|
||||
@@ -1,78 +1,78 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 公众反馈提交请求数据传输对象
|
||||
*
|
||||
* <p>用于接收公众用户提交的环境问题反馈,包含问题描述、分类和位置信息
|
||||
*
|
||||
* <p>业务规则:
|
||||
* <ul>
|
||||
* <li>提交后状态自动设为PENDING_REVIEW(待审核)</li>
|
||||
* <li>需经过主管审核后才能创建任务</li>
|
||||
* <li>位置信息必须包含有效的网格坐标</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param title 反馈标题(必填,最长255字符)
|
||||
* @param description 问题详细描述(可选)
|
||||
* @param pollutionType 污染类型(必填)
|
||||
* @param severityLevel 严重等级(必填)
|
||||
* @param location 地理位置信息(必填)
|
||||
* @see com.dne.ems.model.enums.PollutionType 污染类型枚举
|
||||
* @see com.dne.ems.model.enums.SeverityLevel 严重级别枚举
|
||||
*/
|
||||
public record FeedbackSubmissionRequest(
|
||||
@NotEmpty(message = "标题不能为空")
|
||||
@Size(max = 255, message = "标题长度不能超过255个字符")
|
||||
String title,
|
||||
|
||||
String description,
|
||||
|
||||
@NotNull(message = "污染类型不能为空")
|
||||
PollutionType pollutionType,
|
||||
|
||||
@NotNull(message = "严重等级不能为空")
|
||||
SeverityLevel severityLevel,
|
||||
|
||||
@NotNull(message = "地理位置信息不能为空")
|
||||
@Valid
|
||||
LocationData location
|
||||
) {
|
||||
/**
|
||||
* 地理位置数据(嵌套记录)
|
||||
*
|
||||
* <p>用于精确定位反馈问题的发生位置
|
||||
*
|
||||
* <p>验证规则:
|
||||
* <ul>
|
||||
* <li>文字地址必须符合"省-市-区"格式</li>
|
||||
* <li>网格坐标必须在有效范围内(1-1000)</li>
|
||||
* <li>经纬度坐标可选,但提供时可提高定位精度</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param latitude 纬度坐标(可选)
|
||||
* @param longitude 经度坐标(可选)
|
||||
* @param textAddress 文字地址(必填,格式:"省-市-区")
|
||||
* @param gridX 网格X坐标(必填,范围1-1000)
|
||||
* @param gridY 网格Y坐标(必填,范围1-1000)
|
||||
*/
|
||||
public record LocationData(
|
||||
Double latitude,
|
||||
Double longitude,
|
||||
@NotEmpty(message = "文字地址不能为空")
|
||||
String textAddress,
|
||||
|
||||
@NotNull(message = "网格X坐标不能为空")
|
||||
Integer gridX,
|
||||
|
||||
@NotNull(message = "网格Y坐标不能为空")
|
||||
Integer gridY
|
||||
) {}
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 公众反馈提交请求数据传输对象
|
||||
*
|
||||
* <p>用于接收公众用户提交的环境问题反馈,包含问题描述、分类和位置信息
|
||||
*
|
||||
* <p>业务规则:
|
||||
* <ul>
|
||||
* <li>提交后状态自动设为PENDING_REVIEW(待审核)</li>
|
||||
* <li>需经过主管审核后才能创建任务</li>
|
||||
* <li>位置信息必须包含有效的网格坐标</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param title 反馈标题(必填,最长255字符)
|
||||
* @param description 问题详细描述(可选)
|
||||
* @param pollutionType 污染类型(必填)
|
||||
* @param severityLevel 严重等级(必填)
|
||||
* @param location 地理位置信息(必填)
|
||||
* @see com.dne.ems.model.enums.PollutionType 污染类型枚举
|
||||
* @see com.dne.ems.model.enums.SeverityLevel 严重级别枚举
|
||||
*/
|
||||
public record FeedbackSubmissionRequest(
|
||||
@NotEmpty(message = "标题不能为空")
|
||||
@Size(max = 255, message = "标题长度不能超过255个字符")
|
||||
String title,
|
||||
|
||||
String description,
|
||||
|
||||
@NotNull(message = "污染类型不能为空")
|
||||
PollutionType pollutionType,
|
||||
|
||||
@NotNull(message = "严重等级不能为空")
|
||||
SeverityLevel severityLevel,
|
||||
|
||||
@NotNull(message = "地理位置信息不能为空")
|
||||
@Valid
|
||||
LocationData location
|
||||
) {
|
||||
/**
|
||||
* 地理位置数据(嵌套记录)
|
||||
*
|
||||
* <p>用于精确定位反馈问题的发生位置
|
||||
*
|
||||
* <p>验证规则:
|
||||
* <ul>
|
||||
* <li>文字地址必须符合"省-市-区"格式</li>
|
||||
* <li>网格坐标必须在有效范围内(1-1000)</li>
|
||||
* <li>经纬度坐标可选,但提供时可提高定位精度</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param latitude 纬度坐标(可选)
|
||||
* @param longitude 经度坐标(可选)
|
||||
* @param textAddress 文字地址(必填,格式:"省-市-区")
|
||||
* @param gridX 网格X坐标(必填,范围1-1000)
|
||||
* @param gridY 网格Y坐标(必填,范围1-1000)
|
||||
*/
|
||||
public record LocationData(
|
||||
Double latitude,
|
||||
Double longitude,
|
||||
@NotEmpty(message = "文字地址不能为空")
|
||||
String textAddress,
|
||||
|
||||
@NotNull(message = "网格X坐标不能为空")
|
||||
Integer gridX,
|
||||
|
||||
@NotNull(message = "网格Y坐标不能为空")
|
||||
Integer gridY
|
||||
) {}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 网格员分配请求的数据传输对象 (DTO)。
|
||||
* <p>
|
||||
* 用于封装将用户分配到特定网格时所需的 `userId`。
|
||||
*/
|
||||
@Data
|
||||
public class GridAssignmentRequest {
|
||||
|
||||
/**
|
||||
* 要分配的用户的ID。
|
||||
*/
|
||||
@NotNull(message = "用户ID不能为空")
|
||||
private Long userId;
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 网格员分配请求的数据传输对象 (DTO)。
|
||||
* <p>
|
||||
* 用于封装将用户分配到特定网格时所需的 `userId`。
|
||||
*/
|
||||
@Data
|
||||
public class GridAssignmentRequest {
|
||||
|
||||
/**
|
||||
* 要分配的用户的ID。
|
||||
*/
|
||||
@NotNull(message = "用户ID不能为空")
|
||||
private Long userId;
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
/**
|
||||
* DTO for representing grid coverage statistics for a specific area.
|
||||
*/
|
||||
public record GridCoverageDTO(
|
||||
String city, // 城市名称
|
||||
Long coveredGrids, // 已覆盖的网格数量
|
||||
Long totalGrids, // 总网格数量
|
||||
Double coverageRate // 覆盖率
|
||||
) {
|
||||
/**
|
||||
* 原始构造函数,用于兼容现有查询
|
||||
* @param areaName 区域名称
|
||||
* @param gridCount 网格数量
|
||||
*/
|
||||
public GridCoverageDTO(String areaName, Long gridCount) {
|
||||
this(areaName, gridCount, gridCount * 2, gridCount > 0 ? (double) gridCount / (gridCount * 2) * 100 : 0.0);
|
||||
}
|
||||
package com.dne.ems.dto;
|
||||
|
||||
/**
|
||||
* DTO for representing grid coverage statistics for a specific area.
|
||||
*/
|
||||
public record GridCoverageDTO(
|
||||
String city, // 城市名称
|
||||
Long coveredGrids, // 已覆盖的网格数量
|
||||
Long totalGrids, // 总网格数量
|
||||
Double coverageRate // 覆盖率
|
||||
) {
|
||||
/**
|
||||
* 原始构造函数,用于兼容现有查询
|
||||
* @param areaName 区域名称
|
||||
* @param gridCount 网格数量
|
||||
*/
|
||||
public GridCoverageDTO(String areaName, Long gridCount) {
|
||||
this(areaName, gridCount, gridCount * 2, gridCount > 0 ? (double) gridCount / (gridCount * 2) * 100 : 0.0);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* DTO for updating a grid's properties.
|
||||
*
|
||||
* @param isObstacle The new obstacle status for the grid.
|
||||
* @param description The new description for the grid.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class GridUpdateRequest {
|
||||
|
||||
@NotNull(message = "Obstacle status cannot be null")
|
||||
private Boolean isObstacle;
|
||||
private String description;
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* DTO for updating a grid's properties.
|
||||
*
|
||||
* @param isObstacle The new obstacle status for the grid.
|
||||
* @param description The new description for the grid.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class GridUpdateRequest {
|
||||
|
||||
@NotNull(message = "Obstacle status cannot be null")
|
||||
private Boolean isObstacle;
|
||||
private String description;
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
/**
|
||||
* 热力图数据点传输对象
|
||||
*
|
||||
* <p>该类用于表示热力图上的单个数据点,包含网格坐标和该点的强度值。
|
||||
* 主要用于生成各类热力图可视化展示,如反馈密度、任务分布等。</p>
|
||||
*
|
||||
* @param gridX 网格X坐标
|
||||
* @param gridY 网格Y坐标
|
||||
* @param intensity 该点的强度值(如反馈事件数量、任务数量等)
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public record HeatmapPointDTO(
|
||||
Integer gridX,
|
||||
Integer gridY,
|
||||
long intensity
|
||||
) {
|
||||
package com.dne.ems.dto;
|
||||
|
||||
/**
|
||||
* 热力图数据点传输对象
|
||||
*
|
||||
* <p>该类用于表示热力图上的单个数据点,包含网格坐标和该点的强度值。
|
||||
* 主要用于生成各类热力图可视化展示,如反馈密度、任务分布等。</p>
|
||||
*
|
||||
* @param gridX 网格X坐标
|
||||
* @param gridY 网格Y坐标
|
||||
* @param intensity 该点的强度值(如反馈事件数量、任务数量等)
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public record HeatmapPointDTO(
|
||||
Integer gridX,
|
||||
Integer gridY,
|
||||
long intensity
|
||||
) {
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
/**
|
||||
* JWT认证响应数据传输对象
|
||||
*
|
||||
* <p>该类用于封装认证成功后返回给客户端的JWT令牌信息。
|
||||
* 主要用于用户登录或令牌刷新操作的响应数据。</p>
|
||||
*
|
||||
* @param accessToken JWT访问令牌,用于后续请求的身份验证
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public record JwtAuthenticationResponse(
|
||||
String accessToken
|
||||
package com.dne.ems.dto;
|
||||
|
||||
/**
|
||||
* JWT认证响应数据传输对象
|
||||
*
|
||||
* <p>该类用于封装认证成功后返回给客户端的JWT令牌信息。
|
||||
* 主要用于用户登录或令牌刷新操作的响应数据。</p>
|
||||
*
|
||||
* @param accessToken JWT访问令牌,用于后续请求的身份验证
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public record JwtAuthenticationResponse(
|
||||
String accessToken
|
||||
) {}
|
||||
@@ -1,34 +1,34 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 位置更新请求数据传输对象
|
||||
*
|
||||
* <p>用于接收用户位置更新请求,包含经纬度坐标信息。
|
||||
* 主要用于网格员上报当前位置或移动设备用户更新位置信息。</p>
|
||||
*
|
||||
* <p>验证规则:
|
||||
* <ul>
|
||||
* <li>纬度范围必须在-90到90度之间</li>
|
||||
* <li>经度范围必须在-180到180度之间</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param latitude 纬度坐标,有效范围-90到90度
|
||||
* @param longitude 经度坐标,有效范围-180到180度
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public record LocationUpdateRequest(
|
||||
@NotNull(message = "Latitude cannot be null")
|
||||
@Min(value = -90, message = "Latitude must be between -90 and 90")
|
||||
@Max(value = 90, message = "Latitude must be between -90 and 90")
|
||||
Double latitude,
|
||||
|
||||
@NotNull(message = "Longitude cannot be null")
|
||||
@Min(value = -180, message = "Longitude must be between -180 and 180")
|
||||
@Max(value = 180, message = "Longitude must be between -180 and 180")
|
||||
Double longitude
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 位置更新请求数据传输对象
|
||||
*
|
||||
* <p>用于接收用户位置更新请求,包含经纬度坐标信息。
|
||||
* 主要用于网格员上报当前位置或移动设备用户更新位置信息。</p>
|
||||
*
|
||||
* <p>验证规则:
|
||||
* <ul>
|
||||
* <li>纬度范围必须在-90到90度之间</li>
|
||||
* <li>经度范围必须在-180到180度之间</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param latitude 纬度坐标,有效范围-90到90度
|
||||
* @param longitude 经度坐标,有效范围-180到180度
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public record LocationUpdateRequest(
|
||||
@NotNull(message = "Latitude cannot be null")
|
||||
@Min(value = -90, message = "Latitude must be between -90 and 90")
|
||||
@Max(value = 90, message = "Latitude must be between -90 and 90")
|
||||
Double latitude,
|
||||
|
||||
@NotNull(message = "Longitude cannot be null")
|
||||
@Min(value = -180, message = "Longitude must be between -180 and 180")
|
||||
@Max(value = 180, message = "Longitude must be between -180 and 180")
|
||||
Double longitude
|
||||
) {}
|
||||
@@ -1,30 +1,30 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
/**
|
||||
* 登录请求数据传输对象
|
||||
*
|
||||
* <p>该类用于封装用户登录请求的数据,包含邮箱和密码信息。
|
||||
* 主要用于用户认证流程中的登录请求处理。</p>
|
||||
*
|
||||
* <p>验证规则:
|
||||
* <ul>
|
||||
* <li>邮箱不能为空且必须符合邮箱格式</li>
|
||||
* <li>密码不能为空</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param email 用户邮箱,作为登录标识
|
||||
* @param password 用户密码,用于验证身份
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public record LoginRequest(
|
||||
@NotEmpty(message = "Email cannot be empty.")
|
||||
@Email(message = "Invalid email format.")
|
||||
String email,
|
||||
|
||||
@NotEmpty(message = "Password cannot be empty.")
|
||||
String password
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
/**
|
||||
* 登录请求数据传输对象
|
||||
*
|
||||
* <p>该类用于封装用户登录请求的数据,包含邮箱和密码信息。
|
||||
* 主要用于用户认证流程中的登录请求处理。</p>
|
||||
*
|
||||
* <p>验证规则:
|
||||
* <ul>
|
||||
* <li>邮箱不能为空且必须符合邮箱格式</li>
|
||||
* <li>密码不能为空</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param email 用户邮箱,作为登录标识
|
||||
* @param password 用户密码,用于验证身份
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public record LoginRequest(
|
||||
@NotEmpty(message = "Email cannot be empty.")
|
||||
@Email(message = "Invalid email format.")
|
||||
String email,
|
||||
|
||||
@NotEmpty(message = "Password cannot be empty.")
|
||||
String password
|
||||
) {}
|
||||
@@ -1,101 +1,101 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.dne.ems.model.enums.OperationType;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 操作日志数据传输对象,用于前端展示用户操作记录。
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025-06-20
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class OperationLogDTO {
|
||||
/**
|
||||
* 操作日志ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 用户名称
|
||||
*/
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* 操作类型
|
||||
*/
|
||||
private OperationType operationType;
|
||||
|
||||
/**
|
||||
* 操作类型的中文描述
|
||||
*/
|
||||
private String operationTypeDesc;
|
||||
|
||||
/**
|
||||
* 操作描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 操作对象ID
|
||||
*/
|
||||
private String targetId;
|
||||
|
||||
/**
|
||||
* 操作对象类型
|
||||
*/
|
||||
private String targetType;
|
||||
|
||||
/**
|
||||
* 客户端IP地址
|
||||
*/
|
||||
private String ipAddress;
|
||||
|
||||
/**
|
||||
* 操作时间
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 根据操作类型获取中文描述
|
||||
*
|
||||
* @param operationType 操作类型
|
||||
* @return 中文描述
|
||||
*/
|
||||
public static String getOperationTypeDesc(OperationType operationType) {
|
||||
if (operationType == null) {
|
||||
return "未知操作";
|
||||
}
|
||||
|
||||
return switch (operationType) {
|
||||
case LOGIN -> "登录";
|
||||
case LOGOUT -> "登出";
|
||||
case CREATE -> "创建";
|
||||
case UPDATE -> "更新";
|
||||
case DELETE -> "删除";
|
||||
case ASSIGN_TASK -> "分配任务";
|
||||
case SUBMIT_TASK -> "提交任务";
|
||||
case APPROVE_TASK -> "审批任务";
|
||||
case REJECT_TASK -> "拒绝任务";
|
||||
case SUBMIT_FEEDBACK -> "提交反馈";
|
||||
case APPROVE_FEEDBACK -> "审批反馈";
|
||||
case REJECT_FEEDBACK -> "拒绝反馈";
|
||||
case OTHER -> "其他操作";
|
||||
default -> "未知操作";
|
||||
};
|
||||
}
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.dne.ems.model.enums.OperationType;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 操作日志数据传输对象,用于前端展示用户操作记录。
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025-06-20
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class OperationLogDTO {
|
||||
/**
|
||||
* 操作日志ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 用户名称
|
||||
*/
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* 操作类型
|
||||
*/
|
||||
private OperationType operationType;
|
||||
|
||||
/**
|
||||
* 操作类型的中文描述
|
||||
*/
|
||||
private String operationTypeDesc;
|
||||
|
||||
/**
|
||||
* 操作描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 操作对象ID
|
||||
*/
|
||||
private String targetId;
|
||||
|
||||
/**
|
||||
* 操作对象类型
|
||||
*/
|
||||
private String targetType;
|
||||
|
||||
/**
|
||||
* 客户端IP地址
|
||||
*/
|
||||
private String ipAddress;
|
||||
|
||||
/**
|
||||
* 操作时间
|
||||
*/
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 根据操作类型获取中文描述
|
||||
*
|
||||
* @param operationType 操作类型
|
||||
* @return 中文描述
|
||||
*/
|
||||
public static String getOperationTypeDesc(OperationType operationType) {
|
||||
if (operationType == null) {
|
||||
return "未知操作";
|
||||
}
|
||||
|
||||
return switch (operationType) {
|
||||
case LOGIN -> "登录";
|
||||
case LOGOUT -> "登出";
|
||||
case CREATE -> "创建";
|
||||
case UPDATE -> "更新";
|
||||
case DELETE -> "删除";
|
||||
case ASSIGN_TASK -> "分配任务";
|
||||
case SUBMIT_TASK -> "提交任务";
|
||||
case APPROVE_TASK -> "审批任务";
|
||||
case REJECT_TASK -> "拒绝任务";
|
||||
case SUBMIT_FEEDBACK -> "提交反馈";
|
||||
case APPROVE_FEEDBACK -> "审批反馈";
|
||||
case REJECT_FEEDBACK -> "拒绝反馈";
|
||||
case OTHER -> "其他操作";
|
||||
default -> "未知操作";
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,48 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 分页数据传输对象
|
||||
*
|
||||
* <p>该类用于封装分页查询的结果数据,安全地向前端暴露分页信息,
|
||||
* 避免了Spring Data的Page接口序列化问题。</p>
|
||||
*
|
||||
* @param <T> 分页内容的数据类型
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PageDTO<T> {
|
||||
|
||||
/**
|
||||
* 当前页的内容列表
|
||||
*/
|
||||
private List<T> content;
|
||||
|
||||
/**
|
||||
* 所有页面的元素总数
|
||||
*/
|
||||
private long totalElements;
|
||||
|
||||
/**
|
||||
* 总页数
|
||||
*/
|
||||
private int totalPages;
|
||||
|
||||
/**
|
||||
* 当前页码(从0开始)
|
||||
*/
|
||||
private int number;
|
||||
|
||||
/**
|
||||
* 当前页面的元素数量
|
||||
*/
|
||||
private int size;
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 分页数据传输对象
|
||||
*
|
||||
* <p>该类用于封装分页查询的结果数据,安全地向前端暴露分页信息,
|
||||
* 避免了Spring Data的Page接口序列化问题。</p>
|
||||
*
|
||||
* @param <T> 分页内容的数据类型
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PageDTO<T> {
|
||||
|
||||
/**
|
||||
* 当前页的内容列表
|
||||
*/
|
||||
private List<T> content;
|
||||
|
||||
/**
|
||||
* 所有页面的元素总数
|
||||
*/
|
||||
private long totalElements;
|
||||
|
||||
/**
|
||||
* 总页数
|
||||
*/
|
||||
private int totalPages;
|
||||
|
||||
/**
|
||||
* 当前页码(从0开始)
|
||||
*/
|
||||
private int number;
|
||||
|
||||
/**
|
||||
* 当前页面的元素数量
|
||||
*/
|
||||
private int size;
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public record PasswordResetDto(
|
||||
@NotEmpty(message = "Token cannot be empty.")
|
||||
String token,
|
||||
|
||||
@NotEmpty(message = "New password cannot be empty.")
|
||||
@Size(min = 8, message = "Password must be at least 8 characters long.")
|
||||
String newPassword
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public record PasswordResetDto(
|
||||
@NotEmpty(message = "Token cannot be empty.")
|
||||
String token,
|
||||
|
||||
@NotEmpty(message = "New password cannot be empty.")
|
||||
@Size(min = 8, message = "Password must be at least 8 characters long.")
|
||||
String newPassword
|
||||
) {}
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
public record PasswordResetRequest(
|
||||
@NotEmpty(message = "Email cannot be empty.")
|
||||
@Email(message = "Invalid email format.")
|
||||
String email
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
public record PasswordResetRequest(
|
||||
@NotEmpty(message = "Email cannot be empty.")
|
||||
@Email(message = "Invalid email format.")
|
||||
String email
|
||||
) {}
|
||||
@@ -1,21 +1,21 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 基于验证码的密码重置DTO
|
||||
*/
|
||||
public record PasswordResetWithCodeDto(
|
||||
@NotEmpty(message = "邮箱不能为空")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
String email,
|
||||
|
||||
@NotEmpty(message = "验证码不能为空")
|
||||
String code,
|
||||
|
||||
@NotEmpty(message = "新密码不能为空")
|
||||
@Size(min = 8, message = "密码长度至少为8个字符")
|
||||
String newPassword
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 基于验证码的密码重置DTO
|
||||
*/
|
||||
public record PasswordResetWithCodeDto(
|
||||
@NotEmpty(message = "邮箱不能为空")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
String email,
|
||||
|
||||
@NotEmpty(message = "验证码不能为空")
|
||||
String code,
|
||||
|
||||
@NotEmpty(message = "新密码不能为空")
|
||||
@Size(min = 8, message = "密码长度至少为8个字符")
|
||||
String newPassword
|
||||
) {}
|
||||
@@ -1,30 +1,30 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* A DTO for requesting a path from the A* service.
|
||||
*
|
||||
* <p>This data transfer object encapsulates the start and end coordinates
|
||||
* needed for the A* pathfinding algorithm to calculate an optimal path.
|
||||
* Used primarily by the {@link com.dne.ems.controller.PathfindingController}.</p>
|
||||
*
|
||||
* @see com.dne.ems.service.pathfinding.AStarService
|
||||
* @see com.dne.ems.dto.Point
|
||||
*/
|
||||
@Data
|
||||
public class PathfindingRequest {
|
||||
|
||||
/**
|
||||
* The starting point for the pathfinding.
|
||||
*/
|
||||
private int startX;
|
||||
private int startY;
|
||||
|
||||
/**
|
||||
* The target (end) point for the pathfinding.
|
||||
*/
|
||||
private int endX;
|
||||
private int endY;
|
||||
package com.dne.ems.dto;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* A DTO for requesting a path from the A* service.
|
||||
*
|
||||
* <p>This data transfer object encapsulates the start and end coordinates
|
||||
* needed for the A* pathfinding algorithm to calculate an optimal path.
|
||||
* Used primarily by the {@link com.dne.ems.controller.PathfindingController}.</p>
|
||||
*
|
||||
* @see com.dne.ems.service.pathfinding.AStarService
|
||||
* @see com.dne.ems.dto.Point
|
||||
*/
|
||||
@Data
|
||||
public class PathfindingRequest {
|
||||
|
||||
/**
|
||||
* The starting point for the pathfinding.
|
||||
*/
|
||||
private int startX;
|
||||
private int startY;
|
||||
|
||||
/**
|
||||
* The target (end) point for the pathfinding.
|
||||
*/
|
||||
private int endX;
|
||||
private int endY;
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
/**
|
||||
* 二维坐标点数据传输对象
|
||||
*
|
||||
* <p>该类用于表示具有整数坐标的二维点,主要用于路径查找请求和在网格上表示位置。
|
||||
* 作为简单的数据结构,用于各种空间计算和位置表示场景。</p>
|
||||
*
|
||||
* @param x X坐标值
|
||||
* @param y Y坐标值
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public record Point(int x, int y) {
|
||||
package com.dne.ems.dto;
|
||||
|
||||
/**
|
||||
* 二维坐标点数据传输对象
|
||||
*
|
||||
* <p>该类用于表示具有整数坐标的二维点,主要用于路径查找请求和在网格上表示位置。
|
||||
* 作为简单的数据结构,用于各种空间计算和位置表示场景。</p>
|
||||
*
|
||||
* @param x X坐标值
|
||||
* @param y Y坐标值
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public record Point(int x, int y) {
|
||||
}
|
||||
@@ -1,135 +1,135 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
|
||||
/**
|
||||
* 污染物阈值数据传输对象
|
||||
*
|
||||
* <p>该类用于封装污染物的阈值相关信息,包括污染物类型、名称、阈值值、单位和描述等。
|
||||
* 主要用于环境监测系统中污染物标准管理和阈值展示。</p>
|
||||
*
|
||||
* <p>提供了多种构造方法,支持通过污染物类型枚举或污染物名称字符串创建对象。
|
||||
* 当使用名称字符串创建时,会尝试将其转换为对应的PollutionType枚举值。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
* @see com.dne.ems.model.enums.PollutionType 污染物类型枚举
|
||||
*/
|
||||
public class PollutantThresholdDTO {
|
||||
/**
|
||||
* 污染物类型枚举
|
||||
*/
|
||||
private PollutionType pollutionType;
|
||||
|
||||
/**
|
||||
* 污染物名称
|
||||
*/
|
||||
private String pollutantName;
|
||||
|
||||
/**
|
||||
* 污染物阈值
|
||||
*/
|
||||
private Double threshold;
|
||||
|
||||
/**
|
||||
* 阈值单位(如mg/m³, μg/m³等)
|
||||
*/
|
||||
private String unit;
|
||||
|
||||
/**
|
||||
* 污染物及阈值的描述说明
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 默认构造函数
|
||||
*/
|
||||
public PollutantThresholdDTO() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 全参数构造函数
|
||||
*
|
||||
* @param pollutionType 污染物类型
|
||||
* @param pollutantName 污染物名称
|
||||
* @param threshold 阈值
|
||||
* @param unit 单位
|
||||
* @param description 描述
|
||||
*/
|
||||
public PollutantThresholdDTO(PollutionType pollutionType, String pollutantName, Double threshold, String unit, String description) {
|
||||
this.pollutionType = pollutionType;
|
||||
this.pollutantName = pollutantName;
|
||||
this.threshold = threshold;
|
||||
this.unit = unit;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用污染物名称的构造函数
|
||||
*
|
||||
* @param pollutantName 污染物名称
|
||||
* @param threshold 阈值
|
||||
* @param unit 单位
|
||||
* @param description 描述
|
||||
*/
|
||||
public PollutantThresholdDTO(String pollutantName, Double threshold, String unit, String description) {
|
||||
this.pollutantName = pollutantName;
|
||||
try {
|
||||
this.pollutionType = PollutionType.valueOf(pollutantName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// 如果不是有效的枚举值,默认为OTHER
|
||||
this.pollutionType = PollutionType.OTHER;
|
||||
}
|
||||
this.threshold = threshold;
|
||||
this.unit = unit;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public PollutionType getPollutionType() {
|
||||
return pollutionType;
|
||||
}
|
||||
|
||||
public void setPollutionType(PollutionType pollutionType) {
|
||||
this.pollutionType = pollutionType;
|
||||
this.pollutantName = pollutionType.name();
|
||||
}
|
||||
|
||||
public String getPollutantName() {
|
||||
return pollutantName;
|
||||
}
|
||||
|
||||
public void setPollutantName(String pollutantName) {
|
||||
this.pollutantName = pollutantName;
|
||||
try {
|
||||
this.pollutionType = PollutionType.valueOf(pollutantName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// 如果不是有效的枚举值,默认为OTHER
|
||||
this.pollutionType = PollutionType.OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
public Double getThreshold() {
|
||||
return threshold;
|
||||
}
|
||||
|
||||
public void setThreshold(Double threshold) {
|
||||
this.threshold = threshold;
|
||||
}
|
||||
|
||||
public String getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public void setUnit(String unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
|
||||
/**
|
||||
* 污染物阈值数据传输对象
|
||||
*
|
||||
* <p>该类用于封装污染物的阈值相关信息,包括污染物类型、名称、阈值值、单位和描述等。
|
||||
* 主要用于环境监测系统中污染物标准管理和阈值展示。</p>
|
||||
*
|
||||
* <p>提供了多种构造方法,支持通过污染物类型枚举或污染物名称字符串创建对象。
|
||||
* 当使用名称字符串创建时,会尝试将其转换为对应的PollutionType枚举值。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
* @see com.dne.ems.model.enums.PollutionType 污染物类型枚举
|
||||
*/
|
||||
public class PollutantThresholdDTO {
|
||||
/**
|
||||
* 污染物类型枚举
|
||||
*/
|
||||
private PollutionType pollutionType;
|
||||
|
||||
/**
|
||||
* 污染物名称
|
||||
*/
|
||||
private String pollutantName;
|
||||
|
||||
/**
|
||||
* 污染物阈值
|
||||
*/
|
||||
private Double threshold;
|
||||
|
||||
/**
|
||||
* 阈值单位(如mg/m³, μg/m³等)
|
||||
*/
|
||||
private String unit;
|
||||
|
||||
/**
|
||||
* 污染物及阈值的描述说明
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 默认构造函数
|
||||
*/
|
||||
public PollutantThresholdDTO() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 全参数构造函数
|
||||
*
|
||||
* @param pollutionType 污染物类型
|
||||
* @param pollutantName 污染物名称
|
||||
* @param threshold 阈值
|
||||
* @param unit 单位
|
||||
* @param description 描述
|
||||
*/
|
||||
public PollutantThresholdDTO(PollutionType pollutionType, String pollutantName, Double threshold, String unit, String description) {
|
||||
this.pollutionType = pollutionType;
|
||||
this.pollutantName = pollutantName;
|
||||
this.threshold = threshold;
|
||||
this.unit = unit;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用污染物名称的构造函数
|
||||
*
|
||||
* @param pollutantName 污染物名称
|
||||
* @param threshold 阈值
|
||||
* @param unit 单位
|
||||
* @param description 描述
|
||||
*/
|
||||
public PollutantThresholdDTO(String pollutantName, Double threshold, String unit, String description) {
|
||||
this.pollutantName = pollutantName;
|
||||
try {
|
||||
this.pollutionType = PollutionType.valueOf(pollutantName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// 如果不是有效的枚举值,默认为OTHER
|
||||
this.pollutionType = PollutionType.OTHER;
|
||||
}
|
||||
this.threshold = threshold;
|
||||
this.unit = unit;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public PollutionType getPollutionType() {
|
||||
return pollutionType;
|
||||
}
|
||||
|
||||
public void setPollutionType(PollutionType pollutionType) {
|
||||
this.pollutionType = pollutionType;
|
||||
this.pollutantName = pollutionType.name();
|
||||
}
|
||||
|
||||
public String getPollutantName() {
|
||||
return pollutantName;
|
||||
}
|
||||
|
||||
public void setPollutantName(String pollutantName) {
|
||||
this.pollutantName = pollutantName;
|
||||
try {
|
||||
this.pollutionType = PollutionType.valueOf(pollutantName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// 如果不是有效的枚举值,默认为OTHER
|
||||
this.pollutionType = PollutionType.OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
public Double getThreshold() {
|
||||
return threshold;
|
||||
}
|
||||
|
||||
public void setThreshold(Double threshold) {
|
||||
this.threshold = threshold;
|
||||
}
|
||||
|
||||
public String getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public void setUnit(String unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
|
||||
/**
|
||||
* DTO for representing pollution statistics.
|
||||
*
|
||||
* @param pollutionType The type of pollution.
|
||||
* @param count The number of feedback events for this pollution type.
|
||||
*/
|
||||
public record PollutionStatsDTO(
|
||||
PollutionType pollutionType,
|
||||
Long count
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
|
||||
/**
|
||||
* DTO for representing pollution statistics.
|
||||
*
|
||||
* @param pollutionType The type of pollution.
|
||||
* @param count The number of feedback events for this pollution type.
|
||||
*/
|
||||
public record PollutionStatsDTO(
|
||||
PollutionType pollutionType,
|
||||
Long count
|
||||
) {}
|
||||
@@ -1,25 +1,25 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.FeedbackStatus;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* DTO for processing a feedback.
|
||||
* This is used when an admin or supervisor approves or takes action on a feedback entry.
|
||||
*/
|
||||
@Data
|
||||
public class ProcessFeedbackRequest {
|
||||
|
||||
/**
|
||||
* The new status to set for the feedback.
|
||||
*/
|
||||
@NotNull(message = "New status cannot be null")
|
||||
private FeedbackStatus status;
|
||||
|
||||
/**
|
||||
* Optional notes related to the processing action.
|
||||
*/
|
||||
private String notes;
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.FeedbackStatus;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* DTO for processing a feedback.
|
||||
* This is used when an admin or supervisor approves or takes action on a feedback entry.
|
||||
*/
|
||||
@Data
|
||||
public class ProcessFeedbackRequest {
|
||||
|
||||
/**
|
||||
* The new status to set for the feedback.
|
||||
*/
|
||||
@NotNull(message = "New status cannot be null")
|
||||
private FeedbackStatus status;
|
||||
|
||||
/**
|
||||
* Optional notes related to the processing action.
|
||||
*/
|
||||
private String notes;
|
||||
}
|
||||
@@ -1,56 +1,56 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* DTO for receiving feedback from the public.
|
||||
*/
|
||||
@Data
|
||||
public class PublicFeedbackRequest {
|
||||
|
||||
/**
|
||||
* The title of the feedback.
|
||||
*/
|
||||
@NotBlank(message = "Title cannot be blank")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* The detailed description of the feedback.
|
||||
*/
|
||||
@NotBlank(message = "Description cannot be blank")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* The type of pollution.
|
||||
*/
|
||||
@NotNull(message = "Pollution type cannot be null")
|
||||
private String pollutionType;
|
||||
|
||||
/**
|
||||
* The severity level of the issue.
|
||||
*/
|
||||
@NotNull(message = "Severity level cannot be null")
|
||||
private SeverityLevel severityLevel;
|
||||
|
||||
/**
|
||||
* The text description of the address of the event.
|
||||
*/
|
||||
@NotBlank(message = "Text address cannot be blank")
|
||||
private String textAddress;
|
||||
|
||||
/**
|
||||
* The grid coordinate X.
|
||||
*/
|
||||
@NotNull(message = "Grid X coordinate cannot be null")
|
||||
private Double gridX;
|
||||
|
||||
/**
|
||||
* The grid coordinate Y.
|
||||
*/
|
||||
@NotNull(message = "Grid Y coordinate cannot be null")
|
||||
private Double gridY;
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* DTO for receiving feedback from the public.
|
||||
*/
|
||||
@Data
|
||||
public class PublicFeedbackRequest {
|
||||
|
||||
/**
|
||||
* The title of the feedback.
|
||||
*/
|
||||
@NotBlank(message = "Title cannot be blank")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* The detailed description of the feedback.
|
||||
*/
|
||||
@NotBlank(message = "Description cannot be blank")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* The type of pollution.
|
||||
*/
|
||||
@NotNull(message = "Pollution type cannot be null")
|
||||
private String pollutionType;
|
||||
|
||||
/**
|
||||
* The severity level of the issue.
|
||||
*/
|
||||
@NotNull(message = "Severity level cannot be null")
|
||||
private SeverityLevel severityLevel;
|
||||
|
||||
/**
|
||||
* The text description of the address of the event.
|
||||
*/
|
||||
@NotBlank(message = "Text address cannot be blank")
|
||||
private String textAddress;
|
||||
|
||||
/**
|
||||
* The grid coordinate X.
|
||||
*/
|
||||
@NotNull(message = "Grid X coordinate cannot be null")
|
||||
private Double gridX;
|
||||
|
||||
/**
|
||||
* The grid coordinate Y.
|
||||
*/
|
||||
@NotNull(message = "Grid Y coordinate cannot be null")
|
||||
private Double gridY;
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* DTO for rejecting a feedback.
|
||||
* This is used when a supervisor rejects a feedback submission.
|
||||
*/
|
||||
@Data
|
||||
public class RejectFeedbackRequest {
|
||||
|
||||
/**
|
||||
* The reason for rejecting the feedback.
|
||||
*/
|
||||
private String notes;
|
||||
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* DTO for rejecting a feedback.
|
||||
* This is used when a supervisor rejects a feedback submission.
|
||||
*/
|
||||
@Data
|
||||
public class RejectFeedbackRequest {
|
||||
|
||||
/**
|
||||
* The reason for rejecting the feedback.
|
||||
*/
|
||||
private String notes;
|
||||
|
||||
}
|
||||
@@ -1,30 +1,30 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.Role;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public record SignUpRequest(
|
||||
@NotBlank(message = "姓名不能为空")
|
||||
String name,
|
||||
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
String email,
|
||||
|
||||
@NotBlank(message = "手机号不能为空")
|
||||
String phone,
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 8, max = 20, message = "密码长度必须在8到20个字符之间")
|
||||
String password,
|
||||
|
||||
@NotNull(message = "角色不能为空")
|
||||
Role role,
|
||||
|
||||
@NotBlank(message = "验证码不能为空")
|
||||
String verificationCode
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.Role;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public record SignUpRequest(
|
||||
@NotBlank(message = "姓名不能为空")
|
||||
String name,
|
||||
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
@Email(message = "邮箱格式不正确")
|
||||
String email,
|
||||
|
||||
@NotBlank(message = "手机号不能为空")
|
||||
String phone,
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 8, max = 20, message = "密码长度必须在8到20个字符之间")
|
||||
String password,
|
||||
|
||||
@NotNull(message = "角色不能为空")
|
||||
Role role,
|
||||
|
||||
@NotBlank(message = "验证码不能为空")
|
||||
String verificationCode
|
||||
) {}
|
||||
@@ -1,24 +1,24 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 任务审核请求DTO
|
||||
*
|
||||
* <p>用于管理员对提交的任务进行审核决策
|
||||
*
|
||||
* @param approved 是否通过审核(必填)
|
||||
* @param comments 审核意见(可选)
|
||||
*
|
||||
* <p>业务规则:
|
||||
* <ul>
|
||||
* <li>只有状态为SUBMITTED的任务才能审核</li>
|
||||
* <li>审核通过后任务状态变为COMPLETED</li>
|
||||
* <li>审核不通过则返回ASSIGNED状态</li>
|
||||
* </ul>
|
||||
*/
|
||||
public record TaskApprovalRequest(
|
||||
@NotNull
|
||||
Boolean approved,
|
||||
String comments
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 任务审核请求DTO
|
||||
*
|
||||
* <p>用于管理员对提交的任务进行审核决策
|
||||
*
|
||||
* @param approved 是否通过审核(必填)
|
||||
* @param comments 审核意见(可选)
|
||||
*
|
||||
* <p>业务规则:
|
||||
* <ul>
|
||||
* <li>只有状态为SUBMITTED的任务才能审核</li>
|
||||
* <li>审核通过后任务状态变为COMPLETED</li>
|
||||
* <li>审核不通过则返回ASSIGNED状态</li>
|
||||
* </ul>
|
||||
*/
|
||||
public record TaskApprovalRequest(
|
||||
@NotNull
|
||||
Boolean approved,
|
||||
String comments
|
||||
) {}
|
||||
@@ -1,25 +1,25 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 任务分配请求数据传输对象
|
||||
*
|
||||
* <p>用于主管将待分配任务指派给特定网格工作人员
|
||||
*
|
||||
* <p>业务规则:
|
||||
* <ul>
|
||||
* <li>只能分配给角色为GRID_WORKER的用户</li>
|
||||
* <li>任务状态必须为PENDING_ASSIGNMENT(待分配)或ASSIGNED(已分配)</li>
|
||||
* <li>分配后任务状态变为ASSIGNED</li>
|
||||
* <li>会生成任务分配记录</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param assigneeId 网格工作人员ID(必填)
|
||||
* @see com.dne.ems.model.enums.TaskStatus 任务状态枚举
|
||||
* @see com.dne.ems.model.Assignment 任务分配记录实体
|
||||
*/
|
||||
public record TaskAssignmentRequest(
|
||||
@NotNull
|
||||
Long assigneeId
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 任务分配请求数据传输对象
|
||||
*
|
||||
* <p>用于主管将待分配任务指派给特定网格工作人员
|
||||
*
|
||||
* <p>业务规则:
|
||||
* <ul>
|
||||
* <li>只能分配给角色为GRID_WORKER的用户</li>
|
||||
* <li>任务状态必须为PENDING_ASSIGNMENT(待分配)或ASSIGNED(已分配)</li>
|
||||
* <li>分配后任务状态变为ASSIGNED</li>
|
||||
* <li>会生成任务分配记录</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param assigneeId 网格工作人员ID(必填)
|
||||
* @see com.dne.ems.model.enums.TaskStatus 任务状态枚举
|
||||
* @see com.dne.ems.model.Assignment 任务分配记录实体
|
||||
*/
|
||||
public record TaskAssignmentRequest(
|
||||
@NotNull
|
||||
Long assigneeId
|
||||
) {}
|
||||
@@ -1,74 +1,74 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 任务创建请求数据传输对象
|
||||
*
|
||||
* <p>用于主管直接创建任务,包含任务基本属性和位置信息
|
||||
*
|
||||
* <p>业务规则:
|
||||
* <ul>
|
||||
* <li>创建后状态为PENDING_ASSIGNMENT(待分配)</li>
|
||||
* <li>需指定有效的污染类型和严重级别</li>
|
||||
* <li>位置信息必须包含有效的网格坐标</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param title 任务标题(必填,最长255字符)
|
||||
* @param description 任务详细描述(可选)
|
||||
* @param pollutionType 污染类型(必填)
|
||||
* @param severityLevel 严重等级(必填)
|
||||
* @param location 位置信息(必填)
|
||||
* @see com.dne.ems.model.enums.PollutionType 污染类型枚举
|
||||
* @see com.dne.ems.model.enums.SeverityLevel 严重级别枚举
|
||||
*/
|
||||
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
|
||||
) {
|
||||
/**
|
||||
* 位置数据(嵌套记录)
|
||||
*
|
||||
* <p>用于精确定位任务发生位置
|
||||
*
|
||||
* <p>验证规则:
|
||||
* <ul>
|
||||
* <li>文字地址必须符合"省-市-区"格式</li>
|
||||
* <li>网格坐标必须在有效范围内(1-1000)</li>
|
||||
* <li>经纬度坐标可选,但提供时可提高定位精度</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param latitude 纬度坐标(可选)
|
||||
* @param longitude 经度坐标(可选)
|
||||
* @param textAddress 文字地址(必填,格式:"省-市-区")
|
||||
* @param gridX 网格X坐标(必填,范围1-1000)
|
||||
* @param gridY 网格Y坐标(必填,范围1-1000)
|
||||
*/
|
||||
public record LocationDTO(
|
||||
Double latitude,
|
||||
Double longitude,
|
||||
@NotBlank(message = "Text address is mandatory")
|
||||
String textAddress,
|
||||
|
||||
@NotNull(message = "Grid X coordinate is mandatory")
|
||||
Integer gridX,
|
||||
|
||||
@NotNull(message = "Grid Y coordinate is mandatory")
|
||||
Integer gridY
|
||||
) {}
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 任务创建请求数据传输对象
|
||||
*
|
||||
* <p>用于主管直接创建任务,包含任务基本属性和位置信息
|
||||
*
|
||||
* <p>业务规则:
|
||||
* <ul>
|
||||
* <li>创建后状态为PENDING_ASSIGNMENT(待分配)</li>
|
||||
* <li>需指定有效的污染类型和严重级别</li>
|
||||
* <li>位置信息必须包含有效的网格坐标</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param title 任务标题(必填,最长255字符)
|
||||
* @param description 任务详细描述(可选)
|
||||
* @param pollutionType 污染类型(必填)
|
||||
* @param severityLevel 严重等级(必填)
|
||||
* @param location 位置信息(必填)
|
||||
* @see com.dne.ems.model.enums.PollutionType 污染类型枚举
|
||||
* @see com.dne.ems.model.enums.SeverityLevel 严重级别枚举
|
||||
*/
|
||||
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
|
||||
) {
|
||||
/**
|
||||
* 位置数据(嵌套记录)
|
||||
*
|
||||
* <p>用于精确定位任务发生位置
|
||||
*
|
||||
* <p>验证规则:
|
||||
* <ul>
|
||||
* <li>文字地址必须符合"省-市-区"格式</li>
|
||||
* <li>网格坐标必须在有效范围内(1-1000)</li>
|
||||
* <li>经纬度坐标可选,但提供时可提高定位精度</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param latitude 纬度坐标(可选)
|
||||
* @param longitude 经度坐标(可选)
|
||||
* @param textAddress 文字地址(必填,格式:"省-市-区")
|
||||
* @param gridX 网格X坐标(必填,范围1-1000)
|
||||
* @param gridY 网格Y坐标(必填,范围1-1000)
|
||||
*/
|
||||
public record LocationDTO(
|
||||
Double latitude,
|
||||
Double longitude,
|
||||
@NotBlank(message = "Text address is mandatory")
|
||||
String textAddress,
|
||||
|
||||
@NotNull(message = "Grid X coordinate is mandatory")
|
||||
Integer gridX,
|
||||
|
||||
@NotNull(message = "Grid Y coordinate is mandatory")
|
||||
Integer gridY
|
||||
) {}
|
||||
}
|
||||
@@ -1,41 +1,41 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.TaskStatus;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* 任务数据传输对象
|
||||
*
|
||||
* <p>该类用于封装任务的基本信息,包含任务ID、标题、描述和状态等核心数据。
|
||||
* 主要用于任务列表展示和简单数据传输场景。</p>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TaskDTO {
|
||||
/**
|
||||
* 任务ID,唯一标识
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 任务标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 任务详细描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 任务当前状态
|
||||
*/
|
||||
private TaskStatus status;
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.TaskStatus;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* 任务数据传输对象
|
||||
*
|
||||
* <p>该类用于封装任务的基本信息,包含任务ID、标题、描述和状态等核心数据。
|
||||
* 主要用于任务列表展示和简单数据传输场景。</p>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TaskDTO {
|
||||
/**
|
||||
* 任务ID,唯一标识
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 任务标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 任务详细描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 任务当前状态
|
||||
*/
|
||||
private TaskStatus status;
|
||||
}
|
||||
@@ -1,98 +1,98 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
import com.dne.ems.model.enums.TaskStatus;
|
||||
|
||||
/**
|
||||
* 任务详情DTO
|
||||
*
|
||||
* <p>包含任务的完整详细信息,用于详情展示
|
||||
*
|
||||
* @param id 任务ID
|
||||
* @param feedback 关联的反馈详情
|
||||
* @param title 任务标题
|
||||
* @param description 任务描述
|
||||
* @param severityLevel 严重等级
|
||||
* @param pollutionType 污染类型
|
||||
* @param textAddress 文字地址
|
||||
* @param gridX 网格X坐标
|
||||
* @param gridY 网格Y坐标
|
||||
* @param status 任务状态
|
||||
* @param assignee 分配的工作人员(可能为null)
|
||||
* @param assignedAt 分配时间(可能为null)
|
||||
* @param completedAt 完成时间(可能为null)
|
||||
* @param history 任务历史记录
|
||||
* @param submissionInfo 任务提交信息(如果已提交)
|
||||
*/
|
||||
public record TaskDetailDTO(
|
||||
Long id,
|
||||
FeedbackDTO feedback,
|
||||
String title,
|
||||
String description,
|
||||
SeverityLevel severityLevel,
|
||||
PollutionType pollutionType,
|
||||
String textAddress,
|
||||
Integer gridX,
|
||||
Integer gridY,
|
||||
TaskStatus status,
|
||||
AssigneeDTO assignee,
|
||||
LocalDateTime assignedAt,
|
||||
LocalDateTime completedAt,
|
||||
List<TaskHistoryDTO> history,
|
||||
SubmissionInfoDTO submissionInfo
|
||||
) {
|
||||
/**
|
||||
* 任务关联的反馈详情DTO
|
||||
*
|
||||
* @param feedbackId 反馈ID
|
||||
* @param eventId 事件ID
|
||||
* @param title 反馈标题
|
||||
* @param description 反馈描述
|
||||
* @param severityLevel 严重等级
|
||||
* @param textAddress 文字地址
|
||||
* @param submitterId 提交人ID
|
||||
* @param submitterName 提交人姓名
|
||||
* @param createdAt 创建时间
|
||||
* @param imageUrls 图片URL列表
|
||||
*/
|
||||
public record FeedbackDTO(
|
||||
Long feedbackId,
|
||||
String eventId,
|
||||
String title,
|
||||
String description,
|
||||
SeverityLevel severityLevel,
|
||||
String textAddress,
|
||||
Long submitterId,
|
||||
String submitterName,
|
||||
LocalDateTime createdAt,
|
||||
List<String> imageUrls
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 任务分配人详情DTO
|
||||
*
|
||||
* @param id 工作人员ID
|
||||
* @param name 工作人员姓名
|
||||
* @param phone 联系电话
|
||||
*/
|
||||
public record AssigneeDTO(
|
||||
Long id,
|
||||
String name,
|
||||
String phone
|
||||
) {}
|
||||
|
||||
/**
|
||||
* DTO for task submission details.
|
||||
*
|
||||
* @param comments The comments or notes from the submission.
|
||||
* @param attachments A list of attachments included in the submission.
|
||||
*/
|
||||
public record SubmissionInfoDTO(
|
||||
String comments,
|
||||
List<AttachmentDTO> attachments
|
||||
) {}
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import com.dne.ems.model.enums.PollutionType;
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
import com.dne.ems.model.enums.TaskStatus;
|
||||
|
||||
/**
|
||||
* 任务详情DTO
|
||||
*
|
||||
* <p>包含任务的完整详细信息,用于详情展示
|
||||
*
|
||||
* @param id 任务ID
|
||||
* @param feedback 关联的反馈详情
|
||||
* @param title 任务标题
|
||||
* @param description 任务描述
|
||||
* @param severityLevel 严重等级
|
||||
* @param pollutionType 污染类型
|
||||
* @param textAddress 文字地址
|
||||
* @param gridX 网格X坐标
|
||||
* @param gridY 网格Y坐标
|
||||
* @param status 任务状态
|
||||
* @param assignee 分配的工作人员(可能为null)
|
||||
* @param assignedAt 分配时间(可能为null)
|
||||
* @param completedAt 完成时间(可能为null)
|
||||
* @param history 任务历史记录
|
||||
* @param submissionInfo 任务提交信息(如果已提交)
|
||||
*/
|
||||
public record TaskDetailDTO(
|
||||
Long id,
|
||||
FeedbackDTO feedback,
|
||||
String title,
|
||||
String description,
|
||||
SeverityLevel severityLevel,
|
||||
PollutionType pollutionType,
|
||||
String textAddress,
|
||||
Integer gridX,
|
||||
Integer gridY,
|
||||
TaskStatus status,
|
||||
AssigneeDTO assignee,
|
||||
LocalDateTime assignedAt,
|
||||
LocalDateTime completedAt,
|
||||
List<TaskHistoryDTO> history,
|
||||
SubmissionInfoDTO submissionInfo
|
||||
) {
|
||||
/**
|
||||
* 任务关联的反馈详情DTO
|
||||
*
|
||||
* @param feedbackId 反馈ID
|
||||
* @param eventId 事件ID
|
||||
* @param title 反馈标题
|
||||
* @param description 反馈描述
|
||||
* @param severityLevel 严重等级
|
||||
* @param textAddress 文字地址
|
||||
* @param submitterId 提交人ID
|
||||
* @param submitterName 提交人姓名
|
||||
* @param createdAt 创建时间
|
||||
* @param imageUrls 图片URL列表
|
||||
*/
|
||||
public record FeedbackDTO(
|
||||
Long feedbackId,
|
||||
String eventId,
|
||||
String title,
|
||||
String description,
|
||||
SeverityLevel severityLevel,
|
||||
String textAddress,
|
||||
Long submitterId,
|
||||
String submitterName,
|
||||
LocalDateTime createdAt,
|
||||
List<String> imageUrls
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 任务分配人详情DTO
|
||||
*
|
||||
* @param id 工作人员ID
|
||||
* @param name 工作人员姓名
|
||||
* @param phone 联系电话
|
||||
*/
|
||||
public record AssigneeDTO(
|
||||
Long id,
|
||||
String name,
|
||||
String phone
|
||||
) {}
|
||||
|
||||
/**
|
||||
* DTO for task submission details.
|
||||
*
|
||||
* @param comments The comments or notes from the submission.
|
||||
* @param attachments A list of attachments included in the submission.
|
||||
*/
|
||||
public record SubmissionInfoDTO(
|
||||
String comments,
|
||||
List<AttachmentDTO> attachments
|
||||
) {}
|
||||
}
|
||||
@@ -1,61 +1,61 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 从反馈创建任务请求数据传输对象
|
||||
*
|
||||
* <p>用于主管将公众反馈转化为可分配的任务,包含任务基本属性和优先级设置
|
||||
*
|
||||
* <p>业务规则:
|
||||
* <ul>
|
||||
* <li>反馈状态必须为PENDING_REVIEW(待审核)</li>
|
||||
* <li>创建后原反馈状态自动更新为PROCESSED(已处理)</li>
|
||||
* <li>新任务初始状态为PENDING_ASSIGNMENT(待分配)</li>
|
||||
* <li>任务标题需简明描述问题本质</li>
|
||||
* <li>严重级别影响任务分配优先级和响应时限</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024-03
|
||||
* @see com.dne.ems.model.enums.SeverityLevel 严重级别枚举定义
|
||||
* @see com.dne.ems.model.Feedback 关联的反馈实体
|
||||
*/
|
||||
@Data
|
||||
public class TaskFromFeedbackRequest {
|
||||
|
||||
/**
|
||||
* 任务标题
|
||||
*
|
||||
* <p>必填字段,用于简要描述任务内容
|
||||
* <p>验证规则:
|
||||
* <ul>
|
||||
* <li>不能为空(@NotBlank)</li>
|
||||
* <li>最大长度255字符(JPA约束)</li>
|
||||
* <li>建议包含问题类型和位置关键词</li>
|
||||
* </ul>
|
||||
*/
|
||||
@NotBlank(message = "任务标题不能为空")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 严重级别
|
||||
*
|
||||
* <p>必填字段,用于确定任务处理优先级和响应时限
|
||||
* <p>可选值:
|
||||
* <ul>
|
||||
* <li>LOW - 低优先级(72小时内处理)</li>
|
||||
* <li>MEDIUM - 中优先级(48小时内处理)</li>
|
||||
* <li>HIGH - 高优先级(24小时内处理)</li>
|
||||
* <li>CRITICAL - 紧急(立即处理)</li>
|
||||
* </ul>
|
||||
* @see com.dne.ems.model.enums.SeverityLevel 完整枚举定义
|
||||
*/
|
||||
@NotNull(message = "严重级别不能为空")
|
||||
private SeverityLevel severityLevel;
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 从反馈创建任务请求数据传输对象
|
||||
*
|
||||
* <p>用于主管将公众反馈转化为可分配的任务,包含任务基本属性和优先级设置
|
||||
*
|
||||
* <p>业务规则:
|
||||
* <ul>
|
||||
* <li>反馈状态必须为PENDING_REVIEW(待审核)</li>
|
||||
* <li>创建后原反馈状态自动更新为PROCESSED(已处理)</li>
|
||||
* <li>新任务初始状态为PENDING_ASSIGNMENT(待分配)</li>
|
||||
* <li>任务标题需简明描述问题本质</li>
|
||||
* <li>严重级别影响任务分配优先级和响应时限</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author DNE开发团队
|
||||
* @version 1.0
|
||||
* @since 2024-03
|
||||
* @see com.dne.ems.model.enums.SeverityLevel 严重级别枚举定义
|
||||
* @see com.dne.ems.model.Feedback 关联的反馈实体
|
||||
*/
|
||||
@Data
|
||||
public class TaskFromFeedbackRequest {
|
||||
|
||||
/**
|
||||
* 任务标题
|
||||
*
|
||||
* <p>必填字段,用于简要描述任务内容
|
||||
* <p>验证规则:
|
||||
* <ul>
|
||||
* <li>不能为空(@NotBlank)</li>
|
||||
* <li>最大长度255字符(JPA约束)</li>
|
||||
* <li>建议包含问题类型和位置关键词</li>
|
||||
* </ul>
|
||||
*/
|
||||
@NotBlank(message = "任务标题不能为空")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 严重级别
|
||||
*
|
||||
* <p>必填字段,用于确定任务处理优先级和响应时限
|
||||
* <p>可选值:
|
||||
* <ul>
|
||||
* <li>LOW - 低优先级(72小时内处理)</li>
|
||||
* <li>MEDIUM - 中优先级(48小时内处理)</li>
|
||||
* <li>HIGH - 高优先级(24小时内处理)</li>
|
||||
* <li>CRITICAL - 紧急(立即处理)</li>
|
||||
* </ul>
|
||||
* @see com.dne.ems.model.enums.SeverityLevel 完整枚举定义
|
||||
*/
|
||||
@NotNull(message = "严重级别不能为空")
|
||||
private SeverityLevel severityLevel;
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.dne.ems.model.enums.TaskStatus;
|
||||
|
||||
public record TaskHistoryDTO(
|
||||
Long id,
|
||||
TaskStatus oldStatus,
|
||||
TaskStatus newStatus,
|
||||
String comments,
|
||||
LocalDateTime changedAt,
|
||||
UserDTO changedBy
|
||||
) {
|
||||
public record UserDTO(Long id, String name) {}
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.dne.ems.model.enums.TaskStatus;
|
||||
|
||||
public record TaskHistoryDTO(
|
||||
Long id,
|
||||
TaskStatus oldStatus,
|
||||
TaskStatus newStatus,
|
||||
String comments,
|
||||
LocalDateTime changedAt,
|
||||
UserDTO changedBy
|
||||
) {
|
||||
public record UserDTO(Long id, String name) {}
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.TaskStatus;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TaskInfoDTO {
|
||||
private Long id;
|
||||
private TaskStatus status;
|
||||
private AssigneeInfoDTO assignee;
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.TaskStatus;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TaskInfoDTO {
|
||||
private Long id;
|
||||
private TaskStatus status;
|
||||
private AssigneeInfoDTO assignee;
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TaskRejectionRequest {
|
||||
|
||||
@NotBlank(message = "Rejection reason cannot be blank.")
|
||||
private String reason;
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TaskRejectionRequest {
|
||||
|
||||
@NotBlank(message = "Rejection reason cannot be blank.")
|
||||
private String reason;
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
/**
|
||||
* DTO for representing task completion statistics.
|
||||
*
|
||||
* @param totalTasks The total number of tasks.
|
||||
* @param completedTasks The number of completed tasks.
|
||||
* @param completionRate The calculated completion rate (completedTasks / totalTasks).
|
||||
*/
|
||||
public record TaskStatsDTO(
|
||||
Long totalTasks,
|
||||
Long completedTasks,
|
||||
Double completionRate
|
||||
package com.dne.ems.dto;
|
||||
|
||||
/**
|
||||
* DTO for representing task completion statistics.
|
||||
*
|
||||
* @param totalTasks The total number of tasks.
|
||||
* @param completedTasks The number of completed tasks.
|
||||
* @param completionRate The calculated completion rate (completedTasks / totalTasks).
|
||||
*/
|
||||
public record TaskStatsDTO(
|
||||
Long totalTasks,
|
||||
Long completedTasks,
|
||||
Double completionRate
|
||||
) {}
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* DTO for submitting task updates.
|
||||
*
|
||||
* @param comments The comments or notes for the task update.
|
||||
*/
|
||||
public record TaskSubmissionRequest(
|
||||
@NotBlank(message = "Comments cannot be blank")
|
||||
String comments
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* DTO for submitting task updates.
|
||||
*
|
||||
* @param comments The comments or notes for the task update.
|
||||
*/
|
||||
public record TaskSubmissionRequest(
|
||||
@NotBlank(message = "Comments cannot be blank")
|
||||
String comments
|
||||
) {}
|
||||
@@ -1,36 +1,36 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
import com.dne.ems.model.enums.TaskStatus;
|
||||
|
||||
/**
|
||||
* 任务摘要数据传输对象
|
||||
*
|
||||
* <p>该类用于封装任务的摘要信息,主要用于任务列表展示和简单数据传输场景。
|
||||
* 包含任务的基本标识、状态、分配信息和位置等核心数据。</p>
|
||||
*
|
||||
* @param id 任务ID,唯一标识
|
||||
* @param title 任务标题
|
||||
* @param description 任务描述
|
||||
* @param status 任务当前状态
|
||||
* @param assigneeName 被分配的网格员姓名(可能为空)
|
||||
* @param createdAt 任务创建时间
|
||||
* @param textAddress 任务地点文字描述
|
||||
* @param imageUrl 任务相关图片URL
|
||||
* @param severity 任务严重程度
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public record TaskSummaryDTO(
|
||||
Long id,
|
||||
String title,
|
||||
String description,
|
||||
TaskStatus status,
|
||||
String assigneeName,
|
||||
LocalDateTime createdAt,
|
||||
String textAddress,
|
||||
String imageUrl,
|
||||
SeverityLevel severity
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.dne.ems.model.enums.SeverityLevel;
|
||||
import com.dne.ems.model.enums.TaskStatus;
|
||||
|
||||
/**
|
||||
* 任务摘要数据传输对象
|
||||
*
|
||||
* <p>该类用于封装任务的摘要信息,主要用于任务列表展示和简单数据传输场景。
|
||||
* 包含任务的基本标识、状态、分配信息和位置等核心数据。</p>
|
||||
*
|
||||
* @param id 任务ID,唯一标识
|
||||
* @param title 任务标题
|
||||
* @param description 任务描述
|
||||
* @param status 任务当前状态
|
||||
* @param assigneeName 被分配的网格员姓名(可能为空)
|
||||
* @param createdAt 任务创建时间
|
||||
* @param textAddress 任务地点文字描述
|
||||
* @param imageUrl 任务相关图片URL
|
||||
* @param severity 任务严重程度
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public record TaskSummaryDTO(
|
||||
Long id,
|
||||
String title,
|
||||
String description,
|
||||
TaskStatus status,
|
||||
String assigneeName,
|
||||
LocalDateTime createdAt,
|
||||
String textAddress,
|
||||
String imageUrl,
|
||||
SeverityLevel severity
|
||||
) {}
|
||||
@@ -1,55 +1,55 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
/**
|
||||
* 趋势数据点传输对象
|
||||
*
|
||||
* <p>该类用于表示时间序列趋势图中的单个数据点,主要用于环境数据趋势分析和可视化展示。
|
||||
* 包含时间标识(通常为年月格式)和对应的数值(如超标次数)。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public class TrendDataPointDTO {
|
||||
/**
|
||||
* 月份标识,通常使用"YYYY-MM"格式
|
||||
*/
|
||||
private String yearMonth;
|
||||
|
||||
/**
|
||||
* 该月的数值(如超标次数)
|
||||
*/
|
||||
private Long count;
|
||||
|
||||
/**
|
||||
* JPA所需的默认构造函数
|
||||
*/
|
||||
public TrendDataPointDTO() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于JPQL查询的构造函数
|
||||
*
|
||||
* @param yearMonth 月份标识,格式为"YYYY-MM"
|
||||
* @param count 该月的计数值
|
||||
*/
|
||||
public TrendDataPointDTO(String yearMonth, Long count) {
|
||||
this.yearMonth = yearMonth;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public String getYearMonth() {
|
||||
return yearMonth;
|
||||
}
|
||||
|
||||
public void setYearMonth(String yearMonth) {
|
||||
this.yearMonth = yearMonth;
|
||||
}
|
||||
|
||||
public Long getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(Long count) {
|
||||
this.count = count;
|
||||
}
|
||||
package com.dne.ems.dto;
|
||||
|
||||
/**
|
||||
* 趋势数据点传输对象
|
||||
*
|
||||
* <p>该类用于表示时间序列趋势图中的单个数据点,主要用于环境数据趋势分析和可视化展示。
|
||||
* 包含时间标识(通常为年月格式)和对应的数值(如超标次数)。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public class TrendDataPointDTO {
|
||||
/**
|
||||
* 月份标识,通常使用"YYYY-MM"格式
|
||||
*/
|
||||
private String yearMonth;
|
||||
|
||||
/**
|
||||
* 该月的数值(如超标次数)
|
||||
*/
|
||||
private Long count;
|
||||
|
||||
/**
|
||||
* JPA所需的默认构造函数
|
||||
*/
|
||||
public TrendDataPointDTO() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于JPQL查询的构造函数
|
||||
*
|
||||
* @param yearMonth 月份标识,格式为"YYYY-MM"
|
||||
* @param count 该月的计数值
|
||||
*/
|
||||
public TrendDataPointDTO(String yearMonth, Long count) {
|
||||
this.yearMonth = yearMonth;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public String getYearMonth() {
|
||||
return yearMonth;
|
||||
}
|
||||
|
||||
public void setYearMonth(String yearMonth) {
|
||||
this.yearMonth = yearMonth;
|
||||
}
|
||||
|
||||
public Long getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(Long count) {
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,30 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.Role;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public record UserCreationRequest(
|
||||
@NotBlank(message = "Name is mandatory")
|
||||
String name,
|
||||
|
||||
@NotBlank(message = "Email is mandatory")
|
||||
@Email(message = "Email should be valid")
|
||||
String email,
|
||||
|
||||
@NotBlank(message = "Phone number is mandatory")
|
||||
String phone,
|
||||
|
||||
@NotBlank(message = "Password is mandatory")
|
||||
@Size(min = 8, message = "Password must be at least 8 characters long")
|
||||
String password,
|
||||
|
||||
@NotNull(message = "Role is mandatory")
|
||||
Role role,
|
||||
|
||||
String region
|
||||
) {
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.Role;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public record UserCreationRequest(
|
||||
@NotBlank(message = "Name is mandatory")
|
||||
String name,
|
||||
|
||||
@NotBlank(message = "Email is mandatory")
|
||||
@Email(message = "Email should be valid")
|
||||
String email,
|
||||
|
||||
@NotBlank(message = "Phone number is mandatory")
|
||||
String phone,
|
||||
|
||||
@NotBlank(message = "Password is mandatory")
|
||||
@Size(min = 8, message = "Password must be at least 8 characters long")
|
||||
String password,
|
||||
|
||||
@NotNull(message = "Role is mandatory")
|
||||
Role role,
|
||||
|
||||
String region
|
||||
) {
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.FeedbackStatus;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* A DTO representing a summary of a feedback submission for the user's history view.
|
||||
*
|
||||
* @param eventId The unique business ID of the feedback event.
|
||||
* @param title The title of the feedback.
|
||||
* @param status The current status of the feedback.
|
||||
* @param createdAt The timestamp when the feedback was submitted.
|
||||
* @param imageUrl The URL of the first attachment image, if any.
|
||||
*/
|
||||
public record UserFeedbackSummaryDTO(
|
||||
String eventId,
|
||||
String title,
|
||||
FeedbackStatus status,
|
||||
LocalDateTime createdAt,
|
||||
String imageUrl
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.FeedbackStatus;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* A DTO representing a summary of a feedback submission for the user's history view.
|
||||
*
|
||||
* @param eventId The unique business ID of the feedback event.
|
||||
* @param title The title of the feedback.
|
||||
* @param status The current status of the feedback.
|
||||
* @param createdAt The timestamp when the feedback was submitted.
|
||||
* @param imageUrl The URL of the first attachment image, if any.
|
||||
*/
|
||||
public record UserFeedbackSummaryDTO(
|
||||
String eventId,
|
||||
String title,
|
||||
FeedbackStatus status,
|
||||
LocalDateTime createdAt,
|
||||
String imageUrl
|
||||
) {}
|
||||
@@ -1,55 +1,55 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.UserAccount;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 用户信息数据传输对象
|
||||
*
|
||||
* <p>该类用于封装用户基本信息,包含用户ID、姓名、电话和邮箱等核心数据。
|
||||
* 主要用于在不暴露敏感信息的情况下,向前端传递用户信息。</p>
|
||||
*
|
||||
* <p>提供了从用户实体(UserAccount)转换为DTO的静态方法,便于服务层使用。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserInfoDTO {
|
||||
/**
|
||||
* 用户ID,唯一标识
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户姓名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 用户电话号码
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 用户邮箱地址
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 从用户实体创建DTO对象的工厂方法
|
||||
*
|
||||
* @param user 用户实体对象
|
||||
* @return 对应的UserInfoDTO对象,如果输入为null则返回null
|
||||
*/
|
||||
public static UserInfoDTO fromEntity(UserAccount user) {
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
return new UserInfoDTO(user.getId(), user.getName(), user.getPhone(), user.getEmail());
|
||||
}
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.UserAccount;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 用户信息数据传输对象
|
||||
*
|
||||
* <p>该类用于封装用户基本信息,包含用户ID、姓名、电话和邮箱等核心数据。
|
||||
* 主要用于在不暴露敏感信息的情况下,向前端传递用户信息。</p>
|
||||
*
|
||||
* <p>提供了从用户实体(UserAccount)转换为DTO的静态方法,便于服务层使用。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserInfoDTO {
|
||||
/**
|
||||
* 用户ID,唯一标识
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户姓名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 用户电话号码
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 用户邮箱地址
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 从用户实体创建DTO对象的工厂方法
|
||||
*
|
||||
* @param user 用户实体对象
|
||||
* @return 对应的UserInfoDTO对象,如果输入为null则返回null
|
||||
*/
|
||||
public static UserInfoDTO fromEntity(UserAccount user) {
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
return new UserInfoDTO(user.getId(), user.getName(), user.getPhone(), user.getEmail());
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,33 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.validation.ValidPassword;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
// import jakarta.validation.constraints.Pattern;
|
||||
// import jakarta.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for user registration requests.
|
||||
* Carries data from the controller to the service layer.
|
||||
* Includes validation annotations to ensure data integrity.
|
||||
*
|
||||
* @param name The user's real name.
|
||||
* @param phone The user's unique phone number.
|
||||
* @param email The user's unique email address.
|
||||
* @param password The user's password, which must meet complexity requirements.
|
||||
*/
|
||||
public record UserRegistrationRequest(
|
||||
@NotEmpty(message = "姓名不能为空")
|
||||
String name,
|
||||
|
||||
@NotEmpty(message = "手机号不能为空")
|
||||
String phone,
|
||||
|
||||
@NotEmpty(message = "邮箱不能为空")
|
||||
@Email(message = "无效的邮箱格式")
|
||||
String email,
|
||||
|
||||
@NotEmpty(message = "密码不能为空")
|
||||
@ValidPassword
|
||||
String password
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.validation.ValidPassword;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
// import jakarta.validation.constraints.Pattern;
|
||||
// import jakarta.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* Data Transfer Object for user registration requests.
|
||||
* Carries data from the controller to the service layer.
|
||||
* Includes validation annotations to ensure data integrity.
|
||||
*
|
||||
* @param name The user's real name.
|
||||
* @param phone The user's unique phone number.
|
||||
* @param email The user's unique email address.
|
||||
* @param password The user's password, which must meet complexity requirements.
|
||||
*/
|
||||
public record UserRegistrationRequest(
|
||||
@NotEmpty(message = "姓名不能为空")
|
||||
String name,
|
||||
|
||||
@NotEmpty(message = "手机号不能为空")
|
||||
String phone,
|
||||
|
||||
@NotEmpty(message = "邮箱不能为空")
|
||||
@Email(message = "无效的邮箱格式")
|
||||
String email,
|
||||
|
||||
@NotEmpty(message = "密码不能为空")
|
||||
@ValidPassword
|
||||
String password
|
||||
) {}
|
||||
@@ -1,19 +1,19 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.Role;
|
||||
import com.dne.ems.validation.GridWorkerLocationRequired;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@GridWorkerLocationRequired
|
||||
public class UserRoleUpdateRequest {
|
||||
|
||||
@NotNull(message = "角色不能为空")
|
||||
private Role role;
|
||||
|
||||
private Integer gridX;
|
||||
|
||||
private Integer gridY;
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.Role;
|
||||
import com.dne.ems.validation.GridWorkerLocationRequired;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@GridWorkerLocationRequired
|
||||
public class UserRoleUpdateRequest {
|
||||
|
||||
@NotNull(message = "角色不能为空")
|
||||
private Role role;
|
||||
|
||||
private Integer gridX;
|
||||
|
||||
private Integer gridY;
|
||||
}
|
||||
@@ -1,28 +1,28 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.Role;
|
||||
import com.dne.ems.model.enums.UserStatus;
|
||||
|
||||
/**
|
||||
* 用户摘要数据传输对象
|
||||
*
|
||||
* <p>该类用于封装用户的摘要信息,主要用于用户列表展示和简单数据传输场景。
|
||||
* 包含用户的基本标识、个人信息、角色和状态等核心数据。</p>
|
||||
*
|
||||
* @param id 用户ID,唯一标识
|
||||
* @param name 用户姓名
|
||||
* @param email 用户邮箱地址
|
||||
* @param phone 用户电话号码
|
||||
* @param role 用户角色
|
||||
* @param status 用户账号状态
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public record UserSummaryDTO(
|
||||
Long id,
|
||||
String name,
|
||||
String email,
|
||||
String phone,
|
||||
Role role,
|
||||
UserStatus status
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.Role;
|
||||
import com.dne.ems.model.enums.UserStatus;
|
||||
|
||||
/**
|
||||
* 用户摘要数据传输对象
|
||||
*
|
||||
* <p>该类用于封装用户的摘要信息,主要用于用户列表展示和简单数据传输场景。
|
||||
* 包含用户的基本标识、个人信息、角色和状态等核心数据。</p>
|
||||
*
|
||||
* @param id 用户ID,唯一标识
|
||||
* @param name 用户姓名
|
||||
* @param email 用户邮箱地址
|
||||
* @param phone 用户电话号码
|
||||
* @param role 用户角色
|
||||
* @param status 用户账号状态
|
||||
* @version 1.0
|
||||
* @since 2024
|
||||
*/
|
||||
public record UserSummaryDTO(
|
||||
Long id,
|
||||
String name,
|
||||
String email,
|
||||
String phone,
|
||||
Role role,
|
||||
UserStatus status
|
||||
) {}
|
||||
@@ -1,39 +1,39 @@
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.Role;
|
||||
import com.dne.ems.model.enums.UserStatus;
|
||||
import com.dne.ems.model.enums.Gender;
|
||||
import com.dne.ems.model.enums.Level;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A DTO for updating user account information.
|
||||
*
|
||||
* @param name The new name for the user. Can be null if not updating.
|
||||
* @param phone The new phone for the user. Can be null if not updating.
|
||||
* @param region The new region for the user. Can be null if not updating.
|
||||
* @param role The new role for the user. Can be null if not updating.
|
||||
* @param status The new status for the user. Can be null if not updating.
|
||||
* @param gender The new gender for the user. Can be null if not updating.
|
||||
* @param gridX The new grid X coordinate.
|
||||
* @param gridY The new grid Y coordinate.
|
||||
* @param level The new level for the user.
|
||||
* @param skills The new skills for the user.
|
||||
* @param currentLatitude The new latitude.
|
||||
* @param currentLongitude The new longitude.
|
||||
*/
|
||||
public record UserUpdateRequest(
|
||||
String name,
|
||||
String phone,
|
||||
String region,
|
||||
Role role,
|
||||
UserStatus status,
|
||||
Gender gender,
|
||||
Integer gridX,
|
||||
Integer gridY,
|
||||
Level level,
|
||||
List<String> skills,
|
||||
Double currentLatitude,
|
||||
Double currentLongitude
|
||||
package com.dne.ems.dto;
|
||||
|
||||
import com.dne.ems.model.enums.Role;
|
||||
import com.dne.ems.model.enums.UserStatus;
|
||||
import com.dne.ems.model.enums.Gender;
|
||||
import com.dne.ems.model.enums.Level;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A DTO for updating user account information.
|
||||
*
|
||||
* @param name The new name for the user. Can be null if not updating.
|
||||
* @param phone The new phone for the user. Can be null if not updating.
|
||||
* @param region The new region for the user. Can be null if not updating.
|
||||
* @param role The new role for the user. Can be null if not updating.
|
||||
* @param status The new status for the user. Can be null if not updating.
|
||||
* @param gender The new gender for the user. Can be null if not updating.
|
||||
* @param gridX The new grid X coordinate.
|
||||
* @param gridY The new grid Y coordinate.
|
||||
* @param level The new level for the user.
|
||||
* @param skills The new skills for the user.
|
||||
* @param currentLatitude The new latitude.
|
||||
* @param currentLongitude The new longitude.
|
||||
*/
|
||||
public record UserUpdateRequest(
|
||||
String name,
|
||||
String phone,
|
||||
String region,
|
||||
Role role,
|
||||
UserStatus status,
|
||||
Gender gender,
|
||||
Integer gridX,
|
||||
Integer gridY,
|
||||
Level level,
|
||||
List<String> skills,
|
||||
Double currentLatitude,
|
||||
Double currentLongitude
|
||||
) {}
|
||||
@@ -1,145 +1,145 @@
|
||||
package com.dne.ems.dto.ai;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* 火山引擎聊天API请求DTO
|
||||
*
|
||||
* <p>该类封装了向火山引擎AI聊天服务发送请求所需的所有数据结构。
|
||||
* 包括模型名称、消息列表和工具列表等核心组件。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025-06
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class VolcanoChatRequest {
|
||||
/**
|
||||
* 要使用的AI模型名称
|
||||
*/
|
||||
private String model;
|
||||
|
||||
/**
|
||||
* 对话消息列表,包含用户和系统的对话历史
|
||||
*/
|
||||
private List<Message> messages;
|
||||
|
||||
/**
|
||||
* 可用工具列表,定义AI可以调用的函数
|
||||
*/
|
||||
private List<Tool> tools;
|
||||
|
||||
/**
|
||||
* 表示对话中的一条消息
|
||||
* <p>包含角色信息和内容部分列表</p>
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
public static class Message {
|
||||
private String role;
|
||||
private List<ContentPart> content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内容部分接口,用于定义消息内容的不同类型
|
||||
* <p>作为TextContentPart和ImageUrlContentPart的共同父接口</p>
|
||||
*/
|
||||
public interface ContentPart {
|
||||
}
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
public static class TextContentPart implements ContentPart {
|
||||
/**
|
||||
* 内容类型,固定为"text"
|
||||
*/
|
||||
@Builder.Default
|
||||
private String type = "text";
|
||||
|
||||
/**
|
||||
* 文本内容
|
||||
*/
|
||||
private String text;
|
||||
}
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
public static class ImageUrlContentPart implements ContentPart {
|
||||
/**
|
||||
* 内容类型,固定为"image_url"
|
||||
*/
|
||||
@Builder.Default
|
||||
private String type = "image_url";
|
||||
|
||||
/**
|
||||
* 图片URL信息
|
||||
*/
|
||||
@JsonProperty("image_url")
|
||||
private ImageUrl imageUrl;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public static class ImageUrl {
|
||||
/**
|
||||
* 图片的URL地址
|
||||
*/
|
||||
private String url;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public static class Tool {
|
||||
/**
|
||||
* 工具类型
|
||||
*/
|
||||
private String type;
|
||||
/**
|
||||
* 函数定义
|
||||
*/
|
||||
private FunctionDef function;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public static class FunctionDef {
|
||||
/**
|
||||
* 函数名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 函数描述
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 函数参数定义
|
||||
*/
|
||||
private Parameters parameters;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public static class Parameters {
|
||||
/**
|
||||
* 参数类型
|
||||
*/
|
||||
private String type;
|
||||
/**
|
||||
* 参数属性定义
|
||||
*/
|
||||
private Object properties;
|
||||
/**
|
||||
* 必需的参数列表
|
||||
*/
|
||||
@JsonProperty("required")
|
||||
private List<String> required;
|
||||
}
|
||||
package com.dne.ems.dto.ai;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* 火山引擎聊天API请求DTO
|
||||
*
|
||||
* <p>该类封装了向火山引擎AI聊天服务发送请求所需的所有数据结构。
|
||||
* 包括模型名称、消息列表和工具列表等核心组件。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025-06
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class VolcanoChatRequest {
|
||||
/**
|
||||
* 要使用的AI模型名称
|
||||
*/
|
||||
private String model;
|
||||
|
||||
/**
|
||||
* 对话消息列表,包含用户和系统的对话历史
|
||||
*/
|
||||
private List<Message> messages;
|
||||
|
||||
/**
|
||||
* 可用工具列表,定义AI可以调用的函数
|
||||
*/
|
||||
private List<Tool> tools;
|
||||
|
||||
/**
|
||||
* 表示对话中的一条消息
|
||||
* <p>包含角色信息和内容部分列表</p>
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
public static class Message {
|
||||
private String role;
|
||||
private List<ContentPart> content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内容部分接口,用于定义消息内容的不同类型
|
||||
* <p>作为TextContentPart和ImageUrlContentPart的共同父接口</p>
|
||||
*/
|
||||
public interface ContentPart {
|
||||
}
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
public static class TextContentPart implements ContentPart {
|
||||
/**
|
||||
* 内容类型,固定为"text"
|
||||
*/
|
||||
@Builder.Default
|
||||
private String type = "text";
|
||||
|
||||
/**
|
||||
* 文本内容
|
||||
*/
|
||||
private String text;
|
||||
}
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
public static class ImageUrlContentPart implements ContentPart {
|
||||
/**
|
||||
* 内容类型,固定为"image_url"
|
||||
*/
|
||||
@Builder.Default
|
||||
private String type = "image_url";
|
||||
|
||||
/**
|
||||
* 图片URL信息
|
||||
*/
|
||||
@JsonProperty("image_url")
|
||||
private ImageUrl imageUrl;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public static class ImageUrl {
|
||||
/**
|
||||
* 图片的URL地址
|
||||
*/
|
||||
private String url;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public static class Tool {
|
||||
/**
|
||||
* 工具类型
|
||||
*/
|
||||
private String type;
|
||||
/**
|
||||
* 函数定义
|
||||
*/
|
||||
private FunctionDef function;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public static class FunctionDef {
|
||||
/**
|
||||
* 函数名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 函数描述
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 函数参数定义
|
||||
*/
|
||||
private Parameters parameters;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public static class Parameters {
|
||||
/**
|
||||
* 参数类型
|
||||
*/
|
||||
private String type;
|
||||
/**
|
||||
* 参数属性定义
|
||||
*/
|
||||
private Object properties;
|
||||
/**
|
||||
* 必需的参数列表
|
||||
*/
|
||||
@JsonProperty("required")
|
||||
private List<String> required;
|
||||
}
|
||||
}
|
||||
@@ -1,85 +1,85 @@
|
||||
package com.dne.ems.dto.ai;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 火山引擎聊天API响应DTO
|
||||
*
|
||||
* <p>该类封装了从火山引擎AI聊天服务接收到的响应数据结构。
|
||||
* 包含AI生成的回复内容和可能的工具调用信息。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025-06
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class VolcanoChatResponse {
|
||||
private List<Choice> choices;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class Choice {
|
||||
/**
|
||||
* 包含AI回复的消息对象
|
||||
*/
|
||||
private Message message;
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class Message {
|
||||
/**
|
||||
* 消息的角色(如system、user、assistant)
|
||||
*/
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* 消息的文本内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 工具调用列表,包含AI请求调用的工具信息
|
||||
*/
|
||||
@JsonProperty("tool_calls")
|
||||
private List<ToolCall> toolCalls;
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class ToolCall {
|
||||
/**
|
||||
* 工具调用的唯一标识符
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 工具调用的类型
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 函数调用信息
|
||||
*/
|
||||
private FunctionCall function;
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class FunctionCall {
|
||||
/**
|
||||
* 被调用的函数名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 函数调用的参数,JSON格式字符串
|
||||
*/
|
||||
private String arguments;
|
||||
}
|
||||
package com.dne.ems.dto.ai;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 火山引擎聊天API响应DTO
|
||||
*
|
||||
* <p>该类封装了从火山引擎AI聊天服务接收到的响应数据结构。
|
||||
* 包含AI生成的回复内容和可能的工具调用信息。</p>
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025-06
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class VolcanoChatResponse {
|
||||
private List<Choice> choices;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class Choice {
|
||||
/**
|
||||
* 包含AI回复的消息对象
|
||||
*/
|
||||
private Message message;
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class Message {
|
||||
/**
|
||||
* 消息的角色(如system、user、assistant)
|
||||
*/
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* 消息的文本内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 工具调用列表,包含AI请求调用的工具信息
|
||||
*/
|
||||
@JsonProperty("tool_calls")
|
||||
private List<ToolCall> toolCalls;
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class ToolCall {
|
||||
/**
|
||||
* 工具调用的唯一标识符
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 工具调用的类型
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 函数调用信息
|
||||
*/
|
||||
private FunctionCall function;
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class FunctionCall {
|
||||
/**
|
||||
* 被调用的函数名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 函数调用的参数,JSON格式字符串
|
||||
*/
|
||||
private String arguments;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
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;
|
||||
}
|
||||
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,18 +1,18 @@
|
||||
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;
|
||||
}
|
||||
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,24 +1,24 @@
|
||||
package com.dne.ems.exception;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* A standard DTO for returning API error responses.
|
||||
* This class provides a consistent error structure for all API endpoints.
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ErrorResponseDTO {
|
||||
|
||||
private LocalDateTime timestamp;
|
||||
private int status;
|
||||
private String error;
|
||||
private String message;
|
||||
private String path;
|
||||
|
||||
package com.dne.ems.exception;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* A standard DTO for returning API error responses.
|
||||
* This class provides a consistent error structure for all API endpoints.
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ErrorResponseDTO {
|
||||
|
||||
private LocalDateTime timestamp;
|
||||
private int status;
|
||||
private String error;
|
||||
private String message;
|
||||
private String path;
|
||||
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
package com.dne.ems.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* Custom exception thrown when a requested file is not found.
|
||||
* Annotated with @ResponseStatus to automatically return a 404 Not Found HTTP status.
|
||||
*/
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
public class FileNotFoundException extends RuntimeException {
|
||||
|
||||
public FileNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public FileNotFoundException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
package com.dne.ems.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* Custom exception thrown when a requested file is not found.
|
||||
* Annotated with @ResponseStatus to automatically return a 404 Not Found HTTP status.
|
||||
*/
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
public class FileNotFoundException extends RuntimeException {
|
||||
|
||||
public FileNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public FileNotFoundException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user