初始提交:DesignHub设计师任务管理系统
This commit is contained in:
commit
35e4f8f677
68
.gitignore
vendored
Normal file
68
.gitignore
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
dist/
|
||||||
|
dist-ssr/
|
||||||
|
build/
|
||||||
|
target/
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.ear
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Environment files
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Database
|
||||||
|
*.sql~
|
||||||
|
*.db
|
||||||
|
|
||||||
|
# OS
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
|
||||||
|
# Java
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.ctxt
|
||||||
|
.mtj.tmp/
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
# Maven
|
||||||
|
target/
|
||||||
|
pom.xml.tag
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
pom.xml.next
|
||||||
|
release.properties
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
buildNumber.properties
|
||||||
|
.mvn/timing.properties
|
||||||
|
.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
# Spring Boot
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache/
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
dump.rdb
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
|
||||||
850
DEPLOYMENT_GUIDE.md
Normal file
850
DEPLOYMENT_GUIDE.md
Normal file
@ -0,0 +1,850 @@
|
|||||||
|
# 🚀 DesignHub 部署指南
|
||||||
|
|
||||||
|
本文档详细说明如何在本地开发环境和生产环境中部署 DesignHub 系统。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 目录
|
||||||
|
|
||||||
|
1. [环境要求](#环境要求)
|
||||||
|
2. [本地开发环境部署](#本地开发环境部署)
|
||||||
|
3. [生产环境部署](#生产环境部署)
|
||||||
|
4. [Docker部署](#docker部署)
|
||||||
|
5. [常见问题](#常见问题)
|
||||||
|
6. [维护与监控](#维护与监控)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 环境要求
|
||||||
|
|
||||||
|
### 开发环境
|
||||||
|
| 组件 | 版本要求 | 说明 |
|
||||||
|
|------|---------|------|
|
||||||
|
| JDK | 17+ | 建议使用 OpenJDK 17 |
|
||||||
|
| Node.js | 18+ | 建议使用 LTS 版本 |
|
||||||
|
| MySQL | 8.0+ | 必须 |
|
||||||
|
| Redis | 6.0+ | 建议 7.0+ |
|
||||||
|
| Maven | 3.6+ | 后端构建工具 |
|
||||||
|
| pnpm | 8.0+ | 前端包管理器(可选npm/yarn) |
|
||||||
|
|
||||||
|
### 生产环境
|
||||||
|
| 组件 | 版本要求 | 配置建议 |
|
||||||
|
|------|---------|---------|
|
||||||
|
| 服务器 | Linux | CentOS 7+, Ubuntu 20.04+ |
|
||||||
|
| CPU | 4核+ | 推荐 8核 |
|
||||||
|
| 内存 | 8GB+ | 推荐 16GB |
|
||||||
|
| 磁盘 | 50GB+ | SSD推荐 |
|
||||||
|
| 带宽 | 10Mbps+ | 推荐 100Mbps |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💻 本地开发环境部署
|
||||||
|
|
||||||
|
### 1. 克隆项目
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 克隆代码仓库
|
||||||
|
cd /Users/lanmu/Desktop/qiangdanxitong
|
||||||
|
# 或使用 Git
|
||||||
|
# git clone <repository_url>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 配置数据库
|
||||||
|
|
||||||
|
#### 2.1 安装 MySQL 8.0
|
||||||
|
|
||||||
|
**macOS**:
|
||||||
|
```bash
|
||||||
|
brew install mysql@8.0
|
||||||
|
brew services start mysql@8.0
|
||||||
|
```
|
||||||
|
|
||||||
|
**Linux**:
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install mysql-server
|
||||||
|
sudo systemctl start mysql
|
||||||
|
sudo systemctl enable mysql
|
||||||
|
```
|
||||||
|
|
||||||
|
**Windows**:
|
||||||
|
- 下载MySQL 8.0安装包
|
||||||
|
- 运行安装向导
|
||||||
|
|
||||||
|
#### 2.2 创建数据库
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 登录MySQL
|
||||||
|
mysql -u root -p
|
||||||
|
|
||||||
|
-- 创建数据库
|
||||||
|
CREATE DATABASE design_hub CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- 创建用户(可选)
|
||||||
|
CREATE USER 'designhub'@'localhost' IDENTIFIED BY 'your_password';
|
||||||
|
GRANT ALL PRIVILEGES ON design_hub.* TO 'designhub'@'localhost';
|
||||||
|
FLUSH PRIVILEGES;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.3 导入数据库表
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 执行数据库脚本(如果有SQL文件)
|
||||||
|
mysql -u root -p design_hub < database/schema.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意**:项目使用MyBatis Plus,实体类会自动映射到数据库表。确保在启动前,数据库连接配置正确。
|
||||||
|
|
||||||
|
### 3. 配置 Redis
|
||||||
|
|
||||||
|
#### 3.1 安装 Redis
|
||||||
|
|
||||||
|
**macOS**:
|
||||||
|
```bash
|
||||||
|
brew install redis
|
||||||
|
brew services start redis
|
||||||
|
```
|
||||||
|
|
||||||
|
**Linux**:
|
||||||
|
```bash
|
||||||
|
sudo apt install redis-server
|
||||||
|
sudo systemctl start redis
|
||||||
|
sudo systemctl enable redis
|
||||||
|
```
|
||||||
|
|
||||||
|
**Windows**:
|
||||||
|
- 下载Redis for Windows
|
||||||
|
- 解压并运行 `redis-server.exe`
|
||||||
|
|
||||||
|
#### 3.2 验证Redis
|
||||||
|
```bash
|
||||||
|
redis-cli ping
|
||||||
|
# 应返回:PONG
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 配置后端
|
||||||
|
|
||||||
|
#### 4.1 编辑配置文件
|
||||||
|
|
||||||
|
编辑 `/backend/src/main/resources/application.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
url: jdbc:mysql://localhost:3306/design_hub?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
|
||||||
|
username: root # 修改为你的MySQL用户名
|
||||||
|
password: your_password # 修改为你的MySQL密码
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
|
||||||
|
redis:
|
||||||
|
host: localhost
|
||||||
|
port: 6379
|
||||||
|
password: # 如果Redis没有密码,留空
|
||||||
|
database: 0
|
||||||
|
timeout: 3000
|
||||||
|
|
||||||
|
jwt:
|
||||||
|
secret: your-secret-key-change-this-in-production # 生产环境请修改
|
||||||
|
access-token-expiration: 7200000 # 2小时
|
||||||
|
refresh-token-expiration: 604800000 # 7天
|
||||||
|
|
||||||
|
server:
|
||||||
|
port: 8080
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.2 构建并运行后端
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
|
||||||
|
# 使用 Maven 构建
|
||||||
|
mvn clean install -DskipTests
|
||||||
|
|
||||||
|
# 运行后端
|
||||||
|
mvn spring-boot:run
|
||||||
|
|
||||||
|
# 或者使用 JAR 文件
|
||||||
|
java -jar target/designhub-backend-1.0.0.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
**后端启动成功标志**:
|
||||||
|
- 控制台输出 `Started DesignHubApplication`
|
||||||
|
- 访问 http://localhost:8080/swagger-ui.html 查看API文档
|
||||||
|
|
||||||
|
### 5. 配置前端
|
||||||
|
|
||||||
|
#### 5.1 安装依赖
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
|
||||||
|
# 使用 pnpm(推荐)
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
# 或使用 npm
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# 或使用 yarn
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.2 配置环境变量
|
||||||
|
|
||||||
|
创建 `/frontend/.env.development`:
|
||||||
|
|
||||||
|
```env
|
||||||
|
# 开发环境配置
|
||||||
|
VITE_APP_BASE_API=http://localhost:8080/api
|
||||||
|
VITE_APP_TITLE=DesignHub
|
||||||
|
VITE_APP_VERSION=6.0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.3 运行前端
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 开发模式
|
||||||
|
pnpm dev
|
||||||
|
|
||||||
|
# 或
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
**前端启动成功标志**:
|
||||||
|
- 控制台输出本地访问地址(默认:http://localhost:5173)
|
||||||
|
- 浏览器自动打开页面
|
||||||
|
|
||||||
|
### 6. 访问系统
|
||||||
|
|
||||||
|
打开浏览器,访问 `http://localhost:5173`
|
||||||
|
|
||||||
|
**默认测试账号**:
|
||||||
|
- 用户名:`admin`
|
||||||
|
- 密码:`123456`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌐 生产环境部署
|
||||||
|
|
||||||
|
### 1. 服务器准备
|
||||||
|
|
||||||
|
#### 1.1 更新系统
|
||||||
|
```bash
|
||||||
|
# CentOS/RHEL
|
||||||
|
sudo yum update -y
|
||||||
|
|
||||||
|
# Ubuntu/Debian
|
||||||
|
sudo apt update && sudo apt upgrade -y
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 1.2 安装必要软件
|
||||||
|
```bash
|
||||||
|
# 安装 JDK 17
|
||||||
|
sudo yum install java-17-openjdk -y
|
||||||
|
|
||||||
|
# 安装 Nginx
|
||||||
|
sudo yum install nginx -y
|
||||||
|
|
||||||
|
# 安装 Git(如需)
|
||||||
|
sudo yum install git -y
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 部署数据库
|
||||||
|
|
||||||
|
#### 2.1 安装 MySQL 8.0
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 下载MySQL Yum Repository
|
||||||
|
wget https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
|
||||||
|
|
||||||
|
# 安装Repository
|
||||||
|
sudo rpm -ivh mysql80-community-release-el7-3.noarch.rpm
|
||||||
|
|
||||||
|
# 安装MySQL
|
||||||
|
sudo yum install mysql-server -y
|
||||||
|
|
||||||
|
# 启动MySQL
|
||||||
|
sudo systemctl start mysqld
|
||||||
|
sudo systemctl enable mysqld
|
||||||
|
|
||||||
|
# 获取临时密码
|
||||||
|
sudo grep 'temporary password' /var/log/mysqld.log
|
||||||
|
|
||||||
|
# 修改root密码
|
||||||
|
mysql_secure_installation
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.2 创建数据库和用户
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE DATABASE design_hub CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
CREATE USER 'designhub'@'%' IDENTIFIED BY 'StrongPassword123!';
|
||||||
|
GRANT ALL PRIVILEGES ON design_hub.* TO 'designhub'@'%';
|
||||||
|
FLUSH PRIVILEGES;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.3 导入数据
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mysql -u designhub -p design_hub < /path/to/schema.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 部署 Redis
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装Redis
|
||||||
|
sudo yum install redis -y
|
||||||
|
|
||||||
|
# 配置Redis(可选设置密码)
|
||||||
|
sudo vi /etc/redis.conf
|
||||||
|
# 找到 requirepass,设置密码
|
||||||
|
requirepass YourStrongPassword
|
||||||
|
|
||||||
|
# 启动Redis
|
||||||
|
sudo systemctl start redis
|
||||||
|
sudo systemctl enable redis
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 部署后端
|
||||||
|
|
||||||
|
#### 4.1 上传代码
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 创建应用目录
|
||||||
|
sudo mkdir -p /opt/designhub/backend
|
||||||
|
sudo chown -R $USER:$USER /opt/designhub
|
||||||
|
|
||||||
|
# 上传代码(使用scp、rsync或git)
|
||||||
|
# scp方式
|
||||||
|
scp -r backend/* user@server:/opt/designhub/backend/
|
||||||
|
|
||||||
|
# 或使用git
|
||||||
|
cd /opt/designhub
|
||||||
|
git clone <repository_url>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.2 配置生产环境
|
||||||
|
|
||||||
|
编辑 `/opt/designhub/backend/src/main/resources/application-prod.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
url: jdbc:mysql://localhost:3306/design_hub?useSSL=true
|
||||||
|
username: designhub
|
||||||
|
password: StrongPassword123!
|
||||||
|
|
||||||
|
redis:
|
||||||
|
host: localhost
|
||||||
|
port: 6379
|
||||||
|
password: YourStrongPassword
|
||||||
|
|
||||||
|
jwt:
|
||||||
|
secret: change-this-to-a-very-strong-secret-key-in-production
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
root: INFO
|
||||||
|
com.designhub: INFO
|
||||||
|
file:
|
||||||
|
name: /opt/designhub/logs/application.log
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.3 构建后端
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/designhub/backend
|
||||||
|
|
||||||
|
# 构建
|
||||||
|
mvn clean package -DskipTests -Pprod
|
||||||
|
|
||||||
|
# 移动JAR文件
|
||||||
|
mv target/designhub-backend-1.0.0.jar /opt/designhub/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.4 创建系统服务
|
||||||
|
|
||||||
|
创建 `/etc/systemd/system/designhub-backend.service`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=DesignHub Backend Service
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=designhub
|
||||||
|
WorkingDirectory=/opt/designhub
|
||||||
|
ExecStart=/usr/bin/java -jar /opt/designhub/designhub-backend-1.0.0.jar --spring.profiles.active=prod
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.5 启动后端服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 重新加载systemd
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
|
||||||
|
# 启动服务
|
||||||
|
sudo systemctl start designhub-backend
|
||||||
|
|
||||||
|
# 设置开机自启
|
||||||
|
sudo systemctl enable designhub-backend
|
||||||
|
|
||||||
|
# 查看状态
|
||||||
|
sudo systemctl status designhub-backend
|
||||||
|
|
||||||
|
# 查看日志
|
||||||
|
sudo journalctl -u designhub-backend -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 部署前端
|
||||||
|
|
||||||
|
#### 5.1 构建前端
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/designhub/frontend
|
||||||
|
|
||||||
|
# 安装依赖
|
||||||
|
pnpm install --production
|
||||||
|
|
||||||
|
# 创建生产环境配置
|
||||||
|
cat > .env.production << EOF
|
||||||
|
VITE_APP_BASE_API=https://api.yourdomain.com/api
|
||||||
|
VITE_APP_TITLE=DesignHub
|
||||||
|
VITE_APP_VERSION=6.0
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 构建
|
||||||
|
pnpm build
|
||||||
|
|
||||||
|
# 构建产物在 dist/ 目录
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.2 配置 Nginx
|
||||||
|
|
||||||
|
创建 `/etc/nginx/conf.d/designhub.conf`:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name yourdomain.com www.yourdomain.com;
|
||||||
|
|
||||||
|
# 前端静态资源
|
||||||
|
root /opt/designhub/frontend/dist;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# 前端路由
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# API代理
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://localhost:8080/api/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
# WebSocket支持(如需)
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Gzip压缩
|
||||||
|
gzip on;
|
||||||
|
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||||
|
|
||||||
|
# 静态资源缓存
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
|
||||||
|
expires 30d;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.3 配置 HTTPS (Let's Encrypt)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装certbot
|
||||||
|
sudo yum install certbot python3-certbot-nginx -y
|
||||||
|
|
||||||
|
# 获取SSL证书
|
||||||
|
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
|
||||||
|
|
||||||
|
# 自动续期测试
|
||||||
|
sudo certbot renew --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.4 启动 Nginx
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 测试配置
|
||||||
|
sudo nginx -t
|
||||||
|
|
||||||
|
# 启动Nginx
|
||||||
|
sudo systemctl start nginx
|
||||||
|
sudo systemctl enable nginx
|
||||||
|
|
||||||
|
# 重新加载配置
|
||||||
|
sudo systemctl reload nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. 防火墙配置
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# CentOS/RHEL (firewalld)
|
||||||
|
sudo firewall-cmd --permanent --add-service=http
|
||||||
|
sudo firewall-cmd --permanent --add-service=https
|
||||||
|
sudo firewall-cmd --reload
|
||||||
|
|
||||||
|
# Ubuntu (ufw)
|
||||||
|
sudo ufw allow 80/tcp
|
||||||
|
sudo ufw allow 443/tcp
|
||||||
|
sudo ufw enable
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐳 Docker 部署
|
||||||
|
|
||||||
|
### 1. 后端 Dockerfile
|
||||||
|
|
||||||
|
创建 `/backend/Dockerfile`:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM openjdk:17-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 复制JAR文件
|
||||||
|
COPY target/designhub-backend-1.0.0.jar app.jar
|
||||||
|
|
||||||
|
# 设置时区
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||||
|
|
||||||
|
# 暴露端口
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# 启动命令
|
||||||
|
ENTRYPOINT ["java", "-jar", "app.jar", "--spring.profiles.active=prod"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 前端 Dockerfile
|
||||||
|
|
||||||
|
创建 `/frontend/Dockerfile`:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM node:18-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 复制package文件
|
||||||
|
COPY package*.json ./
|
||||||
|
COPY pnpm-lock.yaml ./
|
||||||
|
|
||||||
|
# 安装依赖
|
||||||
|
RUN npm install -g pnpm
|
||||||
|
RUN pnpm install
|
||||||
|
|
||||||
|
# 复制源代码
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 构建
|
||||||
|
RUN pnpm build
|
||||||
|
|
||||||
|
# 生产环境
|
||||||
|
FROM nginx:alpine
|
||||||
|
|
||||||
|
# 复制构建产物
|
||||||
|
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||||
|
|
||||||
|
# 复制Nginx配置
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Docker Compose
|
||||||
|
|
||||||
|
创建 `/docker-compose.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
container_name: designhub-mysql
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: rootpassword
|
||||||
|
MYSQL_DATABASE: design_hub
|
||||||
|
MYSQL_USER: designhub
|
||||||
|
MYSQL_PASSWORD: designhubpass
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
volumes:
|
||||||
|
- mysql_data:/var/lib/mysql
|
||||||
|
networks:
|
||||||
|
- designhub-network
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
container_name: designhub-redis
|
||||||
|
command: redis-server --requirepass redispassword
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
networks:
|
||||||
|
- designhub-network
|
||||||
|
|
||||||
|
backend:
|
||||||
|
build: ./backend
|
||||||
|
container_name: designhub-backend
|
||||||
|
environment:
|
||||||
|
SPRING_PROFILES_ACTIVE: prod
|
||||||
|
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/design_hub
|
||||||
|
SPRING_DATASOURCE_USERNAME: designhub
|
||||||
|
SPRING_DATASOURCE_PASSWORD: designhubpass
|
||||||
|
SPRING_REDIS_HOST: redis
|
||||||
|
SPRING_REDIS_PASSWORD: redispassword
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
depends_on:
|
||||||
|
- mysql
|
||||||
|
- redis
|
||||||
|
networks:
|
||||||
|
- designhub-network
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build: ./frontend
|
||||||
|
container_name: designhub-frontend
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
networks:
|
||||||
|
- designhub-network
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mysql_data:
|
||||||
|
redis_data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
designhub-network:
|
||||||
|
driver: bridge
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 启动 Docker Compose
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 构建并启动所有服务
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# 查看日志
|
||||||
|
docker-compose logs -f
|
||||||
|
|
||||||
|
# 停止所有服务
|
||||||
|
docker-compose down
|
||||||
|
|
||||||
|
# 重启服务
|
||||||
|
docker-compose restart
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ❓ 常见问题
|
||||||
|
|
||||||
|
### 1. 后端无法连接数据库
|
||||||
|
|
||||||
|
**问题**:`java.sql.SQLException: Access denied for user`
|
||||||
|
|
||||||
|
**解决**:
|
||||||
|
```sql
|
||||||
|
-- 检查用户权限
|
||||||
|
SELECT User, Host FROM mysql.user;
|
||||||
|
|
||||||
|
-- 重新授权
|
||||||
|
GRANT ALL PRIVILEGES ON design_hub.* TO 'designhub'@'%' IDENTIFIED BY 'password';
|
||||||
|
FLUSH PRIVILEGES;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Redis连接失败
|
||||||
|
|
||||||
|
**问题**:`Could not connect to Redis at localhost:6379`
|
||||||
|
|
||||||
|
**解决**:
|
||||||
|
```bash
|
||||||
|
# 检查Redis是否运行
|
||||||
|
sudo systemctl status redis
|
||||||
|
|
||||||
|
# 检查端口
|
||||||
|
netstat -tuln | grep 6379
|
||||||
|
|
||||||
|
# 测试连接
|
||||||
|
redis-cli -h localhost -p 6379 -a password ping
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 前端API请求跨域
|
||||||
|
|
||||||
|
**问题**:`CORS policy: No 'Access-Control-Allow-Origin' header`
|
||||||
|
|
||||||
|
**解决**:后端添加CORS配置(已在项目中配置)
|
||||||
|
|
||||||
|
### 4. JWT Token过期
|
||||||
|
|
||||||
|
**问题**:频繁退出登录
|
||||||
|
|
||||||
|
**解决**:调整Token过期时间或实现自动刷新机制(已实现)
|
||||||
|
|
||||||
|
### 5. 文件上传失败
|
||||||
|
|
||||||
|
**问题**:上传大文件失败
|
||||||
|
|
||||||
|
**解决**:
|
||||||
|
```yaml
|
||||||
|
# application.yml
|
||||||
|
spring:
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 100MB
|
||||||
|
max-request-size: 100MB
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ 维护与监控
|
||||||
|
|
||||||
|
### 1. 日志管理
|
||||||
|
|
||||||
|
#### 后端日志
|
||||||
|
```bash
|
||||||
|
# 查看实时日志
|
||||||
|
tail -f /opt/designhub/logs/application.log
|
||||||
|
|
||||||
|
# 按日期查看
|
||||||
|
less /opt/designhub/logs/application-2025-11-07.log
|
||||||
|
|
||||||
|
# 日志清理(保留30天)
|
||||||
|
find /opt/designhub/logs -name "*.log" -mtime +30 -delete
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Nginx日志
|
||||||
|
```bash
|
||||||
|
# 访问日志
|
||||||
|
tail -f /var/log/nginx/access.log
|
||||||
|
|
||||||
|
# 错误日志
|
||||||
|
tail -f /var/log/nginx/error.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 数据库备份
|
||||||
|
|
||||||
|
#### 自动备份脚本
|
||||||
|
|
||||||
|
创建 `/opt/designhub/scripts/backup_db.sh`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 配置
|
||||||
|
DB_NAME="design_hub"
|
||||||
|
DB_USER="designhub"
|
||||||
|
DB_PASS="password"
|
||||||
|
BACKUP_DIR="/opt/designhub/backups"
|
||||||
|
DATE=$(date +%Y%m%d_%H%M%S)
|
||||||
|
|
||||||
|
# 创建备份目录
|
||||||
|
mkdir -p $BACKUP_DIR
|
||||||
|
|
||||||
|
# 备份数据库
|
||||||
|
mysqldump -u$DB_USER -p$DB_PASS $DB_NAME > $BACKUP_DIR/backup_$DATE.sql
|
||||||
|
|
||||||
|
# 压缩备份
|
||||||
|
gzip $BACKUP_DIR/backup_$DATE.sql
|
||||||
|
|
||||||
|
# 删除7天前的备份
|
||||||
|
find $BACKUP_DIR -name "backup_*.sql.gz" -mtime +7 -delete
|
||||||
|
|
||||||
|
echo "Backup completed: backup_$DATE.sql.gz"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 设置定时任务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 编辑crontab
|
||||||
|
crontab -e
|
||||||
|
|
||||||
|
# 每天凌晨2点备份
|
||||||
|
0 2 * * * /opt/designhub/scripts/backup_db.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 性能监控
|
||||||
|
|
||||||
|
#### 系统资源监控
|
||||||
|
```bash
|
||||||
|
# CPU和内存
|
||||||
|
top
|
||||||
|
|
||||||
|
# 磁盘使用
|
||||||
|
df -h
|
||||||
|
|
||||||
|
# 网络连接
|
||||||
|
netstat -tuln
|
||||||
|
|
||||||
|
# 进程监控
|
||||||
|
ps aux | grep java
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 应用监控
|
||||||
|
|
||||||
|
**Spring Boot Actuator**(已集成):
|
||||||
|
- 访问 http://yourdomain.com/actuator/health
|
||||||
|
- 访问 http://yourdomain.com/actuator/metrics
|
||||||
|
|
||||||
|
### 4. 安全加固
|
||||||
|
|
||||||
|
#### 系统安全
|
||||||
|
```bash
|
||||||
|
# 关闭不必要的服务
|
||||||
|
sudo systemctl disable bluetooth
|
||||||
|
sudo systemctl disable cups
|
||||||
|
|
||||||
|
# 更新安全补丁
|
||||||
|
sudo yum update --security -y
|
||||||
|
|
||||||
|
# 配置fail2ban
|
||||||
|
sudo yum install fail2ban -y
|
||||||
|
sudo systemctl enable fail2ban
|
||||||
|
sudo systemctl start fail2ban
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 应用安全
|
||||||
|
- 定期更新依赖版本
|
||||||
|
- 使用强密码
|
||||||
|
- 启用HTTPS
|
||||||
|
- 配置防火墙
|
||||||
|
- 定期安全审计
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 技术支持
|
||||||
|
|
||||||
|
如遇到部署问题,请:
|
||||||
|
1. 查看本文档"常见问题"部分
|
||||||
|
2. 检查日志文件
|
||||||
|
3. 联系技术支持团队
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**祝您部署顺利!** 🎉
|
||||||
|
|
||||||
418
DEVELOPMENT_GUIDE.md
Normal file
418
DEVELOPMENT_GUIDE.md
Normal file
@ -0,0 +1,418 @@
|
|||||||
|
# DesignHub 开发启动指南
|
||||||
|
|
||||||
|
## 📋 目录
|
||||||
|
|
||||||
|
- [环境准备](#环境准备)
|
||||||
|
- [项目初始化](#项目初始化)
|
||||||
|
- [数据库配置](#数据库配置)
|
||||||
|
- [后端启动](#后端启动)
|
||||||
|
- [前端启动](#前端启动)
|
||||||
|
- [开发规范](#开发规范)
|
||||||
|
- [常见问题](#常见问题)
|
||||||
|
|
||||||
|
## 🛠️ 环境准备
|
||||||
|
|
||||||
|
### 必备软件
|
||||||
|
|
||||||
|
确保您的开发环境已安装以下软件:
|
||||||
|
|
||||||
|
| 软件 | 版本要求 | 说明 |
|
||||||
|
|------|---------|------|
|
||||||
|
| Java | >= 17 | 后端运行环境 |
|
||||||
|
| Node.js | >= 18.0 | 前端运行环境 |
|
||||||
|
| MySQL | >= 8.0 | 数据库 |
|
||||||
|
| Redis | >= 7.0 | 缓存服务(可选,开发阶段可不启用) |
|
||||||
|
| Maven | >= 3.8 | Java构建工具 |
|
||||||
|
| Git | 最新版本 | 版本控制 |
|
||||||
|
|
||||||
|
### 推荐开发工具
|
||||||
|
|
||||||
|
- **后端开发**: IntelliJ IDEA / Eclipse
|
||||||
|
- **前端开发**: VS Code
|
||||||
|
- **数据库管理**: Navicat / DBeaver / MySQL Workbench
|
||||||
|
- **API测试**: Postman / Apifox
|
||||||
|
- **Redis管理**: RedisInsight / Another Redis Desktop Manager
|
||||||
|
|
||||||
|
## 🚀 项目初始化
|
||||||
|
|
||||||
|
### 1. 克隆项目(如果从远程仓库)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <repository-url>
|
||||||
|
cd qiangdanxitong
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 项目结构说明
|
||||||
|
|
||||||
|
```
|
||||||
|
qiangdanxitong/
|
||||||
|
├── frontend/ # 前端项目(Vue 3)
|
||||||
|
├── backend/ # 后端项目(Spring Boot)
|
||||||
|
├── database/ # 数据库脚本
|
||||||
|
├── docs/ # 文档
|
||||||
|
├── PROJECT_REQUIREMENTS.md # 需求文档
|
||||||
|
└── README.md # 项目说明
|
||||||
|
```
|
||||||
|
|
||||||
|
## 💾 数据库配置
|
||||||
|
|
||||||
|
### 1. 创建数据库
|
||||||
|
|
||||||
|
使用 MySQL 客户端或管理工具执行以下命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 方式1:使用命令行
|
||||||
|
mysql -u root -p < database/schema.sql
|
||||||
|
mysql -u root -p < database/init-data.sql
|
||||||
|
|
||||||
|
# 方式2:使用 MySQL 客户端
|
||||||
|
mysql -u root -p
|
||||||
|
source /Users/lanmu/Desktop/qiangdanxitong/database/schema.sql;
|
||||||
|
source /Users/lanmu/Desktop/qiangdanxitong/database/init-data.sql;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 验证数据库
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 登录MySQL
|
||||||
|
mysql -u root -p
|
||||||
|
|
||||||
|
-- 切换到designhub数据库
|
||||||
|
USE designhub;
|
||||||
|
|
||||||
|
-- 查看所有表
|
||||||
|
SHOW TABLES;
|
||||||
|
|
||||||
|
-- 查看部门数据
|
||||||
|
SELECT * FROM sys_dept;
|
||||||
|
|
||||||
|
-- 查看任务类型
|
||||||
|
SELECT * FROM task_type;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 配置数据库连接
|
||||||
|
|
||||||
|
编辑 `backend/src/main/resources/application-dev.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
url: jdbc:mysql://localhost:3306/designhub?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
|
||||||
|
username: root
|
||||||
|
password: 你的MySQL密码
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 后端启动
|
||||||
|
|
||||||
|
### 方式1:使用IDE启动(推荐)
|
||||||
|
|
||||||
|
1. 用 IntelliJ IDEA 打开 `backend` 目录
|
||||||
|
2. 等待 Maven 自动下载依赖
|
||||||
|
3. 找到 `DesignHubApplication.java` 主类
|
||||||
|
4. 右键 -> Run 'DesignHubApplication'
|
||||||
|
|
||||||
|
### 方式2:使用命令行启动
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
|
||||||
|
# 首次启动需要先编译
|
||||||
|
mvn clean install
|
||||||
|
|
||||||
|
# 启动应用
|
||||||
|
mvn spring-boot:run
|
||||||
|
|
||||||
|
# 或者直接运行jar包
|
||||||
|
java -jar target/designhub-backend-1.0.0.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
### 验证后端启动
|
||||||
|
|
||||||
|
启动成功后,访问以下地址:
|
||||||
|
|
||||||
|
- **API文档**: http://localhost:8080/api/doc.html
|
||||||
|
- **健康检查**: http://localhost:8080/api/actuator/health(如果配置了actuator)
|
||||||
|
|
||||||
|
控制台应该输出:
|
||||||
|
|
||||||
|
```
|
||||||
|
╔═══════════════════════════════════════════════════════╗
|
||||||
|
║ ║
|
||||||
|
║ 🎨 DesignHub Backend Service Started 🎨 ║
|
||||||
|
║ ║
|
||||||
|
║ Port: 8080 ║
|
||||||
|
║ Swagger UI: http://localhost:8080/api/doc.html ║
|
||||||
|
║ Version: 1.0.0 ║
|
||||||
|
║ ║
|
||||||
|
╚═══════════════════════════════════════════════════════╝
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎨 前端启动
|
||||||
|
|
||||||
|
### 1. 安装依赖
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
|
||||||
|
# 使用npm
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# 或使用pnpm(推荐,更快)
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
# 或使用yarn
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 启动开发服务器
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用npm
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# 使用pnpm
|
||||||
|
pnpm dev
|
||||||
|
|
||||||
|
# 使用yarn
|
||||||
|
yarn dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 访问前端
|
||||||
|
|
||||||
|
浏览器自动打开或手动访问:
|
||||||
|
|
||||||
|
- **开发地址**: http://localhost:3000
|
||||||
|
|
||||||
|
控制台应该输出:
|
||||||
|
|
||||||
|
```
|
||||||
|
VITE v5.0.0 ready in 500 ms
|
||||||
|
|
||||||
|
➜ Local: http://localhost:3000/
|
||||||
|
➜ Network: use --host to expose
|
||||||
|
➜ press h + enter to show help
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📝 开发规范
|
||||||
|
|
||||||
|
### Git提交规范
|
||||||
|
|
||||||
|
使用 Conventional Commits 规范:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 功能开发
|
||||||
|
git commit -m "feat: 添加任务大厅页面"
|
||||||
|
|
||||||
|
# Bug修复
|
||||||
|
git commit -m "fix: 修复任务列表显示问题"
|
||||||
|
|
||||||
|
# 文档更新
|
||||||
|
git commit -m "docs: 更新README文档"
|
||||||
|
|
||||||
|
# 样式调整
|
||||||
|
git commit -m "style: 调整按钮样式"
|
||||||
|
|
||||||
|
# 代码重构
|
||||||
|
git commit -m "refactor: 重构任务服务层代码"
|
||||||
|
|
||||||
|
# 性能优化
|
||||||
|
git commit -m "perf: 优化数据库查询性能"
|
||||||
|
|
||||||
|
# 测试相关
|
||||||
|
git commit -m "test: 添加任务模块单元测试"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 分支策略
|
||||||
|
|
||||||
|
```
|
||||||
|
main/master -> 生产环境分支
|
||||||
|
develop -> 开发分支
|
||||||
|
feature/* -> 功能分支
|
||||||
|
hotfix/* -> 紧急修复分支
|
||||||
|
release/* -> 发布分支
|
||||||
|
```
|
||||||
|
|
||||||
|
### 代码风格
|
||||||
|
|
||||||
|
#### 前端(TypeScript/Vue)
|
||||||
|
|
||||||
|
- 使用 ESLint + Prettier 格式化代码
|
||||||
|
- 组件命名使用 PascalCase
|
||||||
|
- 文件命名使用 kebab-case
|
||||||
|
- 使用 Composition API
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 格式化代码
|
||||||
|
npm run format
|
||||||
|
|
||||||
|
# 检查代码
|
||||||
|
npm run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 后端(Java/Spring Boot)
|
||||||
|
|
||||||
|
- 遵循 阿里巴巴 Java 开发手册
|
||||||
|
- 类名使用 PascalCase
|
||||||
|
- 方法名使用 camelCase
|
||||||
|
- 常量使用 UPPER_SNAKE_CASE
|
||||||
|
|
||||||
|
## 🔍 常见问题
|
||||||
|
|
||||||
|
### Q1: 后端启动失败,提示数据库连接错误
|
||||||
|
|
||||||
|
**A**: 检查以下几点:
|
||||||
|
1. MySQL 是否已启动
|
||||||
|
2. 数据库密码是否正确配置在 `application-dev.yml`
|
||||||
|
3. 数据库 `designhub` 是否已创建
|
||||||
|
4. 数据库端口是否为 3306
|
||||||
|
|
||||||
|
### Q2: 前端启动后页面空白
|
||||||
|
|
||||||
|
**A**:
|
||||||
|
1. 检查浏览器控制台是否有报错
|
||||||
|
2. 确认后端API是否正常运行(访问 http://localhost:8080/api/doc.html)
|
||||||
|
3. 检查前端代理配置(vite.config.ts)
|
||||||
|
|
||||||
|
### Q3: Maven 依赖下载缓慢
|
||||||
|
|
||||||
|
**A**: 配置国内Maven镜像(阿里云)
|
||||||
|
|
||||||
|
编辑 `~/.m2/settings.xml`:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<mirror>
|
||||||
|
<id>aliyun</id>
|
||||||
|
<mirrorOf>central</mirrorOf>
|
||||||
|
<name>Aliyun Maven Mirror</name>
|
||||||
|
<url>https://maven.aliyun.com/repository/public</url>
|
||||||
|
</mirror>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q4: npm 安装依赖失败
|
||||||
|
|
||||||
|
**A**: 切换npm镜像源
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 临时使用淘宝镜像
|
||||||
|
npm install --registry=https://registry.npmmirror.com
|
||||||
|
|
||||||
|
# 永久设置
|
||||||
|
npm config set registry https://registry.npmmirror.com
|
||||||
|
|
||||||
|
# 使用 nrm 管理镜像源(推荐)
|
||||||
|
npm install -g nrm
|
||||||
|
nrm use taobao
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q5: 钉钉登录如何配置
|
||||||
|
|
||||||
|
**A**:
|
||||||
|
1. 登录钉钉开放平台 https://open.dingtalk.com
|
||||||
|
2. 创建应用,获取 AppKey 和 AppSecret
|
||||||
|
3. 配置到 `application.yml` 的 `dingtalk` 部分
|
||||||
|
4. 配置回调地址和权限
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dingtalk:
|
||||||
|
app-key: your-app-key
|
||||||
|
app-secret: your-app-secret
|
||||||
|
agent-id: your-agent-id
|
||||||
|
corp-id: your-corp-id
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q6: Redis 连接失败
|
||||||
|
|
||||||
|
**A**: 开发阶段可以暂时禁用 Redis
|
||||||
|
|
||||||
|
在 `application-dev.yml` 中注释掉 Redis 配置,或确保 Redis 已启动:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 启动 Redis(Mac)
|
||||||
|
brew services start redis
|
||||||
|
|
||||||
|
# 启动 Redis(Linux)
|
||||||
|
redis-server
|
||||||
|
|
||||||
|
# 启动 Redis(Windows)
|
||||||
|
redis-server.exe
|
||||||
|
|
||||||
|
# 测试连接
|
||||||
|
redis-cli ping
|
||||||
|
# 应该返回 PONG
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📚 下一步
|
||||||
|
|
||||||
|
### 开发优先级
|
||||||
|
|
||||||
|
1. ✅ **基础架构** - 已完成
|
||||||
|
- 项目结构
|
||||||
|
- 数据库设计
|
||||||
|
- 前后端框架搭建
|
||||||
|
|
||||||
|
2. 🔄 **核心功能开发** - 进行中
|
||||||
|
- [ ] 用户认证模块(Casdoor + 钉钉登录)
|
||||||
|
- [ ] 任务管理模块
|
||||||
|
- [ ] 积分管理模块
|
||||||
|
- [ ] 通知系统
|
||||||
|
|
||||||
|
3. ⏳ **进阶功能** - 待开发
|
||||||
|
- [ ] 激励系统
|
||||||
|
- [ ] 数据分析
|
||||||
|
- [ ] 管理后台
|
||||||
|
|
||||||
|
### 推荐开发顺序
|
||||||
|
|
||||||
|
```
|
||||||
|
1. 用户认证 → 2. 组织架构 → 3. 任务发起 → 4. 任务大厅 →
|
||||||
|
5. 我的任务 → 6. 设计师大厅 → 7. 积分系统 → 8. 数据分析 →
|
||||||
|
9. 激励系统 → 10. 系统配置 → 11. 通知系统
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🤝 团队协作
|
||||||
|
|
||||||
|
### 每日开发流程
|
||||||
|
|
||||||
|
1. **早上**:拉取最新代码
|
||||||
|
```bash
|
||||||
|
git checkout develop
|
||||||
|
git pull origin develop
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **开发**:创建功能分支
|
||||||
|
```bash
|
||||||
|
git checkout -b feature/task-hall
|
||||||
|
# 编码...
|
||||||
|
git add .
|
||||||
|
git commit -m "feat: 完成任务大厅页面"
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **提交**:推送到远程并创建PR
|
||||||
|
```bash
|
||||||
|
git push origin feature/task-hall
|
||||||
|
# 在 GitLab/GitHub 创建 Merge Request / Pull Request
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **审查**:代码审查通过后合并到 develop
|
||||||
|
|
||||||
|
### 代码审查清单
|
||||||
|
|
||||||
|
- [ ] 代码符合规范
|
||||||
|
- [ ] 没有明显的 bug
|
||||||
|
- [ ] 功能完整
|
||||||
|
- [ ] 已添加必要的注释
|
||||||
|
- [ ] 通过单元测试
|
||||||
|
- [ ] UI 符合 OpenAI 设计规范
|
||||||
|
|
||||||
|
## 📞 技术支持
|
||||||
|
|
||||||
|
如有问题,请联系:
|
||||||
|
|
||||||
|
- **技术负责人**: [姓名] - [邮箱]
|
||||||
|
- **项目文档**: PROJECT_REQUIREMENTS.md
|
||||||
|
- **API文档**: http://localhost:8080/api/doc.html
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**祝开发愉快!** 🎉
|
||||||
|
|
||||||
459
Nginx缓存配置说明.md
Normal file
459
Nginx缓存配置说明.md
Normal file
@ -0,0 +1,459 @@
|
|||||||
|
# Nginx缓存配置说明
|
||||||
|
|
||||||
|
## 📝 配置概述
|
||||||
|
|
||||||
|
**更新时间**: 2025-12-23
|
||||||
|
**服务器**: 公网服务器 156.225.27.52:50232
|
||||||
|
**配置文件**: /www/server/panel/vhost/nginx/qd.yydd.ai.conf
|
||||||
|
**状态**: ✅ 已禁用所有缓存(开发模式)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚫 当前缓存设置
|
||||||
|
|
||||||
|
### 已禁用的缓存功能
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location / {
|
||||||
|
# 反向代理配置
|
||||||
|
proxy_pass http://127.0.0.1:5246;
|
||||||
|
|
||||||
|
# 🚫 禁用缓存 - 开发模式
|
||||||
|
proxy_buffering off; # 禁用代理缓冲
|
||||||
|
proxy_cache off; # 禁用代理缓存
|
||||||
|
proxy_no_cache 1; # 不缓存响应
|
||||||
|
proxy_cache_bypass 1; # 绕过缓存检查
|
||||||
|
|
||||||
|
# 添加缓存控制头
|
||||||
|
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
|
||||||
|
add_header Pragma "no-cache";
|
||||||
|
add_header Expires "0";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 缓存控制说明
|
||||||
|
|
||||||
|
### Nginx层面缓存控制
|
||||||
|
|
||||||
|
| 指令 | 作用 | 值 |
|
||||||
|
|------|------|-----|
|
||||||
|
| `proxy_buffering` | 代理缓冲 | off(禁用) |
|
||||||
|
| `proxy_cache` | 代理缓存 | off(禁用) |
|
||||||
|
| `proxy_no_cache` | 不缓存响应 | 1(启用) |
|
||||||
|
| `proxy_cache_bypass` | 绕过缓存 | 1(启用) |
|
||||||
|
|
||||||
|
### HTTP响应头
|
||||||
|
|
||||||
|
| 响应头 | 作用 | 值 |
|
||||||
|
|--------|------|-----|
|
||||||
|
| `Cache-Control` | 缓存策略 | no-store, no-cache, must-revalidate |
|
||||||
|
| `Pragma` | HTTP/1.0兼容 | no-cache |
|
||||||
|
| `Expires` | 过期时间 | 0(立即过期) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 效果说明
|
||||||
|
|
||||||
|
### 对开发的好处
|
||||||
|
|
||||||
|
1. **✅ 实时更新**
|
||||||
|
- 前端代码更新立即生效
|
||||||
|
- 无需手动清除浏览器缓存
|
||||||
|
- API响应不会被缓存
|
||||||
|
|
||||||
|
2. **✅ 调试便利**
|
||||||
|
- 每次请求都是最新内容
|
||||||
|
- 便于测试新功能
|
||||||
|
- 避免缓存导致的问题
|
||||||
|
|
||||||
|
3. **✅ 快速迭代**
|
||||||
|
- 修改代码后立即看到效果
|
||||||
|
- 减少"看不到更新"的困惑
|
||||||
|
- 提高开发效率
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 切换到生产模式
|
||||||
|
|
||||||
|
### 当系统正式上线后
|
||||||
|
|
||||||
|
**建议**:启用缓存以提高性能和减少服务器负载
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:5246;
|
||||||
|
|
||||||
|
# 启用缓存(生产模式)
|
||||||
|
proxy_buffering on;
|
||||||
|
|
||||||
|
# 静态资源缓存(仅示例,根据实际调整)
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||||||
|
proxy_pass http://127.0.0.1:5246;
|
||||||
|
proxy_cache my_cache;
|
||||||
|
proxy_cache_valid 200 30d;
|
||||||
|
proxy_cache_valid 404 1m;
|
||||||
|
add_header Cache-Control "public, max-age=2592000";
|
||||||
|
}
|
||||||
|
|
||||||
|
# API响应不缓存
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://127.0.0.1:5246;
|
||||||
|
proxy_buffering off;
|
||||||
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 切换步骤
|
||||||
|
|
||||||
|
### 从开发模式切换到生产模式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. SSH登录公网服务器
|
||||||
|
ssh -p 50232 root@156.225.27.52
|
||||||
|
|
||||||
|
# 2. 编辑Nginx配置
|
||||||
|
vi /www/server/panel/vhost/nginx/qd.yydd.ai.conf
|
||||||
|
|
||||||
|
# 3. 修改缓存配置(根据需要)
|
||||||
|
# 移除或注释 proxy_buffering off 等指令
|
||||||
|
# 添加适当的缓存配置
|
||||||
|
|
||||||
|
# 4. 测试配置
|
||||||
|
nginx -t
|
||||||
|
|
||||||
|
# 5. 重载Nginx
|
||||||
|
nginx -s reload
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 测试缓存配置
|
||||||
|
|
||||||
|
### 验证缓存已禁用
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 方式1:查看HTTP响应头
|
||||||
|
curl -I https://qd.yydd.ai
|
||||||
|
|
||||||
|
# 应该看到:
|
||||||
|
# Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0
|
||||||
|
# Pragma: no-cache
|
||||||
|
# Expires: 0
|
||||||
|
|
||||||
|
# 方式2:在浏览器开发者工具中
|
||||||
|
# Network → Headers → Response Headers
|
||||||
|
# 检查 Cache-Control 等头部
|
||||||
|
```
|
||||||
|
|
||||||
|
### 测试更新是否实时
|
||||||
|
|
||||||
|
1. **前端更新测试**
|
||||||
|
```bash
|
||||||
|
# 修改前端代码
|
||||||
|
cd /Users/lanmu/Desktop/qiangdanxitong/frontend
|
||||||
|
# 编辑任意文件
|
||||||
|
|
||||||
|
# 构建并上传
|
||||||
|
pnpm build
|
||||||
|
scp -P 5204 -r dist/* root@156.225.27.52:/opt/designhub/frontend/
|
||||||
|
|
||||||
|
# 立即刷新浏览器(Ctrl+R,无需Ctrl+Shift+R强制刷新)
|
||||||
|
# 应该看到更新
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **API更新测试**
|
||||||
|
```bash
|
||||||
|
# 修改后端代码
|
||||||
|
# 重启后端服务
|
||||||
|
ssh -p 5204 root@156.225.27.52
|
||||||
|
systemctl restart designhub-backend
|
||||||
|
|
||||||
|
# 立即在浏览器中调用API
|
||||||
|
# 应该返回最新结果
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 注意事项
|
||||||
|
|
||||||
|
### 开发模式的影响
|
||||||
|
|
||||||
|
**优点**:
|
||||||
|
- ✅ 开发调试方便
|
||||||
|
- ✅ 代码更新实时生效
|
||||||
|
- ✅ 避免缓存问题
|
||||||
|
|
||||||
|
**缺点**:
|
||||||
|
- ⚠️ 服务器负载略高(每次请求都处理)
|
||||||
|
- ⚠️ 用户体验略差(无缓存加速)
|
||||||
|
- ⚠️ 带宽消耗略大
|
||||||
|
|
||||||
|
**建议**:
|
||||||
|
- 开发阶段:使用当前配置(无缓存)
|
||||||
|
- 生产阶段:启用适当缓存(性能优化)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 完整配置文件
|
||||||
|
|
||||||
|
### 当前配置(开发模式)
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
# /www/server/panel/vhost/nginx/qd.yydd.ai.conf
|
||||||
|
|
||||||
|
# HTTP自动跳转HTTPS
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name qd.yydd.ai;
|
||||||
|
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /www/wwwroot/qd.yydd.ai;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 301 https://$server_name$request_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# HTTPS配置
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name qd.yydd.ai;
|
||||||
|
|
||||||
|
# SSL证书
|
||||||
|
ssl_certificate /etc/letsencrypt/live/qd.yydd.ai/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/qd.yydd.ai/privkey.pem;
|
||||||
|
|
||||||
|
# SSL优化
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
ssl_session_cache shared:SSL:10m;
|
||||||
|
ssl_session_timeout 10m;
|
||||||
|
|
||||||
|
client_max_body_size 100M;
|
||||||
|
|
||||||
|
# 反向代理 - 无缓存模式
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:5246;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
# WebSocket支持
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
||||||
|
# 超时设置
|
||||||
|
proxy_connect_timeout 300;
|
||||||
|
proxy_send_timeout 300;
|
||||||
|
proxy_read_timeout 300;
|
||||||
|
|
||||||
|
# 🚫 禁用缓存 - 开发模式
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_cache off;
|
||||||
|
proxy_no_cache 1;
|
||||||
|
proxy_cache_bypass 1;
|
||||||
|
|
||||||
|
# 添加缓存控制头
|
||||||
|
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
|
||||||
|
add_header Pragma "no-cache";
|
||||||
|
add_header Expires "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
access_log /www/wwwlogs/qd.yydd.ai.https.access.log;
|
||||||
|
error_log /www/wwwlogs/qd.yydd.ai.https.error.log;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 常用命令
|
||||||
|
|
||||||
|
### 修改配置后重载
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# SSH登录
|
||||||
|
ssh -p 50232 root@156.225.27.52
|
||||||
|
|
||||||
|
# 编辑配置
|
||||||
|
vi /www/server/panel/vhost/nginx/qd.yydd.ai.conf
|
||||||
|
|
||||||
|
# 测试配置
|
||||||
|
nginx -t
|
||||||
|
|
||||||
|
# 重载Nginx(不中断服务)
|
||||||
|
nginx -s reload
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查看配置
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 查看当前配置
|
||||||
|
ssh -p 50232 root@156.225.27.52
|
||||||
|
cat /www/server/panel/vhost/nginx/qd.yydd.ai.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
### 验证缓存状态
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 方式1:curl查看响应头
|
||||||
|
curl -I https://qd.yydd.ai
|
||||||
|
|
||||||
|
# 方式2:详细查看
|
||||||
|
curl -v https://qd.yydd.ai 2>&1 | grep -i cache
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 浏览器缓存说明
|
||||||
|
|
||||||
|
### 浏览器本地缓存
|
||||||
|
|
||||||
|
**注意**: 即使Nginx禁用了缓存,浏览器仍可能有自己的缓存
|
||||||
|
|
||||||
|
**开发时的刷新方式**:
|
||||||
|
|
||||||
|
| 刷新方式 | 快捷键 | 效果 |
|
||||||
|
|----------|--------|------|
|
||||||
|
| 普通刷新 | F5 或 Ctrl+R | 可能使用缓存 |
|
||||||
|
| 硬刷新 | Ctrl+Shift+R | 强制不使用缓存 |
|
||||||
|
| 清空缓存 | DevTools → Network → Disable cache | 开发时建议勾选 |
|
||||||
|
|
||||||
|
**建议**:
|
||||||
|
- 开发时打开浏览器开发者工具(F12)
|
||||||
|
- 勾选 Network → Disable cache
|
||||||
|
- 这样可以完全禁用浏览器缓存
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 性能对比
|
||||||
|
|
||||||
|
### 开发模式 vs 生产模式
|
||||||
|
|
||||||
|
| 指标 | 开发模式(无缓存) | 生产模式(有缓存) |
|
||||||
|
|------|-------------------|-------------------|
|
||||||
|
| 首次加载 | 正常 | 正常 |
|
||||||
|
| 重复访问 | 每次都请求服务器 | 使用缓存,快速 |
|
||||||
|
| 服务器负载 | 较高 | 较低 |
|
||||||
|
| 带宽消耗 | 较高 | 较低 |
|
||||||
|
| 开发便利性 | ⭐⭐⭐⭐⭐ | ⭐⭐ |
|
||||||
|
| 用户体验 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 最佳实践
|
||||||
|
|
||||||
|
### 开发阶段(当前)
|
||||||
|
|
||||||
|
✅ **已实施**:
|
||||||
|
- Nginx层面禁用所有缓存
|
||||||
|
- 添加 no-cache 响应头
|
||||||
|
- 便于快速迭代开发
|
||||||
|
|
||||||
|
### 测试阶段(即将上线)
|
||||||
|
|
||||||
|
建议:
|
||||||
|
- 保持当前配置
|
||||||
|
- 测试功能完整性
|
||||||
|
- 验证无缓存相关bug
|
||||||
|
|
||||||
|
### 生产阶段(正式上线后)
|
||||||
|
|
||||||
|
建议:
|
||||||
|
- 启用静态资源缓存(JS/CSS/图片)
|
||||||
|
- API响应保持不缓存
|
||||||
|
- 设置合理的缓存时间
|
||||||
|
- 使用CDN加速
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 快速切换配置
|
||||||
|
|
||||||
|
### 配置模板
|
||||||
|
|
||||||
|
**开发模式配置**(当前):
|
||||||
|
```nginx
|
||||||
|
# 完全禁用缓存
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_cache off;
|
||||||
|
proxy_no_cache 1;
|
||||||
|
proxy_cache_bypass 1;
|
||||||
|
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
||||||
|
```
|
||||||
|
|
||||||
|
**生产模式配置**(未来):
|
||||||
|
```nginx
|
||||||
|
# 启用适度缓存
|
||||||
|
proxy_buffering on;
|
||||||
|
|
||||||
|
# 静态资源缓存7天
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
|
||||||
|
add_header Cache-Control "public, max-age=604800";
|
||||||
|
}
|
||||||
|
|
||||||
|
# API不缓存
|
||||||
|
location /api/ {
|
||||||
|
add_header Cache-Control "no-cache";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 技术支持
|
||||||
|
|
||||||
|
### 修改缓存配置
|
||||||
|
|
||||||
|
如需修改缓存配置,请:
|
||||||
|
|
||||||
|
1. SSH登录公网服务器
|
||||||
|
2. 编辑 `/www/server/panel/vhost/nginx/qd.yydd.ai.conf`
|
||||||
|
3. 测试配置:`nginx -t`
|
||||||
|
4. 重载Nginx:`nginx -s reload`
|
||||||
|
|
||||||
|
### 验证配置生效
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 查看HTTP响应头
|
||||||
|
curl -I https://qd.yydd.ai | grep -i cache
|
||||||
|
|
||||||
|
# 应该看到当前设置的缓存策略
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 更新记录
|
||||||
|
|
||||||
|
| 日期 | 操作 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 2025-12-23 | 禁用缓存 | 开发模式,便于测试 |
|
||||||
|
| 待定 | 启用缓存 | 生产模式,性能优化 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 总结
|
||||||
|
|
||||||
|
✅ **当前状态**: 所有缓存已禁用
|
||||||
|
✅ **适用场景**: 开发测试阶段
|
||||||
|
✅ **效果**: 代码更新立即生效
|
||||||
|
✅ **访问地址**: https://qd.yydd.ai
|
||||||
|
|
||||||
|
**开发体验**:
|
||||||
|
- 修改前端代码 → 构建上传 → 普通刷新(F5)即可看到更新
|
||||||
|
- 修改后端代码 → 重启服务 → 立即生效
|
||||||
|
- 无需清除浏览器缓存或强制刷新
|
||||||
|
|
||||||
|
🎉 **现在可以愉快地开发测试了!**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**创建时间**: 2025-12-23
|
||||||
|
**配置状态**: ✅ 已生效
|
||||||
|
**验证方式**: 访问 https://qd.yydd.ai 并查看响应头
|
||||||
6335
PROJECT_REQUIREMENTS.md
Normal file
6335
PROJECT_REQUIREMENTS.md
Normal file
File diff suppressed because it is too large
Load Diff
362
README.md
Normal file
362
README.md
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
# 🎨 DesignHub - 设计师任务管理平台
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
**现代化的设计师任务抢单与管理系统**
|
||||||
|
|
||||||
|
[功能特性](#-核心功能) • [技术栈](#-技术栈) • [快速开始](#-快速开始) • [文档](#-项目文档) • [截图](#-系统截图)
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📖 项目简介
|
||||||
|
|
||||||
|
DesignHub是一个功能完善的设计师任务管理平台,旨在帮助企业高效管理设计任务的发布、接收、执行和评审全流程。系统采用积分制管理,配备完整的激励机制和数据分析功能,大幅提升设计团队的工作效率和积极性。
|
||||||
|
|
||||||
|
### 🎯 核心价值
|
||||||
|
|
||||||
|
- **任务流转高效化**:从发布到完成,全程可视化管理
|
||||||
|
- **积分控制精准化**:多维度积分管理,合理分配资源
|
||||||
|
- **激励机制完善化**:签到、成就、排行榜,激发团队活力
|
||||||
|
- **数据分析智能化**:多维度数据统计,辅助决策
|
||||||
|
- **消息通知及时化**:实时推送,确保信息触达
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✨ 核心功能
|
||||||
|
|
||||||
|
### 🔐 用户认证与权限
|
||||||
|
- JWT双令牌认证机制
|
||||||
|
- RBAC权限模型(7种角色)
|
||||||
|
- 钉钉登录预留接口
|
||||||
|
- 细粒度权限控制
|
||||||
|
|
||||||
|
### 📋 任务管理
|
||||||
|
- 任务发起(配置化类型、品牌、平台)
|
||||||
|
- 任务抢单(自由抢单/指定分配/招募)
|
||||||
|
- 任务执行(进度跟踪、时效计算)
|
||||||
|
- 任务评审(五星评分、质量评价)
|
||||||
|
- 管理员调配(分配、转移、优先级调整)
|
||||||
|
|
||||||
|
### 💰 积分管理
|
||||||
|
- 团队积分池
|
||||||
|
- 积分充值与审批
|
||||||
|
- 周期限额管理(日/周/月)
|
||||||
|
- 积分消耗与奖励
|
||||||
|
- 延误扣分机制
|
||||||
|
|
||||||
|
### 🏆 激励系统
|
||||||
|
- **签到系统**:每日签到、连续追踪、奖励递增
|
||||||
|
- **成就系统**:10个默认成就、自动解锁、奖励发放
|
||||||
|
- **排行榜**:好评排行、积分排行、效率排行
|
||||||
|
|
||||||
|
### 🔔 通知系统
|
||||||
|
- 站内消息(4种类型)
|
||||||
|
- 通知分类与筛选
|
||||||
|
- 已读/未读管理
|
||||||
|
- 钉钉推送预留接口
|
||||||
|
|
||||||
|
### 📊 数据分析
|
||||||
|
- 需求统计(总览/趋势/类型分布)
|
||||||
|
- 用户排行(需求量/完成率/满意度)
|
||||||
|
- 部门统计(积分使用/预警机制)
|
||||||
|
- 设计师统计(任务量/积分/好评率)
|
||||||
|
- ECharts可视化图表
|
||||||
|
|
||||||
|
### 🏢 部门管理
|
||||||
|
- 部门CRUD操作
|
||||||
|
- 部门类型(需求/接单)
|
||||||
|
- 部门层级结构
|
||||||
|
- 积分限额配置
|
||||||
|
|
||||||
|
### ⚙️ 配置管理
|
||||||
|
- 需求类型配置
|
||||||
|
- 品牌/平台/类目配置
|
||||||
|
- 系统参数配置
|
||||||
|
- 延误扣分规则
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ 技术栈
|
||||||
|
|
||||||
|
### 前端技术
|
||||||
|
| 技术 | 版本 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| Vue | 3.x | 渐进式JavaScript框架 |
|
||||||
|
| TypeScript | 5.x | 类型安全 |
|
||||||
|
| Ant Design Vue | 4.x | UI组件库 |
|
||||||
|
| Pinia | 2.x | 状态管理 |
|
||||||
|
| Vue Router | 4.x | 路由管理 |
|
||||||
|
| Axios | 1.x | HTTP客户端 |
|
||||||
|
| ECharts | 5.x | 数据可视化 |
|
||||||
|
| SCSS | - | CSS预处理器 |
|
||||||
|
|
||||||
|
### 后端技术
|
||||||
|
| 技术 | 版本 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| Spring Boot | 3.x | Java应用框架 |
|
||||||
|
| MySQL | 8.0 | 关系型数据库 |
|
||||||
|
| Redis | 7.x | 缓存数据库 |
|
||||||
|
| MyBatis Plus | 3.x | ORM框架 |
|
||||||
|
| JWT | - | Token认证 |
|
||||||
|
| Swagger | 3.x | API文档 |
|
||||||
|
| Quartz | 2.x | 任务调度 |
|
||||||
|
| Apache POI | 5.x | Excel操作 |
|
||||||
|
|
||||||
|
### 开发工具
|
||||||
|
- IntelliJ IDEA - 后端开发
|
||||||
|
- VS Code - 前端开发
|
||||||
|
- Maven - 后端构建
|
||||||
|
- pnpm - 前端包管理
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 快速开始
|
||||||
|
|
||||||
|
### 环境要求
|
||||||
|
|
||||||
|
- JDK 17+
|
||||||
|
- Node.js 18+
|
||||||
|
- MySQL 8.0+
|
||||||
|
- Redis 6.0+
|
||||||
|
- Maven 3.6+
|
||||||
|
- pnpm 8.0+
|
||||||
|
|
||||||
|
### 1. 克隆项目
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <repository_url>
|
||||||
|
cd qiangdanxitong
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 配置数据库
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 创建数据库
|
||||||
|
CREATE DATABASE design_hub CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- 创建用户
|
||||||
|
CREATE USER 'designhub'@'localhost' IDENTIFIED BY 'your_password';
|
||||||
|
GRANT ALL PRIVILEGES ON design_hub.* TO 'designhub'@'localhost';
|
||||||
|
FLUSH PRIVILEGES;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 启动后端
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
|
||||||
|
# 修改配置文件
|
||||||
|
vi src/main/resources/application.yml
|
||||||
|
|
||||||
|
# 构建并运行
|
||||||
|
mvn clean install -DskipTests
|
||||||
|
mvn spring-boot:run
|
||||||
|
```
|
||||||
|
|
||||||
|
后端启动成功后,访问:http://localhost:8080/swagger-ui.html
|
||||||
|
|
||||||
|
### 4. 启动前端
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
|
||||||
|
# 安装依赖
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
# 启动开发服务器
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
前端启动成功后,访问:http://localhost:5173
|
||||||
|
|
||||||
|
### 5. 登录系统
|
||||||
|
|
||||||
|
**测试账号**:
|
||||||
|
- 用户名:`admin`
|
||||||
|
- 密码:`123456`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 项目文档
|
||||||
|
|
||||||
|
| 文档名称 | 说明 | 链接 |
|
||||||
|
|---------|------|------|
|
||||||
|
| 需求文档 | 详细的功能需求和设计规范 | [PROJECT_REQUIREMENTS.md](./PROJECT_REQUIREMENTS.md) |
|
||||||
|
| 项目总结 | 项目概述、功能、技术亮点 | [PROJECT_SUMMARY.md](./PROJECT_SUMMARY.md) |
|
||||||
|
| 部署指南 | 本地和生产环境部署指南 | [DEPLOYMENT_GUIDE.md](./DEPLOYMENT_GUIDE.md) |
|
||||||
|
| 当前状态 | 开发进度和模块完成情况 | [CURRENT_STATUS.md](./CURRENT_STATUS.md) |
|
||||||
|
| 阶段报告 | 各开发阶段的完成报告 | PHASE1-6_COMPLETED.md |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📸 系统截图
|
||||||
|
|
||||||
|
### 登录页面
|
||||||
|
渐变背景、玻璃拟态设计、流畅动画效果
|
||||||
|
|
||||||
|
### 首页工作台
|
||||||
|
数据卡片、快速操作、待办任务、本周统计
|
||||||
|
|
||||||
|
### 任务大厅
|
||||||
|
任务列表、筛选排序、抢单操作
|
||||||
|
|
||||||
|
### 我的任务
|
||||||
|
双视图(发布/接收)、进度追踪、甘特图
|
||||||
|
|
||||||
|
### 设计师大厅
|
||||||
|
实时状态、进度展示、好评排行榜
|
||||||
|
|
||||||
|
### 数据分析
|
||||||
|
ECharts图表、多维度统计、趋势分析
|
||||||
|
|
||||||
|
### 通知中心
|
||||||
|
消息列表、分类筛选、已读管理
|
||||||
|
|
||||||
|
### 签到打卡
|
||||||
|
签到状态、奖励规则、历史记录
|
||||||
|
|
||||||
|
### 成就中心
|
||||||
|
徽章展示、等级标识、奖励领取
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 项目数据
|
||||||
|
|
||||||
|
### 代码统计
|
||||||
|
- 后端Java文件:67个
|
||||||
|
- 前端Vue/TS文件:36个
|
||||||
|
- API接口:118+个
|
||||||
|
- 数据库表:29张
|
||||||
|
- 完整页面:20+个
|
||||||
|
- 总代码行数:13000+行
|
||||||
|
|
||||||
|
### 功能模块
|
||||||
|
- 核心业务模块:16个
|
||||||
|
- 前端页面组件:20+个
|
||||||
|
- 公共组件:10+个
|
||||||
|
- 工具类:5个
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎨 UI设计规范
|
||||||
|
|
||||||
|
项目严格遵循**OpenAI官方UI设计规范**:
|
||||||
|
|
||||||
|
- ✅ 极简主义设计
|
||||||
|
- ✅ 现代科技感
|
||||||
|
- ✅ 清晰的层级结构
|
||||||
|
- ✅ 一致的交互体验
|
||||||
|
- ✅ 深色模式优先
|
||||||
|
|
||||||
|
### 配色方案
|
||||||
|
- Primary: `#1890ff` (蓝色)
|
||||||
|
- Success: `#52c41a` (绿色)
|
||||||
|
- Warning: `#faad14` (橙色)
|
||||||
|
- Error: `#f5222d` (红色)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📈 项目进度
|
||||||
|
|
||||||
|
**当前版本**:v6.0
|
||||||
|
**完成度**:100% ✅
|
||||||
|
|
||||||
|
### 开发阶段
|
||||||
|
- ✅ 阶段1:基础配置与核心功能(完成)
|
||||||
|
- ✅ 阶段2:部门管理与数据分析(完成)
|
||||||
|
- ✅ 阶段3:数据导出与系统配置(完成)
|
||||||
|
- ✅ 阶段4:JWT Token与参数配置(完成)
|
||||||
|
- ✅ 阶段5:通知、激励、管理员调配(完成)
|
||||||
|
- ✅ 阶段6:最终收尾与文档完善(完成)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔮 未来规划
|
||||||
|
|
||||||
|
### 短期(1周内)
|
||||||
|
- 系统测试(功能/性能/兼容性)
|
||||||
|
- Bug修复与优化
|
||||||
|
- 用户操作手册
|
||||||
|
|
||||||
|
### 中期(1-3月)
|
||||||
|
- 钉钉深度集成
|
||||||
|
- 移动端适配
|
||||||
|
- AI推荐功能
|
||||||
|
- 性能优化
|
||||||
|
|
||||||
|
### 长期(3-6月)
|
||||||
|
- 微服务化架构
|
||||||
|
- 容器化部署
|
||||||
|
- 数据大屏展示
|
||||||
|
- 第三方API开放
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🤝 贡献指南
|
||||||
|
|
||||||
|
欢迎贡献代码、报告问题或提出建议!
|
||||||
|
|
||||||
|
1. Fork本项目
|
||||||
|
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
|
||||||
|
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
|
||||||
|
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
||||||
|
5. 提交Pull Request
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ❓ 常见问题
|
||||||
|
|
||||||
|
### 如何重置密码?
|
||||||
|
管理员可在用户管理模块重置用户密码。
|
||||||
|
|
||||||
|
### 如何配置钉钉登录?
|
||||||
|
参考 `DEPLOYMENT_GUIDE.md` 中的钉钉集成部分。
|
||||||
|
|
||||||
|
### 如何备份数据?
|
||||||
|
参考 `DEPLOYMENT_GUIDE.md` 中的数据库备份部分。
|
||||||
|
|
||||||
|
### 更多问题
|
||||||
|
请查看 [DEPLOYMENT_GUIDE.md](./DEPLOYMENT_GUIDE.md) 的"常见问题"章节。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 开源协议
|
||||||
|
|
||||||
|
本项目采用 [MIT](LICENSE) 协议开源。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🙏 致谢
|
||||||
|
|
||||||
|
感谢以下开源项目:
|
||||||
|
|
||||||
|
- [Vue.js](https://vuejs.org/)
|
||||||
|
- [Spring Boot](https://spring.io/projects/spring-boot)
|
||||||
|
- [Ant Design](https://ant.design/)
|
||||||
|
- [ECharts](https://echarts.apache.org/)
|
||||||
|
- [MyBatis Plus](https://baomidou.com/)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 联系方式
|
||||||
|
|
||||||
|
- **项目名称**:DesignHub
|
||||||
|
- **当前版本**:v6.0
|
||||||
|
- **完成度**:100%
|
||||||
|
- **开发时间**:2025年11月7日
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
**⭐ 如果这个项目对您有帮助,请给个Star支持一下!⭐**
|
||||||
|
|
||||||
|
Made with ❤️ by DesignHub Team
|
||||||
|
|
||||||
|
</div>
|
||||||
331
S3文件存储系统实现说明.md
Normal file
331
S3文件存储系统实现说明.md
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
# S3文件存储系统实现说明
|
||||||
|
|
||||||
|
## 📋 概述
|
||||||
|
|
||||||
|
本系统实现了基于S3兼容存储(MinIO)的文件上传、下载和管理功能,包括:
|
||||||
|
|
||||||
|
1. **S3配置管理**:支持多配置管理和动态切换
|
||||||
|
2. **文件上传**:支持多文件上传到S3存储
|
||||||
|
3. **文件下载**:带登录验证的文件下载
|
||||||
|
4. **文件记录**:完整的文件上传记录和关联管理
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗄️ 数据库设计
|
||||||
|
|
||||||
|
### 1. S3配置表 (`sys_s3_config`)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE `sys_s3_config` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`endpoint` varchar(255) NOT NULL COMMENT 'S3服务端点',
|
||||||
|
`bucket` varchar(100) NOT NULL COMMENT '存储桶名称',
|
||||||
|
`access_key_id` varchar(255) NOT NULL,
|
||||||
|
`secret_access_key` varchar(255) NOT NULL,
|
||||||
|
`region` varchar(50) DEFAULT 'us-east-1',
|
||||||
|
`public_domain` varchar(255),
|
||||||
|
`enable_path_style` tinyint(1) DEFAULT 1,
|
||||||
|
`file_path` varchar(255) DEFAULT '/',
|
||||||
|
`is_active` tinyint(1) DEFAULT 1,
|
||||||
|
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
`deleted` tinyint(1) DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 文件上传记录表 (`sys_file_upload`)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE `sys_file_upload` (
|
||||||
|
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||||
|
`original_name` varchar(255) NOT NULL,
|
||||||
|
`stored_name` varchar(255) NOT NULL,
|
||||||
|
`file_path` varchar(500) NOT NULL,
|
||||||
|
`file_url` varchar(500) NOT NULL,
|
||||||
|
`file_size` bigint NOT NULL,
|
||||||
|
`file_type` varchar(100),
|
||||||
|
`uploader_id` bigint NOT NULL,
|
||||||
|
`uploader_name` varchar(100),
|
||||||
|
`related_type` varchar(50),
|
||||||
|
`related_id` bigint,
|
||||||
|
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
`deleted` tinyint(1) DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_uploader` (`uploader_id`),
|
||||||
|
KEY `idx_related` (`related_type`, `related_id`)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 后端实现
|
||||||
|
|
||||||
|
### 1. Maven依赖
|
||||||
|
|
||||||
|
在 `pom.xml` 中添加:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- AWS SDK for S3 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>software.amazon.awssdk</groupId>
|
||||||
|
<artifactId>s3</artifactId>
|
||||||
|
<version>2.20.26</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Apache Commons IO -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>2.11.0</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 核心服务
|
||||||
|
|
||||||
|
#### S3Service
|
||||||
|
- `uploadFile()`: 上传文件到S3
|
||||||
|
- `getFileUploadById()`: 获取文件记录
|
||||||
|
- `getFilesByRelated()`: 获取关联文件列表
|
||||||
|
|
||||||
|
#### S3ConfigService
|
||||||
|
- `getActiveConfig()`: 获取激活的配置
|
||||||
|
- `createConfig()`: 创建新配置
|
||||||
|
- `updateConfig()`: 更新配置
|
||||||
|
- `activateConfig()`: 激活指定配置
|
||||||
|
|
||||||
|
### 3. API接口
|
||||||
|
|
||||||
|
#### S3配置管理 (`/api/s3/config`)
|
||||||
|
|
||||||
|
- `GET /active` - 获取激活的配置
|
||||||
|
- `GET /list` - 获取所有配置
|
||||||
|
- `GET /{id}` - 获取指定配置
|
||||||
|
- `POST /` - 创建配置
|
||||||
|
- `PUT /{id}` - 更新配置
|
||||||
|
- `DELETE /{id}` - 删除配置
|
||||||
|
- `PUT /{id}/activate` - 激活配置
|
||||||
|
|
||||||
|
#### 文件管理 (`/api/file`)
|
||||||
|
|
||||||
|
- `POST /upload` - 上传文件(需要登录)
|
||||||
|
- 参数:`files` (MultipartFile[]), `relatedType`, `relatedId`
|
||||||
|
- 返回:文件URL列表
|
||||||
|
|
||||||
|
- `GET /download/{id}` - 下载文件(需要登录,验证token)
|
||||||
|
- 自动重定向到S3文件URL
|
||||||
|
|
||||||
|
- `GET /{id}` - 获取文件信息
|
||||||
|
- `GET /list` - 获取关联文件列表
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎨 前端实现
|
||||||
|
|
||||||
|
### 1. S3配置管理页面
|
||||||
|
|
||||||
|
在 `Settings.vue` 中添加S3配置管理标签页:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<a-tab-pane key="s3" tab="S3存储配置">
|
||||||
|
<!-- S3配置表单 -->
|
||||||
|
<a-form :model="s3ConfigForm">
|
||||||
|
<a-form-item label="服务端点">
|
||||||
|
<a-input v-model:value="s3ConfigForm.endpoint" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="存储桶">
|
||||||
|
<a-input v-model:value="s3ConfigForm.bucket" />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- ... 其他配置项 ... -->
|
||||||
|
<a-button @click="saveS3Config">保存配置</a-button>
|
||||||
|
</a-form>
|
||||||
|
</a-tab-pane>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 文件上传组件
|
||||||
|
|
||||||
|
在 `CreateTask.vue` 中修改文件上传逻辑:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 使用新的S3上传接口
|
||||||
|
const handleUpload = async (file: File) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('files', file);
|
||||||
|
formData.append('relatedType', 'task');
|
||||||
|
|
||||||
|
const response = await axios.post('/api/file/upload', formData, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`,
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.data.data; // 返回文件URL数组
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 文件下载
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const downloadFile = (fileId: number) => {
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
window.open(`/api/file/download/${fileId}`, '_blank');
|
||||||
|
// 浏览器会自动携带token(如果在请求头中)
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 安全特性
|
||||||
|
|
||||||
|
### 1. 登录验证
|
||||||
|
- 所有文件上传需要有效的JWT token
|
||||||
|
- 文件下载需要验证登录状态
|
||||||
|
- token验证失败返回401错误
|
||||||
|
|
||||||
|
### 2. 敏感信息保护
|
||||||
|
- API返回时自动隐藏`secretAccessKey`
|
||||||
|
- 前端更新时支持保留原密钥
|
||||||
|
|
||||||
|
### 3. 权限控制
|
||||||
|
- 只有管理员可以配置S3
|
||||||
|
- 普通用户只能上传和下载自己有权限的文件
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 使用流程
|
||||||
|
|
||||||
|
### 1. 配置S3存储
|
||||||
|
|
||||||
|
1. 登录系统,进入**系统设置** > **S3存储配置**
|
||||||
|
2. 填写S3配置信息:
|
||||||
|
- 服务端点:`https://s3.1080p.hk`
|
||||||
|
- 存储桶:`jsxiangche`
|
||||||
|
- AccessKey和SecretKey
|
||||||
|
- 公共域名:`https://s3.1080p.hk/jsxiangche`
|
||||||
|
- 文件路径前缀:`由壹点AI/`
|
||||||
|
3. 点击保存并激活
|
||||||
|
|
||||||
|
### 2. 上传文件
|
||||||
|
|
||||||
|
1. 在创建任务页面选择文件
|
||||||
|
2. 文件自动上传到S3
|
||||||
|
3. 系统生成文件URL并保存到数据库
|
||||||
|
4. 任务中显示附件链接
|
||||||
|
|
||||||
|
### 3. 下载文件
|
||||||
|
|
||||||
|
1. 在任务卡片或详情中点击附件链接
|
||||||
|
2. 系统验证登录状态
|
||||||
|
3. 自动跳转到S3文件下载
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 部署步骤
|
||||||
|
|
||||||
|
### 1. 执行数据库脚本
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mysql -uroot -p designhub < database/create_s3_config.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 更新后端依赖
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
mvn clean install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 重启后端服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl restart designhub-backend
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 更新前端
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
pnpm build
|
||||||
|
# 部署到服务器
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 测试验证
|
||||||
|
|
||||||
|
### 1. 测试S3配置
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X GET http://localhost:8080/api/s3/config/active \
|
||||||
|
-H "Authorization: Bearer YOUR_TOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 测试文件上传
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/api/file/upload \
|
||||||
|
-H "Authorization: Bearer YOUR_TOKEN" \
|
||||||
|
-F "files=@test.pdf" \
|
||||||
|
-F "relatedType=task" \
|
||||||
|
-F "relatedId=1"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 测试文件下载
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X GET http://localhost:8080/api/file/download/1 \
|
||||||
|
-H "Authorization: Bearer YOUR_TOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 注意事项
|
||||||
|
|
||||||
|
1. **S3配置安全**:
|
||||||
|
- AccessKey和SecretKey存储在数据库中,需要加密存储(可选)
|
||||||
|
- 管理界面只对管理员开放
|
||||||
|
|
||||||
|
2. **文件大小限制**:
|
||||||
|
- 需要在Nginx和Spring Boot中配置文件大小限制
|
||||||
|
- 建议单文件不超过100MB
|
||||||
|
|
||||||
|
3. **文件命名**:
|
||||||
|
- 使用时间戳+UUID避免文件名冲突
|
||||||
|
- 保留原始文件名用于显示
|
||||||
|
|
||||||
|
4. **错误处理**:
|
||||||
|
- S3服务不可用时给出友好提示
|
||||||
|
- 上传失败后清理临时文件
|
||||||
|
|
||||||
|
5. **性能优化**:
|
||||||
|
- 使用S3的预签名URL实现直传(可选)
|
||||||
|
- 大文件使用分片上传(可选)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 后续优化建议
|
||||||
|
|
||||||
|
1. **直传优化**:前端直接上传到S3,后端只生成签名
|
||||||
|
2. **CDN加速**:配置CloudFlare等CDN加速文件访问
|
||||||
|
3. **图片处理**:集成图片压缩和格式转换
|
||||||
|
4. **文件预览**:支持PDF、图片等文件在线预览
|
||||||
|
5. **批量操作**:支持批量删除和批量下载
|
||||||
|
6. **存储统计**:显示存储空间使用情况
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 完成清单
|
||||||
|
|
||||||
|
- [x] 数据库表设计
|
||||||
|
- [x] 后端实体类和Mapper
|
||||||
|
- [x] S3Service核心服务
|
||||||
|
- [x] S3ConfigService配置管理
|
||||||
|
- [x] FileUploadController上传下载
|
||||||
|
- [x] S3ConfigController配置管理
|
||||||
|
- [ ] 前端S3配置管理页面
|
||||||
|
- [ ] 前端文件上传组件改造
|
||||||
|
- [ ] 系统测试和部署
|
||||||
195
S3部署说明.md
Normal file
195
S3部署说明.md
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
# S3文件存储系统 - 部署说明
|
||||||
|
|
||||||
|
## 📋 部署步骤
|
||||||
|
|
||||||
|
### 第一步:上传文件到服务器
|
||||||
|
|
||||||
|
在**本地Mac**上执行以下命令,将所有新文件上传到服务器:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 设置变量
|
||||||
|
SERVER="root@192.168.150.22"
|
||||||
|
BACKEND_DIR="/root/designhub-backend" # 请根据实际情况修改
|
||||||
|
|
||||||
|
# 1. 上传实体类
|
||||||
|
scp backend/src/main/java/com/designhub/entity/S3Config.java $SERVER:$BACKEND_DIR/src/main/java/com/designhub/entity/
|
||||||
|
scp backend/src/main/java/com/designhub/entity/FileUpload.java $SERVER:$BACKEND_DIR/src/main/java/com/designhub/entity/
|
||||||
|
|
||||||
|
# 2. 上传Mapper
|
||||||
|
scp backend/src/main/java/com/designhub/mapper/S3ConfigMapper.java $SERVER:$BACKEND_DIR/src/main/java/com/designhub/mapper/
|
||||||
|
scp backend/src/main/java/com/designhub/mapper/FileUploadMapper.java $SERVER:$BACKEND_DIR/src/main/java/com/designhub/mapper/
|
||||||
|
|
||||||
|
# 3. 上传Service
|
||||||
|
scp backend/src/main/java/com/designhub/service/S3Service.java $SERVER:$BACKEND_DIR/src/main/java/com/designhub/service/
|
||||||
|
scp backend/src/main/java/com/designhub/service/S3ConfigService.java $SERVER:$BACKEND_DIR/src/main/java/com/designhub/service/
|
||||||
|
|
||||||
|
# 4. 上传Controller
|
||||||
|
scp backend/src/main/java/com/designhub/controller/S3ConfigController.java $SERVER:$BACKEND_DIR/src/main/java/com/designhub/controller/
|
||||||
|
scp backend/src/main/java/com/designhub/controller/FileUploadController.java $SERVER:$BACKEND_DIR/src/main/java/com/designhub/controller/
|
||||||
|
|
||||||
|
# 5. 上传pom.xml(已包含AWS SDK依赖)
|
||||||
|
scp backend/pom.xml $SERVER:$BACKEND_DIR/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 第二步:在服务器上执行数据库脚本
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# SSH登录到服务器
|
||||||
|
ssh root@192.168.150.22
|
||||||
|
|
||||||
|
# 执行数据库脚本
|
||||||
|
mysql -uroot -p'Abc123456' designhub < /path/to/create_s3_config.sql
|
||||||
|
|
||||||
|
# 或者直接执行SQL(如果文件已上传)
|
||||||
|
mysql -uroot -p'Abc123456' designhub << EOF
|
||||||
|
-- 执行 create_s3_config.sql 中的内容
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### 第三步:编译后端
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 在服务器上
|
||||||
|
cd /root/designhub-backend # 根据实际路径修改
|
||||||
|
|
||||||
|
# 清理并编译
|
||||||
|
mvn clean package -DskipTests
|
||||||
|
```
|
||||||
|
|
||||||
|
### 第四步:部署和重启
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 备份当前JAR
|
||||||
|
cp /path/to/designhub-backend.jar /path/to/designhub-backend.jar.backup
|
||||||
|
|
||||||
|
# 2. 复制新JAR(根据实际路径)
|
||||||
|
cp target/designhub-backend-*.jar /path/to/designhub-backend.jar
|
||||||
|
|
||||||
|
# 3. 重启服务
|
||||||
|
systemctl restart designhub-backend
|
||||||
|
|
||||||
|
# 4. 检查状态
|
||||||
|
systemctl status designhub-backend
|
||||||
|
|
||||||
|
# 5. 查看日志
|
||||||
|
journalctl -u designhub-backend -f
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 验证部署
|
||||||
|
|
||||||
|
### 1. 检查数据库表
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 检查S3配置表
|
||||||
|
SELECT * FROM sys_s3_config;
|
||||||
|
|
||||||
|
-- 检查文件上传表
|
||||||
|
SHOW CREATE TABLE sys_file_upload;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 测试API
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 获取激活的S3配置(需要token)
|
||||||
|
curl -X GET http://localhost:8080/api/s3/config/active \
|
||||||
|
-H "Authorization: Bearer YOUR_TOKEN"
|
||||||
|
|
||||||
|
# 上传文件测试(需要token)
|
||||||
|
curl -X POST http://localhost:8080/api/file/upload \
|
||||||
|
-H "Authorization: Bearer YOUR_TOKEN" \
|
||||||
|
-F "files=@test.pdf" \
|
||||||
|
-F "relatedType=task"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 文件清单
|
||||||
|
|
||||||
|
需要上传的文件:
|
||||||
|
|
||||||
|
1. **实体类** (Entity)
|
||||||
|
- `S3Config.java`
|
||||||
|
- `FileUpload.java`
|
||||||
|
|
||||||
|
2. **Mapper接口**
|
||||||
|
- `S3ConfigMapper.java`
|
||||||
|
- `FileUploadMapper.java`
|
||||||
|
|
||||||
|
3. **服务类** (Service)
|
||||||
|
- `S3Service.java`
|
||||||
|
- `S3ConfigService.java`
|
||||||
|
|
||||||
|
4. **控制器** (Controller)
|
||||||
|
- `S3ConfigController.java`
|
||||||
|
- `FileUploadController.java`
|
||||||
|
|
||||||
|
5. **配置文件**
|
||||||
|
- `pom.xml` (已添加AWS SDK依赖)
|
||||||
|
|
||||||
|
6. **数据库脚本**
|
||||||
|
- `database/create_s3_config.sql`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 注意事项
|
||||||
|
|
||||||
|
1. **路径确认**:请确认服务器上的后端代码路径,可能需要修改 `BACKEND_DIR` 变量
|
||||||
|
|
||||||
|
2. **数据库密码**:如果数据库密码不是 `Abc123456`,请修改脚本中的密码
|
||||||
|
|
||||||
|
3. **Maven依赖**:确保 `pom.xml` 已包含以下依赖:
|
||||||
|
```xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>software.amazon.awssdk</groupId>
|
||||||
|
<artifactId>s3</artifactId>
|
||||||
|
<version>2.20.26</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>2.11.0</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **JwtUtil注入**:`FileUploadController` 需要注入 `JwtUtil`,确保 `JwtUtil` 是 `@Component`
|
||||||
|
|
||||||
|
5. **S3配置**:部署后需要在数据库中激活S3配置,默认配置已插入但需要确认
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 常见问题
|
||||||
|
|
||||||
|
### 问题1:编译失败 - 找不到AWS SDK类
|
||||||
|
|
||||||
|
**解决**:检查 `pom.xml` 是否包含AWS SDK依赖,然后执行 `mvn clean install`
|
||||||
|
|
||||||
|
### 问题2:运行时错误 - S3配置未激活
|
||||||
|
|
||||||
|
**解决**:检查数据库中的 `sys_s3_config` 表,确保有一条 `is_active=1` 的记录
|
||||||
|
|
||||||
|
### 问题3:文件上传失败 - 连接S3超时
|
||||||
|
|
||||||
|
**解决**:
|
||||||
|
- 检查S3服务端点是否可访问
|
||||||
|
- 检查AccessKey和SecretKey是否正确
|
||||||
|
- 检查网络连接和防火墙设置
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 部署完成检查清单
|
||||||
|
|
||||||
|
- [ ] 所有Java文件已上传到服务器
|
||||||
|
- [ ] `pom.xml` 已更新并包含AWS SDK依赖
|
||||||
|
- [ ] 数据库脚本已执行,表已创建
|
||||||
|
- [ ] 后端编译成功,无错误
|
||||||
|
- [ ] JAR文件已部署到正确位置
|
||||||
|
- [ ] 服务已重启
|
||||||
|
- [ ] 服务状态正常(`systemctl status`)
|
||||||
|
- [ ] 日志无错误信息
|
||||||
|
- [ ] API测试通过
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
部署完成后,请告诉我,我将继续完成前端的S3配置管理界面!
|
||||||
BIN
backend/backend-src.tar.gz
Normal file
BIN
backend/backend-src.tar.gz
Normal file
Binary file not shown.
BIN
backend/backend_src.tar.gz
Normal file
BIN
backend/backend_src.tar.gz
Normal file
Binary file not shown.
15
backend/pom-s3-dependency.xml
Normal file
15
backend/pom-s3-dependency.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!-- 将以下依赖添加到 pom.xml 的 <dependencies> 部分 -->
|
||||||
|
|
||||||
|
<!-- AWS SDK for S3 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>software.amazon.awssdk</groupId>
|
||||||
|
<artifactId>s3</artifactId>
|
||||||
|
<version>2.20.26</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Apache Commons IO for file utilities -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>2.11.0</version>
|
||||||
|
</dependency>
|
||||||
205
backend/pom.xml
Normal file
205
backend/pom.xml
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
<?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.1.6</version>
|
||||||
|
<relativePath/>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<groupId>com.designhub</groupId>
|
||||||
|
<artifactId>designhub-backend</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<name>DesignHub Backend</name>
|
||||||
|
<description>设计师任务抢单管理系统 - 后端服务</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<mybatis-plus.version>3.5.7</mybatis-plus.version>
|
||||||
|
<knife4j.version>4.3.0</knife4j.version>
|
||||||
|
<jwt.version>0.12.3</jwt.version>
|
||||||
|
<hutool.version>5.8.24</hutool.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Spring Boot Web -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Boot Validation -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Boot AOP -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Boot Redis -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MySQL Driver -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-j</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MyBatis Plus -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||||
|
<version>${mybatis-plus.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- JWT -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-api</artifactId>
|
||||||
|
<version>${jwt.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-impl</artifactId>
|
||||||
|
<version>${jwt.version}</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt-jackson</artifactId>
|
||||||
|
<version>${jwt.version}</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Security (仅用于BCryptPasswordEncoder) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-crypto</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Knife4j (Swagger UI) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.xiaoymin</groupId>
|
||||||
|
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
|
||||||
|
<version>${knife4j.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Hutool 工具包 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
<version>${hutool.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Lombok -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Boot Test -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 钉钉 SDK -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.aliyun</groupId>
|
||||||
|
<artifactId>dingtalk</artifactId>
|
||||||
|
<version>2.1.67</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Quartz 任务调度 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-quartz</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Apache Commons Lang3 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Apache Commons Collections -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-collections4</artifactId>
|
||||||
|
<version>4.4</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Apache POI (Excel操作) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi-ooxml</artifactId>
|
||||||
|
<version>5.2.5</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- AWS SDK for S3 (MinIO compatible) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>software.amazon.awssdk</groupId>
|
||||||
|
<artifactId>s3</artifactId>
|
||||||
|
<version>2.20.26</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Apache Commons IO -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>2.11.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.11.0</version>
|
||||||
|
<configuration>
|
||||||
|
<source>17</source>
|
||||||
|
<target>17</target>
|
||||||
|
<annotationProcessorPaths>
|
||||||
|
<path>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.34</version>
|
||||||
|
</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>
|
||||||
|
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package com.designhub;
|
||||||
|
|
||||||
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DesignHub 主应用类
|
||||||
|
* 设计师任务抢单管理系统
|
||||||
|
*
|
||||||
|
* @author DesignHub Team
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
@SpringBootApplication
|
||||||
|
@MapperScan("com.designhub.mapper")
|
||||||
|
@EnableScheduling
|
||||||
|
public class DesignHubApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(DesignHubApplication.class, args);
|
||||||
|
System.out.println("""
|
||||||
|
|
||||||
|
╔═══════════════════════════════════════════════════════╗
|
||||||
|
║ ║
|
||||||
|
║ 🎨 DesignHub Backend Service Started 🎨 ║
|
||||||
|
║ ║
|
||||||
|
║ Port: 8080 ║
|
||||||
|
║ Swagger UI: http://localhost:8080/api/doc.html ║
|
||||||
|
║ Version: 1.0.0 ║
|
||||||
|
║ ║
|
||||||
|
╚═══════════════════════════════════════════════════════╝
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package com.designhub.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限验证注解
|
||||||
|
* 用于Controller方法上,标记需要的权限
|
||||||
|
*/
|
||||||
|
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface RequirePermission {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限代码
|
||||||
|
*/
|
||||||
|
String[] value() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限匹配模式:AND-需要所有权限,OR-需要任一权限
|
||||||
|
*/
|
||||||
|
LogicalMode mode() default LogicalMode.AND;
|
||||||
|
|
||||||
|
enum LogicalMode {
|
||||||
|
AND, OR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package com.designhub.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色验证注解
|
||||||
|
* 用于Controller方法上,标记需要的角色
|
||||||
|
*/
|
||||||
|
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface RequireRole {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色代码
|
||||||
|
*/
|
||||||
|
String[] value() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色匹配模式:AND-需要所有角色,OR-需要任一角色
|
||||||
|
*/
|
||||||
|
LogicalMode mode() default LogicalMode.OR;
|
||||||
|
|
||||||
|
enum LogicalMode {
|
||||||
|
AND, OR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
110
backend/src/main/java/com/designhub/common/Result.java
Normal file
110
backend/src/main/java/com/designhub/common/Result.java
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package com.designhub.common;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一响应结果类
|
||||||
|
*
|
||||||
|
* @param <T> 数据类型
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class Result<T> implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应码
|
||||||
|
*/
|
||||||
|
private Integer code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应消息
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应数据
|
||||||
|
*/
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间戳
|
||||||
|
*/
|
||||||
|
private Long timestamp;
|
||||||
|
|
||||||
|
public Result() {
|
||||||
|
this.timestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result(Integer code, String message, T data) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
this.data = data;
|
||||||
|
this.timestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功响应
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> success() {
|
||||||
|
return new Result<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功响应(带数据)
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> success(T data) {
|
||||||
|
return new Result<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功响应(自定义消息)
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> success(String message, T data) {
|
||||||
|
return new Result<>(ResultCode.SUCCESS.getCode(), message, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败响应
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> error() {
|
||||||
|
return new Result<>(ResultCode.ERROR.getCode(), ResultCode.ERROR.getMessage(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败响应(自定义消息)
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> error(String message) {
|
||||||
|
return new Result<>(ResultCode.ERROR.getCode(), message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败响应(自定义码和消息)
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> error(Integer code, String message) {
|
||||||
|
return new Result<>(code, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败响应(使用ResultCode)
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> error(ResultCode resultCode) {
|
||||||
|
return new Result<>(resultCode.getCode(), resultCode.getMessage(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败响应(自定义码、消息和数据)
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> error(Integer code, String message, T data) {
|
||||||
|
return new Result<>(code, message, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败响应(别名方法)
|
||||||
|
*/
|
||||||
|
public static <T> Result<T> fail(String message) {
|
||||||
|
return error(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
59
backend/src/main/java/com/designhub/common/ResultCode.java
Normal file
59
backend/src/main/java/com/designhub/common/ResultCode.java
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package com.designhub.common;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应状态码枚举
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public enum ResultCode {
|
||||||
|
|
||||||
|
// 成功
|
||||||
|
SUCCESS(200, "操作成功"),
|
||||||
|
|
||||||
|
// 客户端错误 4xx
|
||||||
|
BAD_REQUEST(400, "请求参数错误"),
|
||||||
|
PARAM_ERROR(400, "参数错误"),
|
||||||
|
UNAUTHORIZED(401, "未授权,请登录"),
|
||||||
|
FORBIDDEN(403, "没有权限访问该资源"),
|
||||||
|
NOT_FOUND(404, "请求的资源不存在"),
|
||||||
|
METHOD_NOT_ALLOWED(405, "请求方法不支持"),
|
||||||
|
CONFLICT(409, "数据冲突"),
|
||||||
|
|
||||||
|
// 服务器错误 5xx
|
||||||
|
ERROR(500, "服务器内部错误"),
|
||||||
|
SERVICE_UNAVAILABLE(503, "服务暂时不可用"),
|
||||||
|
|
||||||
|
// 业务错误 1xxx
|
||||||
|
USER_NOT_EXIST(1001, "用户不存在"),
|
||||||
|
USER_ALREADY_EXIST(1002, "用户已存在"),
|
||||||
|
PASSWORD_ERROR(1003, "密码错误"),
|
||||||
|
USER_DISABLED(1004, "用户已被禁用"),
|
||||||
|
TOKEN_EXPIRED(1005, "Token已过期"),
|
||||||
|
TOKEN_INVALID(1006, "Token无效"),
|
||||||
|
|
||||||
|
TASK_NOT_EXIST(1101, "任务不存在"),
|
||||||
|
TASK_ALREADY_TAKEN(1102, "任务已被抢单"),
|
||||||
|
INSUFFICIENT_SCORE(1103, "积分不足"),
|
||||||
|
TASK_STATUS_ERROR(1104, "任务状态错误"),
|
||||||
|
|
||||||
|
DEPT_NOT_EXIST(1201, "部门不存在"),
|
||||||
|
DEPT_TYPE_ERROR(1202, "部门类型错误"),
|
||||||
|
SCORE_LIMIT_EXCEEDED(1203, "超出积分消耗限制"),
|
||||||
|
|
||||||
|
FILE_UPLOAD_ERROR(1301, "文件上传失败"),
|
||||||
|
FILE_TYPE_ERROR(1302, "文件类型不支持"),
|
||||||
|
FILE_SIZE_ERROR(1303, "文件大小超出限制"),
|
||||||
|
|
||||||
|
DINGTALK_AUTH_ERROR(1401, "钉钉认证失败"),
|
||||||
|
DINGTALK_API_ERROR(1402, "钉钉接口调用失败");
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
ResultCode(Integer code, String message) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
40
backend/src/main/java/com/designhub/config/CorsConfig.java
Normal file
40
backend/src/main/java/com/designhub/config/CorsConfig.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package com.designhub.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
|
import org.springframework.web.filter.CorsFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跨域配置
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class CorsConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CorsFilter corsFilter() {
|
||||||
|
CorsConfiguration config = new CorsConfiguration();
|
||||||
|
|
||||||
|
// 允许所有域名进行跨域调用(生产环境需要具体指定)
|
||||||
|
config.addAllowedOriginPattern("*");
|
||||||
|
|
||||||
|
// 允许所有请求头
|
||||||
|
config.addAllowedHeader("*");
|
||||||
|
|
||||||
|
// 允许所有请求方法
|
||||||
|
config.addAllowedMethod("*");
|
||||||
|
|
||||||
|
// 允许携带凭证
|
||||||
|
config.setAllowCredentials(true);
|
||||||
|
|
||||||
|
// 暴露哪些头信息
|
||||||
|
config.addExposedHeader("Authorization");
|
||||||
|
|
||||||
|
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||||
|
source.registerCorsConfiguration("/**", config);
|
||||||
|
|
||||||
|
return new CorsFilter(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package com.designhub.config;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.models.OpenAPI;
|
||||||
|
import io.swagger.v3.oas.models.info.Contact;
|
||||||
|
import io.swagger.v3.oas.models.info.Info;
|
||||||
|
import io.swagger.v3.oas.models.info.License;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Knife4j (Swagger) 配置
|
||||||
|
* 访问地址:http://localhost:8080/api/doc.html
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class Knife4jConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public OpenAPI customOpenAPI() {
|
||||||
|
return new OpenAPI()
|
||||||
|
.info(new Info()
|
||||||
|
.title("DesignHub API 文档")
|
||||||
|
.description("设计师任务抢单管理系统 RESTful API 接口文档")
|
||||||
|
.version("v1.0.0")
|
||||||
|
.contact(new Contact()
|
||||||
|
.name("DesignHub Team")
|
||||||
|
.email("support@designhub.com"))
|
||||||
|
.license(new License()
|
||||||
|
.name("MIT License")
|
||||||
|
.url("https://opensource.org/licenses/MIT")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package com.designhub.config;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.DbType;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MyBatis Plus 配置类
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class MybatisPlusConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MyBatis Plus 分页插件配置
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||||
|
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||||
|
// 添加分页插件
|
||||||
|
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
|
||||||
|
// 设置最大单页限制数量,默认 500 条,-1 不受限制
|
||||||
|
paginationInnerInterceptor.setMaxLimit(1000L);
|
||||||
|
interceptor.addInnerInterceptor(paginationInnerInterceptor);
|
||||||
|
return interceptor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package com.designhub.config;
|
||||||
|
|
||||||
|
import com.designhub.service.SystemConfigService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.boot.ApplicationArguments;
|
||||||
|
import org.springframework.boot.ApplicationRunner;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统配置初始化器
|
||||||
|
* 在应用启动时自动初始化默认配置
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SystemConfigInitializer implements ApplicationRunner {
|
||||||
|
|
||||||
|
private final SystemConfigService systemConfigService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(ApplicationArguments args) {
|
||||||
|
try {
|
||||||
|
log.info("开始初始化系统默认配置...");
|
||||||
|
systemConfigService.initDefaultConfigs();
|
||||||
|
log.info("系统默认配置初始化完成");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("系统默认配置初始化失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
backend/src/main/java/com/designhub/config/WebConfig.java
Normal file
38
backend/src/main/java/com/designhub/config/WebConfig.java
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package com.designhub.config;
|
||||||
|
|
||||||
|
import com.designhub.interceptor.PermissionInterceptor;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web配置类
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class WebConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
private final PermissionInterceptor permissionInterceptor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
registry.addInterceptor(permissionInterceptor)
|
||||||
|
.addPathPatterns("/api/**") // 拦截所有API请求
|
||||||
|
.excludePathPatterns(
|
||||||
|
"/api/auth/**", // 排除认证相关接口(包含注册)
|
||||||
|
"/api/departments", // 排除部门列表接口(注册页面需要)
|
||||||
|
"/api/hello", // 排除测试接口
|
||||||
|
"/api/system/health", // 排除健康检查接口
|
||||||
|
"/api/system-config/group/site", // 排除公开的网站设置API(登录页面需要)
|
||||||
|
"/api/system-config/key/site.name", // 排除公开的网站设置API(按key获取)
|
||||||
|
"/api/system-config/key/site.logo", // 排除公开的网站设置API(按key获取)
|
||||||
|
"/api/system-config/map/group/site", // 排除公开的网站设置API(Map格式)
|
||||||
|
"/doc.html", // 排除Swagger文档
|
||||||
|
"/swagger-ui/**",
|
||||||
|
"/v3/api-docs/**",
|
||||||
|
"/webjars/**"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.Achievement;
|
||||||
|
import com.designhub.entity.UserAchievement;
|
||||||
|
import com.designhub.service.AchievementService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成就系统Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "成就系统", description = "成就徽章相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/achievements")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AchievementController {
|
||||||
|
|
||||||
|
private final AchievementService achievementService;
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有成就列表")
|
||||||
|
@GetMapping
|
||||||
|
public Result<List<Achievement>> getAllAchievements() {
|
||||||
|
List<Achievement> achievements = achievementService.getAllAchievements();
|
||||||
|
return Result.success(achievements);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取用户已达成的成就")
|
||||||
|
@GetMapping("/user/{userId}")
|
||||||
|
public Result<List<UserAchievement>> getUserAchievements(@PathVariable Long userId) {
|
||||||
|
List<UserAchievement> achievements = achievementService.getUserAchievements(userId);
|
||||||
|
return Result.success(achievements);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "领取成就奖励")
|
||||||
|
@PostMapping("/{achievementId}/claim")
|
||||||
|
public Result<Boolean> claimReward(@PathVariable Long achievementId, @RequestParam Long userId) {
|
||||||
|
boolean success = achievementService.claimAchievementReward(userId, achievementId);
|
||||||
|
return Result.success(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "初始化默认成就")
|
||||||
|
@PostMapping("/init")
|
||||||
|
public Result<Void> initDefaultAchievements() {
|
||||||
|
achievementService.initDefaultAchievements();
|
||||||
|
return Result.success(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,155 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.Task;
|
||||||
|
import com.designhub.service.AdminTaskService;
|
||||||
|
import com.designhub.util.JwtUtil;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理员任务调配Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "管理员任务管理", description = "管理员任务调配相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/admin/tasks")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AdminTaskController {
|
||||||
|
|
||||||
|
private final AdminTaskService adminTaskService;
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@Operation(summary = "分配任务")
|
||||||
|
@PostMapping("/{taskId}/assign")
|
||||||
|
public Result<Task> assignTask(
|
||||||
|
@PathVariable Long taskId,
|
||||||
|
@RequestParam Long designerId,
|
||||||
|
@RequestHeader("Authorization") String authHeader
|
||||||
|
) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long adminId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
Task task = adminTaskService.assignTask(taskId, designerId, adminId);
|
||||||
|
return Result.success(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "转移任务")
|
||||||
|
@PostMapping("/{taskId}/transfer")
|
||||||
|
public Result<Task> transferTask(
|
||||||
|
@PathVariable Long taskId,
|
||||||
|
@RequestBody TransferRequest request,
|
||||||
|
@RequestHeader("Authorization") String authHeader
|
||||||
|
) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long adminId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
Task task = adminTaskService.transferTask(
|
||||||
|
taskId,
|
||||||
|
request.getNewDesignerId(),
|
||||||
|
adminId,
|
||||||
|
request.getReason()
|
||||||
|
);
|
||||||
|
return Result.success(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "批量分配任务")
|
||||||
|
@PostMapping("/batch-assign")
|
||||||
|
public Result<Integer> batchAssignTasks(
|
||||||
|
@RequestBody BatchAssignRequest request,
|
||||||
|
@RequestHeader("Authorization") String authHeader
|
||||||
|
) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long adminId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
int count = adminTaskService.batchAssignTasks(
|
||||||
|
request.getTaskIds(),
|
||||||
|
request.getDesignerId(),
|
||||||
|
adminId
|
||||||
|
);
|
||||||
|
return Result.success(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "调整任务优先级")
|
||||||
|
@PutMapping("/{taskId}/priority")
|
||||||
|
public Result<Task> adjustTaskPriority(
|
||||||
|
@PathVariable Long taskId,
|
||||||
|
@RequestParam Integer priority,
|
||||||
|
@RequestHeader("Authorization") String authHeader
|
||||||
|
) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long adminId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
Task task = adminTaskService.adjustTaskPriority(taskId, priority, adminId);
|
||||||
|
return Result.success(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取任务池列表")
|
||||||
|
@GetMapping("/pool")
|
||||||
|
public Result<List<Task>> getTaskPool() {
|
||||||
|
List<Task> tasks = adminTaskService.getTaskPool();
|
||||||
|
return Result.success(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有进行中的任务")
|
||||||
|
@GetMapping("/in-progress")
|
||||||
|
public Result<List<Task>> getInProgressTasks() {
|
||||||
|
List<Task> tasks = adminTaskService.getInProgressTasks();
|
||||||
|
return Result.success(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "强制完成任务")
|
||||||
|
@PostMapping("/{taskId}/force-complete")
|
||||||
|
public Result<Task> forceCompleteTask(
|
||||||
|
@PathVariable Long taskId,
|
||||||
|
@RequestBody AdminActionRequest request,
|
||||||
|
@RequestHeader("Authorization") String authHeader
|
||||||
|
) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long adminId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
Task task = adminTaskService.forceCompleteTask(taskId, adminId, request.getReason());
|
||||||
|
return Result.success(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "取消任务")
|
||||||
|
@PostMapping("/{taskId}/cancel")
|
||||||
|
public Result<Task> cancelTask(
|
||||||
|
@PathVariable Long taskId,
|
||||||
|
@RequestBody AdminActionRequest request,
|
||||||
|
@RequestHeader("Authorization") String authHeader
|
||||||
|
) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long adminId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
Task task = adminTaskService.cancelTask(taskId, adminId, request.getReason());
|
||||||
|
return Result.success(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DTO类
|
||||||
|
public static class TransferRequest {
|
||||||
|
private Long newDesignerId;
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
public Long getNewDesignerId() { return newDesignerId; }
|
||||||
|
public void setNewDesignerId(Long newDesignerId) { this.newDesignerId = newDesignerId; }
|
||||||
|
public String getReason() { return reason; }
|
||||||
|
public void setReason(String reason) { this.reason = reason; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BatchAssignRequest {
|
||||||
|
private List<Long> taskIds;
|
||||||
|
private Long designerId;
|
||||||
|
|
||||||
|
public List<Long> getTaskIds() { return taskIds; }
|
||||||
|
public void setTaskIds(List<Long> taskIds) { this.taskIds = taskIds; }
|
||||||
|
public Long getDesignerId() { return designerId; }
|
||||||
|
public void setDesignerId(Long designerId) { this.designerId = designerId; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AdminActionRequest {
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
public String getReason() { return reason; }
|
||||||
|
public void setReason(String reason) { this.reason = reason; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.service.AnalyticsService;
|
||||||
|
import com.designhub.util.JwtUtil;
|
||||||
|
import com.designhub.vo.DemandStatisticsVO;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据分析Controller
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Tag(name = "数据分析", description = "数据分析相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/analytics")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AnalyticsController {
|
||||||
|
|
||||||
|
private final AnalyticsService analyticsService;
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@Operation(summary = "获取需求统计数据(根据角色返回不同维度的数据)")
|
||||||
|
@GetMapping("/demand-statistics")
|
||||||
|
public Result<DemandStatisticsVO> getDemandStatistics(
|
||||||
|
@RequestHeader("Authorization") String authHeader,
|
||||||
|
@RequestParam(required = false, defaultValue = "month") String dateRange,
|
||||||
|
@RequestParam(required = false) Long deptId,
|
||||||
|
@RequestParam(required = false) Long userId
|
||||||
|
) {
|
||||||
|
// 从token获取当前用户ID
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long currentUserId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
|
||||||
|
log.info("获取需求统计数据: currentUserId={}, dateRange={}, deptId={}, userId={}",
|
||||||
|
currentUserId, dateRange, deptId, userId);
|
||||||
|
|
||||||
|
return Result.success(analyticsService.getDemandStatistics(currentUserId, dateRange, deptId, userId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,130 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.dto.LoginDTO;
|
||||||
|
import com.designhub.dto.RegisterDTO;
|
||||||
|
import com.designhub.entity.User;
|
||||||
|
import com.designhub.exception.BusinessException;
|
||||||
|
import com.designhub.service.UserService;
|
||||||
|
import com.designhub.util.JwtUtil;
|
||||||
|
import com.designhub.vo.LoginVO;
|
||||||
|
import com.designhub.vo.UserInfoVO;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证控制器
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Tag(name = "用户认证", description = "用户登录、登出、刷新令牌等接口")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/auth")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AuthController {
|
||||||
|
|
||||||
|
private final UserService userService;
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@Operation(summary = "用户注册", description = "新用户注册")
|
||||||
|
@PostMapping("/register")
|
||||||
|
public Result<User> register(@Valid @RequestBody RegisterDTO registerDTO) {
|
||||||
|
log.info("用户注册请求: {}", registerDTO.getUsername());
|
||||||
|
User user = userService.register(registerDTO);
|
||||||
|
return Result.success(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "用户登录", description = "支持密码登录、钉钉登录、Casdoor登录")
|
||||||
|
@PostMapping("/login")
|
||||||
|
public Result<LoginVO> login(@Valid @RequestBody LoginDTO loginDTO) {
|
||||||
|
log.info("用户登录请求: {}", loginDTO.getUsername());
|
||||||
|
LoginVO loginVO = userService.login(loginDTO);
|
||||||
|
return Result.success(loginVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取当前用户信息", description = "根据Token获取当前登录用户信息")
|
||||||
|
@GetMapping("/userinfo")
|
||||||
|
public Result<UserInfoVO> getUserInfo(@RequestHeader(value = "Authorization", required = false) String authHeader) {
|
||||||
|
log.info("获取用户信息");
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
UserInfoVO userInfo = userService.getUserInfoByToken(token);
|
||||||
|
return Result.success(userInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "用户登出", description = "退出登录,清除Token")
|
||||||
|
@PostMapping("/logout")
|
||||||
|
public Result<String> logout(@RequestHeader(value = "Authorization", required = false) String authHeader) {
|
||||||
|
log.info("用户登出");
|
||||||
|
// Token是无状态的,前端删除即可
|
||||||
|
// 如果需要黑名单机制,可以在Redis中记录
|
||||||
|
return Result.success("登出成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "刷新令牌", description = "使用刷新令牌获取新的访问令牌")
|
||||||
|
@PostMapping("/refresh")
|
||||||
|
public Result<LoginVO> refresh(@RequestHeader("Authorization") String authHeader) {
|
||||||
|
log.info("刷新令牌");
|
||||||
|
LoginVO loginVO = userService.refreshToken(authHeader);
|
||||||
|
return Result.success(loginVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "钉钉扫码登录回调", description = "接收钉钉扫码登录的回调 - 使用OAuth 2.0流程")
|
||||||
|
@GetMapping("/dingtalk/callback")
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Result<LoginVO> dingtalkCallback(
|
||||||
|
@RequestParam String code,
|
||||||
|
@RequestParam(required = false) String state) {
|
||||||
|
log.info("钉钉登录回调 - code: {}, state: {}",
|
||||||
|
code.substring(0, Math.min(10, code.length())) + "...", state);
|
||||||
|
|
||||||
|
// 使用新的OAuth 2.0流程登录
|
||||||
|
LoginDTO loginDTO = new LoginDTO();
|
||||||
|
loginDTO.setLoginType("dingtalk");
|
||||||
|
loginDTO.setAuthCode(code);
|
||||||
|
|
||||||
|
try {
|
||||||
|
LoginVO loginVO = userService.login(loginDTO);
|
||||||
|
log.info("钉钉登录成功: {}", loginVO.getUsername());
|
||||||
|
return Result.success(loginVO);
|
||||||
|
} catch (BusinessException e) {
|
||||||
|
// 检查是否需要选择部门
|
||||||
|
if (e.getMessage() != null && e.getMessage().startsWith("NEED_SELECT_DEPT:")) {
|
||||||
|
String[] parts = e.getMessage().split(":");
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
data.put("needSelectDept", true);
|
||||||
|
data.put("userId", parts[1]);
|
||||||
|
data.put("dingDepartments", parts.length > 2 ? parts[2] : "");
|
||||||
|
Result<Map<String, Object>> result = Result.error(1901, "需要选择部门", data);
|
||||||
|
return (Result<LoginVO>) (Result<?>) result;
|
||||||
|
}
|
||||||
|
log.error("钉钉登录失败", e);
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("钉钉登录失败", e);
|
||||||
|
return Result.error("钉钉登录失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "钉钉用户选择部门", description = "首次登录时选择系统部门")
|
||||||
|
@PostMapping("/dingtalk/select-department")
|
||||||
|
public Result<LoginVO> selectDepartment(
|
||||||
|
@RequestParam Long userId,
|
||||||
|
@RequestParam Long deptId) {
|
||||||
|
log.info("钉钉用户选择部门: userId={}, deptId={}", userId, deptId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
LoginVO loginVO = userService.completeDingTalkUserSetup(userId, deptId);
|
||||||
|
return Result.success(loginVO);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("选择部门失败", e);
|
||||||
|
return Result.error("选择部门失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.CheckIn;
|
||||||
|
import com.designhub.service.CheckInService;
|
||||||
|
import com.designhub.util.JwtUtil;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签到Controller
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Tag(name = "签到系统", description = "每日签到相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/check-in")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CheckInController {
|
||||||
|
|
||||||
|
private final CheckInService checkInService;
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@Operation(summary = "用户签到")
|
||||||
|
@PostMapping
|
||||||
|
public Result<CheckIn> checkIn(@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
if (userId == null) {
|
||||||
|
return Result.error(401, "未登录或登录已过期");
|
||||||
|
}
|
||||||
|
CheckIn checkIn = checkInService.checkIn(userId);
|
||||||
|
return Result.success(checkIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "检查今天是否已签到")
|
||||||
|
@GetMapping("/today")
|
||||||
|
public Result<Boolean> hasCheckedInToday(@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
if (userId == null) {
|
||||||
|
return Result.error(401, "未登录或登录已过期");
|
||||||
|
}
|
||||||
|
boolean checked = checkInService.hasCheckedInToday(userId);
|
||||||
|
return Result.success(checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取签到历史记录")
|
||||||
|
@GetMapping("/history")
|
||||||
|
public Result<List<CheckIn>> getCheckInHistory(
|
||||||
|
@RequestHeader("Authorization") String authHeader,
|
||||||
|
@RequestParam(defaultValue = "30") Integer limit
|
||||||
|
) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
if (userId == null) {
|
||||||
|
return Result.error(401, "未登录或登录已过期");
|
||||||
|
}
|
||||||
|
List<CheckIn> history = checkInService.getUserCheckInHistory(userId, limit);
|
||||||
|
return Result.success(history);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取签到统计信息")
|
||||||
|
@GetMapping("/stats")
|
||||||
|
public Result<Map<String, Object>> getCheckInStats(@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
if (userId == null) {
|
||||||
|
return Result.error(401, "未登录或登录已过期");
|
||||||
|
}
|
||||||
|
Map<String, Object> stats = new HashMap<>();
|
||||||
|
stats.put("continuousDays", checkInService.getContinuousDays(userId));
|
||||||
|
stats.put("totalDays", checkInService.getTotalCheckInDays(userId));
|
||||||
|
stats.put("hasCheckedInToday", checkInService.hasCheckedInToday(userId));
|
||||||
|
return Result.success(stats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,271 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.*;
|
||||||
|
import com.designhub.service.ConfigService;
|
||||||
|
import com.designhub.service.DemandTypeDepartmentService;
|
||||||
|
import com.designhub.service.DepartmentService;
|
||||||
|
import com.designhub.service.TeamScorePoolService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置管理Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "配置管理", description = "系统配置相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/config")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ConfigController {
|
||||||
|
|
||||||
|
private final ConfigService configService;
|
||||||
|
private final DemandTypeDepartmentService demandTypeDepartmentService;
|
||||||
|
private final TeamScorePoolService teamScorePoolService;
|
||||||
|
private final DepartmentService departmentService;
|
||||||
|
|
||||||
|
// ============ 需求类型管理 ============
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有需求类型(包括禁用的,用于管理页面)")
|
||||||
|
@GetMapping("/demand-types")
|
||||||
|
public Result<List<DemandType>> getAllDemandTypes() {
|
||||||
|
return Result.success(configService.getAllDemandTypes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有启用的需求类型(用于下拉选择)")
|
||||||
|
@GetMapping("/demand-types/enabled")
|
||||||
|
public Result<List<DemandType>> getEnabledDemandTypes() {
|
||||||
|
return Result.success(configService.getEnabledDemandTypes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据ID获取需求类型")
|
||||||
|
@GetMapping("/demand-types/{id:\\d+}")
|
||||||
|
public Result<DemandType> getDemandTypeById(@PathVariable Long id) {
|
||||||
|
return Result.success(configService.getDemandTypeById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "创建需求类型")
|
||||||
|
@PostMapping("/demand-types")
|
||||||
|
public Result<DemandType> createDemandType(@RequestBody DemandType demandType) {
|
||||||
|
return Result.success(configService.createDemandType(demandType));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新需求类型")
|
||||||
|
@PutMapping("/demand-types/{id:\\d+}")
|
||||||
|
public Result<Boolean> updateDemandType(@PathVariable Long id, @RequestBody DemandType demandType) {
|
||||||
|
demandType.setId(id);
|
||||||
|
return Result.success(configService.updateDemandType(demandType));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除需求类型")
|
||||||
|
@DeleteMapping("/demand-types/{id:\\d+}")
|
||||||
|
public Result<Boolean> deleteDemandType(@PathVariable Long id) {
|
||||||
|
return Result.success(configService.deleteDemandType(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "设置需求类型的可接单部门")
|
||||||
|
@PutMapping("/demand-types/{id:\\d+}/departments")
|
||||||
|
public Result<Boolean> setDemandTypeDepartments(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestBody List<Long> departmentIds) {
|
||||||
|
return Result.success(demandTypeDepartmentService.setDemandTypeDepartments(id, departmentIds));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取需求类型的可接单部门列表")
|
||||||
|
@GetMapping("/demand-types/{id:\\d+}/departments")
|
||||||
|
public Result<List<Long>> getDemandTypeDepartments(@PathVariable Long id) {
|
||||||
|
return Result.success(demandTypeDepartmentService.getDepartmentIdsByDemandType(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 品牌管理 ============
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有品牌(包括禁用的,用于管理页面)")
|
||||||
|
@GetMapping("/brands")
|
||||||
|
public Result<List<Brand>> getAllBrands() {
|
||||||
|
return Result.success(configService.getAllBrands());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有启用的品牌(用于下拉选择)")
|
||||||
|
@GetMapping("/brands/enabled")
|
||||||
|
public Result<List<Brand>> getEnabledBrands() {
|
||||||
|
return Result.success(configService.getEnabledBrands());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据ID获取品牌")
|
||||||
|
@GetMapping("/brands/{id:\\d+}")
|
||||||
|
public Result<Brand> getBrandById(@PathVariable Long id) {
|
||||||
|
return Result.success(configService.getBrandById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "创建品牌")
|
||||||
|
@PostMapping("/brands")
|
||||||
|
public Result<Brand> createBrand(@RequestBody Brand brand) {
|
||||||
|
return Result.success(configService.createBrand(brand));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新品牌")
|
||||||
|
@PutMapping("/brands/{id:\\d+}")
|
||||||
|
public Result<Boolean> updateBrand(@PathVariable Long id, @RequestBody Brand brand) {
|
||||||
|
brand.setId(id);
|
||||||
|
return Result.success(configService.updateBrand(brand));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除品牌")
|
||||||
|
@DeleteMapping("/brands/{id:\\d+}")
|
||||||
|
public Result<Boolean> deleteBrand(@PathVariable Long id) {
|
||||||
|
return Result.success(configService.deleteBrand(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 平台管理 ============
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有平台(包括禁用的,用于管理页面)")
|
||||||
|
@GetMapping("/platforms")
|
||||||
|
public Result<List<Platform>> getAllPlatforms() {
|
||||||
|
return Result.success(configService.getAllPlatforms());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有启用的平台(用于下拉选择)")
|
||||||
|
@GetMapping("/platforms/enabled")
|
||||||
|
public Result<List<Platform>> getEnabledPlatforms() {
|
||||||
|
return Result.success(configService.getEnabledPlatforms());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据ID获取平台")
|
||||||
|
@GetMapping("/platforms/{id:\\d+}")
|
||||||
|
public Result<Platform> getPlatformById(@PathVariable Long id) {
|
||||||
|
return Result.success(configService.getPlatformById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "创建平台")
|
||||||
|
@PostMapping("/platforms")
|
||||||
|
public Result<Platform> createPlatform(@RequestBody Platform platform) {
|
||||||
|
return Result.success(configService.createPlatform(platform));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新平台")
|
||||||
|
@PutMapping("/platforms/{id:\\d+}")
|
||||||
|
public Result<Boolean> updatePlatform(@PathVariable Long id, @RequestBody Platform platform) {
|
||||||
|
platform.setId(id);
|
||||||
|
return Result.success(configService.updatePlatform(platform));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除平台")
|
||||||
|
@DeleteMapping("/platforms/{id:\\d+}")
|
||||||
|
public Result<Boolean> deletePlatform(@PathVariable Long id) {
|
||||||
|
return Result.success(configService.deletePlatform(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 类目管理 ============
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有叶子类目(包括禁用的,用于管理页面)")
|
||||||
|
@GetMapping("/categories/leaf")
|
||||||
|
public Result<List<Category>> getAllLeafCategories() {
|
||||||
|
return Result.success(configService.getAllLeafCategories());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有启用的叶子类目(用于下拉选择)")
|
||||||
|
@GetMapping("/categories/leaf/enabled")
|
||||||
|
public Result<List<Category>> getEnabledLeafCategories() {
|
||||||
|
return Result.success(configService.getEnabledLeafCategories());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有类目")
|
||||||
|
@GetMapping("/categories")
|
||||||
|
public Result<List<Category>> getAllCategories() {
|
||||||
|
return Result.success(configService.getAllCategories());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据父ID获取子类目")
|
||||||
|
@GetMapping("/categories/parent/{parentId}")
|
||||||
|
public Result<List<Category>> getCategoriesByParentId(@PathVariable Long parentId) {
|
||||||
|
return Result.success(configService.getCategoriesByParentId(parentId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据ID获取类目")
|
||||||
|
@GetMapping("/categories/{id:\\d+}")
|
||||||
|
public Result<Category> getCategoryById(@PathVariable Long id) {
|
||||||
|
return Result.success(configService.getCategoryById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "创建类目")
|
||||||
|
@PostMapping("/categories")
|
||||||
|
public Result<Category> createCategory(@RequestBody Category category) {
|
||||||
|
return Result.success(configService.createCategory(category));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新类目")
|
||||||
|
@PutMapping("/categories/{id:\\d+}")
|
||||||
|
public Result<Boolean> updateCategory(@PathVariable Long id, @RequestBody Category category) {
|
||||||
|
category.setId(id);
|
||||||
|
return Result.success(configService.updateCategory(category));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除类目")
|
||||||
|
@DeleteMapping("/categories/{id:\\d+}")
|
||||||
|
public Result<Boolean> deleteCategory(@PathVariable Long id) {
|
||||||
|
return Result.success(configService.deleteCategory(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 团队积分池管理 ============
|
||||||
|
|
||||||
|
@Operation(summary = "根据部门ID获取积分池")
|
||||||
|
@GetMapping("/score-pool/dept/{deptId}")
|
||||||
|
public Result<TeamScorePool> getScorePoolByDeptId(@PathVariable Long deptId) {
|
||||||
|
TeamScorePool pool = teamScorePoolService.getByDeptId(deptId);
|
||||||
|
// 如果积分池不存在,自动创建
|
||||||
|
if (pool == null) {
|
||||||
|
var department = departmentService.getDepartmentById(deptId);
|
||||||
|
if (department != null) {
|
||||||
|
pool = teamScorePoolService.upsertPool(deptId, department.getDeptName());
|
||||||
|
} else {
|
||||||
|
// 部门不存在,返回一个默认的积分池对象(余额为0)
|
||||||
|
pool = new TeamScorePool();
|
||||||
|
pool.setDeptId(deptId);
|
||||||
|
pool.setBalance(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.success(pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "检查积分余额是否足够")
|
||||||
|
@GetMapping("/score-pool/check-balance")
|
||||||
|
public Result<Boolean> checkBalance(@RequestParam Long deptId, @RequestParam Integer score) {
|
||||||
|
return Result.success(teamScorePoolService.checkBalance(deptId, score));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "检查是否超过周期限制")
|
||||||
|
@GetMapping("/score-pool/check-limit")
|
||||||
|
public Result<Boolean> checkPeriodLimit(@RequestParam Long deptId, @RequestParam Integer score) {
|
||||||
|
return Result.success(teamScorePoolService.checkPeriodLimit(deptId, score));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "充值积分")
|
||||||
|
@PostMapping("/score-pool/recharge")
|
||||||
|
public Result<Void> rechargeScore(@RequestParam Long deptId, @RequestParam Integer score) {
|
||||||
|
teamScorePoolService.rechargeScore(deptId, score);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "创建积分池")
|
||||||
|
@PostMapping("/score-pool")
|
||||||
|
public Result<TeamScorePool> createScorePool(@RequestBody TeamScorePool pool) {
|
||||||
|
return Result.success(teamScorePoolService.createPool(pool));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新积分池配置")
|
||||||
|
@PutMapping("/score-pool/{id}")
|
||||||
|
public Result<Boolean> updateScorePool(@PathVariable Long id, @RequestBody TeamScorePool pool) {
|
||||||
|
pool.setId(id);
|
||||||
|
return Result.success(teamScorePoolService.updatePool(pool));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "检查是否需要预警")
|
||||||
|
@GetMapping("/score-pool/alert/{deptId}")
|
||||||
|
public Result<Boolean> isAlert(@PathVariable Long deptId) {
|
||||||
|
return Result.success(teamScorePoolService.isAlert(deptId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,122 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.Contest;
|
||||||
|
import com.designhub.entity.ContestParticipant;
|
||||||
|
import com.designhub.service.ContestService;
|
||||||
|
import com.designhub.util.JwtUtil;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 竞赛活动Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "竞赛活动", description = "竞赛活动管理相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/contests")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ContestController {
|
||||||
|
|
||||||
|
private final ContestService contestService;
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@Operation(summary = "创建竞赛")
|
||||||
|
@PostMapping
|
||||||
|
public Result<Contest> createContest(
|
||||||
|
@RequestBody Contest contest,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
contest.setCreatorId(userId);
|
||||||
|
return Result.success(contestService.createContest(contest));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新竞赛")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<Contest> updateContest(@PathVariable Long id, @RequestBody Contest contest) {
|
||||||
|
return Result.success(contestService.updateContest(id, contest));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "发布竞赛")
|
||||||
|
@PostMapping("/{id}/publish")
|
||||||
|
public Result<Contest> publishContest(@PathVariable Long id) {
|
||||||
|
return Result.success(contestService.publishContest(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "结束竞赛")
|
||||||
|
@PostMapping("/{id}/end")
|
||||||
|
public Result<Void> endContest(@PathVariable Long id) {
|
||||||
|
contestService.endContest(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "取消竞赛")
|
||||||
|
@PostMapping("/{id}/cancel")
|
||||||
|
public Result<Void> cancelContest(@PathVariable Long id) {
|
||||||
|
contestService.cancelContest(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除竞赛")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Void> deleteContest(@PathVariable Long id) {
|
||||||
|
contestService.deleteContest(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取竞赛列表")
|
||||||
|
@GetMapping
|
||||||
|
public Result<IPage<Contest>> getContestList(
|
||||||
|
@RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||||
|
@RequestParam(required = false) String status) {
|
||||||
|
return Result.success(contestService.getContestList(page, pageSize, status));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取竞赛详情")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<Contest> getContestById(@PathVariable Long id) {
|
||||||
|
return Result.success(contestService.getContestById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取竞赛排行榜")
|
||||||
|
@GetMapping("/{id}/leaderboard")
|
||||||
|
public Result<List<ContestParticipant>> getLeaderboard(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestParam(required = false, defaultValue = "50") Integer limit) {
|
||||||
|
return Result.success(contestService.getLeaderboard(id, limit));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取用户在竞赛中的数据")
|
||||||
|
@GetMapping("/{contestId}/user/{userId}")
|
||||||
|
public Result<ContestParticipant> getUserContestData(
|
||||||
|
@PathVariable Long contestId,
|
||||||
|
@PathVariable Long userId) {
|
||||||
|
return Result.success(contestService.getUserContestData(contestId, userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取我的竞赛数据")
|
||||||
|
@GetMapping("/{contestId}/my")
|
||||||
|
public Result<ContestParticipant> getMyContestData(
|
||||||
|
@PathVariable Long contestId,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
return Result.success(contestService.getUserContestData(contestId, userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "领取竞赛奖励")
|
||||||
|
@PostMapping("/{contestId}/claim-reward")
|
||||||
|
public Result<Boolean> claimReward(
|
||||||
|
@PathVariable Long contestId,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
return Result.success(contestService.claimReward(contestId, userId));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.DelayPenaltyRecord;
|
||||||
|
import com.designhub.service.DelayPenaltyService;
|
||||||
|
import com.designhub.util.JwtUtil;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 延误扣分Controller
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Tag(name = "延误扣分管理", description = "延误扣分和申诉相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/delay-penalty")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DelayPenaltyController {
|
||||||
|
|
||||||
|
private final DelayPenaltyService delayPenaltyService;
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@Operation(summary = "根据任务ID查询延误记录")
|
||||||
|
@GetMapping("/task/{taskId}")
|
||||||
|
public Result<List<DelayPenaltyRecord>> getByTaskId(@PathVariable Long taskId) {
|
||||||
|
return Result.success(delayPenaltyService.getDelayRecordsByTaskId(taskId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据设计师ID查询延误记录")
|
||||||
|
@GetMapping("/designer/{designerId}")
|
||||||
|
public Result<List<DelayPenaltyRecord>> getByDesignerId(@PathVariable Long designerId) {
|
||||||
|
return Result.success(delayPenaltyService.getDelayRecordsByDesignerId(designerId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "统计设计师的延误扣分总数")
|
||||||
|
@GetMapping("/designer/{designerId}/total")
|
||||||
|
public Result<Integer> getTotalPenaltyScore(@PathVariable Long designerId) {
|
||||||
|
return Result.success(delayPenaltyService.getTotalPenaltyScoreByDesignerId(designerId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "申诉延误扣分")
|
||||||
|
@PostMapping("/{recordId}/appeal")
|
||||||
|
public Result<String> appealDelayPenalty(
|
||||||
|
@PathVariable Long recordId,
|
||||||
|
@RequestParam String appealReason,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
delayPenaltyService.appealDelayPenalty(recordId, userId, appealReason);
|
||||||
|
return Result.success("申诉提交成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "管理员审批申诉")
|
||||||
|
@PostMapping("/{recordId}/approve")
|
||||||
|
public Result<String> approveAppeal(
|
||||||
|
@PathVariable Long recordId,
|
||||||
|
@RequestParam Boolean approved,
|
||||||
|
@RequestParam(required = false) String appealResult,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long approverId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
delayPenaltyService.approveAppeal(recordId, approverId, approved,
|
||||||
|
appealResult != null ? appealResult : (approved ? "申诉通过" : "申诉驳回"));
|
||||||
|
return Result.success(approved ? "申诉已通过" : "申诉已驳回");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,114 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.dto.DepartmentCreateRequest;
|
||||||
|
import com.designhub.dto.DepartmentLimitSettingsRequest;
|
||||||
|
import com.designhub.dto.DepartmentScoreAdjustRequest;
|
||||||
|
import com.designhub.dto.DepartmentUpdateRequest;
|
||||||
|
import com.designhub.entity.Department;
|
||||||
|
import com.designhub.entity.TeamScorePool;
|
||||||
|
import com.designhub.service.DepartmentService;
|
||||||
|
import com.designhub.service.TeamScorePoolService;
|
||||||
|
import com.designhub.vo.DepartmentOverviewVO;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门管理Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "部门管理", description = "部门管理相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/departments")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DepartmentController {
|
||||||
|
|
||||||
|
private final DepartmentService departmentService;
|
||||||
|
private final TeamScorePoolService teamScorePoolService;
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有部门列表")
|
||||||
|
@GetMapping
|
||||||
|
public Result<List<Department>> getAllDepartments() {
|
||||||
|
return Result.success(departmentService.getAllDepartments());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "部门概览(含积分信息)")
|
||||||
|
@GetMapping("/overview")
|
||||||
|
public Result<List<DepartmentOverviewVO>> getDepartmentOverview() {
|
||||||
|
return Result.success(departmentService.getDepartmentOverviewList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取部门树形结构")
|
||||||
|
@GetMapping("/tree")
|
||||||
|
public Result<List<Department>> getDepartmentTree() {
|
||||||
|
return Result.success(departmentService.getDepartmentTree());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据类型获取部门列表")
|
||||||
|
@GetMapping("/type/{deptType}")
|
||||||
|
public Result<List<Department>> getDepartmentsByType(@PathVariable String deptType) {
|
||||||
|
return Result.success(departmentService.getDepartmentsByType(deptType));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据ID获取部门")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<Department> getDepartmentById(@PathVariable Long id) {
|
||||||
|
return Result.success(departmentService.getDepartmentById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "创建部门")
|
||||||
|
@PostMapping
|
||||||
|
public Result<DepartmentOverviewVO> createDepartment(@Valid @RequestBody DepartmentCreateRequest request) {
|
||||||
|
return Result.success(departmentService.createDepartment(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新部门")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<Boolean> updateDepartment(@PathVariable Long id,
|
||||||
|
@Valid @RequestBody DepartmentUpdateRequest request) {
|
||||||
|
return Result.success(departmentService.updateDepartment(id, request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新部门积分限制配置")
|
||||||
|
@PutMapping("/{id}/limit")
|
||||||
|
public Result<Boolean> updateLimitSettings(@PathVariable Long id,
|
||||||
|
@Valid @RequestBody DepartmentLimitSettingsRequest request) {
|
||||||
|
return Result.success(departmentService.updateLimitSettings(id, request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除部门")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Boolean> deleteDepartment(@PathVariable Long id) {
|
||||||
|
return Result.success(departmentService.deleteDepartment(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "设置部门负责人/主管")
|
||||||
|
@PostMapping("/{deptId}/head")
|
||||||
|
public Result<Boolean> setDepartmentHead(
|
||||||
|
@PathVariable Long deptId,
|
||||||
|
@RequestParam Long userId,
|
||||||
|
@RequestParam String type
|
||||||
|
) {
|
||||||
|
return Result.success(departmentService.setDepartmentHead(deptId, userId, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "调整部门积分")
|
||||||
|
@PostMapping("/{deptId}/score-adjust")
|
||||||
|
public Result<Void> adjustScore(@PathVariable Long deptId,
|
||||||
|
@Valid @RequestBody DepartmentScoreAdjustRequest request) {
|
||||||
|
departmentService.adjustScore(deptId, request);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取部门积分池详情")
|
||||||
|
@GetMapping("/{deptId}/score-pool")
|
||||||
|
public Result<TeamScorePool> getScorePool(@PathVariable Long deptId) {
|
||||||
|
return Result.success(teamScorePoolService.getByDeptId(deptId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.Task;
|
||||||
|
import com.designhub.service.DesignerService;
|
||||||
|
import com.designhub.vo.DesignerStatsVO;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设计师统计Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "设计师管理", description = "设计师统计和排行榜相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/designers")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DesignerController {
|
||||||
|
|
||||||
|
private final DesignerService designerService;
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有设计师统计信息")
|
||||||
|
@GetMapping("/stats")
|
||||||
|
public Result<List<DesignerStatsVO>> getAllDesignerStats() {
|
||||||
|
return Result.success(designerService.getAllDesignerStats());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取单个设计师统计信息")
|
||||||
|
@GetMapping("/stats/{userId}")
|
||||||
|
public Result<DesignerStatsVO> getDesignerStats(@PathVariable Long userId) {
|
||||||
|
return Result.success(designerService.getDesignerStats(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取排行榜")
|
||||||
|
@GetMapping("/leaderboard")
|
||||||
|
public Result<List<DesignerStatsVO>> getLeaderboard(
|
||||||
|
@RequestParam(required = false, defaultValue = "score") String type,
|
||||||
|
@RequestParam(required = false, defaultValue = "10") Integer limit,
|
||||||
|
@Parameter(description = "月份,格式:YYYY-MM,不传则查询当月") @RequestParam(required = false) String month
|
||||||
|
) {
|
||||||
|
return Result.success(designerService.getLeaderboard(type, limit, month));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取设计师进行中的任务列表")
|
||||||
|
@GetMapping("/{userId}/in-progress-tasks")
|
||||||
|
public Result<List<Task>> getDesignerInProgressTasks(@PathVariable Long userId) {
|
||||||
|
return Result.success(designerService.getDesignerInProgressTasks(userId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,174 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.DingTalkConfig;
|
||||||
|
import com.designhub.service.DingTalkService;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 钉钉集成 Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "钉钉集成", description = "钉钉集成管理API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/dingtalk")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DingTalkController {
|
||||||
|
|
||||||
|
private final DingTalkService dingTalkService;
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@Operation(summary = "获取钉钉配置")
|
||||||
|
@GetMapping("/config")
|
||||||
|
public Result<DingTalkConfig> getConfig() {
|
||||||
|
DingTalkConfig config = dingTalkService.getConfig();
|
||||||
|
|
||||||
|
// 如果没有配置,返回默认配置(仅用于前端显示)
|
||||||
|
if (config == null) {
|
||||||
|
config = new DingTalkConfig();
|
||||||
|
config.setName("默认配置");
|
||||||
|
config.setEnabled(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏敏感信息
|
||||||
|
if (config.getAppSecret() != null && !config.getAppSecret().isEmpty()) {
|
||||||
|
config.setAppSecret("******");
|
||||||
|
}
|
||||||
|
if (config.getRobotSecret() != null && !config.getRobotSecret().isEmpty()) {
|
||||||
|
config.setRobotSecret("******");
|
||||||
|
}
|
||||||
|
config.setAccessToken(null);
|
||||||
|
|
||||||
|
return Result.success(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "保存钉钉配置")
|
||||||
|
@PostMapping("/config")
|
||||||
|
public Result<DingTalkConfig> saveConfig(@RequestBody Map<String, Object> configMap) {
|
||||||
|
try {
|
||||||
|
// 处理enabledFeatures字段
|
||||||
|
Object enabledFeaturesObj = configMap.get("enabledFeatures");
|
||||||
|
String enabledFeaturesJson = null;
|
||||||
|
if (enabledFeaturesObj != null) {
|
||||||
|
if (enabledFeaturesObj instanceof String) {
|
||||||
|
enabledFeaturesJson = (String) enabledFeaturesObj;
|
||||||
|
} else {
|
||||||
|
// 转换为JSON字符串
|
||||||
|
enabledFeaturesJson = objectMapper.writeValueAsString(enabledFeaturesObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建配置对象
|
||||||
|
DingTalkConfig config = new DingTalkConfig();
|
||||||
|
config.setId(configMap.get("id") != null ? ((Number) configMap.get("id")).longValue() : null);
|
||||||
|
config.setName((String) configMap.get("name"));
|
||||||
|
config.setAgentId((String) configMap.get("agentId"));
|
||||||
|
config.setAppKey((String) configMap.get("appKey"));
|
||||||
|
config.setAppSecret((String) configMap.get("appSecret"));
|
||||||
|
config.setCorpId((String) configMap.get("corpId"));
|
||||||
|
config.setRobotWebhook((String) configMap.get("robotWebhook"));
|
||||||
|
config.setRobotSecret((String) configMap.get("robotSecret"));
|
||||||
|
config.setEnabled(configMap.get("enabled") != null ? ((Number) configMap.get("enabled")).intValue() : 1);
|
||||||
|
config.setEnabledFeatures(enabledFeaturesJson);
|
||||||
|
|
||||||
|
// 如果前端传来的appSecret是占位符,则保留原有的secret
|
||||||
|
if ("******".equals(config.getAppSecret())) {
|
||||||
|
DingTalkConfig existingConfig = dingTalkService.getConfig();
|
||||||
|
if (existingConfig != null) {
|
||||||
|
config.setAppSecret(existingConfig.getAppSecret());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ("******".equals(config.getRobotSecret())) {
|
||||||
|
DingTalkConfig existingConfig = dingTalkService.getConfig();
|
||||||
|
if (existingConfig != null) {
|
||||||
|
config.setRobotSecret(existingConfig.getRobotSecret());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DingTalkConfig saved = dingTalkService.saveConfig(config);
|
||||||
|
|
||||||
|
// 返回时隐藏敏感信息
|
||||||
|
if (saved.getAppSecret() != null && !saved.getAppSecret().isEmpty()) {
|
||||||
|
saved.setAppSecret("******");
|
||||||
|
}
|
||||||
|
if (saved.getRobotSecret() != null && !saved.getRobotSecret().isEmpty()) {
|
||||||
|
saved.setRobotSecret("******");
|
||||||
|
}
|
||||||
|
saved.setAccessToken(null);
|
||||||
|
|
||||||
|
return Result.success(saved);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.error("保存配置失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "测试钉钉连接")
|
||||||
|
@PostMapping("/test")
|
||||||
|
public Result<Boolean> testConnection(@RequestBody DingTalkConfig config) {
|
||||||
|
return Result.success(dingTalkService.testConnection(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "发送测试消息")
|
||||||
|
@PostMapping("/test-message")
|
||||||
|
public Result<Boolean> sendTestMessage(@RequestBody Map<String, String> params) {
|
||||||
|
String title = params.getOrDefault("title", "测试消息");
|
||||||
|
String content = params.getOrDefault("content", "这是一条来自DesignHub的测试消息");
|
||||||
|
return Result.success(dingTalkService.sendRobotMessage(title, content));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取钉钉部门列表")
|
||||||
|
@GetMapping("/departments")
|
||||||
|
public Result<List<Map<String, Object>>> getDepartmentList() {
|
||||||
|
return Result.success(dingTalkService.getDepartmentList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "同步钉钉用户")
|
||||||
|
@PostMapping("/sync-users")
|
||||||
|
public Result<Integer> syncUsers() {
|
||||||
|
return Result.success(dingTalkService.syncUsers());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "检查功能状态")
|
||||||
|
@GetMapping("/feature/{feature}")
|
||||||
|
public Result<Boolean> isFeatureEnabled(@PathVariable String feature) {
|
||||||
|
return Result.success(dingTalkService.isFeatureEnabled(feature));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "钉钉OAuth登录发起", description = "生成钉钉登录URL")
|
||||||
|
@GetMapping("/oauth/login")
|
||||||
|
public Result<Map<String, String>> initiateLogin(@RequestParam(required = false) String redirectUrl) {
|
||||||
|
if (!dingTalkService.isConfigured()) {
|
||||||
|
return Result.error("钉钉登录未配置或未启用,请联系管理员在系统设置中配置");
|
||||||
|
}
|
||||||
|
|
||||||
|
DingTalkConfig config = dingTalkService.getConfig();
|
||||||
|
|
||||||
|
// 构建回调URL
|
||||||
|
String callbackUrl = redirectUrl != null ? redirectUrl : "http://localhost:3000/auth/dingtalk/callback";
|
||||||
|
|
||||||
|
// 构建钉钉OAuth URL
|
||||||
|
String authUrl = String.format(
|
||||||
|
"https://login.dingtalk.com/oauth2/auth?redirect_uri=%s&response_type=code&client_id=%s&scope=openid&state=STATE&prompt=consent",
|
||||||
|
java.net.URLEncoder.encode(callbackUrl, java.nio.charset.StandardCharsets.UTF_8),
|
||||||
|
config.getAppKey()
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, String> result = new HashMap<>();
|
||||||
|
result.put("authUrl", authUrl);
|
||||||
|
result.put("appKey", config.getAppKey());
|
||||||
|
return Result.success(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "检查钉钉登录是否已配置")
|
||||||
|
@GetMapping("/is-configured")
|
||||||
|
public Result<Boolean> isConfigured() {
|
||||||
|
return Result.success(dingTalkService.isConfigured());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.service.ExportService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据导出Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "数据导出", description = "数据导出相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/export")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ExportController {
|
||||||
|
|
||||||
|
private final ExportService exportService;
|
||||||
|
|
||||||
|
@Operation(summary = "导出任务列表")
|
||||||
|
@GetMapping("/tasks")
|
||||||
|
public void exportTasks(
|
||||||
|
HttpServletResponse response,
|
||||||
|
@RequestParam(required = false) String status,
|
||||||
|
@RequestParam(required = false) Long publisherId
|
||||||
|
) throws IOException {
|
||||||
|
exportService.exportTasks(response, status, publisherId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "导出用户列表")
|
||||||
|
@GetMapping("/users")
|
||||||
|
public void exportUsers(
|
||||||
|
HttpServletResponse response,
|
||||||
|
@RequestParam(required = false) Long deptId,
|
||||||
|
@RequestParam(required = false) Integer status
|
||||||
|
) throws IOException {
|
||||||
|
exportService.exportUsers(response, deptId, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "导出部门列表")
|
||||||
|
@GetMapping("/departments")
|
||||||
|
public void exportDepartments(HttpServletResponse response) throws IOException {
|
||||||
|
exportService.exportDepartments(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "导出设计师统计")
|
||||||
|
@GetMapping("/designer-stats")
|
||||||
|
public void exportDesignerStats(HttpServletResponse response) throws IOException {
|
||||||
|
exportService.exportDesignerStats(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,184 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.entity.FileUpload;
|
||||||
|
import com.designhub.entity.User;
|
||||||
|
import com.designhub.service.S3Service;
|
||||||
|
import com.designhub.service.UserService;
|
||||||
|
import com.designhub.util.JwtUtil;
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传下载Controller
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/file")
|
||||||
|
public class FileUploadController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private S3Service s3Service;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件
|
||||||
|
*
|
||||||
|
* @param files 文件列表
|
||||||
|
* @param relatedType 关联类型(可选)
|
||||||
|
* @param relatedId 关联ID(可选)
|
||||||
|
* @param request HttpServletRequest
|
||||||
|
* @return 文件URL列表
|
||||||
|
*/
|
||||||
|
@PostMapping("/upload")
|
||||||
|
public Result<List<String>> uploadFiles(
|
||||||
|
@RequestParam("files") MultipartFile[] files,
|
||||||
|
@RequestParam(value = "relatedType", required = false) String relatedType,
|
||||||
|
@RequestParam(value = "relatedId", required = false) Long relatedId,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 从token获取用户信息
|
||||||
|
String token = request.getHeader("Authorization");
|
||||||
|
if (token == null || !token.startsWith("Bearer ")) {
|
||||||
|
return Result.error("未登录或token无效");
|
||||||
|
}
|
||||||
|
token = token.substring(7);
|
||||||
|
|
||||||
|
String username = jwtUtil.getUsernameFromToken(token);
|
||||||
|
if (username == null) {
|
||||||
|
return Result.error("token无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
User user = userService.getUserByUsername(username);
|
||||||
|
if (user == null) {
|
||||||
|
return Result.error("用户不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
List<String> fileUrls = new ArrayList<>();
|
||||||
|
for (MultipartFile file : files) {
|
||||||
|
if (file.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fileUrl = s3Service.uploadFile(
|
||||||
|
file,
|
||||||
|
user.getId(),
|
||||||
|
user.getRealName() != null ? user.getRealName() : user.getUsername(),
|
||||||
|
relatedType,
|
||||||
|
relatedId
|
||||||
|
);
|
||||||
|
fileUrls.add(fileUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.success(fileUrls);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.error("文件上传失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件(需要登录)
|
||||||
|
*
|
||||||
|
* @param id 文件ID
|
||||||
|
* @param request HttpServletRequest
|
||||||
|
* @param response HttpServletResponse
|
||||||
|
*/
|
||||||
|
@GetMapping("/download/{id}")
|
||||||
|
public void downloadFile(
|
||||||
|
@PathVariable Long id,
|
||||||
|
HttpServletRequest request,
|
||||||
|
HttpServletResponse response) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 验证登录状态
|
||||||
|
String token = request.getHeader("Authorization");
|
||||||
|
if (token == null || !token.startsWith("Bearer ")) {
|
||||||
|
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
response.getWriter().write("未登录,请先登录");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
token = token.substring(7);
|
||||||
|
|
||||||
|
String username = jwtUtil.getUsernameFromToken(token);
|
||||||
|
if (username == null) {
|
||||||
|
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
response.getWriter().write("token无效");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取文件信息
|
||||||
|
FileUpload fileUpload = s3Service.getFileUploadById(id);
|
||||||
|
if (fileUpload == null) {
|
||||||
|
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
response.getWriter().write("文件不存在");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重定向到文件URL(S3提供的公开URL)
|
||||||
|
response.sendRedirect(fileUpload.getFileUrl());
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
try {
|
||||||
|
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
response.getWriter().write("文件下载失败: " + e.getMessage());
|
||||||
|
} catch (IOException ex) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件信息
|
||||||
|
*
|
||||||
|
* @param id 文件ID
|
||||||
|
* @return 文件信息
|
||||||
|
*/
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<FileUpload> getFileInfo(@PathVariable Long id) {
|
||||||
|
try {
|
||||||
|
FileUpload fileUpload = s3Service.getFileUploadById(id);
|
||||||
|
if (fileUpload == null) {
|
||||||
|
return Result.error("文件不存在");
|
||||||
|
}
|
||||||
|
return Result.success(fileUpload);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.error("获取文件信息失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据关联信息获取文件列表
|
||||||
|
*
|
||||||
|
* @param relatedType 关联类型
|
||||||
|
* @param relatedId 关联ID
|
||||||
|
* @return 文件列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/list")
|
||||||
|
public Result<List<FileUpload>> getFilesByRelated(
|
||||||
|
@RequestParam String relatedType,
|
||||||
|
@RequestParam Long relatedId) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<FileUpload> files = s3Service.getFilesByRelated(relatedType, relatedId);
|
||||||
|
return Result.success(files);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.error("获取文件列表失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试控制器
|
||||||
|
*/
|
||||||
|
@Tag(name = "测试接口", description = "用于测试系统是否正常运行")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/hello")
|
||||||
|
public class HelloController {
|
||||||
|
|
||||||
|
@Operation(summary = "Hello World", description = "测试接口是否可访问")
|
||||||
|
@GetMapping
|
||||||
|
public Result<String> hello() {
|
||||||
|
return Result.success("Hello, DesignHub! 系统运行正常 ✅");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "系统信息", description = "获取系统基本信息")
|
||||||
|
@GetMapping("/info")
|
||||||
|
public Result<Map<String, Object>> info() {
|
||||||
|
Map<String, Object> info = new HashMap<>();
|
||||||
|
info.put("name", "DesignHub - 设计师任务管理平台");
|
||||||
|
info.put("version", "1.0.0");
|
||||||
|
info.put("time", LocalDateTime.now());
|
||||||
|
info.put("status", "running");
|
||||||
|
info.put("description", "基于 Spring Boot 3.x + Vue 3 开发");
|
||||||
|
return Result.success(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "健康检查", description = "检查系统健康状态")
|
||||||
|
@GetMapping("/health")
|
||||||
|
public Result<Map<String, String>> health() {
|
||||||
|
Map<String, String> health = new HashMap<>();
|
||||||
|
health.put("status", "UP");
|
||||||
|
health.put("database", "OK");
|
||||||
|
health.put("api", "OK");
|
||||||
|
return Result.success(health);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.Notification;
|
||||||
|
import com.designhub.service.NotificationService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知消息Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "通知消息", description = "通知消息相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/notifications")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class NotificationController {
|
||||||
|
|
||||||
|
private final NotificationService notificationService;
|
||||||
|
|
||||||
|
@Operation(summary = "获取用户通知列表(分页)")
|
||||||
|
@GetMapping
|
||||||
|
public Result<Page<Notification>> getUserNotifications(
|
||||||
|
@RequestParam Long receiverId,
|
||||||
|
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||||
|
@RequestParam(defaultValue = "20") Integer pageSize,
|
||||||
|
@RequestParam(required = false) Boolean unreadOnly
|
||||||
|
) {
|
||||||
|
Page<Notification> page = notificationService.getUserNotifications(receiverId, pageNum, pageSize, unreadOnly);
|
||||||
|
return Result.success(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取未读数量")
|
||||||
|
@GetMapping("/unread-count")
|
||||||
|
public Result<Long> getUnreadCount(@RequestParam Long receiverId) {
|
||||||
|
long count = notificationService.getUnreadCount(receiverId);
|
||||||
|
return Result.success(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "标记通知为已读")
|
||||||
|
@PutMapping("/{id}/read")
|
||||||
|
public Result<Boolean> markAsRead(@PathVariable Long id, @RequestParam Long receiverId) {
|
||||||
|
boolean success = notificationService.markAsRead(id, receiverId);
|
||||||
|
return Result.success(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "批量标记所有通知为已读")
|
||||||
|
@PutMapping("/read-all")
|
||||||
|
public Result<Integer> markAllAsRead(@RequestParam Long receiverId) {
|
||||||
|
int count = notificationService.markAllAsRead(receiverId);
|
||||||
|
return Result.success(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除通知")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Boolean> deleteNotification(@PathVariable Long id, @RequestParam Long receiverId) {
|
||||||
|
boolean success = notificationService.deleteNotification(id, receiverId);
|
||||||
|
return Result.success(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "发送通知(管理员)")
|
||||||
|
@PostMapping
|
||||||
|
public Result<Notification> sendNotification(@RequestBody Notification notification) {
|
||||||
|
Notification result = notificationService.sendNotification(notification);
|
||||||
|
return Result.success(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.Permission;
|
||||||
|
import com.designhub.service.PermissionService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限管理Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "权限管理", description = "权限管理相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/permissions")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class PermissionController {
|
||||||
|
|
||||||
|
private final PermissionService permissionService;
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有权限列表")
|
||||||
|
@GetMapping
|
||||||
|
public Result<List<Permission>> getAllPermissions() {
|
||||||
|
return Result.success(permissionService.getAllPermissions());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取权限树形结构")
|
||||||
|
@GetMapping("/tree")
|
||||||
|
public Result<List<Permission>> getPermissionTree() {
|
||||||
|
return Result.success(permissionService.getPermissionTree());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据ID获取权限")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<Permission> getPermissionById(@PathVariable Long id) {
|
||||||
|
return Result.success(permissionService.getPermissionById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据用户ID获取权限列表")
|
||||||
|
@GetMapping("/user/{userId}")
|
||||||
|
public Result<List<Permission>> getPermissionsByUserId(@PathVariable Long userId) {
|
||||||
|
return Result.success(permissionService.getPermissionsByUserId(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据用户ID获取权限代码列表")
|
||||||
|
@GetMapping("/user/{userId}/codes")
|
||||||
|
public Result<List<String>> getPermissionCodesByUserId(@PathVariable Long userId) {
|
||||||
|
return Result.success(permissionService.getPermissionCodesByUserId(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "创建权限")
|
||||||
|
@PostMapping
|
||||||
|
public Result<Permission> createPermission(@RequestBody Permission permission) {
|
||||||
|
return Result.success(permissionService.createPermission(permission));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新权限")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<Boolean> updatePermission(@PathVariable Long id, @RequestBody Permission permission) {
|
||||||
|
permission.setId(id);
|
||||||
|
return Result.success(permissionService.updatePermission(permission));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除权限")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Boolean> deletePermission(@PathVariable Long id) {
|
||||||
|
return Result.success(permissionService.deletePermission(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.RewardRule;
|
||||||
|
import com.designhub.service.RewardRuleService;
|
||||||
|
import com.designhub.util.JwtUtil;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动奖励规则 Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "自动奖励规则", description = "自动奖励规则管理API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/reward-rules")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class RewardRuleController {
|
||||||
|
|
||||||
|
private final RewardRuleService rewardRuleService;
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@Operation(summary = "创建奖励规则")
|
||||||
|
@PostMapping
|
||||||
|
public Result<RewardRule> createRule(
|
||||||
|
@RequestBody RewardRule rule,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
rule.setCreatorId(userId);
|
||||||
|
return Result.success(rewardRuleService.createRule(rule));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新奖励规则")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<RewardRule> updateRule(@PathVariable Long id, @RequestBody RewardRule rule) {
|
||||||
|
return Result.success(rewardRuleService.updateRule(id, rule));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "启用规则")
|
||||||
|
@PostMapping("/{id}/enable")
|
||||||
|
public Result<Void> enableRule(@PathVariable Long id) {
|
||||||
|
rewardRuleService.toggleRule(id, true);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "禁用规则")
|
||||||
|
@PostMapping("/{id}/disable")
|
||||||
|
public Result<Void> disableRule(@PathVariable Long id) {
|
||||||
|
rewardRuleService.toggleRule(id, false);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除规则")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Void> deleteRule(@PathVariable Long id) {
|
||||||
|
rewardRuleService.deleteRule(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取规则列表")
|
||||||
|
@GetMapping
|
||||||
|
public Result<IPage<RewardRule>> getRuleList(
|
||||||
|
@RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||||
|
@RequestParam(required = false) String ruleType) {
|
||||||
|
return Result.success(rewardRuleService.getRuleList(page, pageSize, ruleType));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取规则详情")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<RewardRule> getRuleById(@PathVariable Long id) {
|
||||||
|
return Result.success(rewardRuleService.getRuleById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有启用的规则")
|
||||||
|
@GetMapping("/enabled")
|
||||||
|
public Result<List<RewardRule>> getEnabledRules() {
|
||||||
|
return Result.success(rewardRuleService.getEnabledRules());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "初始化默认规则")
|
||||||
|
@PostMapping("/init")
|
||||||
|
public Result<Void> initDefaultRules() {
|
||||||
|
rewardRuleService.initDefaultRules();
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.Role;
|
||||||
|
import com.designhub.service.RoleService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色管理Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "角色管理", description = "角色管理相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/roles")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class RoleController {
|
||||||
|
|
||||||
|
private final RoleService roleService;
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有角色列表")
|
||||||
|
@GetMapping
|
||||||
|
public Result<List<Role>> getAllRoles() {
|
||||||
|
return Result.success(roleService.getAllRoles());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据ID获取角色")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<Role> getRoleById(@PathVariable Long id) {
|
||||||
|
return Result.success(roleService.getRoleById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "创建角色")
|
||||||
|
@PostMapping
|
||||||
|
public Result<Role> createRole(@RequestBody Role role) {
|
||||||
|
return Result.success(roleService.createRole(role));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新角色")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<Boolean> updateRole(@PathVariable Long id, @RequestBody Role role) {
|
||||||
|
role.setId(id);
|
||||||
|
return Result.success(roleService.updateRole(role));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除角色")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Boolean> deleteRole(@PathVariable Long id) {
|
||||||
|
return Result.success(roleService.deleteRole(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "为角色分配权限")
|
||||||
|
@PostMapping("/{id}/permissions")
|
||||||
|
public Result<Boolean> assignPermissions(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestBody List<Long> permissionIds
|
||||||
|
) {
|
||||||
|
return Result.success(roleService.assignPermissions(id, permissionIds));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取角色的权限ID列表")
|
||||||
|
@GetMapping("/{id}/permissions")
|
||||||
|
public Result<List<Long>> getRolePermissionIds(@PathVariable Long id) {
|
||||||
|
return Result.success(roleService.getRolePermissionIds(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "启用/禁用角色")
|
||||||
|
@PutMapping("/{id}/status")
|
||||||
|
public Result<Boolean> toggleRoleStatus(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestParam Integer status
|
||||||
|
) {
|
||||||
|
return Result.success(roleService.toggleRoleStatus(id, status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,132 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.entity.S3Config;
|
||||||
|
import com.designhub.service.S3ConfigService;
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* S3配置管理Controller
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/s3/config")
|
||||||
|
public class S3ConfigController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private S3ConfigService s3ConfigService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取激活的S3配置
|
||||||
|
*/
|
||||||
|
@GetMapping("/active")
|
||||||
|
public Result<S3Config> getActiveConfig() {
|
||||||
|
try {
|
||||||
|
S3Config config = s3ConfigService.getActiveConfig();
|
||||||
|
// 隐藏敏感信息
|
||||||
|
if (config != null) {
|
||||||
|
config.setSecretAccessKey("******");
|
||||||
|
}
|
||||||
|
return Result.success(config);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.error("获取S3配置失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有S3配置
|
||||||
|
*/
|
||||||
|
@GetMapping("/list")
|
||||||
|
public Result<List<S3Config>> getAllConfigs() {
|
||||||
|
try {
|
||||||
|
List<S3Config> configs = s3ConfigService.getAllConfigs();
|
||||||
|
// 隐藏敏感信息
|
||||||
|
configs.forEach(config -> config.setSecretAccessKey("******"));
|
||||||
|
return Result.success(configs);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.error("获取S3配置列表失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取S3配置
|
||||||
|
*/
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<S3Config> getConfigById(@PathVariable Long id) {
|
||||||
|
try {
|
||||||
|
S3Config config = s3ConfigService.getConfigById(id);
|
||||||
|
if (config == null) {
|
||||||
|
return Result.error("S3配置不存在");
|
||||||
|
}
|
||||||
|
// 隐藏敏感信息
|
||||||
|
config.setSecretAccessKey("******");
|
||||||
|
return Result.success(config);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.error("获取S3配置失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建S3配置
|
||||||
|
*/
|
||||||
|
@PostMapping
|
||||||
|
public Result<S3Config> createConfig(@RequestBody S3Config config) {
|
||||||
|
try {
|
||||||
|
S3Config created = s3ConfigService.createConfig(config);
|
||||||
|
created.setSecretAccessKey("******");
|
||||||
|
return Result.success(created);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.error("创建S3配置失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新S3配置
|
||||||
|
*/
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<S3Config> updateConfig(@PathVariable Long id, @RequestBody S3Config config) {
|
||||||
|
try {
|
||||||
|
// 如果secretAccessKey为"******",说明没有修改,从数据库中获取原值
|
||||||
|
if ("******".equals(config.getSecretAccessKey())) {
|
||||||
|
S3Config existingConfig = s3ConfigService.getConfigById(id);
|
||||||
|
if (existingConfig != null) {
|
||||||
|
config.setSecretAccessKey(existingConfig.getSecretAccessKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
S3Config updated = s3ConfigService.updateConfig(id, config);
|
||||||
|
updated.setSecretAccessKey("******");
|
||||||
|
return Result.success(updated);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.error("更新S3配置失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除S3配置
|
||||||
|
*/
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Void> deleteConfig(@PathVariable Long id) {
|
||||||
|
try {
|
||||||
|
s3ConfigService.deleteConfig(id);
|
||||||
|
return Result.success();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.error("删除S3配置失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 激活指定的S3配置
|
||||||
|
*/
|
||||||
|
@PutMapping("/{id}/activate")
|
||||||
|
public Result<Void> activateConfig(@PathVariable Long id) {
|
||||||
|
try {
|
||||||
|
s3ConfigService.activateConfig(id);
|
||||||
|
return Result.success();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.error("激活S3配置失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,102 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.ScorePool;
|
||||||
|
import com.designhub.entity.ScorePoolRecord;
|
||||||
|
import com.designhub.service.ScorePoolService;
|
||||||
|
import com.designhub.util.JwtUtil;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 激励积分池 Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "激励积分池", description = "激励积分池管理API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/score-pools")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ScorePoolController {
|
||||||
|
|
||||||
|
private final ScorePoolService scorePoolService;
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@Operation(summary = "创建积分池")
|
||||||
|
@PostMapping
|
||||||
|
public Result<ScorePool> createPool(
|
||||||
|
@RequestBody ScorePool pool,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
pool.setCreatorId(userId);
|
||||||
|
return Result.success(scorePoolService.createPool(pool));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新积分池")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<ScorePool> updatePool(@PathVariable Long id, @RequestBody ScorePool pool) {
|
||||||
|
return Result.success(scorePoolService.updatePool(id, pool));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取积分池列表")
|
||||||
|
@GetMapping
|
||||||
|
public Result<IPage<ScorePool>> getPoolList(
|
||||||
|
@RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||||
|
@RequestParam(required = false) String poolType,
|
||||||
|
@RequestParam(required = false) Integer year) {
|
||||||
|
return Result.success(scorePoolService.getPoolList(page, pageSize, poolType, year));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取积分池详情")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<ScorePool> getPoolById(@PathVariable Long id) {
|
||||||
|
return Result.success(scorePoolService.getPoolById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取当前活跃积分池")
|
||||||
|
@GetMapping("/current")
|
||||||
|
public Result<ScorePool> getCurrentActivePool() {
|
||||||
|
return Result.success(scorePoolService.getCurrentActivePool());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取积分池统计信息")
|
||||||
|
@GetMapping("/{id}/statistics")
|
||||||
|
public Result<Map<String, Object>> getPoolStatistics(@PathVariable Long id) {
|
||||||
|
return Result.success(scorePoolService.getPoolStatistics(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取积分池使用记录")
|
||||||
|
@GetMapping("/{id}/records")
|
||||||
|
public Result<IPage<ScorePoolRecord>> getPoolRecords(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@RequestParam(defaultValue = "20") Integer pageSize) {
|
||||||
|
return Result.success(scorePoolService.getPoolRecords(id, page, pageSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "关闭积分池")
|
||||||
|
@PostMapping("/{id}/close")
|
||||||
|
public Result<Void> closePool(@PathVariable Long id) {
|
||||||
|
scorePoolService.closePool(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "结算积分池")
|
||||||
|
@PostMapping("/{id}/settle")
|
||||||
|
public Result<Void> settlePool(@PathVariable Long id) {
|
||||||
|
scorePoolService.settlePool(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除积分池")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Void> deletePool(@PathVariable Long id) {
|
||||||
|
scorePoolService.deletePool(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.SystemConfig;
|
||||||
|
import com.designhub.service.SystemConfigService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统配置Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "系统配置", description = "系统配置相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/system-config")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SystemConfigController {
|
||||||
|
|
||||||
|
private final SystemConfigService systemConfigService;
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有配置列表")
|
||||||
|
@GetMapping
|
||||||
|
public Result<List<SystemConfig>> getAllConfigs() {
|
||||||
|
return Result.success(systemConfigService.getAllConfigs());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据分组获取配置列表")
|
||||||
|
@GetMapping("/group/{group}")
|
||||||
|
public Result<List<SystemConfig>> getConfigsByGroup(@PathVariable String group) {
|
||||||
|
return Result.success(systemConfigService.getConfigsByGroup(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据键获取配置值")
|
||||||
|
@GetMapping("/key/{key}")
|
||||||
|
public Result<String> getConfigValue(@PathVariable String key) {
|
||||||
|
return Result.success(systemConfigService.getConfigValue(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有配置的Map")
|
||||||
|
@GetMapping("/map")
|
||||||
|
public Result<Map<String, String>> getAllConfigsMap() {
|
||||||
|
return Result.success(systemConfigService.getAllConfigsMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据分组获取配置Map")
|
||||||
|
@GetMapping("/map/group/{group}")
|
||||||
|
public Result<Map<String, String>> getConfigsMapByGroup(@PathVariable String group) {
|
||||||
|
return Result.success(systemConfigService.getConfigsMapByGroup(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "创建配置")
|
||||||
|
@PostMapping
|
||||||
|
public Result<SystemConfig> createConfig(@RequestBody SystemConfig config) {
|
||||||
|
return Result.success(systemConfigService.createConfig(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新配置")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<Boolean> updateConfig(@PathVariable Long id, @RequestBody SystemConfig config) {
|
||||||
|
config.setId(id);
|
||||||
|
return Result.success(systemConfigService.updateConfig(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "批量更新配置值")
|
||||||
|
@PutMapping("/batch")
|
||||||
|
public Result<Boolean> batchUpdateValues(@RequestBody Map<String, String> configMap) {
|
||||||
|
return Result.success(systemConfigService.batchUpdateValues(configMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除配置")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Boolean> deleteConfig(@PathVariable Long id) {
|
||||||
|
return Result.success(systemConfigService.deleteConfig(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "初始化默认配置")
|
||||||
|
@PostMapping("/init")
|
||||||
|
public Result<Void> initDefaultConfigs() {
|
||||||
|
systemConfigService.initDefaultConfigs();
|
||||||
|
return Result.success(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.service.SystemHealthService;
|
||||||
|
import com.designhub.vo.SystemHealthVO;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统健康检查控制器
|
||||||
|
*/
|
||||||
|
@Tag(name = "系统健康", description = "系统自检与健康检查接口")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/system")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SystemHealthController {
|
||||||
|
|
||||||
|
private final SystemHealthService systemHealthService;
|
||||||
|
|
||||||
|
@Operation(summary = "健康检查", description = "用于部署后快速检测数据库与关键表状态")
|
||||||
|
@GetMapping("/health")
|
||||||
|
public Result<SystemHealthVO> health() {
|
||||||
|
return Result.success(systemHealthService.check());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.dto.TaskApprovalDTO;
|
||||||
|
import com.designhub.entity.Task;
|
||||||
|
import com.designhub.service.TaskApprovalService;
|
||||||
|
import com.designhub.util.JwtUtil;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务审核控制器
|
||||||
|
*/
|
||||||
|
@Tag(name = "任务审核管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/tasks")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class TaskApprovalController {
|
||||||
|
|
||||||
|
private final TaskApprovalService taskApprovalService;
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@Operation(summary = "需求部门审核通过")
|
||||||
|
@PostMapping("/{id}/approve-demand")
|
||||||
|
public Result<Void> approveDemandLevel(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestBody TaskApprovalDTO dto,
|
||||||
|
@RequestHeader("Authorization") String authHeader
|
||||||
|
) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long approverId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
taskApprovalService.approveDemandLevel(id, approverId, dto);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "需求部门驳回")
|
||||||
|
@PostMapping("/{id}/reject-demand")
|
||||||
|
public Result<Void> rejectByDemandLeader(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestBody Map<String, String> params,
|
||||||
|
@RequestHeader("Authorization") String authHeader
|
||||||
|
) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long approverId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
String reason = params.getOrDefault("reason", "");
|
||||||
|
taskApprovalService.rejectByDemandLeader(id, approverId, reason);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "接单部门审核通过")
|
||||||
|
@PostMapping("/{id}/approve-receiving")
|
||||||
|
public Result<Void> approveReceivingLevel(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestBody TaskApprovalDTO dto,
|
||||||
|
@RequestHeader("Authorization") String authHeader
|
||||||
|
) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long approverId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
taskApprovalService.approveReceivingLevel(id, approverId, dto);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "接单部门驳回")
|
||||||
|
@PostMapping("/{id}/reject-receiving")
|
||||||
|
public Result<Void> rejectByReceivingLeader(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestBody Map<String, String> params,
|
||||||
|
@RequestHeader("Authorization") String authHeader
|
||||||
|
) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long approverId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
String reason = params.getOrDefault("reason", "");
|
||||||
|
taskApprovalService.rejectByReceivingLeader(id, approverId, reason);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取待审核任务列表")
|
||||||
|
@GetMapping("/pending-approval")
|
||||||
|
public Result<List<Task>> getPendingApprovalTasks(
|
||||||
|
@RequestHeader("Authorization") String authHeader
|
||||||
|
) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
List<Task> tasks = taskApprovalService.getPendingApprovalTasks(userId);
|
||||||
|
return Result.success(tasks);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,400 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.dto.TaskAssignRequest;
|
||||||
|
import com.designhub.dto.TaskAuditRequest;
|
||||||
|
import com.designhub.entity.Task;
|
||||||
|
import com.designhub.service.TaskService;
|
||||||
|
import com.designhub.util.JwtUtil;
|
||||||
|
import com.designhub.vo.DashboardStatsVO;
|
||||||
|
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 lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务管理控制器
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Tag(name = "任务管理", description = "任务的增删改查、抢单、审核等接口")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/tasks")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class TaskController {
|
||||||
|
|
||||||
|
private final TaskService taskService;
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@Operation(summary = "创建任务", description = "发起新的设计任务")
|
||||||
|
@PostMapping
|
||||||
|
public Result<Long> createTask(@RequestBody Task task, @RequestHeader("Authorization") String authHeader) {
|
||||||
|
log.info("创建任务: {}", task.getTitle());
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
task.setPublisherId(userId);
|
||||||
|
Long taskId = taskService.createTask(task);
|
||||||
|
return Result.success(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "创建流水线任务", description = "创建包含多个子任务的流水线任务")
|
||||||
|
@PostMapping("/pipeline")
|
||||||
|
public Result<Long> createPipelineTask(
|
||||||
|
@RequestBody com.designhub.dto.PipelineTaskDTO dto,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
|
||||||
|
log.info("创建流水线任务: {}", dto.getTitle());
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
Long taskId = taskService.createPipelineTask(dto, userId);
|
||||||
|
return Result.success("流水线任务创建成功", taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取任务列表", description = "分页查询任务列表,支持多种筛选条件")
|
||||||
|
@GetMapping
|
||||||
|
public Result<IPage<Task>> getTaskList(
|
||||||
|
@Parameter(description = "任务状态") @RequestParam(required = false) String status,
|
||||||
|
@Parameter(description = "任务类型ID") @RequestParam(required = false) Long taskTypeId,
|
||||||
|
@Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@Parameter(description = "每页数量") @RequestParam(defaultValue = "20") Integer pageSize) {
|
||||||
|
|
||||||
|
log.info("查询任务列表: status={}, taskTypeId={}, page={}, pageSize={}",
|
||||||
|
status, taskTypeId, page, pageSize);
|
||||||
|
|
||||||
|
IPage<Task> taskPage = taskService.getTaskList(page, pageSize, status, taskTypeId);
|
||||||
|
return Result.success(taskPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有待审核任务", description = "获取所有处于审批流程中的任务(包括需求部门审核和接单部门审核)")
|
||||||
|
@GetMapping("/pending-approval-all")
|
||||||
|
public Result<IPage<Task>> getAllPendingApprovalTasks(
|
||||||
|
@Parameter(description = "任务类型ID") @RequestParam(required = false) Long taskTypeId,
|
||||||
|
@Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@Parameter(description = "每页数量") @RequestParam(defaultValue = "20") Integer pageSize) {
|
||||||
|
|
||||||
|
log.info("查询所有待审核任务: taskTypeId={}, page={}, pageSize={}",
|
||||||
|
taskTypeId, page, pageSize);
|
||||||
|
|
||||||
|
IPage<Task> taskPage = taskService.getAllPendingApprovalTasks(page, pageSize, taskTypeId);
|
||||||
|
return Result.success(taskPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取任务详情", description = "根据任务ID获取详细信息")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<Task> getTaskById(@PathVariable Long id) {
|
||||||
|
log.info("查询任务详情: {}", id);
|
||||||
|
Task task = taskService.getTaskById(id);
|
||||||
|
return Result.success(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新任务", description = "更新任务信息")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<String> updateTask(@PathVariable Long id, @RequestBody Task task) {
|
||||||
|
log.info("更新任务: {}", id);
|
||||||
|
taskService.updateTask(id, task);
|
||||||
|
return Result.success("更新成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "抢单", description = "设计师抢单")
|
||||||
|
@PostMapping("/{id}/grab")
|
||||||
|
public Result<String> grabTask(@PathVariable Long id, @RequestHeader("Authorization") String authHeader) {
|
||||||
|
log.info("抢单: {}", id);
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
taskService.grabTask(id, userId);
|
||||||
|
return Result.success("抢单成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "提交任务", description = "设计师提交完成的任务")
|
||||||
|
@PostMapping("/{id}/submit")
|
||||||
|
public Result<String> submitTask(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestParam String deliveryPath,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
|
||||||
|
log.info("提交任务: {}, 交付路径: {}", id, deliveryPath);
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
taskService.submitTask(id, userId, deliveryPath);
|
||||||
|
return Result.success("提交成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "审核任务", description = "需求方审核任务")
|
||||||
|
@PostMapping("/{id}/review")
|
||||||
|
public Result<String> reviewTask(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestParam Boolean approved,
|
||||||
|
@RequestParam(required = false) String comment,
|
||||||
|
@RequestParam(required = false) Integer rating,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
|
||||||
|
log.info("审核任务: {}, 通过: {}, 评分: {}", id, approved, rating);
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
taskService.reviewTask(id, userId, approved, comment, rating);
|
||||||
|
return Result.success(approved ? "审核通过" : "已驳回");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "取消任务", description = "发布人取消任务")
|
||||||
|
@PostMapping("/{id}/cancel")
|
||||||
|
public Result<String> cancelTask(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestParam String reason,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
|
||||||
|
log.info("取消任务: {}, 原因: {}", id, reason);
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
taskService.cancelTask(id, userId, reason);
|
||||||
|
return Result.success("取消成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取已完成任务列表", description = "根据用户角色返回不同范围的已完成任务")
|
||||||
|
@GetMapping("/completed")
|
||||||
|
public Result<IPage<Task>> getCompletedTasks(
|
||||||
|
@Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@Parameter(description = "每页数量") @RequestParam(defaultValue = "12") Integer pageSize,
|
||||||
|
@Parameter(description = "关键词") @RequestParam(required = false) String keyword,
|
||||||
|
@Parameter(description = "排序方式") @RequestParam(required = false) String sortBy,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
|
||||||
|
log.info("查询已完成任务: page={}, pageSize={}, keyword={}, sortBy={}",
|
||||||
|
page, pageSize, keyword, sortBy);
|
||||||
|
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
|
||||||
|
IPage<Task> taskPage = taskService.getCompletedTasks(page, pageSize, userId, keyword, sortBy);
|
||||||
|
return Result.success(taskPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "任务大厅", description = "获取可接单的任务列表,支持多种筛选、搜索和排序")
|
||||||
|
@GetMapping("/hall")
|
||||||
|
public Result<IPage<Task>> getTaskHall(
|
||||||
|
@Parameter(description = "任务类型ID") @RequestParam(required = false) Long taskTypeId,
|
||||||
|
@Parameter(description = "需求类型ID") @RequestParam(required = false) Long demandTypeId,
|
||||||
|
@Parameter(description = "品牌ID") @RequestParam(required = false) Long brandId,
|
||||||
|
@Parameter(description = "类目ID") @RequestParam(required = false) Long categoryId,
|
||||||
|
@Parameter(description = "平台ID") @RequestParam(required = false) Long platformId,
|
||||||
|
@Parameter(description = "任务子类型") @RequestParam(required = false) String taskSubType,
|
||||||
|
@Parameter(description = "最小积分") @RequestParam(required = false) Integer minScore,
|
||||||
|
@Parameter(description = "最大积分") @RequestParam(required = false) Integer maxScore,
|
||||||
|
@Parameter(description = "关键词搜索") @RequestParam(required = false) String keyword,
|
||||||
|
@Parameter(description = "发布人姓名") @RequestParam(required = false) String publisherName,
|
||||||
|
@Parameter(description = "发布时间范围:today/week/month") @RequestParam(required = false) String timeRange,
|
||||||
|
@Parameter(description = "排序方式:latest/score/deadline/hot") @RequestParam(required = false) String sortBy,
|
||||||
|
@Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@Parameter(description = "每页数量") @RequestParam(defaultValue = "20") Integer pageSize) {
|
||||||
|
|
||||||
|
log.info("查询任务大厅: taskTypeId={}, demandTypeId={}, brandId={}, categoryId={}, platformId={}, " +
|
||||||
|
"taskSubType={}, minScore={}, maxScore={}, keyword={}, publisherName={}, timeRange={}, " +
|
||||||
|
"sortBy={}, page={}, pageSize={}",
|
||||||
|
taskTypeId, demandTypeId, brandId, categoryId, platformId, taskSubType,
|
||||||
|
minScore, maxScore, keyword, publisherName, timeRange, sortBy, page, pageSize);
|
||||||
|
|
||||||
|
IPage<Task> taskPage = taskService.getTaskHall(page, pageSize, taskTypeId, demandTypeId,
|
||||||
|
brandId, categoryId, platformId, taskSubType, minScore, maxScore, keyword,
|
||||||
|
publisherName, timeRange, sortBy);
|
||||||
|
return Result.success(taskPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "我的任务", description = "获取当前用户的任务列表")
|
||||||
|
@GetMapping("/my")
|
||||||
|
public Result<IPage<Task>> getMyTasks(
|
||||||
|
@RequestParam(required = false) String role, // publisher 或 receiver
|
||||||
|
@RequestParam(required = false) String status,
|
||||||
|
@RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@RequestParam(defaultValue = "20") Integer pageSize,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
|
||||||
|
log.info("查询我的任务: role={}, status={}, page={}, pageSize={}",
|
||||||
|
role, status, page, pageSize);
|
||||||
|
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
|
||||||
|
IPage<Task> taskPage;
|
||||||
|
if ("publisher".equals(role)) {
|
||||||
|
taskPage = taskService.getMyPublishedTasks(page, pageSize, userId, status);
|
||||||
|
} else {
|
||||||
|
taskPage = taskService.getMyReceivedTasks(page, pageSize, userId, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.success(taskPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "提交任务审核", description = "任务发起人提交任务给部门负责人审核")
|
||||||
|
@PostMapping("/{id}/submit-audit")
|
||||||
|
public Result<Boolean> submitTaskForAudit(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
log.info("提交任务审核: taskId={}", id);
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
return Result.success(taskService.submitTaskForAudit(id, userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "审核任务", description = "部门负责人审核任务(通过或驳回)")
|
||||||
|
@PostMapping("/audit")
|
||||||
|
public Result<Boolean> auditTask(
|
||||||
|
@Valid @RequestBody TaskAuditRequest request,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
log.info("审核任务: request={}", request);
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long auditorId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
return Result.success(taskService.auditTask(request, auditorId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "强制指派任务", description = "部门负责人强制指派任务给部门成员")
|
||||||
|
@PostMapping("/assign")
|
||||||
|
public Result<Boolean> assignTask(
|
||||||
|
@Valid @RequestBody TaskAssignRequest request,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
log.info("强制指派任务: request={}", request);
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long assignerId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
return Result.success(taskService.assignTask(request, assignerId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "负责人任务列表", description = "需求部门负责人查看部门内所有任务")
|
||||||
|
@GetMapping("/leader/demand")
|
||||||
|
public Result<IPage<Task>> getLeaderTaskList(
|
||||||
|
@RequestParam(required = false) String auditStatus,
|
||||||
|
@RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@RequestParam(defaultValue = "20") Integer pageSize,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
log.info("查询负责人任务列表: auditStatus={}, page={}, pageSize={}",
|
||||||
|
auditStatus, page, pageSize);
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
return Result.success(taskService.getLeaderTaskList(userId, page, pageSize, auditStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "接单部门负责人视图", description = "接单部门负责人查看部门成员接单明细")
|
||||||
|
@GetMapping("/leader/receiving")
|
||||||
|
public Result<IPage<Task>> getLeaderReceivingTaskList(
|
||||||
|
@RequestParam(required = false) String status,
|
||||||
|
@RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@RequestParam(defaultValue = "20") Integer pageSize,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
log.info("查询接单部门负责人视图: status={}, page={}, pageSize={}",
|
||||||
|
status, page, pageSize);
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
return Result.success(taskService.getLeaderReceivingTaskList(userId, page, pageSize, status));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取工作台统计数据")
|
||||||
|
@GetMapping("/dashboard/stats")
|
||||||
|
public Result<DashboardStatsVO> getDashboardStats(
|
||||||
|
@Parameter(description = "月份,格式:YYYY-MM,不传则查询当月") @RequestParam(required = false) String month,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
log.info("获取工作台统计数据: userId={}, month={}", userId, month);
|
||||||
|
return Result.success(taskService.getDashboardStats(userId, month));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取我的进行中任务")
|
||||||
|
@GetMapping("/my-in-progress")
|
||||||
|
public Result<java.util.List<Task>> getMyInProgressTasks(@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
return Result.success(taskService.getMyInProgressTasks(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "批量更新任务进度")
|
||||||
|
@PostMapping("/batch-update-progress")
|
||||||
|
public Result<String> batchUpdateProgress(
|
||||||
|
@RequestBody java.util.Map<String, Object> payload,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
java.util.List<java.util.Map<String, Object>> updates =
|
||||||
|
(java.util.List<java.util.Map<String, Object>>) payload.get("updates");
|
||||||
|
|
||||||
|
taskService.batchUpdateProgress(userId, updates);
|
||||||
|
return Result.success("进度更新成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新任务进度", description = "接单人更新任务进度")
|
||||||
|
@PostMapping("/{id}/progress")
|
||||||
|
public Result<String> updateTaskProgress(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestBody java.util.Map<String, Object> payload,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
|
||||||
|
log.info("更新任务进度: taskId={}", id);
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
|
||||||
|
Integer progress = (Integer) payload.get("progress");
|
||||||
|
if (progress == null) {
|
||||||
|
return Result.error("进度值不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
taskService.updateTaskProgress(id, userId, progress);
|
||||||
|
return Result.success("进度更新成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "完结任务", description = "接单人完结任务并提交交付路径")
|
||||||
|
@PostMapping("/{id}/complete")
|
||||||
|
public Result<String> completeTask(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestBody java.util.Map<String, String> payload,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
|
||||||
|
log.info("完结任务: taskId={}", id);
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
|
||||||
|
String deliveryPath = payload.get("deliveryPath");
|
||||||
|
if (deliveryPath == null || deliveryPath.trim().isEmpty()) {
|
||||||
|
return Result.error("交付路径不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
taskService.completeTask(id, userId, deliveryPath);
|
||||||
|
return Result.success("任务已提交审核");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "审核通过任务", description = "发起人或部门负责人审核通过任务")
|
||||||
|
@PostMapping("/{id}/approve")
|
||||||
|
public Result<String> approveTask(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
|
||||||
|
log.info("审核通过任务: taskId={}", id);
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
|
||||||
|
taskService.approveTask(id, userId);
|
||||||
|
return Result.success("审核通过");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "驳回任务", description = "发起人或部门负责人驳回任务")
|
||||||
|
@PostMapping("/{id}/reject")
|
||||||
|
public Result<String> rejectTask(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestBody java.util.Map<String, String> payload,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
|
||||||
|
log.info("驳回任务: taskId={}", id);
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
|
||||||
|
String reason = payload.get("reason");
|
||||||
|
if (reason == null || reason.trim().isEmpty()) {
|
||||||
|
return Result.error("驳回原因不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
taskService.rejectTask(id, userId, reason);
|
||||||
|
return Result.success("任务已驳回");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,174 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.TaskItem;
|
||||||
|
import com.designhub.service.TaskItemService;
|
||||||
|
import com.designhub.util.JwtUtil;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务子项控制器(流水线任务)
|
||||||
|
*/
|
||||||
|
@Tag(name = "流水线任务管理", description = "流水线任务子项相关接口")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/tasks/items")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class TaskItemController {
|
||||||
|
|
||||||
|
private final TaskItemService taskItemService;
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取可接单的子任务列表
|
||||||
|
*/
|
||||||
|
@Operation(summary = "获取可接单的子任务列表")
|
||||||
|
@GetMapping("/available")
|
||||||
|
public Result<IPage<TaskItem>> getAvailableItems(
|
||||||
|
@Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@Parameter(description = "每页数量") @RequestParam(defaultValue = "10") Integer pageSize,
|
||||||
|
@Parameter(description = "任务类型ID") @RequestParam(required = false) Long taskTypeId,
|
||||||
|
@Parameter(description = "搜索关键词") @RequestParam(required = false) String keyword) {
|
||||||
|
|
||||||
|
IPage<TaskItem> items = taskItemService.getAvailableItems(page, pageSize, taskTypeId, keyword);
|
||||||
|
return Result.success(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取进行中的子任务列表
|
||||||
|
*/
|
||||||
|
@Operation(summary = "获取进行中的子任务列表")
|
||||||
|
@GetMapping("/in-progress")
|
||||||
|
public Result<IPage<TaskItem>> getInProgressItems(
|
||||||
|
@Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@Parameter(description = "每页数量") @RequestParam(defaultValue = "10") Integer pageSize,
|
||||||
|
@RequestHeader("Authorization") String token) {
|
||||||
|
|
||||||
|
String actualToken = token.replace("Bearer ", "");
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(actualToken);
|
||||||
|
IPage<TaskItem> items = taskItemService.getInProgressItems(page, pageSize, userId);
|
||||||
|
return Result.success(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取已完成的子任务列表
|
||||||
|
*/
|
||||||
|
@Operation(summary = "获取已完成的子任务列表")
|
||||||
|
@GetMapping("/completed")
|
||||||
|
public Result<IPage<TaskItem>> getCompletedItems(
|
||||||
|
@Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@Parameter(description = "每页数量") @RequestParam(defaultValue = "10") Integer pageSize,
|
||||||
|
@RequestHeader("Authorization") String token) {
|
||||||
|
|
||||||
|
String actualToken = token.replace("Bearer ", "");
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(actualToken);
|
||||||
|
IPage<TaskItem> items = taskItemService.getCompletedItems(page, pageSize, userId);
|
||||||
|
return Result.success(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主任务ID获取所有子任务
|
||||||
|
*/
|
||||||
|
@Operation(summary = "根据主任务ID获取所有子任务")
|
||||||
|
@GetMapping("/task/{taskId}")
|
||||||
|
public Result<List<TaskItem>> getItemsByTaskId(
|
||||||
|
@Parameter(description = "主任务ID") @PathVariable Long taskId) {
|
||||||
|
|
||||||
|
List<TaskItem> items = taskItemService.getItemsByTaskId(taskId);
|
||||||
|
return Result.success(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取子任务详情
|
||||||
|
*/
|
||||||
|
@Operation(summary = "获取子任务详情")
|
||||||
|
@GetMapping("/{itemId}")
|
||||||
|
public Result<TaskItem> getItemById(
|
||||||
|
@Parameter(description = "子任务ID") @PathVariable Long itemId) {
|
||||||
|
|
||||||
|
TaskItem item = taskItemService.getItemById(itemId);
|
||||||
|
return Result.success(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抢单子任务
|
||||||
|
*/
|
||||||
|
@Operation(summary = "抢单子任务")
|
||||||
|
@PostMapping("/{itemId}/grab")
|
||||||
|
public Result<String> grabItem(
|
||||||
|
@Parameter(description = "子任务ID") @PathVariable Long itemId,
|
||||||
|
@RequestHeader("Authorization") String token) {
|
||||||
|
|
||||||
|
String actualToken = token.replace("Bearer ", "");
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(actualToken);
|
||||||
|
taskItemService.grabItem(itemId, userId);
|
||||||
|
return Result.success("抢单成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新子任务进度
|
||||||
|
*/
|
||||||
|
@Operation(summary = "更新子任务进度")
|
||||||
|
@PostMapping("/{itemId}/progress")
|
||||||
|
public Result<String> updateProgress(
|
||||||
|
@Parameter(description = "子任务ID") @PathVariable Long itemId,
|
||||||
|
@Parameter(description = "进度(0-100)") @RequestParam Integer progress,
|
||||||
|
@RequestHeader("Authorization") String token) {
|
||||||
|
|
||||||
|
String actualToken = token.replace("Bearer ", "");
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(actualToken);
|
||||||
|
taskItemService.updateProgress(itemId, userId, progress);
|
||||||
|
return Result.success("进度更新成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完结子任务
|
||||||
|
*/
|
||||||
|
@Operation(summary = "完结子任务")
|
||||||
|
@PostMapping("/{itemId}/complete")
|
||||||
|
public Result<Map<String, Object>> completeItem(
|
||||||
|
@Parameter(description = "子任务ID") @PathVariable Long itemId,
|
||||||
|
@Parameter(description = "交付路径") @RequestBody Map<String, String> request,
|
||||||
|
@RequestHeader("Authorization") String token) {
|
||||||
|
|
||||||
|
String actualToken = token.replace("Bearer ", "");
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(actualToken);
|
||||||
|
String deliveryPath = request.get("deliveryPath");
|
||||||
|
|
||||||
|
Map<String, Object> result = taskItemService.completeItem(itemId, userId, deliveryPath);
|
||||||
|
return Result.success("子任务完成成功", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 驳回子任务
|
||||||
|
*/
|
||||||
|
@Operation(summary = "驳回子任务")
|
||||||
|
@PostMapping("/{itemId}/reject")
|
||||||
|
public Result<String> rejectItem(
|
||||||
|
@Parameter(description = "子任务ID") @PathVariable Long itemId,
|
||||||
|
@Parameter(description = "驳回原因") @RequestBody Map<String, String> request) {
|
||||||
|
|
||||||
|
String reason = request.get("reason");
|
||||||
|
taskItemService.rejectItem(itemId, reason);
|
||||||
|
return Result.success("子任务已驳回");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取主任务进度统计
|
||||||
|
*/
|
||||||
|
@Operation(summary = "获取主任务进度统计")
|
||||||
|
@GetMapping("/task/{taskId}/progress")
|
||||||
|
public Result<Map<String, Object>> getTaskProgress(
|
||||||
|
@Parameter(description = "主任务ID") @PathVariable Long taskId) {
|
||||||
|
|
||||||
|
Map<String, Object> progress = taskItemService.getTaskProgress(taskId);
|
||||||
|
return Result.success(progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.TaskSubType;
|
||||||
|
import com.designhub.service.TaskSubTypeService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务子类型控制器
|
||||||
|
*/
|
||||||
|
@Tag(name = "任务子类型管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/task-sub-types")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class TaskSubTypeController {
|
||||||
|
|
||||||
|
private final TaskSubTypeService taskSubTypeService;
|
||||||
|
|
||||||
|
@Operation(summary = "获取所有子类型")
|
||||||
|
@GetMapping
|
||||||
|
public Result<List<TaskSubType>> getAllSubTypes() {
|
||||||
|
return Result.success(taskSubTypeService.getAllSubTypes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取启用的子类型")
|
||||||
|
@GetMapping("/enabled")
|
||||||
|
public Result<List<TaskSubType>> getEnabledSubTypes() {
|
||||||
|
return Result.success(taskSubTypeService.getEnabledSubTypes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据ID获取子类型")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<TaskSubType> getById(@PathVariable Long id) {
|
||||||
|
return Result.success(taskSubTypeService.getById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "创建子类型")
|
||||||
|
@PostMapping
|
||||||
|
public Result<TaskSubType> createSubType(@RequestBody TaskSubType subType) {
|
||||||
|
return Result.success(taskSubTypeService.createSubType(subType));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新子类型")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<TaskSubType> updateSubType(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestBody TaskSubType subType
|
||||||
|
) {
|
||||||
|
return Result.success(taskSubTypeService.updateSubType(id, subType));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除子类型")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Void> deleteSubType(@PathVariable Long id) {
|
||||||
|
taskSubTypeService.deleteSubType(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "切换启用状态")
|
||||||
|
@PostMapping("/{id}/toggle-enabled")
|
||||||
|
public Result<Void> toggleEnabled(@PathVariable Long id) {
|
||||||
|
taskSubTypeService.toggleEnabled(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新排序")
|
||||||
|
@PostMapping("/{id}/sort")
|
||||||
|
public Result<Void> updateSortOrder(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@Parameter(description = "排序值") @RequestParam Integer sortOrder
|
||||||
|
) {
|
||||||
|
taskSubTypeService.updateSortOrder(id, sortOrder);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.TeamPK;
|
||||||
|
import com.designhub.service.TeamPKService;
|
||||||
|
import com.designhub.util.JwtUtil;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 团队PK Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "团队PK", description = "团队PK管理相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/team-pk")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class TeamPKController {
|
||||||
|
|
||||||
|
private final TeamPKService teamPKService;
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@Operation(summary = "创建团队PK")
|
||||||
|
@PostMapping
|
||||||
|
public Result<TeamPK> createTeamPK(
|
||||||
|
@RequestBody TeamPK teamPK,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
teamPK.setCreatorId(userId);
|
||||||
|
return Result.success(teamPKService.createTeamPK(teamPK));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "发布团队PK")
|
||||||
|
@PostMapping("/{id}/publish")
|
||||||
|
public Result<TeamPK> publishTeamPK(@PathVariable Long id) {
|
||||||
|
return Result.success(teamPKService.publishTeamPK(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "结束团队PK")
|
||||||
|
@PostMapping("/{id}/end")
|
||||||
|
public Result<Void> endTeamPK(@PathVariable Long id) {
|
||||||
|
teamPKService.endTeamPK(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "取消团队PK")
|
||||||
|
@PostMapping("/{id}/cancel")
|
||||||
|
public Result<Void> cancelTeamPK(@PathVariable Long id) {
|
||||||
|
teamPKService.cancelTeamPK(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除团队PK")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Void> deleteTeamPK(@PathVariable Long id) {
|
||||||
|
teamPKService.deleteTeamPK(id);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取团队PK列表")
|
||||||
|
@GetMapping
|
||||||
|
public Result<IPage<TeamPK>> getPKList(
|
||||||
|
@RequestParam(defaultValue = "1") Integer page,
|
||||||
|
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||||
|
@RequestParam(required = false) String status) {
|
||||||
|
return Result.success(teamPKService.getPKList(page, pageSize, status));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取团队PK详情")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<TeamPK> getPKById(@PathVariable Long id) {
|
||||||
|
return Result.success(teamPKService.getPKById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "刷新PK数据")
|
||||||
|
@PostMapping("/{id}/refresh")
|
||||||
|
public Result<TeamPK> refreshPKStats(@PathVariable Long id) {
|
||||||
|
TeamPK pk = teamPKService.getPKById(id);
|
||||||
|
teamPKService.refreshPKStats(pk);
|
||||||
|
return Result.success(teamPKService.getPKById(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.service.ThirdPartyCallbackService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第三方平台回调控制器
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Tag(name = "第三方回调", description = "接收第三方平台的任务完成回调")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/third-party")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ThirdPartyCallbackController {
|
||||||
|
|
||||||
|
private final ThirdPartyCallbackService callbackService;
|
||||||
|
|
||||||
|
@Operation(summary = "任务完成回调", description = "第三方平台通知任务已完成")
|
||||||
|
@PostMapping("/task-complete")
|
||||||
|
public Result<String> taskComplete(@RequestBody Map<String, Object> payload) {
|
||||||
|
log.info("收到第三方任务完成回调: {}", payload);
|
||||||
|
|
||||||
|
try {
|
||||||
|
String thirdPartyTaskNo = (String) payload.get("thirdPartyTaskNo");
|
||||||
|
if (thirdPartyTaskNo == null || thirdPartyTaskNo.isEmpty()) {
|
||||||
|
return Result.fail("缺少必需参数: thirdPartyTaskNo");
|
||||||
|
}
|
||||||
|
|
||||||
|
callbackService.handleTaskCompleteCallback(thirdPartyTaskNo, payload);
|
||||||
|
return Result.success("回调处理成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("处理任务完成回调失败", e);
|
||||||
|
return Result.fail("处理失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "任务状态更新回调", description = "第三方平台通知任务状态变更")
|
||||||
|
@PostMapping("/task-status-update")
|
||||||
|
public Result<String> taskStatusUpdate(@RequestBody Map<String, Object> payload) {
|
||||||
|
log.info("收到第三方任务状态更新回调: {}", payload);
|
||||||
|
|
||||||
|
try {
|
||||||
|
String thirdPartyTaskNo = (String) payload.get("thirdPartyTaskNo");
|
||||||
|
String status = (String) payload.get("status");
|
||||||
|
|
||||||
|
if (thirdPartyTaskNo == null || thirdPartyTaskNo.isEmpty()) {
|
||||||
|
return Result.fail("缺少必需参数: thirdPartyTaskNo");
|
||||||
|
}
|
||||||
|
|
||||||
|
callbackService.handleTaskStatusUpdateCallback(thirdPartyTaskNo, status, payload);
|
||||||
|
return Result.success("回调处理成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("处理任务状态更新回调失败", e);
|
||||||
|
return Result.fail("处理失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "查询回调记录", description = "根据第三方任务编号查询回调记录")
|
||||||
|
@GetMapping("/callback-records/{thirdPartyTaskNo}")
|
||||||
|
public Result<?> getCallbackRecords(
|
||||||
|
@Parameter(description = "第三方任务编号") @PathVariable String thirdPartyTaskNo) {
|
||||||
|
return Result.success(callbackService.getCallbackRecords(thirdPartyTaskNo));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,171 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传Controller
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/upload")
|
||||||
|
@Tag(name = "文件上传", description = "文件上传相关API")
|
||||||
|
public class UploadController {
|
||||||
|
|
||||||
|
@Value("${file.upload.path:./uploads}")
|
||||||
|
private String uploadPath;
|
||||||
|
|
||||||
|
@Value("${file.upload.max-size:52428800}")
|
||||||
|
private long maxSize;
|
||||||
|
|
||||||
|
@Value("${file.upload.allowed-extensions:.xls,.xlsx,.doc,.docx,.pdf,.zip}")
|
||||||
|
private String allowedExtensions;
|
||||||
|
|
||||||
|
private static final Set<String> ALLOWED_EXTENSIONS_SET = new HashSet<>();
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
String[] extensions = allowedExtensions.split(",");
|
||||||
|
for (String ext : extensions) {
|
||||||
|
ALLOWED_EXTENSIONS_SET.add(ext.trim().toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保上传目录存在
|
||||||
|
try {
|
||||||
|
Path path = Paths.get(uploadPath);
|
||||||
|
if (!Files.exists(path)) {
|
||||||
|
Files.createDirectories(path);
|
||||||
|
log.info("创建上传目录: {}", uploadPath);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("创建上传目录失败: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件
|
||||||
|
*/
|
||||||
|
@PostMapping("/file")
|
||||||
|
@Operation(summary = "上传文件", description = "支持Excel、Word、PDF、ZIP等格式")
|
||||||
|
public Result<Map<String, Object>> uploadFile(@RequestParam("file") MultipartFile file) {
|
||||||
|
try {
|
||||||
|
// 验证文件
|
||||||
|
if (file == null || file.isEmpty()) {
|
||||||
|
return Result.error(400, "文件不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证文件大小
|
||||||
|
if (file.getSize() > maxSize) {
|
||||||
|
return Result.error(400, String.format("文件大小不能超过 %d MB", maxSize / 1024 / 1024));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证文件扩展名
|
||||||
|
String originalFilename = file.getOriginalFilename();
|
||||||
|
if (originalFilename == null || originalFilename.isEmpty()) {
|
||||||
|
return Result.error(400, "文件名不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
String extension = getFileExtension(originalFilename);
|
||||||
|
if (!ALLOWED_EXTENSIONS_SET.contains(extension.toLowerCase())) {
|
||||||
|
return Result.error(400, String.format("不支持的文件格式,支持的格式:%s", allowedExtensions));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成文件存储路径
|
||||||
|
String dateDir = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
|
||||||
|
String fileName = generateFileName(originalFilename);
|
||||||
|
String relativePath = dateDir + "/" + fileName;
|
||||||
|
String fullPath = uploadPath + "/" + relativePath;
|
||||||
|
|
||||||
|
// 确保目录存在
|
||||||
|
Path targetPath = Paths.get(fullPath);
|
||||||
|
Files.createDirectories(targetPath.getParent());
|
||||||
|
|
||||||
|
// 保存文件
|
||||||
|
file.transferTo(targetPath.toFile());
|
||||||
|
|
||||||
|
log.info("文件上传成功: originalName={}, savedPath={}, size={}",
|
||||||
|
originalFilename, fullPath, file.getSize());
|
||||||
|
|
||||||
|
// 构建返回数据
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
data.put("fileName", originalFilename);
|
||||||
|
data.put("filePath", relativePath);
|
||||||
|
data.put("fileUrl", "/api/upload/download?path=" + relativePath);
|
||||||
|
data.put("fileSize", file.getSize());
|
||||||
|
data.put("fileType", extension);
|
||||||
|
|
||||||
|
return Result.success(data);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("文件上传失败: {}", e.getMessage(), e);
|
||||||
|
return Result.error(500, "文件上传失败: " + e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("文件上传异常: {}", e.getMessage(), e);
|
||||||
|
return Result.error(500, "文件上传异常: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件
|
||||||
|
*/
|
||||||
|
@GetMapping("/download")
|
||||||
|
@Operation(summary = "下载文件", description = "根据文件路径下载文件")
|
||||||
|
public org.springframework.http.ResponseEntity<org.springframework.core.io.Resource> downloadFile(
|
||||||
|
@RequestParam("path") String filePath) {
|
||||||
|
try {
|
||||||
|
String fullPath = uploadPath + "/" + filePath;
|
||||||
|
Path path = Paths.get(fullPath);
|
||||||
|
|
||||||
|
if (!Files.exists(path)) {
|
||||||
|
return org.springframework.http.ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
org.springframework.core.io.Resource resource =
|
||||||
|
new org.springframework.core.io.UrlResource(path.toUri());
|
||||||
|
|
||||||
|
return org.springframework.http.ResponseEntity.ok()
|
||||||
|
.header(org.springframework.http.HttpHeaders.CONTENT_DISPOSITION,
|
||||||
|
"attachment; filename=\"" + resource.getFilename() + "\"")
|
||||||
|
.body(resource);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("文件下载失败: {}", e.getMessage(), e);
|
||||||
|
return org.springframework.http.ResponseEntity.internalServerError().build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件扩展名
|
||||||
|
*/
|
||||||
|
private String getFileExtension(String filename) {
|
||||||
|
int lastDotIndex = filename.lastIndexOf('.');
|
||||||
|
if (lastDotIndex > 0 && lastDotIndex < filename.length() - 1) {
|
||||||
|
return filename.substring(lastDotIndex);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成唯一文件名
|
||||||
|
*/
|
||||||
|
private String generateFileName(String originalFilename) {
|
||||||
|
String extension = getFileExtension(originalFilename);
|
||||||
|
String nameWithoutExt = originalFilename.substring(0, originalFilename.lastIndexOf('.'));
|
||||||
|
String timestamp = String.valueOf(System.currentTimeMillis());
|
||||||
|
String random = UUID.randomUUID().toString().substring(0, 8);
|
||||||
|
return nameWithoutExt + "_" + timestamp + "_" + random + extension;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,129 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.User;
|
||||||
|
import com.designhub.service.UserService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户管理Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "用户管理", description = "用户管理相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/users")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class UserController {
|
||||||
|
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
@Operation(summary = "分页查询用户列表")
|
||||||
|
@GetMapping
|
||||||
|
public Result<Page<User>> getUserList(
|
||||||
|
@RequestParam(defaultValue = "1") int pageNum,
|
||||||
|
@RequestParam(defaultValue = "10") int pageSize,
|
||||||
|
@RequestParam(required = false) String keyword,
|
||||||
|
@RequestParam(required = false) Long deptId,
|
||||||
|
@RequestParam(required = false) Integer status
|
||||||
|
) {
|
||||||
|
return Result.success(userService.getUserList(pageNum, pageSize, keyword, deptId, status));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取全部用户列表")
|
||||||
|
@GetMapping("/all")
|
||||||
|
public Result<List<User>> getAllUsers() {
|
||||||
|
return Result.success(userService.getAllUsers());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据ID获取用户")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<User> getUserById(@PathVariable Long id) {
|
||||||
|
return Result.success(userService.getUserById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "创建用户")
|
||||||
|
@PostMapping
|
||||||
|
public Result<User> createUser(@RequestBody User user) {
|
||||||
|
return Result.success(userService.createUser(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新用户")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<Boolean> updateUser(@PathVariable Long id, @RequestBody User user) {
|
||||||
|
user.setId(id);
|
||||||
|
return Result.success(userService.updateUser(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除用户")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Boolean> deleteUser(@PathVariable Long id) {
|
||||||
|
return Result.success(userService.deleteUser(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "重置密码")
|
||||||
|
@PostMapping("/{id}/reset-password")
|
||||||
|
public Result<Boolean> resetPassword(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestBody Map<String, String> params
|
||||||
|
) {
|
||||||
|
String newPassword = params.getOrDefault("newPassword", "123456");
|
||||||
|
return Result.success(userService.resetPassword(id, newPassword));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "修改密码")
|
||||||
|
@PostMapping("/{id}/change-password")
|
||||||
|
public Result<Boolean> changePassword(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestBody Map<String, String> params
|
||||||
|
) {
|
||||||
|
String oldPassword = params.get("oldPassword");
|
||||||
|
String newPassword = params.get("newPassword");
|
||||||
|
return Result.success(userService.changePassword(id, oldPassword, newPassword));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "为用户分配角色")
|
||||||
|
@PostMapping("/{id}/roles")
|
||||||
|
public Result<Boolean> assignRoles(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestBody List<Long> roleIds
|
||||||
|
) {
|
||||||
|
return Result.success(userService.assignRoles(id, roleIds));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取用户的角色ID列表")
|
||||||
|
@GetMapping("/{id}/roles")
|
||||||
|
public Result<List<Long>> getUserRoleIds(@PathVariable Long id) {
|
||||||
|
return Result.success(userService.getUserRoleIds(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "启用/禁用用户")
|
||||||
|
@PutMapping("/{id}/status")
|
||||||
|
public Result<Boolean> toggleUserStatus(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestParam Integer status
|
||||||
|
) {
|
||||||
|
return Result.success(userService.toggleUserStatus(id, status));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "调整用户所属部门")
|
||||||
|
@PutMapping("/{id}/department")
|
||||||
|
public Result<Boolean> updateUserDepartment(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestBody Map<String, Long> params
|
||||||
|
) {
|
||||||
|
Long deptId = params.get("deptId");
|
||||||
|
log.info("调整用户部门请求: userId={}, deptId={}, params={}", id, deptId, params);
|
||||||
|
boolean result = userService.updateUserDepartment(id, deptId);
|
||||||
|
log.info("调整用户部门结果: userId={}, deptId={}, result={}", id, deptId, result);
|
||||||
|
return Result.success(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,117 @@
|
|||||||
|
package com.designhub.controller;
|
||||||
|
|
||||||
|
import com.designhub.common.Result;
|
||||||
|
import com.designhub.entity.UserGoal;
|
||||||
|
import com.designhub.service.UserGoalService;
|
||||||
|
import com.designhub.util.JwtUtil;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户目标Controller
|
||||||
|
*/
|
||||||
|
@Tag(name = "目标管理", description = "用户目标管理相关API")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/goals")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class UserGoalController {
|
||||||
|
|
||||||
|
private final UserGoalService userGoalService;
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@Operation(summary = "创建目标")
|
||||||
|
@PostMapping
|
||||||
|
public Result<UserGoal> createGoal(
|
||||||
|
@RequestBody UserGoal goal,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
goal.setUserId(userId);
|
||||||
|
return Result.success(userGoalService.createGoal(goal));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "创建预设目标")
|
||||||
|
@PostMapping("/preset/{type}")
|
||||||
|
public Result<UserGoal> createPresetGoal(
|
||||||
|
@PathVariable String type,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
return Result.success(userGoalService.createPresetGoal(userId, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新目标")
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public Result<UserGoal> updateGoal(@PathVariable Long id, @RequestBody UserGoal goal) {
|
||||||
|
return Result.success(userGoalService.updateGoal(id, goal));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "更新目标进度")
|
||||||
|
@PutMapping("/{id}/progress")
|
||||||
|
public Result<UserGoal> updateProgress(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestParam Integer progressValue) {
|
||||||
|
return Result.success(userGoalService.updateProgress(id, progressValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取我的目标列表")
|
||||||
|
@GetMapping("/my")
|
||||||
|
public Result<List<UserGoal>> getMyGoals(
|
||||||
|
@RequestParam(required = false) String status,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
return Result.success(userGoalService.getUserGoals(userId, status));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取我的活跃目标")
|
||||||
|
@GetMapping("/my/active")
|
||||||
|
public Result<List<UserGoal>> getMyActiveGoals(
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
return Result.success(userGoalService.getActiveGoals(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取目标详情")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<UserGoal> getGoalById(@PathVariable Long id) {
|
||||||
|
return Result.success(userGoalService.getGoalById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "领取目标奖励")
|
||||||
|
@PostMapping("/{id}/claim-reward")
|
||||||
|
public Result<Boolean> claimReward(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
return Result.success(userGoalService.claimReward(id, userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "取消目标")
|
||||||
|
@PostMapping("/{id}/cancel")
|
||||||
|
public Result<Void> cancelGoal(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
userGoalService.cancelGoal(id, userId);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "删除目标")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public Result<Void> deleteGoal(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestHeader("Authorization") String authHeader) {
|
||||||
|
String token = jwtUtil.extractTokenFromHeader(authHeader);
|
||||||
|
Long userId = jwtUtil.getUserIdFromToken(token);
|
||||||
|
userGoalService.deleteGoal(id, userId);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
package com.designhub.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Min;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门创建请求
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DepartmentCreateRequest {
|
||||||
|
|
||||||
|
@NotBlank(message = "部门名称不能为空")
|
||||||
|
private String deptName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门编码,可选
|
||||||
|
*/
|
||||||
|
private String deptCode;
|
||||||
|
|
||||||
|
@NotBlank(message = "部门类型不能为空")
|
||||||
|
private String deptType; // demand / receiving
|
||||||
|
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门负责人
|
||||||
|
*/
|
||||||
|
private Long leaderId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序
|
||||||
|
*/
|
||||||
|
@Min(value = 0, message = "排序必须大于等于0")
|
||||||
|
private Integer sortOrder = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态:0-禁用 1-启用
|
||||||
|
*/
|
||||||
|
private Integer status = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接单部门允许的需求类型
|
||||||
|
*/
|
||||||
|
private List<String> allowedDemandTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用积分限制(仅需求部门)
|
||||||
|
*/
|
||||||
|
private Integer limitEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 限制周期:DAILY/WEEKLY/MONTHLY
|
||||||
|
*/
|
||||||
|
private String limitPeriod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 周期限额
|
||||||
|
*/
|
||||||
|
private Integer periodLimit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预警阈值(百分比)
|
||||||
|
*/
|
||||||
|
private Integer alertThreshold;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 超限动作:alert/block
|
||||||
|
*/
|
||||||
|
private String overLimitAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始积分余额(仅需求部门)
|
||||||
|
*/
|
||||||
|
@Min(value = 0, message = "初始积分不能为负数")
|
||||||
|
private Integer initialScoreBalance;
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package com.designhub.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Min;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门积分限制配置请求
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DepartmentLimitSettingsRequest {
|
||||||
|
|
||||||
|
@NotNull(message = "是否启用限制不能为空")
|
||||||
|
private Integer limitEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 限制周期:DAILY/WEEKLY/MONTHLY
|
||||||
|
*/
|
||||||
|
private String limitPeriod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 周期限额
|
||||||
|
*/
|
||||||
|
@Min(value = 0, message = "限制额度不能为负数")
|
||||||
|
private Integer periodLimit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预警阈值
|
||||||
|
*/
|
||||||
|
private Integer alertThreshold;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 超限动作
|
||||||
|
*/
|
||||||
|
private String overLimitAction;
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.designhub.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Min;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
public class DepartmentScoreAdjustRequest {
|
||||||
|
|
||||||
|
@NotBlank(message = "操作类型不能为空")
|
||||||
|
private String action;
|
||||||
|
@Min(value = 1, message = "调整积分必须大于0")
|
||||||
|
private Integer amount;
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
public String getAction() { return action; }
|
||||||
|
public void setAction(String action) { this.action = action; }
|
||||||
|
public Integer getAmount() { return amount; }
|
||||||
|
public void setAmount(Integer amount) { this.amount = amount; }
|
||||||
|
public String getRemark() { return remark; }
|
||||||
|
public void setRemark(String remark) { this.remark = remark; }
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
package com.designhub.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.Min;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DepartmentUpdateRequest {
|
||||||
|
|
||||||
|
private String deptName;
|
||||||
|
private String deptCode;
|
||||||
|
private String deptType;
|
||||||
|
private Long parentId;
|
||||||
|
private Long leaderId;
|
||||||
|
private String description;
|
||||||
|
@Min(value = 0, message = "排序必须大于等于0")
|
||||||
|
private Integer sortOrder;
|
||||||
|
private Integer status;
|
||||||
|
private List<String> allowedDemandTypes;
|
||||||
|
private Integer limitEnabled;
|
||||||
|
private String limitPeriod;
|
||||||
|
private Integer periodLimit;
|
||||||
|
private Integer alertThreshold;
|
||||||
|
private String overLimitAction;
|
||||||
|
|
||||||
|
public String getDeptName() { return deptName; }
|
||||||
|
public void setDeptName(String deptName) { this.deptName = deptName; }
|
||||||
|
public String getDeptCode() { return deptCode; }
|
||||||
|
public void setDeptCode(String deptCode) { this.deptCode = deptCode; }
|
||||||
|
public String getDeptType() { return deptType; }
|
||||||
|
public void setDeptType(String deptType) { this.deptType = deptType; }
|
||||||
|
public Long getParentId() { return parentId; }
|
||||||
|
public void setParentId(Long parentId) { this.parentId = parentId; }
|
||||||
|
public Long getLeaderId() { return leaderId; }
|
||||||
|
public void setLeaderId(Long leaderId) { this.leaderId = leaderId; }
|
||||||
|
public String getDescription() { return description; }
|
||||||
|
public void setDescription(String description) { this.description = description; }
|
||||||
|
public Integer getSortOrder() { return sortOrder; }
|
||||||
|
public void setSortOrder(Integer sortOrder) { this.sortOrder = sortOrder; }
|
||||||
|
public Integer getStatus() { return status; }
|
||||||
|
public void setStatus(Integer status) { this.status = status; }
|
||||||
|
public List<String> getAllowedDemandTypes() { return allowedDemandTypes; }
|
||||||
|
public void setAllowedDemandTypes(List<String> allowedDemandTypes) { this.allowedDemandTypes = allowedDemandTypes; }
|
||||||
|
public Integer getLimitEnabled() { return limitEnabled; }
|
||||||
|
public void setLimitEnabled(Integer limitEnabled) { this.limitEnabled = limitEnabled; }
|
||||||
|
public String getLimitPeriod() { return limitPeriod; }
|
||||||
|
public void setLimitPeriod(String limitPeriod) { this.limitPeriod = limitPeriod; }
|
||||||
|
public Integer getPeriodLimit() { return periodLimit; }
|
||||||
|
public void setPeriodLimit(Integer periodLimit) { this.periodLimit = periodLimit; }
|
||||||
|
public Integer getAlertThreshold() { return alertThreshold; }
|
||||||
|
public void setAlertThreshold(Integer alertThreshold) { this.alertThreshold = alertThreshold; }
|
||||||
|
public String getOverLimitAction() { return overLimitAction; }
|
||||||
|
public void setOverLimitAction(String overLimitAction) { this.overLimitAction = overLimitAction; }
|
||||||
|
}
|
||||||
|
|
||||||
34
backend/src/main/java/com/designhub/dto/LoginDTO.java
Normal file
34
backend/src/main/java/com/designhub/dto/LoginDTO.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package com.designhub.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录请求DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class LoginDTO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "用户名不能为空")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "密码不能为空")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录类型:password-密码登录 dingtalk-钉钉登录 casdoor-Casdoor登录
|
||||||
|
*/
|
||||||
|
private String loginType = "password";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 钉钉授权码(钉钉登录时使用)
|
||||||
|
*/
|
||||||
|
private String authCode;
|
||||||
|
}
|
||||||
|
|
||||||
78
backend/src/main/java/com/designhub/dto/PipelineTaskDTO.java
Normal file
78
backend/src/main/java/com/designhub/dto/PipelineTaskDTO.java
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package com.designhub.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流水线任务创建请求DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class PipelineTaskDTO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务标题
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否流水线任务
|
||||||
|
*/
|
||||||
|
private Boolean isPipeline;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流水线模式:sequential(顺序)/parallel(并行)
|
||||||
|
*/
|
||||||
|
private String pipelineMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 子任务列表
|
||||||
|
*/
|
||||||
|
private List<TaskItemDTO> items;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务其他字段(与普通任务相同)
|
||||||
|
*/
|
||||||
|
private Long brandId;
|
||||||
|
private Long categoryId;
|
||||||
|
private Long platformId;
|
||||||
|
private String spuInfo;
|
||||||
|
private String taskSubType;
|
||||||
|
private String attachmentUrl;
|
||||||
|
private String remark;
|
||||||
|
private Integer priority;
|
||||||
|
private LocalDateTime deadline; // 截止时间
|
||||||
|
private String releaseType; // 发布类型:default/recruitment/assigned
|
||||||
|
private Long receiverId; // 指定接收人ID(assigned模式使用)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 子任务项DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class TaskItemDTO {
|
||||||
|
/**
|
||||||
|
* 任务类型ID
|
||||||
|
*/
|
||||||
|
private Long taskTypeId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数量
|
||||||
|
*/
|
||||||
|
private Integer quantity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单价积分
|
||||||
|
*/
|
||||||
|
private Integer unitScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预估工时
|
||||||
|
*/
|
||||||
|
private Double estimatedHours;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序
|
||||||
|
*/
|
||||||
|
private Integer sortOrder;
|
||||||
|
}
|
||||||
|
}
|
||||||
52
backend/src/main/java/com/designhub/dto/RegisterDTO.java
Normal file
52
backend/src/main/java/com/designhub/dto/RegisterDTO.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package com.designhub.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册请求DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class RegisterDTO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "用户名不能为空")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "密码不能为空")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 真实姓名
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "真实姓名不能为空")
|
||||||
|
private String realName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色ID(5-设计师DESIGNER, 6-需求方REQUESTER)
|
||||||
|
*/
|
||||||
|
@NotNull(message = "请选择角色")
|
||||||
|
private Long roleId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门ID
|
||||||
|
*/
|
||||||
|
@NotNull(message = "请选择部门")
|
||||||
|
private Long deptId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱(可选)
|
||||||
|
*/
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手机号(可选)
|
||||||
|
*/
|
||||||
|
private String phone;
|
||||||
|
}
|
||||||
36
backend/src/main/java/com/designhub/dto/TaskApprovalDTO.java
Normal file
36
backend/src/main/java/com/designhub/dto/TaskApprovalDTO.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package com.designhub.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务审核DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class TaskApprovalDTO {
|
||||||
|
/**
|
||||||
|
* 审核意见
|
||||||
|
*/
|
||||||
|
private String comment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务更新数据(审核时可修改任务)
|
||||||
|
*/
|
||||||
|
private TaskUpdateDTO taskUpdates;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public String getComment() {
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setComment(String comment) {
|
||||||
|
this.comment = comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskUpdateDTO getTaskUpdates() {
|
||||||
|
return taskUpdates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaskUpdates(TaskUpdateDTO taskUpdates) {
|
||||||
|
this.taskUpdates = taskUpdates;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package com.designhub.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务指派请求DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class TaskAssignRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务ID
|
||||||
|
*/
|
||||||
|
@NotNull(message = "任务ID不能为空")
|
||||||
|
private Long taskId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 被指派人ID
|
||||||
|
*/
|
||||||
|
@NotNull(message = "被指派人ID不能为空")
|
||||||
|
private Long assigneeId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指派类型:force-强制指派 recommend-推荐
|
||||||
|
*/
|
||||||
|
private String assignType = "force";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指派说明
|
||||||
|
*/
|
||||||
|
private String comment;
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package com.designhub.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class TaskAuditRequest {
|
||||||
|
|
||||||
|
@NotNull(message = "任务ID不能为空")
|
||||||
|
private Long taskId;
|
||||||
|
@NotNull(message = "审核结果不能为空")
|
||||||
|
private String result;
|
||||||
|
private String comment;
|
||||||
|
|
||||||
|
public Long getTaskId() { return taskId; }
|
||||||
|
public void setTaskId(Long taskId) { this.taskId = taskId; }
|
||||||
|
public String getResult() { return result; }
|
||||||
|
public void setResult(String result) { this.result = result; }
|
||||||
|
public String getComment() { return comment; }
|
||||||
|
public void setComment(String comment) { this.comment = comment; }
|
||||||
|
}
|
||||||
116
backend/src/main/java/com/designhub/dto/TaskUpdateDTO.java
Normal file
116
backend/src/main/java/com/designhub/dto/TaskUpdateDTO.java
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package com.designhub.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务更新DTO(用于审核时修改任务)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class TaskUpdateDTO {
|
||||||
|
private String title;
|
||||||
|
private String taskSubType;
|
||||||
|
private Long brandId;
|
||||||
|
private Long platformId;
|
||||||
|
private Long categoryId;
|
||||||
|
private String spuInfo;
|
||||||
|
private String remark;
|
||||||
|
private String attachmentUrl;
|
||||||
|
private List<TaskTypeUpdateDTO> taskTypes;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class TaskTypeUpdateDTO {
|
||||||
|
private Long taskTypeId;
|
||||||
|
private Integer quantity;
|
||||||
|
|
||||||
|
public Long getTaskTypeId() {
|
||||||
|
return taskTypeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaskTypeId(Long taskTypeId) {
|
||||||
|
this.taskTypeId = taskTypeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getQuantity() {
|
||||||
|
return quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQuantity(Integer quantity) {
|
||||||
|
this.quantity = quantity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTaskSubType() {
|
||||||
|
return taskSubType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaskSubType(String taskSubType) {
|
||||||
|
this.taskSubType = taskSubType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getBrandId() {
|
||||||
|
return brandId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBrandId(Long brandId) {
|
||||||
|
this.brandId = brandId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getPlatformId() {
|
||||||
|
return platformId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlatformId(Long platformId) {
|
||||||
|
this.platformId = platformId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getCategoryId() {
|
||||||
|
return categoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCategoryId(Long categoryId) {
|
||||||
|
this.categoryId = categoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSpuInfo() {
|
||||||
|
return spuInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpuInfo(String spuInfo) {
|
||||||
|
this.spuInfo = spuInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRemark() {
|
||||||
|
return remark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRemark(String remark) {
|
||||||
|
this.remark = remark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAttachmentUrl() {
|
||||||
|
return attachmentUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttachmentUrl(String attachmentUrl) {
|
||||||
|
this.attachmentUrl = attachmentUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TaskTypeUpdateDTO> getTaskTypes() {
|
||||||
|
return taskTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaskTypes(List<TaskTypeUpdateDTO> taskTypes) {
|
||||||
|
this.taskTypes = taskTypes;
|
||||||
|
}
|
||||||
|
}
|
||||||
95
backend/src/main/java/com/designhub/entity/Achievement.java
Normal file
95
backend/src/main/java/com/designhub/entity/Achievement.java
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成就徽章实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("achievement")
|
||||||
|
public class Achievement implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成就代码(唯一标识)
|
||||||
|
*/
|
||||||
|
private String achievementCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成就名称
|
||||||
|
*/
|
||||||
|
private String achievementName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成就描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成就图标URL
|
||||||
|
*/
|
||||||
|
private String iconUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成就类型:task-任务类, score-积分类, quality-质量类, speed-速度类
|
||||||
|
*/
|
||||||
|
private String achievementType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 达成条件(JSON格式)
|
||||||
|
* 例如:{"type":"task_count","value":100}
|
||||||
|
*/
|
||||||
|
@TableField("`condition`")
|
||||||
|
private String condition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 奖励积分
|
||||||
|
*/
|
||||||
|
private Integer rewardScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成就等级:bronze-铜牌, silver-银牌, gold-金牌, diamond-钻石
|
||||||
|
*/
|
||||||
|
private String level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序号
|
||||||
|
*/
|
||||||
|
private Integer sortOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态:0-禁用,1-启用
|
||||||
|
*/
|
||||||
|
@TableField("enabled")
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除标志:0-未删除,1-已删除
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
}
|
||||||
|
|
||||||
69
backend/src/main/java/com/designhub/entity/Brand.java
Normal file
69
backend/src/main/java/com/designhub/entity/Brand.java
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 品牌实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("brand")
|
||||||
|
public class Brand implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 品牌名称
|
||||||
|
*/
|
||||||
|
@TableField("brand_name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 品牌编码
|
||||||
|
*/
|
||||||
|
@TableField("brand_code")
|
||||||
|
private String brandCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 品牌Logo URL
|
||||||
|
*/
|
||||||
|
@TableField("logo")
|
||||||
|
private String logoUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 品牌描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序号
|
||||||
|
*/
|
||||||
|
private Integer sortOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用:0-禁用,1-启用
|
||||||
|
*/
|
||||||
|
@TableField("status")
|
||||||
|
private Integer isEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
84
backend/src/main/java/com/designhub/entity/Category.java
Normal file
84
backend/src/main/java/com/designhub/entity/Category.java
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类目实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("category")
|
||||||
|
public class Category implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 父类目ID,0表示一级类目
|
||||||
|
*/
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类目名称
|
||||||
|
*/
|
||||||
|
@TableField("category_name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类目编码
|
||||||
|
*/
|
||||||
|
@TableField("category_code")
|
||||||
|
private String categoryCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类目层级:1-一级,2-二级,3-三级
|
||||||
|
*/
|
||||||
|
private Integer level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否叶子类目:0-否,1-是
|
||||||
|
*/
|
||||||
|
private Integer isLeaf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类目路径,用/分隔,如:/1/12/123(数据库不存在此字段)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类目描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序号
|
||||||
|
*/
|
||||||
|
private Integer sortOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用:0-禁用,1-启用
|
||||||
|
*/
|
||||||
|
@TableField("status")
|
||||||
|
private Integer isEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
68
backend/src/main/java/com/designhub/entity/CheckIn.java
Normal file
68
backend/src/main/java/com/designhub/entity/CheckIn.java
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签到记录实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("user_check_in")
|
||||||
|
public class CheckIn implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签到日期
|
||||||
|
*/
|
||||||
|
private LocalDate checkInDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连续签到天数
|
||||||
|
*/
|
||||||
|
private Integer continuousDays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 奖励积分
|
||||||
|
*/
|
||||||
|
private Integer rewardScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签到备注
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除标志:0-未删除,1-已删除
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
}
|
||||||
|
|
||||||
110
backend/src/main/java/com/designhub/entity/Contest.java
Normal file
110
backend/src/main/java/com/designhub/entity/Contest.java
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 竞赛活动实体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("contest")
|
||||||
|
public class Contest implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 竞赛名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 竞赛描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 竞赛类型:monthly-月度竞赛, weekly-周度竞赛, special-专项竞赛
|
||||||
|
*/
|
||||||
|
private String contestType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime startTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime endTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参与范围:all-全体设计师, department-指定部门, user-指定人员
|
||||||
|
*/
|
||||||
|
private String participantScope;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定部门ID列表(JSON数组)
|
||||||
|
*/
|
||||||
|
private String departmentIds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定用户ID列表(JSON数组)
|
||||||
|
*/
|
||||||
|
private String userIds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 竞赛规则配置(JSON对象)
|
||||||
|
* 例如:{"taskCompleted":{"score":2,"weight":30},"fiveStarReview":{"score":10,"weight":25}}
|
||||||
|
*/
|
||||||
|
private String rules;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 奖励配置(JSON数组)
|
||||||
|
* 例如:[{"rank":1,"score":1500,"badge":"月度冠军"},{"rank":2,"score":1000}]
|
||||||
|
*/
|
||||||
|
private String rewards;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参与奖配置(JSON对象)
|
||||||
|
*/
|
||||||
|
private String participationReward;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 竞赛状态:draft-草稿, scheduled-待开始, active-进行中, ended-已结束, cancelled-已取消
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参与人数
|
||||||
|
*/
|
||||||
|
private Integer participantCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人ID
|
||||||
|
*/
|
||||||
|
private Long creatorId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否删除:0-否,1-是
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
@ -0,0 +1,107 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 竞赛参与者实体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("contest_participant")
|
||||||
|
public class ContestParticipant implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 竞赛ID
|
||||||
|
*/
|
||||||
|
private Long contestId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户姓名(冗余)
|
||||||
|
*/
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门ID
|
||||||
|
*/
|
||||||
|
private Long deptId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门名称(冗余)
|
||||||
|
*/
|
||||||
|
private String deptName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成任务数
|
||||||
|
*/
|
||||||
|
private Integer taskCompleted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 五星好评数
|
||||||
|
*/
|
||||||
|
private Integer fiveStarCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提前完成次数
|
||||||
|
*/
|
||||||
|
private Integer earlyCompletionCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 被表扬次数
|
||||||
|
*/
|
||||||
|
private Integer praiseCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无延误天数
|
||||||
|
*/
|
||||||
|
private Integer noDelayDays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总得分
|
||||||
|
*/
|
||||||
|
private Integer totalScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前排名
|
||||||
|
*/
|
||||||
|
private Integer currentRank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否已领取奖励:0-否,1-是
|
||||||
|
*/
|
||||||
|
private Integer rewardClaimed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 奖励积分
|
||||||
|
*/
|
||||||
|
private Integer rewardScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 奖励领取时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime rewardClaimTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
@ -0,0 +1,106 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 延误扣分记录实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("delay_penalty_record")
|
||||||
|
public class DelayPenaltyRecord implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务ID
|
||||||
|
*/
|
||||||
|
private Long taskId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设计师ID
|
||||||
|
*/
|
||||||
|
private Long designerId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 延误天数
|
||||||
|
*/
|
||||||
|
private Integer delayDays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 延误小时数
|
||||||
|
*/
|
||||||
|
private Long delayHours;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 延误等级:light-轻微 general-一般 severe-严重 major-重大
|
||||||
|
*/
|
||||||
|
private String delayLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扣除积分
|
||||||
|
*/
|
||||||
|
private Integer penaltyScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原截止时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime originalDeadline;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实际完成/转移时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime actualTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态:confirmed-已确认 appealed-已申诉 approved-申诉通过 rejected-申诉驳回
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申诉原因
|
||||||
|
*/
|
||||||
|
private String appealReason;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申诉时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime appealTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申诉结果
|
||||||
|
*/
|
||||||
|
private String appealResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审批人ID
|
||||||
|
*/
|
||||||
|
private Long approverId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审批时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime approveTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 延误扣分规则实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("delay_penalty_rule")
|
||||||
|
public class DelayPenaltyRule implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 等级名称:light-轻微 general-一般 severe-严重 major-重大
|
||||||
|
*/
|
||||||
|
private String levelName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最小延误天数(实际使用小时数)
|
||||||
|
*/
|
||||||
|
private Integer minDays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最小延误小时数
|
||||||
|
*/
|
||||||
|
private Integer minHours;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大延误天数(实际使用小时数,NULL表示无上限)
|
||||||
|
*/
|
||||||
|
private Integer maxDays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大延误小时数(NULL表示无上限)
|
||||||
|
*/
|
||||||
|
private Integer maxHours;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础扣分
|
||||||
|
*/
|
||||||
|
private Integer baseScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每天递增扣分
|
||||||
|
*/
|
||||||
|
private Integer perDayScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每小时扣分(用于按小时计算的等级)
|
||||||
|
*/
|
||||||
|
private Integer perHourScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规则描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用:0-否 1-是
|
||||||
|
*/
|
||||||
|
private Integer enabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
75
backend/src/main/java/com/designhub/entity/DemandType.java
Normal file
75
backend/src/main/java/com/designhub/entity/DemandType.java
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 需求类型实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("task_type")
|
||||||
|
public class DemandType implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型名称
|
||||||
|
*/
|
||||||
|
@TableField("type_name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单价(积分)
|
||||||
|
*/
|
||||||
|
@TableField("unit_score")
|
||||||
|
private Integer unitScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预估工时(小时)
|
||||||
|
*/
|
||||||
|
@TableField("estimated_hours")
|
||||||
|
private Integer estimatedHours;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序号
|
||||||
|
*/
|
||||||
|
private Integer sortOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用:0-禁用,1-启用
|
||||||
|
*/
|
||||||
|
@TableField("status")
|
||||||
|
private Integer isEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可接单部门ID列表(逗号分隔),为空表示所有部门都可接单
|
||||||
|
*/
|
||||||
|
@TableField("allowed_dept_ids")
|
||||||
|
private String allowedDeptIds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 需求类型-接单部门关联表实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("demand_type_department")
|
||||||
|
public class DemandTypeDepartment implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 需求类型ID
|
||||||
|
*/
|
||||||
|
private Long demandTypeId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接单部门ID
|
||||||
|
*/
|
||||||
|
private Long departmentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
}
|
||||||
235
backend/src/main/java/com/designhub/entity/Department.java
Normal file
235
backend/src/main/java/com/designhub/entity/Department.java
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门实体类
|
||||||
|
* 注意:已移除@Data注解,改用手动getter/setter以解决Lombok编译问题
|
||||||
|
*/
|
||||||
|
@TableName("sys_dept")
|
||||||
|
public class Department implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 父部门ID,0表示顶级部门
|
||||||
|
*/
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门名称
|
||||||
|
*/
|
||||||
|
private String deptName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门编码
|
||||||
|
*/
|
||||||
|
private String deptCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 钉钉部门ID
|
||||||
|
*/
|
||||||
|
private String dingDeptId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门类型:demand-需求部门,receiving-接单部门
|
||||||
|
*/
|
||||||
|
private String deptType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门负责人ID
|
||||||
|
*/
|
||||||
|
@TableField("leader_id")
|
||||||
|
private Long leaderId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门积分余额(冗余字段,用于快速展示)
|
||||||
|
*/
|
||||||
|
@TableField("score_balance")
|
||||||
|
private Integer scoreBalance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用积分消耗限制:0-否,1-是
|
||||||
|
*/
|
||||||
|
@TableField("limit_enabled")
|
||||||
|
private Integer limitEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 限制周期:DAILY/WEEKLY/MONTHLY
|
||||||
|
*/
|
||||||
|
@TableField("limit_period")
|
||||||
|
private String limitPeriod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 周期限额
|
||||||
|
*/
|
||||||
|
@TableField("period_limit")
|
||||||
|
private Integer periodLimit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预警阈值(百分比)
|
||||||
|
*/
|
||||||
|
@TableField("alert_threshold")
|
||||||
|
private Integer alertThreshold;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 超限后的动作:alert-仅提醒,block-禁止发起
|
||||||
|
*/
|
||||||
|
@TableField("over_limit_action")
|
||||||
|
private String overLimitAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 允许的需求类型(接单部门使用,逗号分隔)
|
||||||
|
*/
|
||||||
|
@TableField("allowed_demand_types")
|
||||||
|
private String allowedDemandTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门主管ID(接单部门)- 数据库不存在此字段
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Long supervisorId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门描述 - 数据库不存在此字段
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门层级 - 数据库不存在此字段
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Integer level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门路径 - 数据库不存在此字段
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序号
|
||||||
|
*/
|
||||||
|
private Integer sortOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态:0-禁用,1-启用
|
||||||
|
*/
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人ID - 数据库不存在此字段
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Long createBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新人ID - 数据库不存在此字段
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Long updateBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除标志:0-未删除,1-已删除
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
|
||||||
|
// Getters and Setters (手动添加以解决Lombok编译问题)
|
||||||
|
public Long getId() { return id; }
|
||||||
|
public void setId(Long id) { this.id = id; }
|
||||||
|
|
||||||
|
public Long getParentId() { return parentId; }
|
||||||
|
public void setParentId(Long parentId) { this.parentId = parentId; }
|
||||||
|
|
||||||
|
public String getDeptName() { return deptName; }
|
||||||
|
public void setDeptName(String deptName) { this.deptName = deptName; }
|
||||||
|
|
||||||
|
public String getDeptCode() { return deptCode; }
|
||||||
|
public void setDeptCode(String deptCode) { this.deptCode = deptCode; }
|
||||||
|
|
||||||
|
public String getDingDeptId() { return dingDeptId; }
|
||||||
|
public void setDingDeptId(String dingDeptId) { this.dingDeptId = dingDeptId; }
|
||||||
|
|
||||||
|
public String getDeptType() { return deptType; }
|
||||||
|
public void setDeptType(String deptType) { this.deptType = deptType; }
|
||||||
|
|
||||||
|
public Long getLeaderId() { return leaderId; }
|
||||||
|
public void setLeaderId(Long leaderId) { this.leaderId = leaderId; }
|
||||||
|
|
||||||
|
public Integer getScoreBalance() { return scoreBalance; }
|
||||||
|
public void setScoreBalance(Integer scoreBalance) { this.scoreBalance = scoreBalance; }
|
||||||
|
|
||||||
|
public Integer getLimitEnabled() { return limitEnabled; }
|
||||||
|
public void setLimitEnabled(Integer limitEnabled) { this.limitEnabled = limitEnabled; }
|
||||||
|
|
||||||
|
public String getLimitPeriod() { return limitPeriod; }
|
||||||
|
public void setLimitPeriod(String limitPeriod) { this.limitPeriod = limitPeriod; }
|
||||||
|
|
||||||
|
public Integer getPeriodLimit() { return periodLimit; }
|
||||||
|
public void setPeriodLimit(Integer periodLimit) { this.periodLimit = periodLimit; }
|
||||||
|
|
||||||
|
public Integer getAlertThreshold() { return alertThreshold; }
|
||||||
|
public void setAlertThreshold(Integer alertThreshold) { this.alertThreshold = alertThreshold; }
|
||||||
|
|
||||||
|
public String getOverLimitAction() { return overLimitAction; }
|
||||||
|
public void setOverLimitAction(String overLimitAction) { this.overLimitAction = overLimitAction; }
|
||||||
|
|
||||||
|
public String getAllowedDemandTypes() { return allowedDemandTypes; }
|
||||||
|
public void setAllowedDemandTypes(String allowedDemandTypes) { this.allowedDemandTypes = allowedDemandTypes; }
|
||||||
|
|
||||||
|
public Long getSupervisorId() { return supervisorId; }
|
||||||
|
public void setSupervisorId(Long supervisorId) { this.supervisorId = supervisorId; }
|
||||||
|
|
||||||
|
public String getDescription() { return description; }
|
||||||
|
public void setDescription(String description) { this.description = description; }
|
||||||
|
|
||||||
|
public Integer getLevel() { return level; }
|
||||||
|
public void setLevel(Integer level) { this.level = level; }
|
||||||
|
|
||||||
|
public String getPath() { return path; }
|
||||||
|
public void setPath(String path) { this.path = path; }
|
||||||
|
|
||||||
|
public Integer getSortOrder() { return sortOrder; }
|
||||||
|
public void setSortOrder(Integer sortOrder) { this.sortOrder = sortOrder; }
|
||||||
|
|
||||||
|
public Integer getStatus() { return status; }
|
||||||
|
public void setStatus(Integer status) { this.status = status; }
|
||||||
|
|
||||||
|
public Long getCreateBy() { return createBy; }
|
||||||
|
public void setCreateBy(Long createBy) { this.createBy = createBy; }
|
||||||
|
|
||||||
|
public LocalDateTime getCreateTime() { return createTime; }
|
||||||
|
public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
|
||||||
|
|
||||||
|
public Long getUpdateBy() { return updateBy; }
|
||||||
|
public void setUpdateBy(Long updateBy) { this.updateBy = updateBy; }
|
||||||
|
|
||||||
|
public LocalDateTime getUpdateTime() { return updateTime; }
|
||||||
|
public void setUpdateTime(LocalDateTime updateTime) { this.updateTime = updateTime; }
|
||||||
|
|
||||||
|
public Integer getDeleted() { return deleted; }
|
||||||
|
public void setDeleted(Integer deleted) { this.deleted = deleted; }
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 钉钉配置实体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("dingtalk_config")
|
||||||
|
public class DingTalkConfig implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用AgentId
|
||||||
|
*/
|
||||||
|
private String agentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用AppKey
|
||||||
|
*/
|
||||||
|
private String appKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用AppSecret (加密存储)
|
||||||
|
*/
|
||||||
|
private String appSecret;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 企业ID (CorpId)
|
||||||
|
*/
|
||||||
|
private String corpId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器人Webhook地址
|
||||||
|
*/
|
||||||
|
private String robotWebhook;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器人密钥
|
||||||
|
*/
|
||||||
|
private String robotSecret;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用:0-禁用,1-启用
|
||||||
|
*/
|
||||||
|
private Integer enabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用功能配置(JSON)
|
||||||
|
* 例如:{"notification":true,"login":false,"attendance":false}
|
||||||
|
*/
|
||||||
|
private String enabledFeatures;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access Token(缓存)
|
||||||
|
*/
|
||||||
|
private String accessToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access Token过期时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime tokenExpireTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
200
backend/src/main/java/com/designhub/entity/FileUpload.java
Normal file
200
backend/src/main/java/com/designhub/entity/FileUpload.java
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传记录实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("sys_file_upload")
|
||||||
|
public class FileUpload {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原始文件名
|
||||||
|
*/
|
||||||
|
private String originalName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储文件名
|
||||||
|
*/
|
||||||
|
private String storedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件路径
|
||||||
|
*/
|
||||||
|
private String filePath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件访问URL
|
||||||
|
*/
|
||||||
|
private String fileUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件大小(字节)
|
||||||
|
*/
|
||||||
|
private Long fileSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件类型
|
||||||
|
*/
|
||||||
|
private String fileType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传者ID
|
||||||
|
*/
|
||||||
|
private Long uploaderId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传者姓名
|
||||||
|
*/
|
||||||
|
private String uploaderName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联类型:task-任务, user-用户等
|
||||||
|
*/
|
||||||
|
private String relatedType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联ID
|
||||||
|
*/
|
||||||
|
private Long relatedId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除标志:0-未删除,1-已删除
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOriginalName() {
|
||||||
|
return originalName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOriginalName(String originalName) {
|
||||||
|
this.originalName = originalName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStoredName() {
|
||||||
|
return storedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStoredName(String storedName) {
|
||||||
|
this.storedName = storedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilePath() {
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFilePath(String filePath) {
|
||||||
|
this.filePath = filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileUrl() {
|
||||||
|
return fileUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFileUrl(String fileUrl) {
|
||||||
|
this.fileUrl = fileUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getFileSize() {
|
||||||
|
return fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFileSize(Long fileSize) {
|
||||||
|
this.fileSize = fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileType() {
|
||||||
|
return fileType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFileType(String fileType) {
|
||||||
|
this.fileType = fileType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getUploaderId() {
|
||||||
|
return uploaderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUploaderId(Long uploaderId) {
|
||||||
|
this.uploaderId = uploaderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUploaderName() {
|
||||||
|
return uploaderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUploaderName(String uploaderName) {
|
||||||
|
this.uploaderName = uploaderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRelatedType() {
|
||||||
|
return relatedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRelatedType(String relatedType) {
|
||||||
|
this.relatedType = relatedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getRelatedId() {
|
||||||
|
return relatedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRelatedId(Long relatedId) {
|
||||||
|
this.relatedId = relatedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreateTime() {
|
||||||
|
return createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreateTime(LocalDateTime createTime) {
|
||||||
|
this.createTime = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getUpdateTime() {
|
||||||
|
return updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpdateTime(LocalDateTime updateTime) {
|
||||||
|
this.updateTime = updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getDeleted() {
|
||||||
|
return deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeleted(Integer deleted) {
|
||||||
|
this.deleted = deleted;
|
||||||
|
}
|
||||||
|
}
|
||||||
268
backend/src/main/java/com/designhub/entity/Notification.java
Normal file
268
backend/src/main/java/com/designhub/entity/Notification.java
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知消息实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("sys_notification")
|
||||||
|
public class Notification implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知类型:system-系统通知, task-任务通知, score-积分通知, review-评审通知
|
||||||
|
*/
|
||||||
|
@TableField("type")
|
||||||
|
private String notificationType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收用户ID
|
||||||
|
*/
|
||||||
|
@TableField("receiver_id")
|
||||||
|
private Long receiverId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送用户ID(可为空,系统通知为空)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Long senderId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知标题
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联业务ID(任务ID、积分记录ID等)
|
||||||
|
*/
|
||||||
|
@TableField("related_task_id")
|
||||||
|
private Long relatedId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联业务类型:task, score, review, system
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String relatedType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跳转链接
|
||||||
|
*/
|
||||||
|
@TableField("link")
|
||||||
|
private String linkUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否已读:0-未读,1-已读
|
||||||
|
*/
|
||||||
|
@TableField("is_read")
|
||||||
|
private Integer isRead;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 阅读时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime readTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否推送到钉钉:0-否,1-是
|
||||||
|
*/
|
||||||
|
@TableField("ding_enabled")
|
||||||
|
private Integer pushDingtalk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 钉钉推送状态:pending-待推送, success-成功, failed-失败
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String dingtalkStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 钉钉推送时间
|
||||||
|
*/
|
||||||
|
@TableField("ding_push_time")
|
||||||
|
private LocalDateTime dingtalkPushTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优先级:low-低, normal-普通, high-高, urgent-紧急
|
||||||
|
*/
|
||||||
|
@TableField("ding_priority")
|
||||||
|
private Integer priority;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除标志:0-未删除,1-已删除
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
|
||||||
|
// Getters and Setters (手动添加以解决Lombok编译问题)
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNotificationType() {
|
||||||
|
return notificationType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNotificationType(String notificationType) {
|
||||||
|
this.notificationType = notificationType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getReceiverId() {
|
||||||
|
return receiverId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReceiverId(Long receiverId) {
|
||||||
|
this.receiverId = receiverId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getSenderId() {
|
||||||
|
return senderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSenderId(Long senderId) {
|
||||||
|
this.senderId = senderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getRelatedId() {
|
||||||
|
return relatedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRelatedId(Long relatedId) {
|
||||||
|
this.relatedId = relatedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRelatedType() {
|
||||||
|
return relatedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRelatedType(String relatedType) {
|
||||||
|
this.relatedType = relatedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLinkUrl() {
|
||||||
|
return linkUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLinkUrl(String linkUrl) {
|
||||||
|
this.linkUrl = linkUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getIsRead() {
|
||||||
|
return isRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsRead(Integer isRead) {
|
||||||
|
this.isRead = isRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getReadTime() {
|
||||||
|
return readTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReadTime(LocalDateTime readTime) {
|
||||||
|
this.readTime = readTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPushDingtalk() {
|
||||||
|
return pushDingtalk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPushDingtalk(Integer pushDingtalk) {
|
||||||
|
this.pushDingtalk = pushDingtalk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDingtalkStatus() {
|
||||||
|
return dingtalkStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDingtalkStatus(String dingtalkStatus) {
|
||||||
|
this.dingtalkStatus = dingtalkStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getDingtalkPushTime() {
|
||||||
|
return dingtalkPushTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDingtalkPushTime(LocalDateTime dingtalkPushTime) {
|
||||||
|
this.dingtalkPushTime = dingtalkPushTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPriority() {
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPriority(Integer priority) {
|
||||||
|
this.priority = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreateTime() {
|
||||||
|
return createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreateTime(LocalDateTime createTime) {
|
||||||
|
this.createTime = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getUpdateTime() {
|
||||||
|
return updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpdateTime(LocalDateTime updateTime) {
|
||||||
|
this.updateTime = updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getDeleted() {
|
||||||
|
return deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeleted(Integer deleted) {
|
||||||
|
this.deleted = deleted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
96
backend/src/main/java/com/designhub/entity/Permission.java
Normal file
96
backend/src/main/java/com/designhub/entity/Permission.java
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("sys_permission")
|
||||||
|
public class Permission implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 父权限ID,0表示顶级权限
|
||||||
|
*/
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限编码
|
||||||
|
*/
|
||||||
|
private String permissionCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限名称
|
||||||
|
*/
|
||||||
|
private String permissionName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限类型:menu-菜单,button-按钮,api-接口
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限路径/接口路径
|
||||||
|
*/
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求方法:GET/POST/PUT/DELETE等
|
||||||
|
*/
|
||||||
|
private String method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图标
|
||||||
|
*/
|
||||||
|
private String icon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组件路径
|
||||||
|
*/
|
||||||
|
private String component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序号
|
||||||
|
*/
|
||||||
|
private Integer sortOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态:0-禁用,1-启用
|
||||||
|
*/
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除标志:0-未删除,1-已删除
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
}
|
||||||
|
|
||||||
69
backend/src/main/java/com/designhub/entity/Platform.java
Normal file
69
backend/src/main/java/com/designhub/entity/Platform.java
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平台实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("platform")
|
||||||
|
public class Platform implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平台名称
|
||||||
|
*/
|
||||||
|
@TableField("platform_name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平台编码
|
||||||
|
*/
|
||||||
|
@TableField("platform_code")
|
||||||
|
private String platformCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平台图标 URL
|
||||||
|
*/
|
||||||
|
@TableField("icon")
|
||||||
|
private String iconUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平台描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序号
|
||||||
|
*/
|
||||||
|
private Integer sortOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用:0-禁用,1-启用
|
||||||
|
*/
|
||||||
|
@TableField("status")
|
||||||
|
private Integer isEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
109
backend/src/main/java/com/designhub/entity/RewardRule.java
Normal file
109
backend/src/main/java/com/designhub/entity/RewardRule.java
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动奖励规则实体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("reward_rule")
|
||||||
|
public class RewardRule implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规则名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规则描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规则类型:daily-每日奖励, weekly-每周奖励, monthly-每月奖励, task-任务奖励, rating-好评奖励
|
||||||
|
*/
|
||||||
|
private String ruleType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 触发条件(JSON对象)
|
||||||
|
* 例如:{"type":"task_count","operator":">=","value":10}
|
||||||
|
*/
|
||||||
|
private String triggerCondition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 奖励类型:score-积分, badge-徽章, both-两者
|
||||||
|
*/
|
||||||
|
private String rewardType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 奖励积分数
|
||||||
|
*/
|
||||||
|
private Integer rewardScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 奖励徽章(JSON对象)
|
||||||
|
*/
|
||||||
|
private String rewardBadge;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 适用范围:all-全员, designer-设计师, department-指定部门
|
||||||
|
*/
|
||||||
|
private String scope;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定部门ID列表(JSON数组)
|
||||||
|
*/
|
||||||
|
private String scopeDeptIds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用:0-禁用,1-启用
|
||||||
|
*/
|
||||||
|
private Integer enabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行周期(cron 表达式,仅周期性奖励使用)
|
||||||
|
*/
|
||||||
|
private String cronExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上次执行时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime lastExecuteTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优先级(数值越大优先级越高)
|
||||||
|
*/
|
||||||
|
private Integer priority;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否删除:0-否,1-是
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人ID
|
||||||
|
*/
|
||||||
|
private Long creatorId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
103
backend/src/main/java/com/designhub/entity/Role.java
Normal file
103
backend/src/main/java/com/designhub/entity/Role.java
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("sys_role")
|
||||||
|
public class Role implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色编码
|
||||||
|
*/
|
||||||
|
private String roleCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色名称
|
||||||
|
*/
|
||||||
|
private String roleName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色级别:1-最高权限,数字越大权限越低
|
||||||
|
* 当前数据库表中无对应字段,标记为非持久化,避免 SQL 报错
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Integer level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据权限范围:all-全部数据,dept-本部门,self-本人
|
||||||
|
* 数据库当前未存储该字段
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String dataScope;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序号
|
||||||
|
* 数据库当前未存储该字段
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Integer sortOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态:0-禁用,1-启用
|
||||||
|
*/
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否内置角色:0-否,1-是(内置角色不可删除)
|
||||||
|
* 数据库当前未存储该字段
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Integer builtIn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人ID
|
||||||
|
* 数据库当前未存储该字段
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Long createBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新人ID
|
||||||
|
* 数据库当前未存储该字段
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Long updateBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除标志:0-未删除,1-已删除
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色权限关联实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("sys_role_permission")
|
||||||
|
public class RolePermission implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色ID
|
||||||
|
*/
|
||||||
|
private Long roleId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限ID
|
||||||
|
*/
|
||||||
|
private Long permissionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
}
|
||||||
|
|
||||||
187
backend/src/main/java/com/designhub/entity/S3Config.java
Normal file
187
backend/src/main/java/com/designhub/entity/S3Config.java
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* S3存储配置实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("sys_s3_config")
|
||||||
|
public class S3Config {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* S3服务端点
|
||||||
|
*/
|
||||||
|
private String endpoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储桶名称
|
||||||
|
*/
|
||||||
|
private String bucket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问密钥ID
|
||||||
|
*/
|
||||||
|
private String accessKeyId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问密钥
|
||||||
|
*/
|
||||||
|
private String secretAccessKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 区域
|
||||||
|
*/
|
||||||
|
private String region;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公共访问域名
|
||||||
|
*/
|
||||||
|
private String publicDomain;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用路径样式访问
|
||||||
|
*/
|
||||||
|
private Boolean enablePathStyle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件存储路径前缀
|
||||||
|
*/
|
||||||
|
private String filePath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否激活:0-未激活,1-激活
|
||||||
|
*/
|
||||||
|
private Boolean isActive;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除标志:0-未删除,1-已删除
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEndpoint() {
|
||||||
|
return endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEndpoint(String endpoint) {
|
||||||
|
this.endpoint = endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBucket() {
|
||||||
|
return bucket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBucket(String bucket) {
|
||||||
|
this.bucket = bucket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessKeyId() {
|
||||||
|
return accessKeyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccessKeyId(String accessKeyId) {
|
||||||
|
this.accessKeyId = accessKeyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSecretAccessKey() {
|
||||||
|
return secretAccessKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecretAccessKey(String secretAccessKey) {
|
||||||
|
this.secretAccessKey = secretAccessKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRegion() {
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegion(String region) {
|
||||||
|
this.region = region;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPublicDomain() {
|
||||||
|
return publicDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPublicDomain(String publicDomain) {
|
||||||
|
this.publicDomain = publicDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getEnablePathStyle() {
|
||||||
|
return enablePathStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnablePathStyle(Boolean enablePathStyle) {
|
||||||
|
this.enablePathStyle = enablePathStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilePath() {
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFilePath(String filePath) {
|
||||||
|
this.filePath = filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getIsActive() {
|
||||||
|
return isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsActive(Boolean isActive) {
|
||||||
|
this.isActive = isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreateTime() {
|
||||||
|
return createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreateTime(LocalDateTime createTime) {
|
||||||
|
this.createTime = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getUpdateTime() {
|
||||||
|
return updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpdateTime(LocalDateTime updateTime) {
|
||||||
|
this.updateTime = updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getDeleted() {
|
||||||
|
return deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeleted(Integer deleted) {
|
||||||
|
this.deleted = deleted;
|
||||||
|
}
|
||||||
|
}
|
||||||
148
backend/src/main/java/com/designhub/entity/ScorePool.java
Normal file
148
backend/src/main/java/com/designhub/entity/ScorePool.java
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 激励积分池实体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("score_pool")
|
||||||
|
public class ScorePool implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 积分池名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 积分池类型:monthly-月度池, quarterly-季度池, yearly-年度池, special-专项池
|
||||||
|
*/
|
||||||
|
private String poolType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 周期年份
|
||||||
|
*/
|
||||||
|
private Integer year;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 周期月份(月度池使用)
|
||||||
|
*/
|
||||||
|
private Integer month;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 周期季度(季度池使用,1-4)
|
||||||
|
*/
|
||||||
|
private Integer quarter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总预算积分
|
||||||
|
*/
|
||||||
|
private Integer totalBudget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已使用积分
|
||||||
|
*/
|
||||||
|
private Integer usedScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 剩余积分
|
||||||
|
*/
|
||||||
|
private Integer remainingScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务完成奖励预算
|
||||||
|
*/
|
||||||
|
private Integer taskRewardBudget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务完成奖励已使用
|
||||||
|
*/
|
||||||
|
private Integer taskRewardUsed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 好评奖励预算
|
||||||
|
*/
|
||||||
|
private Integer ratingRewardBudget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 好评奖励已使用
|
||||||
|
*/
|
||||||
|
private Integer ratingRewardUsed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 竞赛奖励预算
|
||||||
|
*/
|
||||||
|
private Integer contestRewardBudget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 竞赛奖励已使用
|
||||||
|
*/
|
||||||
|
private Integer contestRewardUsed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签到奖励预算
|
||||||
|
*/
|
||||||
|
private Integer checkinRewardBudget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签到奖励已使用
|
||||||
|
*/
|
||||||
|
private Integer checkinRewardUsed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 其他奖励预算
|
||||||
|
*/
|
||||||
|
private Integer otherRewardBudget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 其他奖励已使用
|
||||||
|
*/
|
||||||
|
private Integer otherRewardUsed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 超支处理策略:stop-停止发放, alert-提醒继续, auto_increase-自动增加预算
|
||||||
|
*/
|
||||||
|
private String overflowStrategy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 超支提醒阈值(百分比,如80表示使用80%时提醒)
|
||||||
|
*/
|
||||||
|
private Integer alertThreshold;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态:active-进行中, closed-已关闭, settled-已结算
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人ID
|
||||||
|
*/
|
||||||
|
private Long creatorId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否删除:0-否,1-是
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 积分池使用记录实体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("score_pool_record")
|
||||||
|
public class ScorePoolRecord implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 积分池ID
|
||||||
|
*/
|
||||||
|
private Long poolId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户姓名
|
||||||
|
*/
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 奖励类型:task-任务, rating-好评, contest-竞赛, checkin-签到, goal-目标, other-其他
|
||||||
|
*/
|
||||||
|
private String rewardType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 积分变动(正数为发放,负数为扣除)
|
||||||
|
*/
|
||||||
|
private Integer scoreChange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 变动前池剩余
|
||||||
|
*/
|
||||||
|
private Integer beforeRemaining;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 变动后池剩余
|
||||||
|
*/
|
||||||
|
private Integer afterRemaining;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联业务类型
|
||||||
|
*/
|
||||||
|
private String bizType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联业务ID
|
||||||
|
*/
|
||||||
|
private Long bizId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
}
|
||||||
81
backend/src/main/java/com/designhub/entity/SystemConfig.java
Normal file
81
backend/src/main/java/com/designhub/entity/SystemConfig.java
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统配置实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("sys_config")
|
||||||
|
public class SystemConfig implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置键
|
||||||
|
*/
|
||||||
|
private String configKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置值
|
||||||
|
*/
|
||||||
|
private String configValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置分组
|
||||||
|
*/
|
||||||
|
private String configGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置名称
|
||||||
|
*/
|
||||||
|
private String configName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 值类型:string/number/boolean/json
|
||||||
|
*/
|
||||||
|
private String valueType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否系统内置:0-否,1-是
|
||||||
|
*/
|
||||||
|
private Integer builtIn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序号
|
||||||
|
*/
|
||||||
|
private Integer sortOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除标志:0-未删除,1-已删除
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
}
|
||||||
|
|
||||||
517
backend/src/main/java/com/designhub/entity/Task.java
Normal file
517
backend/src/main/java/com/designhub/entity/Task.java
Normal file
@ -0,0 +1,517 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务实体类
|
||||||
|
* 注意:已移除@Data注解,改用手动getter/setter以解决Lombok编译问题
|
||||||
|
*/
|
||||||
|
@TableName("task")
|
||||||
|
public class Task implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务编号
|
||||||
|
*/
|
||||||
|
private String taskNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第三方平台任务编号
|
||||||
|
*/
|
||||||
|
private String thirdPartyTaskNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务标题
|
||||||
|
*/
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务类型ID(单个,兼容旧数据)
|
||||||
|
*/
|
||||||
|
private Long taskTypeId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务类型ID列表(多个,逗号分隔,支持多类型)
|
||||||
|
*/
|
||||||
|
private String taskTypeIds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务子类型:new-新品 optimize-优化 redesign-改图 other-其他
|
||||||
|
*/
|
||||||
|
private String taskSubType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数量
|
||||||
|
*/
|
||||||
|
private Integer quantity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预估积分
|
||||||
|
*/
|
||||||
|
private Integer score;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实际积分奖励
|
||||||
|
*/
|
||||||
|
private BigDecimal scoreReward;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预估工时(小时)
|
||||||
|
*/
|
||||||
|
private BigDecimal estimatedHours;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预估时长(小时)
|
||||||
|
*/
|
||||||
|
private Integer estimatedDuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 需求类型ID
|
||||||
|
*/
|
||||||
|
private Long demandTypeId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 品牌ID
|
||||||
|
*/
|
||||||
|
private Long brandId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 叶子类目ID
|
||||||
|
*/
|
||||||
|
private Long categoryId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SPU信息
|
||||||
|
*/
|
||||||
|
private String spuInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平台ID
|
||||||
|
*/
|
||||||
|
private Long platformId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件URL(多个用逗号分隔)
|
||||||
|
*/
|
||||||
|
private String attachmentUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 交付路径
|
||||||
|
*/
|
||||||
|
private String deliveryPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布人ID
|
||||||
|
*/
|
||||||
|
private Long publisherId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布人部门ID
|
||||||
|
*/
|
||||||
|
private Long publisherDeptId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收人ID
|
||||||
|
*/
|
||||||
|
private Long receiverId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收人部门ID
|
||||||
|
*/
|
||||||
|
private Long receiverDeptId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布类型:assigned-指定 recruitment-招募 default-默认
|
||||||
|
*/
|
||||||
|
private String releaseType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态:pending-待接单 in_progress-进行中 review-待审核 rejected-已驳回 completed-已完成 cancelled-已取消
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核状态:draft-草稿 pending_audit-待审核 audit_pass-审核通过 audit_reject-审核驳回
|
||||||
|
*/
|
||||||
|
private String auditStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核人ID
|
||||||
|
*/
|
||||||
|
private Long auditorId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime auditTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核意见
|
||||||
|
*/
|
||||||
|
private String auditComment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成进度(0-100)
|
||||||
|
*/
|
||||||
|
private Integer progress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime startTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实际开始时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime actualStartTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 截止时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime deadline;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime completeTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交审核时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime submitTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实际结束时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime actualEndTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优先级:0-低 1-普通 2-高 3-紧急
|
||||||
|
*/
|
||||||
|
private Integer priority;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注信息
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 评分(1-5星)
|
||||||
|
*/
|
||||||
|
private Integer rating;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核评论
|
||||||
|
*/
|
||||||
|
private String reviewComment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 驳回原因
|
||||||
|
*/
|
||||||
|
private String rejectReason;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 驳回后的最晚交付时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime rejectDeadline;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否紧急:0-否 1-是
|
||||||
|
*/
|
||||||
|
private Integer isUrgent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否延误:0-否 1-是
|
||||||
|
*/
|
||||||
|
private Integer isDelayed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 延误天数
|
||||||
|
*/
|
||||||
|
private Integer delayDays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否流水线任务:0-否,1-是
|
||||||
|
*/
|
||||||
|
private Integer isPipeline;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流水线模式:sequential(顺序)/parallel(并行)
|
||||||
|
*/
|
||||||
|
private String pipelineMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核状态
|
||||||
|
*/
|
||||||
|
private String approvalStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前审核人ID
|
||||||
|
*/
|
||||||
|
private Long currentApproverId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前审核人角色
|
||||||
|
*/
|
||||||
|
private String currentApproverRole;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 需求部门审核时间
|
||||||
|
*/
|
||||||
|
private java.time.LocalDateTime demandApprovalTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接单部门审核时间
|
||||||
|
*/
|
||||||
|
private java.time.LocalDateTime receivingApprovalTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 逻辑删除:0-否 1-是
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联字段(查询时返回,不映射到数据库)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String brandName;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String categoryName;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String platformName;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String publisherName;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String publisherDeptName;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String receiverName;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String auditorName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前审核人姓名(查询时返回,不映射到数据库)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String currentApproverName;
|
||||||
|
|
||||||
|
// ==================== Getters and Setters (手动添加以解决Lombok编译问题) ====================
|
||||||
|
|
||||||
|
public Long getId() { return id; }
|
||||||
|
public void setId(Long id) { this.id = id; }
|
||||||
|
|
||||||
|
public String getTaskNo() { return taskNo; }
|
||||||
|
public void setTaskNo(String taskNo) { this.taskNo = taskNo; }
|
||||||
|
|
||||||
|
public String getThirdPartyTaskNo() { return thirdPartyTaskNo; }
|
||||||
|
public void setThirdPartyTaskNo(String thirdPartyTaskNo) { this.thirdPartyTaskNo = thirdPartyTaskNo; }
|
||||||
|
|
||||||
|
public String getTitle() { return title; }
|
||||||
|
public void setTitle(String title) { this.title = title; }
|
||||||
|
|
||||||
|
public Long getTaskTypeId() { return taskTypeId; }
|
||||||
|
public void setTaskTypeId(Long taskTypeId) { this.taskTypeId = taskTypeId; }
|
||||||
|
|
||||||
|
public String getTaskTypeIds() { return taskTypeIds; }
|
||||||
|
public void setTaskTypeIds(String taskTypeIds) { this.taskTypeIds = taskTypeIds; }
|
||||||
|
|
||||||
|
public String getTaskSubType() { return taskSubType; }
|
||||||
|
public void setTaskSubType(String taskSubType) { this.taskSubType = taskSubType; }
|
||||||
|
|
||||||
|
public Integer getQuantity() { return quantity; }
|
||||||
|
public void setQuantity(Integer quantity) { this.quantity = quantity; }
|
||||||
|
|
||||||
|
public Integer getScore() { return score; }
|
||||||
|
public void setScore(Integer score) { this.score = score; }
|
||||||
|
|
||||||
|
public BigDecimal getScoreReward() { return scoreReward; }
|
||||||
|
public void setScoreReward(BigDecimal scoreReward) { this.scoreReward = scoreReward; }
|
||||||
|
|
||||||
|
public BigDecimal getEstimatedHours() { return estimatedHours; }
|
||||||
|
public void setEstimatedHours(BigDecimal estimatedHours) { this.estimatedHours = estimatedHours; }
|
||||||
|
|
||||||
|
public Integer getEstimatedDuration() { return estimatedDuration; }
|
||||||
|
public void setEstimatedDuration(Integer estimatedDuration) { this.estimatedDuration = estimatedDuration; }
|
||||||
|
|
||||||
|
public Long getDemandTypeId() { return demandTypeId; }
|
||||||
|
public void setDemandTypeId(Long demandTypeId) { this.demandTypeId = demandTypeId; }
|
||||||
|
|
||||||
|
public Long getBrandId() { return brandId; }
|
||||||
|
public void setBrandId(Long brandId) { this.brandId = brandId; }
|
||||||
|
|
||||||
|
public Long getCategoryId() { return categoryId; }
|
||||||
|
public void setCategoryId(Long categoryId) { this.categoryId = categoryId; }
|
||||||
|
|
||||||
|
public String getSpuInfo() { return spuInfo; }
|
||||||
|
public void setSpuInfo(String spuInfo) { this.spuInfo = spuInfo; }
|
||||||
|
|
||||||
|
public Long getPlatformId() { return platformId; }
|
||||||
|
public void setPlatformId(Long platformId) { this.platformId = platformId; }
|
||||||
|
|
||||||
|
public String getAttachmentUrl() { return attachmentUrl; }
|
||||||
|
public void setAttachmentUrl(String attachmentUrl) { this.attachmentUrl = attachmentUrl; }
|
||||||
|
|
||||||
|
public String getDeliveryPath() { return deliveryPath; }
|
||||||
|
public void setDeliveryPath(String deliveryPath) { this.deliveryPath = deliveryPath; }
|
||||||
|
|
||||||
|
public Long getPublisherId() { return publisherId; }
|
||||||
|
public void setPublisherId(Long publisherId) { this.publisherId = publisherId; }
|
||||||
|
|
||||||
|
public Long getPublisherDeptId() { return publisherDeptId; }
|
||||||
|
public void setPublisherDeptId(Long publisherDeptId) { this.publisherDeptId = publisherDeptId; }
|
||||||
|
|
||||||
|
public Long getReceiverId() { return receiverId; }
|
||||||
|
public void setReceiverId(Long receiverId) { this.receiverId = receiverId; }
|
||||||
|
|
||||||
|
public Long getReceiverDeptId() { return receiverDeptId; }
|
||||||
|
public void setReceiverDeptId(Long receiverDeptId) { this.receiverDeptId = receiverDeptId; }
|
||||||
|
|
||||||
|
public String getReleaseType() { return releaseType; }
|
||||||
|
public void setReleaseType(String releaseType) { this.releaseType = releaseType; }
|
||||||
|
|
||||||
|
public String getStatus() { return status; }
|
||||||
|
public void setStatus(String status) { this.status = status; }
|
||||||
|
|
||||||
|
public String getAuditStatus() { return auditStatus; }
|
||||||
|
public void setAuditStatus(String auditStatus) { this.auditStatus = auditStatus; }
|
||||||
|
|
||||||
|
public Long getAuditorId() { return auditorId; }
|
||||||
|
public void setAuditorId(Long auditorId) { this.auditorId = auditorId; }
|
||||||
|
|
||||||
|
public LocalDateTime getAuditTime() { return auditTime; }
|
||||||
|
public void setAuditTime(LocalDateTime auditTime) { this.auditTime = auditTime; }
|
||||||
|
|
||||||
|
public String getAuditComment() { return auditComment; }
|
||||||
|
public void setAuditComment(String auditComment) { this.auditComment = auditComment; }
|
||||||
|
|
||||||
|
public Integer getProgress() { return progress; }
|
||||||
|
public void setProgress(Integer progress) { this.progress = progress; }
|
||||||
|
|
||||||
|
public LocalDateTime getStartTime() { return startTime; }
|
||||||
|
public void setStartTime(LocalDateTime startTime) { this.startTime = startTime; }
|
||||||
|
|
||||||
|
public LocalDateTime getActualStartTime() { return actualStartTime; }
|
||||||
|
public void setActualStartTime(LocalDateTime actualStartTime) { this.actualStartTime = actualStartTime; }
|
||||||
|
|
||||||
|
public LocalDateTime getDeadline() { return deadline; }
|
||||||
|
public void setDeadline(LocalDateTime deadline) { this.deadline = deadline; }
|
||||||
|
|
||||||
|
public LocalDateTime getCompleteTime() { return completeTime; }
|
||||||
|
public void setCompleteTime(LocalDateTime completeTime) { this.completeTime = completeTime; }
|
||||||
|
|
||||||
|
public LocalDateTime getSubmitTime() { return submitTime; }
|
||||||
|
public void setSubmitTime(LocalDateTime submitTime) { this.submitTime = submitTime; }
|
||||||
|
|
||||||
|
public LocalDateTime getActualEndTime() { return actualEndTime; }
|
||||||
|
public void setActualEndTime(LocalDateTime actualEndTime) { this.actualEndTime = actualEndTime; }
|
||||||
|
|
||||||
|
public Integer getPriority() { return priority; }
|
||||||
|
public void setPriority(Integer priority) { this.priority = priority; }
|
||||||
|
|
||||||
|
public String getRemark() { return remark; }
|
||||||
|
public void setRemark(String remark) { this.remark = remark; }
|
||||||
|
|
||||||
|
public Integer getRating() { return rating; }
|
||||||
|
public void setRating(Integer rating) { this.rating = rating; }
|
||||||
|
|
||||||
|
public String getReviewComment() { return reviewComment; }
|
||||||
|
public void setReviewComment(String reviewComment) { this.reviewComment = reviewComment; }
|
||||||
|
|
||||||
|
public String getRejectReason() { return rejectReason; }
|
||||||
|
public void setRejectReason(String rejectReason) { this.rejectReason = rejectReason; }
|
||||||
|
|
||||||
|
public LocalDateTime getRejectDeadline() { return rejectDeadline; }
|
||||||
|
public void setRejectDeadline(LocalDateTime rejectDeadline) { this.rejectDeadline = rejectDeadline; }
|
||||||
|
|
||||||
|
public Integer getIsUrgent() { return isUrgent; }
|
||||||
|
public void setIsUrgent(Integer isUrgent) { this.isUrgent = isUrgent; }
|
||||||
|
|
||||||
|
public Integer getIsDelayed() { return isDelayed; }
|
||||||
|
public void setIsDelayed(Integer isDelayed) { this.isDelayed = isDelayed; }
|
||||||
|
|
||||||
|
public Integer getDelayDays() { return delayDays; }
|
||||||
|
public void setDelayDays(Integer delayDays) { this.delayDays = delayDays; }
|
||||||
|
|
||||||
|
public Integer getIsPipeline() { return isPipeline; }
|
||||||
|
public void setIsPipeline(Integer isPipeline) { this.isPipeline = isPipeline; }
|
||||||
|
|
||||||
|
public String getPipelineMode() { return pipelineMode; }
|
||||||
|
public void setPipelineMode(String pipelineMode) { this.pipelineMode = pipelineMode; }
|
||||||
|
|
||||||
|
public String getApprovalStatus() { return approvalStatus; }
|
||||||
|
public void setApprovalStatus(String approvalStatus) { this.approvalStatus = approvalStatus; }
|
||||||
|
|
||||||
|
public Long getCurrentApproverId() { return currentApproverId; }
|
||||||
|
public void setCurrentApproverId(Long currentApproverId) { this.currentApproverId = currentApproverId; }
|
||||||
|
|
||||||
|
public String getCurrentApproverRole() { return currentApproverRole; }
|
||||||
|
public void setCurrentApproverRole(String currentApproverRole) { this.currentApproverRole = currentApproverRole; }
|
||||||
|
|
||||||
|
public LocalDateTime getDemandApprovalTime() { return demandApprovalTime; }
|
||||||
|
public void setDemandApprovalTime(LocalDateTime demandApprovalTime) { this.demandApprovalTime = demandApprovalTime; }
|
||||||
|
|
||||||
|
public LocalDateTime getReceivingApprovalTime() { return receivingApprovalTime; }
|
||||||
|
public void setReceivingApprovalTime(LocalDateTime receivingApprovalTime) { this.receivingApprovalTime = receivingApprovalTime; }
|
||||||
|
|
||||||
|
public Integer getDeleted() { return deleted; }
|
||||||
|
public void setDeleted(Integer deleted) { this.deleted = deleted; }
|
||||||
|
|
||||||
|
public LocalDateTime getCreateTime() { return createTime; }
|
||||||
|
public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
|
||||||
|
|
||||||
|
public LocalDateTime getUpdateTime() { return updateTime; }
|
||||||
|
public void setUpdateTime(LocalDateTime updateTime) { this.updateTime = updateTime; }
|
||||||
|
|
||||||
|
public String getBrandName() { return brandName; }
|
||||||
|
public void setBrandName(String brandName) { this.brandName = brandName; }
|
||||||
|
|
||||||
|
public String getCategoryName() { return categoryName; }
|
||||||
|
public void setCategoryName(String categoryName) { this.categoryName = categoryName; }
|
||||||
|
|
||||||
|
public String getPlatformName() { return platformName; }
|
||||||
|
public void setPlatformName(String platformName) { this.platformName = platformName; }
|
||||||
|
|
||||||
|
public String getPublisherName() { return publisherName; }
|
||||||
|
public void setPublisherName(String publisherName) { this.publisherName = publisherName; }
|
||||||
|
|
||||||
|
public String getPublisherDeptName() { return publisherDeptName; }
|
||||||
|
public void setPublisherDeptName(String publisherDeptName) { this.publisherDeptName = publisherDeptName; }
|
||||||
|
|
||||||
|
public String getReceiverName() { return receiverName; }
|
||||||
|
public void setReceiverName(String receiverName) { this.receiverName = receiverName; }
|
||||||
|
|
||||||
|
public String getAuditorName() { return auditorName; }
|
||||||
|
public void setAuditorName(String auditorName) { this.auditorName = auditorName; }
|
||||||
|
|
||||||
|
public String getCurrentApproverName() { return currentApproverName; }
|
||||||
|
public void setCurrentApproverName(String currentApproverName) { this.currentApproverName = currentApproverName; }
|
||||||
|
}
|
||||||
|
|
||||||
136
backend/src/main/java/com/designhub/entity/TaskApprovalLog.java
Normal file
136
backend/src/main/java/com/designhub/entity/TaskApprovalLog.java
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务审核记录实体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("task_approval_log")
|
||||||
|
public class TaskApprovalLog implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务ID
|
||||||
|
*/
|
||||||
|
private Long taskId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核级别:1-需求部门,2-接单部门
|
||||||
|
*/
|
||||||
|
private Integer approvalLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核人ID
|
||||||
|
*/
|
||||||
|
private Long approverId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核人姓名
|
||||||
|
*/
|
||||||
|
private String approverName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作:approve/reject/modify
|
||||||
|
*/
|
||||||
|
private String action;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核意见
|
||||||
|
*/
|
||||||
|
private String comment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改的内容(JSON格式)
|
||||||
|
*/
|
||||||
|
private String changes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getTaskId() {
|
||||||
|
return taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaskId(Long taskId) {
|
||||||
|
this.taskId = taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getApprovalLevel() {
|
||||||
|
return approvalLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApprovalLevel(Integer approvalLevel) {
|
||||||
|
this.approvalLevel = approvalLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getApproverId() {
|
||||||
|
return approverId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApproverId(Long approverId) {
|
||||||
|
this.approverId = approverId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApproverName() {
|
||||||
|
return approverName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApproverName(String approverName) {
|
||||||
|
this.approverName = approverName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAction() {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAction(String action) {
|
||||||
|
this.action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getComment() {
|
||||||
|
return comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setComment(String comment) {
|
||||||
|
this.comment = comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChanges() {
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChanges(String changes) {
|
||||||
|
this.changes = changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreateTime() {
|
||||||
|
return createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreateTime(LocalDateTime createTime) {
|
||||||
|
this.createTime = createTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务指派记录实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("task_assign_log")
|
||||||
|
public class TaskAssignLog implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务ID
|
||||||
|
*/
|
||||||
|
private Long taskId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指派人ID(部门负责人)
|
||||||
|
*/
|
||||||
|
private Long assignerId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 被指派人ID
|
||||||
|
*/
|
||||||
|
private Long assigneeId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指派类型:force-强制指派 recommend-推荐
|
||||||
|
*/
|
||||||
|
private String assignType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指派说明
|
||||||
|
*/
|
||||||
|
private String comment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
}
|
||||||
53
backend/src/main/java/com/designhub/entity/TaskAuditLog.java
Normal file
53
backend/src/main/java/com/designhub/entity/TaskAuditLog.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务审核日志实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("task_audit_log")
|
||||||
|
public class TaskAuditLog implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务ID
|
||||||
|
*/
|
||||||
|
private Long taskId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核人ID
|
||||||
|
*/
|
||||||
|
private Long auditorId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核人姓名
|
||||||
|
*/
|
||||||
|
private String auditorName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作:submit-提交审核 pass-审核通过 reject-审核驳回
|
||||||
|
*/
|
||||||
|
private String action;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核意见
|
||||||
|
*/
|
||||||
|
private String comment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
}
|
||||||
197
backend/src/main/java/com/designhub/entity/TaskItem.java
Normal file
197
backend/src/main/java/com/designhub/entity/TaskItem.java
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务子项实体类(流水线任务)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("task_item")
|
||||||
|
public class TaskItem implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主任务ID
|
||||||
|
*/
|
||||||
|
private Long taskId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 子项编号
|
||||||
|
*/
|
||||||
|
private String itemNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 子任务组号(同组任务由同一接单部门处理)
|
||||||
|
*/
|
||||||
|
private Integer groupNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务类型ID
|
||||||
|
*/
|
||||||
|
private Long taskTypeId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数量
|
||||||
|
*/
|
||||||
|
private Integer quantity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单价积分
|
||||||
|
*/
|
||||||
|
private Integer unitScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总积分
|
||||||
|
*/
|
||||||
|
private Integer totalScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预估工时
|
||||||
|
*/
|
||||||
|
private BigDecimal estimatedHours;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接单人ID
|
||||||
|
*/
|
||||||
|
private Long receiverId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接单人姓名
|
||||||
|
*/
|
||||||
|
private String receiverName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态:locked/pending/in_progress/completed/rejected
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进度0-100
|
||||||
|
*/
|
||||||
|
private Integer progress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否锁定:0-否,1-是(顺序流水线中使用)
|
||||||
|
*/
|
||||||
|
private Integer isLocked;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 前置子任务ID(顺序流水线中使用)
|
||||||
|
*/
|
||||||
|
private Long prevItemId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 交付路径
|
||||||
|
*/
|
||||||
|
private String deliveryPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime submitTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime completeTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序
|
||||||
|
*/
|
||||||
|
private Integer sortOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
// ========== 非数据库字段 ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务类型名称(非数据库字段)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String taskTypeName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主任务信息(非数据库字段)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Task mainTask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主任务标题(非数据库字段)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String mainTaskTitle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流水线模式(非数据库字段)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String pipelineMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总子任务数(非数据库字段)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Integer totalItems;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 品牌名称(非数据库字段)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String brandName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类目名称(非数据库字段)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String categoryName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平台名称(非数据库字段)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String platformName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发布人姓名(非数据库字段)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String publisherName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SPU信息(非数据库字段)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String spuInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务子类型(非数据库字段)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String taskSubType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件URL(非数据库字段)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String attachmentUrl;
|
||||||
|
}
|
||||||
@ -0,0 +1,99 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务进度更新记录实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("task_progress_update_log")
|
||||||
|
public class TaskProgressUpdateLog implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务ID
|
||||||
|
*/
|
||||||
|
private Long taskId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务编号
|
||||||
|
*/
|
||||||
|
private String taskNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务标题
|
||||||
|
*/
|
||||||
|
private String taskTitle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接单人ID
|
||||||
|
*/
|
||||||
|
private Long receiverId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接单人姓名
|
||||||
|
*/
|
||||||
|
private String receiverName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查日期
|
||||||
|
*/
|
||||||
|
private LocalDate checkDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当天是否更新过进度:0-未更新 1-已更新
|
||||||
|
*/
|
||||||
|
private Integer isUpdated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最后更新时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime lastUpdateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查前进度
|
||||||
|
*/
|
||||||
|
private Integer progressBefore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查后进度(如果更新了)
|
||||||
|
*/
|
||||||
|
private Integer progressAfter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否已发送通知:0-未发送 1-已发送
|
||||||
|
*/
|
||||||
|
private Integer notificationSent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知发送时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime notificationTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务状态
|
||||||
|
*/
|
||||||
|
private String taskStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
}
|
||||||
65
backend/src/main/java/com/designhub/entity/TaskReview.java
Normal file
65
backend/src/main/java/com/designhub/entity/TaskReview.java
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务评价实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("task_review")
|
||||||
|
public class TaskReview implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务ID
|
||||||
|
*/
|
||||||
|
private Long taskId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 评价人ID(发布者)
|
||||||
|
*/
|
||||||
|
private Long reviewerId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 被评价人ID(接收者)
|
||||||
|
*/
|
||||||
|
private Long receiverId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 评分(1-5星)
|
||||||
|
*/
|
||||||
|
private Integer rating;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 评价内容
|
||||||
|
*/
|
||||||
|
private String comment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 评价标签(JSON数组)
|
||||||
|
*/
|
||||||
|
private String tags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
124
backend/src/main/java/com/designhub/entity/TaskSubType.java
Normal file
124
backend/src/main/java/com/designhub/entity/TaskSubType.java
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务子类型实体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("task_sub_type")
|
||||||
|
public class TaskSubType implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 子类型名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 子类型编码
|
||||||
|
*/
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序
|
||||||
|
*/
|
||||||
|
private Integer sortOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用:0-否,1-是
|
||||||
|
*/
|
||||||
|
private Integer isEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(String code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getSortOrder() {
|
||||||
|
return sortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSortOrder(Integer sortOrder) {
|
||||||
|
this.sortOrder = sortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getIsEnabled() {
|
||||||
|
return isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsEnabled(Integer isEnabled) {
|
||||||
|
this.isEnabled = isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreateTime() {
|
||||||
|
return createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreateTime(LocalDateTime createTime) {
|
||||||
|
this.createTime = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getUpdateTime() {
|
||||||
|
return updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpdateTime(LocalDateTime updateTime) {
|
||||||
|
this.updateTime = updateTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
359
backend/src/main/java/com/designhub/entity/TeamPK.java
Normal file
359
backend/src/main/java/com/designhub/entity/TeamPK.java
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 团队PK实体
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("team_pk")
|
||||||
|
public class TeamPK implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PK名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PK描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 甲方部门ID
|
||||||
|
*/
|
||||||
|
private Long teamADeptId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 甲方部门名称
|
||||||
|
*/
|
||||||
|
private String teamAName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 乙方部门ID
|
||||||
|
*/
|
||||||
|
private Long teamBDeptId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 乙方部门名称
|
||||||
|
*/
|
||||||
|
private String teamBName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime startTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime endTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PK维度配置(JSON对象)
|
||||||
|
* 例如:{"taskCount":{"weight":30},"fiveStarCount":{"weight":25},"onTimeRate":{"weight":25},"avgRating":{"weight":20}}
|
||||||
|
*/
|
||||||
|
private String dimensions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 甲方当前得分
|
||||||
|
*/
|
||||||
|
private Integer teamAScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 乙方当前得分
|
||||||
|
*/
|
||||||
|
private Integer teamBScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 甲方完成任务数
|
||||||
|
*/
|
||||||
|
private Integer teamATaskCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 乙方完成任务数
|
||||||
|
*/
|
||||||
|
private Integer teamBTaskCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 甲方五星好评数
|
||||||
|
*/
|
||||||
|
private Integer teamAFiveStarCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 乙方五星好评数
|
||||||
|
*/
|
||||||
|
private Integer teamBFiveStarCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 甲方准时完成率(百分比*100)
|
||||||
|
*/
|
||||||
|
private Integer teamAOnTimeRate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 乙方准时完成率(百分比*100)
|
||||||
|
*/
|
||||||
|
private Integer teamBOnTimeRate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 胜利奖励配置(JSON对象)
|
||||||
|
* 例如:{"winnerReward":200,"loserReward":100,"mvpReward":300}
|
||||||
|
*/
|
||||||
|
private String rewards;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PK状态:draft-草稿, scheduled-待开始, active-进行中, ended-已结束, cancelled-已取消
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获胜方:A-甲方, B-乙方, draw-平局, null-未结束
|
||||||
|
*/
|
||||||
|
private String winner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MVP用户ID
|
||||||
|
*/
|
||||||
|
private Long mvpUserId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人ID
|
||||||
|
*/
|
||||||
|
private Long creatorId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否删除:0-否,1-是
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
// Getters and Setters (手动添加以解决Lombok编译问题)
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getTeamADeptId() {
|
||||||
|
return teamADeptId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeamADeptId(Long teamADeptId) {
|
||||||
|
this.teamADeptId = teamADeptId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTeamAName() {
|
||||||
|
return teamAName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeamAName(String teamAName) {
|
||||||
|
this.teamAName = teamAName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getTeamBDeptId() {
|
||||||
|
return teamBDeptId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeamBDeptId(Long teamBDeptId) {
|
||||||
|
this.teamBDeptId = teamBDeptId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTeamBName() {
|
||||||
|
return teamBName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeamBName(String teamBName) {
|
||||||
|
this.teamBName = teamBName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getStartTime() {
|
||||||
|
return startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStartTime(LocalDateTime startTime) {
|
||||||
|
this.startTime = startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getEndTime() {
|
||||||
|
return endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEndTime(LocalDateTime endTime) {
|
||||||
|
this.endTime = endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDimensions() {
|
||||||
|
return dimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDimensions(String dimensions) {
|
||||||
|
this.dimensions = dimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getTeamAScore() {
|
||||||
|
return teamAScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeamAScore(Integer teamAScore) {
|
||||||
|
this.teamAScore = teamAScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getTeamBScore() {
|
||||||
|
return teamBScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeamBScore(Integer teamBScore) {
|
||||||
|
this.teamBScore = teamBScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getTeamATaskCount() {
|
||||||
|
return teamATaskCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeamATaskCount(Integer teamATaskCount) {
|
||||||
|
this.teamATaskCount = teamATaskCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getTeamBTaskCount() {
|
||||||
|
return teamBTaskCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeamBTaskCount(Integer teamBTaskCount) {
|
||||||
|
this.teamBTaskCount = teamBTaskCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getTeamAFiveStarCount() {
|
||||||
|
return teamAFiveStarCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeamAFiveStarCount(Integer teamAFiveStarCount) {
|
||||||
|
this.teamAFiveStarCount = teamAFiveStarCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getTeamBFiveStarCount() {
|
||||||
|
return teamBFiveStarCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeamBFiveStarCount(Integer teamBFiveStarCount) {
|
||||||
|
this.teamBFiveStarCount = teamBFiveStarCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getTeamAOnTimeRate() {
|
||||||
|
return teamAOnTimeRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeamAOnTimeRate(Integer teamAOnTimeRate) {
|
||||||
|
this.teamAOnTimeRate = teamAOnTimeRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getTeamBOnTimeRate() {
|
||||||
|
return teamBOnTimeRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeamBOnTimeRate(Integer teamBOnTimeRate) {
|
||||||
|
this.teamBOnTimeRate = teamBOnTimeRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRewards() {
|
||||||
|
return rewards;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRewards(String rewards) {
|
||||||
|
this.rewards = rewards;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(String status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWinner() {
|
||||||
|
return winner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWinner(String winner) {
|
||||||
|
this.winner = winner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getMvpUserId() {
|
||||||
|
return mvpUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMvpUserId(Long mvpUserId) {
|
||||||
|
this.mvpUserId = mvpUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getCreatorId() {
|
||||||
|
return creatorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreatorId(Long creatorId) {
|
||||||
|
this.creatorId = creatorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getDeleted() {
|
||||||
|
return deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeleted(Integer deleted) {
|
||||||
|
this.deleted = deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreateTime() {
|
||||||
|
return createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreateTime(LocalDateTime createTime) {
|
||||||
|
this.createTime = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getUpdateTime() {
|
||||||
|
return updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpdateTime(LocalDateTime updateTime) {
|
||||||
|
this.updateTime = updateTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
135
backend/src/main/java/com/designhub/entity/TeamScorePool.java
Normal file
135
backend/src/main/java/com/designhub/entity/TeamScorePool.java
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 团队积分池实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("team_score_pool")
|
||||||
|
public class TeamScorePool implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门ID
|
||||||
|
*/
|
||||||
|
private Long deptId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门名称
|
||||||
|
*/
|
||||||
|
private String deptName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 积分余额
|
||||||
|
*/
|
||||||
|
private Integer balance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 累计充值积分
|
||||||
|
*/
|
||||||
|
private Integer totalRecharge;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 累计消耗积分
|
||||||
|
*/
|
||||||
|
private Integer totalConsume;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 累计获得积分(接单部门)
|
||||||
|
*/
|
||||||
|
private Integer totalEarn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总积分额度
|
||||||
|
*/
|
||||||
|
private Integer totalScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 剩余积分
|
||||||
|
*/
|
||||||
|
private Integer remainingScore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用消耗限制:0-否,1-是
|
||||||
|
*/
|
||||||
|
private Integer limitEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 限制周期:DAILY-每天,WEEKLY-每周,MONTHLY-每月
|
||||||
|
*/
|
||||||
|
private String limitPeriod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 周期限制额度
|
||||||
|
*/
|
||||||
|
private Integer periodLimit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前周期已消耗
|
||||||
|
*/
|
||||||
|
private Integer periodUsed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前周期开始时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime periodStartTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每日限额
|
||||||
|
*/
|
||||||
|
private Integer dailyLimit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每周限额
|
||||||
|
*/
|
||||||
|
private Integer weeklyLimit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每月限额
|
||||||
|
*/
|
||||||
|
private Integer monthlyLimit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每日已使用
|
||||||
|
*/
|
||||||
|
private Integer dailyUsed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每周已使用
|
||||||
|
*/
|
||||||
|
private Integer weeklyUsed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每月已使用
|
||||||
|
*/
|
||||||
|
private Integer monthlyUsed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预警阈值(百分比,如80表示80%)
|
||||||
|
*/
|
||||||
|
private Integer alertThreshold;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第三方回调记录实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("third_party_callback")
|
||||||
|
public class ThirdPartyCallback {
|
||||||
|
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联的任务ID
|
||||||
|
*/
|
||||||
|
private Long taskId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第三方平台任务编号
|
||||||
|
*/
|
||||||
|
private String thirdPartyTaskNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回调类型:complete-任务完成, status_update-状态更新
|
||||||
|
*/
|
||||||
|
private String callbackType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回调处理状态:pending-待处理, success-成功, failed-失败
|
||||||
|
*/
|
||||||
|
private String callbackStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回调请求数据(JSON格式)
|
||||||
|
*/
|
||||||
|
private String requestData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应数据(JSON格式)
|
||||||
|
*/
|
||||||
|
private String responseData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误信息
|
||||||
|
*/
|
||||||
|
private String errorMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重试次数
|
||||||
|
*/
|
||||||
|
private Integer retryCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回调接收时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime callbackTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理完成时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime processedTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
101
backend/src/main/java/com/designhub/entity/User.java
Normal file
101
backend/src/main/java/com/designhub/entity/User.java
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("sys_user")
|
||||||
|
public class User implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名
|
||||||
|
*/
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码
|
||||||
|
*/
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 真实姓名
|
||||||
|
*/
|
||||||
|
private String realName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱
|
||||||
|
*/
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手机号
|
||||||
|
*/
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 头像URL
|
||||||
|
*/
|
||||||
|
private String avatar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 钉钉用户ID
|
||||||
|
*/
|
||||||
|
private String dingUserId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门ID
|
||||||
|
*/
|
||||||
|
private Long deptId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门名称
|
||||||
|
*/
|
||||||
|
private String deptName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色ID列表(逗号分隔)
|
||||||
|
*/
|
||||||
|
private String roleIds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态:0-禁用 1-启用
|
||||||
|
*/
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户积分
|
||||||
|
*/
|
||||||
|
private Integer score;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 逻辑删除:0-否 1-是
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
package com.designhub.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户成就关联实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("user_achievement")
|
||||||
|
public class UserAchievement implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成就ID
|
||||||
|
*/
|
||||||
|
private Long achievementId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 达成时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime achievedTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前进度值
|
||||||
|
*/
|
||||||
|
private Integer progressValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否已领取奖励
|
||||||
|
*/
|
||||||
|
private Integer rewardClaimed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 领取奖励时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime claimTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除标志:0-未删除,1-已删除
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
}
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user