chore: 初始化项目基础配置
- 添加Maven父POM配置,定义项目依赖版本 - 配置项目模块结构 - 添加.gitignore规则 - 添加项目文档和开发指南
This commit is contained in:
commit
19d3eccb5b
211
api/README.md
Normal file
211
api/README.md
Normal file
@ -0,0 +1,211 @@
|
||||
# Coder Common Thin Backend API 文档
|
||||
|
||||
## 项目概述
|
||||
|
||||
Coder Common Thin Backend 是一个基于Spring Boot 3.5.0的企业级开发框架,采用插件化架构设计,提供了完整的后台管理系统功能。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **后端框架**: Spring Boot 3.5.0 + Java 17
|
||||
- **数据库**: MySQL 8 + MyBatis Plus 3.5.12
|
||||
- **缓存**: Redis (Spring Data Redis)
|
||||
- **安全认证**: Sa-Token 1.43.0
|
||||
- **权限控制**: RBAC (Role-Based Access Control)
|
||||
- **API文档**: OpenAPI 3.0 (Swagger)
|
||||
- **工具库**: Hutool 5.8.38, Fastjson2 2.0.57, Guava 33.4.8
|
||||
|
||||
## 服务器信息
|
||||
|
||||
- **默认端口**: 18099
|
||||
- **基础URL**: http://localhost:18099
|
||||
- **API前缀**: /coder (除登录认证接口外)
|
||||
|
||||
## 认证方式
|
||||
|
||||
系统使用Sa-Token进行身份认证和权限管理:
|
||||
- **Token类型**: Bearer Token
|
||||
- **Token位置**: 请求头 `Authorization` 字段
|
||||
- **Token过期**: 支持自定义过期时间
|
||||
- **权限验证**: 基于注解的权限验证
|
||||
|
||||
## 统一响应格式
|
||||
|
||||
所有接口均采用统一的JSON响应格式:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
// 具体数据
|
||||
},
|
||||
"traceId": "trace-id-value"
|
||||
}
|
||||
```
|
||||
|
||||
### 状态码说明
|
||||
|
||||
- **200**: 成功
|
||||
- **400**: 请求参数错误
|
||||
- **401**: 认证失败/未登录
|
||||
- **403**: 权限不足
|
||||
- **500**: 服务器内部错误
|
||||
|
||||
## 分页查询格式
|
||||
|
||||
分页查询请求参数:
|
||||
```json
|
||||
{
|
||||
"pageNo": 1,
|
||||
"pageSize": 10,
|
||||
"params": {
|
||||
// 查询参数
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
分页查询响应格式:
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"records": [
|
||||
// 数据记录
|
||||
],
|
||||
"total": 100,
|
||||
"size": 10,
|
||||
"current": 1,
|
||||
"pages": 10,
|
||||
"searchCount": true
|
||||
},
|
||||
"traceId": "trace-id-value"
|
||||
}
|
||||
```
|
||||
|
||||
## 权限系统
|
||||
|
||||
系统采用RBAC权限模型:
|
||||
- **用户 (User)**: 系统使用者
|
||||
- **角色 (Role)**: 权限的集合
|
||||
- **权限 (Permission)**: 具体的操作权限
|
||||
- **菜单 (Menu)**: 系统菜单和按钮权限
|
||||
|
||||
### 权限验证流程
|
||||
|
||||
1. 用户登录获取Token
|
||||
2. 请求接口时携带Token
|
||||
3. 系统验证Token有效性
|
||||
4. 根据用户角色验证权限
|
||||
5. 允许或拒绝访问
|
||||
|
||||
## API模块分类
|
||||
|
||||
### 1. 认证模块 (Authentication)
|
||||
- [登录认证API](./authentication/登录认证API)
|
||||
- [验证码API](./authentication/验证码API)
|
||||
|
||||
### 2. 用户管理模块 (User Management)
|
||||
- [用户管理API](./user/用户管理API)
|
||||
|
||||
### 3. 权限管理模块 (Permission Management)
|
||||
- [菜单管理API](./permission/菜单管理API)
|
||||
- [角色管理API](./permission/角色管理API)
|
||||
|
||||
### 4. 系统管理模块 (System Management)
|
||||
- [文件管理API](./system/文件管理API)
|
||||
- [图片管理API](./system/图片管理API)
|
||||
- [登录日志API](./system/登录日志API)
|
||||
|
||||
## 错误处理
|
||||
|
||||
系统提供了完善的错误处理机制:
|
||||
|
||||
### 常见错误类型
|
||||
|
||||
1. **业务异常**: 自定义业务逻辑错误
|
||||
2. **认证异常**: 登录认证相关错误
|
||||
3. **权限异常**: 权限验证失败
|
||||
4. **参数异常**: 请求参数验证失败
|
||||
5. **系统异常**: 服务器内部错误
|
||||
|
||||
### 错误响应格式
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 400,
|
||||
"msg": "具体错误信息",
|
||||
"data": null,
|
||||
"traceId": "trace-id-value"
|
||||
}
|
||||
```
|
||||
|
||||
## 开发环境配置
|
||||
|
||||
### 数据库配置
|
||||
```yaml
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:mysql://localhost:3306/coder_common_thin
|
||||
username: root
|
||||
password: your_password
|
||||
```
|
||||
|
||||
### Redis配置
|
||||
```yaml
|
||||
spring:
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
password: your_password
|
||||
```
|
||||
|
||||
### Sa-Token配置
|
||||
```yaml
|
||||
sa-token:
|
||||
token-name: Authorization
|
||||
timeout: 2592000
|
||||
activity-timeout: -1
|
||||
is-concurrent: true
|
||||
is-share: false
|
||||
is-read-head: true
|
||||
is-read-cookie: false
|
||||
```
|
||||
|
||||
## 接口调用示例
|
||||
|
||||
### 登录接口调用
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/auth/login \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"loginName": "admin",
|
||||
"password": "123456",
|
||||
"codeKey": "uuid-key",
|
||||
"securityCode": "1234"
|
||||
}'
|
||||
```
|
||||
|
||||
### 携带Token调用接口
|
||||
```bash
|
||||
curl -X GET \
|
||||
http://localhost:18099/coder/sysLoginUser/getLoginUserInformation \
|
||||
-H 'Authorization: Bearer your-token-value'
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 所有接口都需要进行认证,除了登录接口和验证码接口
|
||||
2. 权限验证基于角色和权限码进行
|
||||
3. 系统支持多设备登录
|
||||
4. 敏感操作会记录操作日志
|
||||
5. 建议在生产环境中启用HTTPS
|
||||
|
||||
## 更新日志
|
||||
|
||||
- **v1.0.0** (2025-07-05): 初始版本发布
|
||||
|
||||
## 联系方式
|
||||
|
||||
如有问题,请联系开发团队。
|
||||
255
api/authentication/登录认证API.md
Normal file
255
api/authentication/登录认证API.md
Normal file
@ -0,0 +1,255 @@
|
||||
# 登录认证API
|
||||
|
||||
## 概述
|
||||
|
||||
登录认证模块提供用户登录、退出、注册等功能,使用Sa-Token进行身份认证和会话管理。
|
||||
|
||||
## 接口列表
|
||||
|
||||
### 1. 用户登录
|
||||
|
||||
**接口地址**: `POST /auth/login`
|
||||
|
||||
**接口描述**: 用户使用账号密码登录系统
|
||||
|
||||
**是否需要认证**: 否
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"loginName": "admin",
|
||||
"password": "123456",
|
||||
"codeKey": "uuid-key",
|
||||
"securityCode": "1234",
|
||||
"rememberMe": false
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|
||||
|--------|------|------|------|----------|
|
||||
| loginName | String | 是 | 登录账号 | 长度3-16位,只能包含字母和数字 |
|
||||
| password | String | 是 | 登录密码 | 不能为空 |
|
||||
| codeKey | String | 是 | 验证码UUID | 不能为空 |
|
||||
| securityCode | String | 是 | 验证码 | 不能为空 |
|
||||
| rememberMe | Boolean | 否 | 记住登录 | 默认false |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"tokenName": "Authorization",
|
||||
"tokenValue": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
|
||||
},
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
|--------|------|------|
|
||||
| tokenName | String | Token名称 |
|
||||
| tokenValue | String | Token值 |
|
||||
|
||||
**错误码**:
|
||||
|
||||
| 状态码 | 错误信息 | 说明 |
|
||||
|--------|----------|------|
|
||||
| 400 | 账号长度为 3-16 位 | 登录账号长度不符合要求 |
|
||||
| 400 | 账号格式为数字以及字母 | 登录账号格式不正确 |
|
||||
| 400 | 请输入登录名 | 登录名为空 |
|
||||
| 400 | 请输入密码 | 密码为空 |
|
||||
| 400 | 验证码已失效 | 验证码Key无效 |
|
||||
| 400 | 请输入验证码 | 验证码为空 |
|
||||
| 400 | 验证码错误 | 验证码不正确 |
|
||||
| 400 | 用户不存在 | 用户账号不存在 |
|
||||
| 400 | 密码错误 | 密码不正确 |
|
||||
| 400 | 账号已被停用 | 用户账号被禁用 |
|
||||
| 400 | 账号已被锁定 | 用户账号被锁定 |
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/auth/login \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"loginName": "admin",
|
||||
"password": "123456",
|
||||
"codeKey": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"securityCode": "1234",
|
||||
"rememberMe": false
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 用户退出
|
||||
|
||||
**接口地址**: `GET /auth/logout`
|
||||
|
||||
**接口描述**: 用户退出登录,清除会话信息
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**请求头**:
|
||||
|
||||
```
|
||||
Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
|
||||
```
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "退出成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**错误码**:
|
||||
|
||||
| 状态码 | 错误信息 | 说明 |
|
||||
|--------|----------|------|
|
||||
| 401 | 当前会话未登录 | 用户未登录或Token无效 |
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
http://localhost:18099/auth/logout \
|
||||
-H 'Authorization: your-token-value'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 用户注册
|
||||
|
||||
**接口地址**: `POST /auth/register`
|
||||
|
||||
**接口描述**: 新用户注册账号
|
||||
|
||||
**是否需要认证**: 否
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"loginName": "newuser",
|
||||
"password": "123456",
|
||||
"userName": "新用户",
|
||||
"codeKey": "uuid-key",
|
||||
"securityCode": "1234"
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|
||||
|--------|------|------|------|----------|
|
||||
| loginName | String | 是 | 登录账号 | 长度3-16位,只能包含字母和数字 |
|
||||
| password | String | 是 | 登录密码 | 不能为空 |
|
||||
| userName | String | 是 | 用户姓名 | 不能为空 |
|
||||
| codeKey | String | 是 | 验证码UUID | 不能为空 |
|
||||
| securityCode | String | 是 | 验证码 | 不能为空 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "注册成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**错误码**:
|
||||
|
||||
| 状态码 | 错误信息 | 说明 |
|
||||
|--------|----------|------|
|
||||
| 400 | 账号长度为 3-16 位 | 登录账号长度不符合要求 |
|
||||
| 400 | 账号格式为数字以及字母 | 登录账号格式不正确 |
|
||||
| 400 | 请输入登录名 | 登录名为空 |
|
||||
| 400 | 请输入密码 | 密码为空 |
|
||||
| 400 | 请输入用户姓名 | 用户姓名为空 |
|
||||
| 400 | 验证码已失效 | 验证码Key无效 |
|
||||
| 400 | 请输入验证码 | 验证码为空 |
|
||||
| 400 | 验证码错误 | 验证码不正确 |
|
||||
| 400 | 账号已存在 | 登录账号已被注册 |
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/auth/register \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"loginName": "newuser",
|
||||
"password": "123456",
|
||||
"userName": "新用户",
|
||||
"codeKey": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"securityCode": "1234"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 认证流程说明
|
||||
|
||||
### 1. 登录流程
|
||||
|
||||
1. 用户获取验证码(调用验证码接口)
|
||||
2. 用户输入账号、密码、验证码
|
||||
3. 系统验证验证码有效性
|
||||
4. 系统验证用户账号和密码
|
||||
5. 系统检查用户状态(是否禁用/锁定)
|
||||
6. 生成Token并返回给用户
|
||||
7. 用户后续请求携带Token
|
||||
|
||||
### 2. Token使用
|
||||
|
||||
- Token需要在请求头中携带:`Authorization: token-value`
|
||||
- Token有过期时间,过期后需要重新登录
|
||||
- 系统支持多设备登录
|
||||
- 管理员可以强制用户下线
|
||||
|
||||
### 3. 权限验证
|
||||
|
||||
- 登录后系统会根据用户角色加载权限
|
||||
- 每个接口都会验证用户是否有相应权限
|
||||
- 超级管理员拥有所有权限
|
||||
|
||||
### 4. 会话管理
|
||||
|
||||
- 用户登录信息存储在Redis中
|
||||
- 支持会话延长
|
||||
- 支持记住登录功能
|
||||
- 系统会记录用户登录日志
|
||||
|
||||
## 安全特性
|
||||
|
||||
1. **密码加密**: 使用盐值加密存储密码
|
||||
2. **验证码保护**: 防止暴力破解
|
||||
3. **账号锁定**: 连续错误3次自动锁定
|
||||
4. **IP限制**: 可配置IP白名单/黑名单
|
||||
5. **会话安全**: Token有效期管理
|
||||
6. **日志记录**: 记录所有登录操作
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 验证码有效期为5分钟
|
||||
2. Token默认有效期为30天
|
||||
3. 账号锁定时间为10分钟
|
||||
4. 注册功能可通过配置开启/关闭
|
||||
5. 建议在生产环境中启用HTTPS
|
||||
397
api/authentication/验证码API.md
Normal file
397
api/authentication/验证码API.md
Normal file
@ -0,0 +1,397 @@
|
||||
# 验证码API
|
||||
|
||||
## 概述
|
||||
|
||||
验证码模块提供图形验证码生成功能,支持PNG和GIF两种格式,用于防止恶意攻击和机器人注册。
|
||||
|
||||
## 接口列表
|
||||
|
||||
### 1. 生成PNG验证码
|
||||
|
||||
**接口地址**: `GET /captcha/png`
|
||||
|
||||
**接口描述**: 生成PNG格式的图形验证码
|
||||
|
||||
**是否需要认证**: 否
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"uuid": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAkCAYAAAB...",
|
||||
"captchaText": "1234"
|
||||
},
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
|--------|------|------|
|
||||
| uuid | String | 验证码唯一标识,用于后续验证 |
|
||||
| base64 | String | 验证码图片Base64编码 |
|
||||
| captchaText | String | 验证码文本(开发环境返回,生产环境不返回) |
|
||||
|
||||
**特性说明**:
|
||||
|
||||
- 验证码长度:4位数字
|
||||
- 图片尺寸:102x38像素
|
||||
- 有效期:5分钟
|
||||
- 字体:随机字体
|
||||
- 背景:随机背景色
|
||||
- 干扰线:随机干扰线
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
http://localhost:18099/captcha/png \
|
||||
-H 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
**前端使用示例**:
|
||||
|
||||
```javascript
|
||||
// 获取验证码
|
||||
function getCaptcha() {
|
||||
fetch('/captcha/png')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 200) {
|
||||
// 显示验证码图片
|
||||
document.getElementById('captchaImg').src = data.data.base64;
|
||||
// 保存验证码UUID,用于登录时提交
|
||||
document.getElementById('codeKey').value = data.data.uuid;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 点击刷新验证码
|
||||
document.getElementById('captchaImg').onclick = getCaptcha;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 生成GIF验证码
|
||||
|
||||
**接口地址**: `GET /captcha/gif`
|
||||
|
||||
**接口描述**: 生成GIF动画格式的图形验证码
|
||||
|
||||
**是否需要认证**: 否
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"uuid": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"base64": "data:image/gif;base64,R0lGODlhZgAyAPcAAAAAAP///wAAAP...",
|
||||
"captchaText": "5678"
|
||||
},
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
|--------|------|------|
|
||||
| uuid | String | 验证码唯一标识,用于后续验证 |
|
||||
| base64 | String | 验证码图片Base64编码 |
|
||||
| captchaText | String | 验证码文本(开发环境返回,生产环境不返回) |
|
||||
|
||||
**特性说明**:
|
||||
|
||||
- 验证码长度:4位数字
|
||||
- 图片尺寸:102x38像素
|
||||
- 有效期:5分钟
|
||||
- 动画效果:字符摆动动画
|
||||
- 帧数:10帧
|
||||
- 动画速度:100ms/帧
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
http://localhost:18099/captcha/gif \
|
||||
-H 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
**前端使用示例**:
|
||||
|
||||
```javascript
|
||||
// 获取GIF验证码
|
||||
function getGifCaptcha() {
|
||||
fetch('/captcha/gif')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 200) {
|
||||
// 显示验证码图片
|
||||
document.getElementById('captchaImg').src = data.data.base64;
|
||||
// 保存验证码UUID,用于登录时提交
|
||||
document.getElementById('codeKey').value = data.data.uuid;
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 验证码配置
|
||||
|
||||
### 1. 验证码参数配置
|
||||
|
||||
```yaml
|
||||
# application.yml
|
||||
captcha:
|
||||
# 验证码长度
|
||||
length: 4
|
||||
# 验证码宽度
|
||||
width: 102
|
||||
# 验证码高度
|
||||
height: 38
|
||||
# 验证码有效期(分钟)
|
||||
timeout: 5
|
||||
# 字体大小
|
||||
font-size: 25
|
||||
# 干扰线数量
|
||||
line-count: 100
|
||||
# 是否开启验证码
|
||||
enabled: true
|
||||
```
|
||||
|
||||
### 2. 验证码存储
|
||||
|
||||
- 验证码使用Redis存储
|
||||
- Key格式:`captcha:uuid`
|
||||
- 过期时间:5分钟自动删除
|
||||
- 值存储:验证码文本(忽略大小写)
|
||||
|
||||
### 3. 验证码验证
|
||||
|
||||
```java
|
||||
// 验证码验证逻辑
|
||||
public boolean validateCaptcha(String uuid, String inputCode) {
|
||||
// 从Redis获取验证码
|
||||
String correctCode = redisTemplate.opsForValue().get("captcha:" + uuid);
|
||||
|
||||
if (correctCode == null) {
|
||||
throw new BusinessException("验证码已过期");
|
||||
}
|
||||
|
||||
// 不区分大小写验证
|
||||
if (!correctCode.equalsIgnoreCase(inputCode)) {
|
||||
throw new BusinessException("验证码错误");
|
||||
}
|
||||
|
||||
// 验证成功后立即删除验证码(防止重复使用)
|
||||
redisTemplate.delete("captcha:" + uuid);
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 安全特性
|
||||
|
||||
### 1. 防暴力破解
|
||||
|
||||
- 验证码一次性使用
|
||||
- 验证后立即删除
|
||||
- 5分钟自动过期
|
||||
- 支持IP限制
|
||||
|
||||
### 2. 防机器识别
|
||||
|
||||
- 随机字体和颜色
|
||||
- 随机背景和干扰线
|
||||
- 字符位置随机偏移
|
||||
- GIF动画增加识别难度
|
||||
|
||||
### 3. 防重放攻击
|
||||
|
||||
- 每次验证码都有唯一UUID
|
||||
- 验证码使用后立即失效
|
||||
- 不允许重复使用
|
||||
|
||||
## 错误处理
|
||||
|
||||
### 常见错误情况
|
||||
|
||||
| 错误场景 | 错误码 | 错误信息 |
|
||||
|----------|--------|----------|
|
||||
| 验证码过期 | 400 | 验证码已失效 |
|
||||
| 验证码错误 | 400 | 验证码错误 |
|
||||
| 验证码为空 | 400 | 请输入验证码 |
|
||||
| UUID无效 | 400 | 验证码已失效 |
|
||||
| 系统异常 | 500 | 验证码生成失败 |
|
||||
|
||||
### 错误响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 400,
|
||||
"msg": "验证码已失效",
|
||||
"data": null,
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用建议
|
||||
|
||||
### 1. 前端集成
|
||||
|
||||
```html
|
||||
<!-- 验证码显示区域 -->
|
||||
<div class="captcha-container">
|
||||
<img id="captchaImg" src="" alt="验证码" onclick="refreshCaptcha()">
|
||||
<input type="hidden" id="codeKey" value="">
|
||||
<input type="text" id="securityCode" placeholder="请输入验证码">
|
||||
<button type="button" onclick="refreshCaptcha()">刷新</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// 初始化验证码
|
||||
function initCaptcha() {
|
||||
fetch('/captcha/png')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 200) {
|
||||
document.getElementById('captchaImg').src = data.data.base64;
|
||||
document.getElementById('codeKey').value = data.data.uuid;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 刷新验证码
|
||||
function refreshCaptcha() {
|
||||
initCaptcha();
|
||||
}
|
||||
|
||||
// 页面加载时获取验证码
|
||||
window.onload = initCaptcha;
|
||||
```
|
||||
|
||||
### 2. 表单提交
|
||||
|
||||
```javascript
|
||||
// 登录表单提交
|
||||
function login() {
|
||||
const formData = {
|
||||
loginName: document.getElementById('loginName').value,
|
||||
password: document.getElementById('password').value,
|
||||
codeKey: document.getElementById('codeKey').value,
|
||||
securityCode: document.getElementById('securityCode').value
|
||||
};
|
||||
|
||||
fetch('/auth/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(formData)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 200) {
|
||||
// 登录成功
|
||||
localStorage.setItem('token', data.data.tokenValue);
|
||||
window.location.href = '/dashboard';
|
||||
} else {
|
||||
// 登录失败,刷新验证码
|
||||
alert(data.msg);
|
||||
refreshCaptcha();
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 移动端适配
|
||||
|
||||
```css
|
||||
/* 移动端验证码样式 */
|
||||
@media (max-width: 768px) {
|
||||
.captcha-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
#captchaImg {
|
||||
width: 102px;
|
||||
height: 38px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#securityCode {
|
||||
width: 100px;
|
||||
height: 38px;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 1. 缓存策略
|
||||
|
||||
- 验证码图片不缓存
|
||||
- 验证码文本缓存5分钟
|
||||
- 使用Redis集群提高性能
|
||||
|
||||
### 2. 并发控制
|
||||
|
||||
- 支持高并发验证码生成
|
||||
- 每秒可生成1000+验证码
|
||||
- 使用连接池管理Redis连接
|
||||
|
||||
### 3. 资源优化
|
||||
|
||||
- 图片大小优化(小于2KB)
|
||||
- 内存使用优化
|
||||
- 垃圾回收优化
|
||||
|
||||
## 监控和日志
|
||||
|
||||
### 1. 指标监控
|
||||
|
||||
- 验证码生成次数
|
||||
- 验证码验证次数
|
||||
- 验证码成功率
|
||||
- 验证码过期率
|
||||
|
||||
### 2. 日志记录
|
||||
|
||||
- 验证码生成日志
|
||||
- 验证码验证日志
|
||||
- 异常错误日志
|
||||
- 性能监控日志
|
||||
|
||||
### 3. 告警机制
|
||||
|
||||
- 验证码成功率过低告警
|
||||
- 验证码生成异常告警
|
||||
- Redis连接异常告警
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 生产环境不返回验证码文本
|
||||
2. 验证码区分大小写(可配置)
|
||||
3. 验证码只能使用一次
|
||||
4. 建议定期清理过期验证码
|
||||
5. 支持自定义验证码样式和难度
|
||||
1315
api/permission/菜单管理API.md
Normal file
1315
api/permission/菜单管理API.md
Normal file
File diff suppressed because it is too large
Load Diff
882
api/permission/角色管理API.md
Normal file
882
api/permission/角色管理API.md
Normal file
@ -0,0 +1,882 @@
|
||||
# 角色管理API
|
||||
|
||||
## 概述
|
||||
|
||||
角色管理模块是权限系统的重要组成部分,负责管理系统角色、角色权限分配和用户角色关联。基于RBAC权限模型,实现了灵活的角色权限管理机制。
|
||||
|
||||
## 权限说明
|
||||
|
||||
角色管理接口需要相应的权限才能访问:
|
||||
|
||||
| 操作 | 权限码 | 说明 |
|
||||
|------|--------|------|
|
||||
| 查询角色列表 | `system:role:list` | 查看角色列表权限 |
|
||||
| 新增角色 | `system:role:add` | 新增角色权限 |
|
||||
| 修改角色 | `system:role:edit` | 修改角色信息权限 |
|
||||
| 删除角色 | `system:role:remove` | 删除角色权限 |
|
||||
| 分配权限 | `system:role:assign` | 分配角色权限 |
|
||||
| 用户授权 | `system:user:role` | 分配用户角色权限 |
|
||||
|
||||
## 接口列表
|
||||
|
||||
### 1. 分页查询角色列表
|
||||
|
||||
**接口地址**: `GET /coder/sysRole/listPage`
|
||||
|
||||
**接口描述**: 分页查询系统角色列表
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:role:list`
|
||||
|
||||
**请求头**:
|
||||
```
|
||||
Authorization: your-token-value
|
||||
```
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
||||
|--------|------|------|------|------|
|
||||
| pageNo | Integer | 否 | 页码 | 1 |
|
||||
| pageSize | Integer | 否 | 每页大小 | 10 |
|
||||
| roleName | String | 否 | 角色名称 | 管理员 |
|
||||
| roleCode | String | 否 | 角色编码 | admin |
|
||||
| roleStatus | String | 否 | 角色状态 | 0 |
|
||||
| beginTime | String | 否 | 开始时间 | 2024-01-01 |
|
||||
| endTime | String | 否 | 结束时间 | 2024-12-31 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"records": [
|
||||
{
|
||||
"roleId": 1,
|
||||
"roleName": "超级管理员",
|
||||
"roleCode": "admin",
|
||||
"roleStatus": "0",
|
||||
"dataScope": "1",
|
||||
"deptCheckStrictly": "1",
|
||||
"menuCheckStrictly": "1",
|
||||
"sorted": 1,
|
||||
"remark": "超级管理员",
|
||||
"createBy": "admin",
|
||||
"createTime": "2024-01-01 10:00:00",
|
||||
"updateBy": "admin",
|
||||
"updateTime": "2024-01-01 10:00:00",
|
||||
"menuIds": [1, 2, 3, 4, 5],
|
||||
"deptIds": [100, 101, 102]
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"size": 10,
|
||||
"current": 1,
|
||||
"pages": 1
|
||||
},
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
"http://localhost:18099/coder/sysRole/listPage?pageNo=1&pageSize=10&roleName=管理员" \
|
||||
-H "Authorization: your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 查询所有角色
|
||||
|
||||
**接口地址**: `GET /coder/sysRole/list`
|
||||
|
||||
**接口描述**: 查询所有系统角色(不分页)
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:role:list`
|
||||
|
||||
**请求参数**: 同分页查询(除pageNo、pageSize外)
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": [
|
||||
{
|
||||
"roleId": 1,
|
||||
"roleName": "超级管理员",
|
||||
"roleCode": "admin",
|
||||
"roleStatus": "0",
|
||||
"dataScope": "1",
|
||||
"deptCheckStrictly": "1",
|
||||
"menuCheckStrictly": "1",
|
||||
"sorted": 1,
|
||||
"remark": "超级管理员",
|
||||
"createBy": "admin",
|
||||
"createTime": "2024-01-01 10:00:00",
|
||||
"updateBy": "admin",
|
||||
"updateTime": "2024-01-01 10:00:00"
|
||||
}
|
||||
],
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
"http://localhost:18099/coder/sysRole/list" \
|
||||
-H "Authorization: your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 根据ID查询角色
|
||||
|
||||
**接口地址**: `GET /coder/sysRole/getById/{id}`
|
||||
|
||||
**接口描述**: 根据角色ID查询角色详细信息
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:role:list`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| id | Long | 是 | 角色ID |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"roleId": 1,
|
||||
"roleName": "超级管理员",
|
||||
"roleCode": "admin",
|
||||
"roleStatus": "0",
|
||||
"dataScope": "1",
|
||||
"deptCheckStrictly": "1",
|
||||
"menuCheckStrictly": "1",
|
||||
"sorted": 1,
|
||||
"remark": "超级管理员",
|
||||
"createBy": "admin",
|
||||
"createTime": "2024-01-01 10:00:00",
|
||||
"updateBy": "admin",
|
||||
"updateTime": "2024-01-01 10:00:00",
|
||||
"menuIds": [1, 2, 3, 4, 5],
|
||||
"deptIds": [100, 101, 102]
|
||||
},
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
http://localhost:18099/coder/sysRole/getById/1 \
|
||||
-H "Authorization: your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 新增角色
|
||||
|
||||
**接口地址**: `POST /coder/sysRole/add`
|
||||
|
||||
**接口描述**: 新增系统角色
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:role:add`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"roleName": "普通用户",
|
||||
"roleCode": "user",
|
||||
"roleStatus": "0",
|
||||
"dataScope": "2",
|
||||
"deptCheckStrictly": "1",
|
||||
"menuCheckStrictly": "1",
|
||||
"sorted": 2,
|
||||
"remark": "普通用户角色",
|
||||
"menuIds": [1, 2, 3],
|
||||
"deptIds": [100, 101]
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|
||||
|--------|------|------|------|----------|
|
||||
| roleName | String | 是 | 角色名称 | 不能为空,最长30字符 |
|
||||
| roleCode | String | 是 | 角色编码 | 不能为空,最长100字符,唯一 |
|
||||
| roleStatus | String | 是 | 角色状态 | 0-正常 1-停用 |
|
||||
| dataScope | String | 否 | 数据范围 | 1-全部数据 2-自定义数据 3-本部门数据 4-本部门及以下数据 5-仅本人数据 |
|
||||
| deptCheckStrictly | String | 否 | 部门树选择项是否关联显示 | 0-父子不互相关联显示 1-父子互相关联显示 |
|
||||
| menuCheckStrictly | String | 否 | 菜单树选择项是否关联显示 | 0-父子不互相关联显示 1-父子互相关联显示 |
|
||||
| sorted | Integer | 是 | 显示顺序 | 不能为空 |
|
||||
| remark | String | 否 | 备注信息 | 最长200字符 |
|
||||
| menuIds | Long[] | 否 | 菜单ID数组 | 有效的菜单ID |
|
||||
| deptIds | Long[] | 否 | 部门ID数组 | 有效的部门ID |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "新增成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysRole/add \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: your-token-value" \
|
||||
-d '{
|
||||
"roleName": "普通用户",
|
||||
"roleCode": "user",
|
||||
"roleStatus": "0",
|
||||
"dataScope": "2",
|
||||
"deptCheckStrictly": "1",
|
||||
"menuCheckStrictly": "1",
|
||||
"sorted": 2,
|
||||
"remark": "普通用户角色",
|
||||
"menuIds": [1, 2, 3],
|
||||
"deptIds": [100, 101]
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. 获取最新排序号
|
||||
|
||||
**接口地址**: `GET /coder/sysRole/getSorted`
|
||||
|
||||
**接口描述**: 获取角色的最新排序号
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:role:add`
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": 10,
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
http://localhost:18099/coder/sysRole/getSorted \
|
||||
-H "Authorization: your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. 修改角色信息
|
||||
|
||||
**接口地址**: `POST /coder/sysRole/update`
|
||||
|
||||
**接口描述**: 修改系统角色信息
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:role:edit`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"roleId": 1,
|
||||
"roleName": "超级管理员",
|
||||
"roleCode": "admin",
|
||||
"roleStatus": "0",
|
||||
"dataScope": "1",
|
||||
"deptCheckStrictly": "1",
|
||||
"menuCheckStrictly": "1",
|
||||
"sorted": 1,
|
||||
"remark": "超级管理员",
|
||||
"menuIds": [1, 2, 3, 4, 5],
|
||||
"deptIds": [100, 101, 102]
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|
||||
|--------|------|------|------|----------|
|
||||
| roleId | Long | 是 | 角色ID | 必须是有效的角色ID |
|
||||
| 其他参数 | - | - | 同新增角色 | - |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "修改成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysRole/update \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: your-token-value" \
|
||||
-d '{
|
||||
"roleId": 1,
|
||||
"roleName": "超级管理员",
|
||||
"roleCode": "admin",
|
||||
"roleStatus": "0",
|
||||
"sorted": 1
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. 删除角色
|
||||
|
||||
**接口地址**: `POST /coder/sysRole/deleteById/{id}`
|
||||
|
||||
**接口描述**: 根据ID删除角色
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:role:remove`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| id | Long | 是 | 角色ID |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "删除成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysRole/deleteById/1 \
|
||||
-H "Authorization: your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8. 批量删除角色
|
||||
|
||||
**接口地址**: `POST /coder/sysRole/batchDelete`
|
||||
|
||||
**接口描述**: 批量删除角色
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:role:remove`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"ids": [1, 2, 3]
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| ids | Long[] | 是 | 角色ID数组 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "删除成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysRole/batchDelete \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: your-token-value" \
|
||||
-d '{
|
||||
"ids": [1, 2, 3]
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 9. 修改角色状态
|
||||
|
||||
**接口地址**: `POST /coder/sysRole/updateStatus/{roleId}/{roleStatus}`
|
||||
|
||||
**接口描述**: 修改角色状态(正常/停用)
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:role:edit`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| roleId | Long | 是 | 角色ID |
|
||||
| roleStatus | String | 是 | 角色状态(0-正常 1-停用) |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "修改成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysRole/updateStatus/1/0 \
|
||||
-H "Authorization: your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 10. 查询正常角色穿梭框
|
||||
|
||||
**接口地址**: `GET /coder/sysRole/listNormalRole/{userId}`
|
||||
|
||||
**接口描述**: 查询正常状态的角色用于穿梭框显示
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:user:role`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| userId | Long | 是 | 用户ID |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": [
|
||||
{
|
||||
"label": "超级管理员",
|
||||
"value": 1,
|
||||
"parentId": ""
|
||||
},
|
||||
{
|
||||
"label": "普通用户",
|
||||
"value": 2,
|
||||
"parentId": ""
|
||||
}
|
||||
],
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
http://localhost:18099/coder/sysRole/listNormalRole/1 \
|
||||
-H "Authorization: your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 11. 分配用户角色
|
||||
|
||||
**接口地址**: `GET /coder/sysRole/assignUserRole/{userId}/{roleIds}`
|
||||
|
||||
**接口描述**: 为用户分配角色
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:user:role`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| userId | Long | 是 | 用户ID |
|
||||
| roleIds | String | 是 | 角色ID列表(逗号分隔) |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "分配成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
http://localhost:18099/coder/sysRole/assignUserRole/1/1,2,3 \
|
||||
-H "Authorization: your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 12. 获取角色下拉框
|
||||
|
||||
**接口地址**: `GET /coder/sysRole/listRoleElSelect`
|
||||
|
||||
**接口描述**: 获取角色下拉框数据
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:role:list`
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": [
|
||||
{
|
||||
"label": "超级管理员",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"label": "普通用户",
|
||||
"value": 2
|
||||
}
|
||||
],
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
http://localhost:18099/coder/sysRole/listRoleElSelect \
|
||||
-H "Authorization: your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 角色字段说明
|
||||
|
||||
### 基础字段
|
||||
|
||||
| 字段名 | 类型 | 说明 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| roleId | Long | 角色ID | 1 |
|
||||
| roleName | String | 角色名称 | 超级管理员 |
|
||||
| roleCode | String | 角色编码 | admin |
|
||||
| roleStatus | String | 角色状态 | 0-正常 1-停用 |
|
||||
| sorted | Integer | 显示顺序 | 1 |
|
||||
| remark | String | 备注信息 | 超级管理员 |
|
||||
|
||||
### 权限字段
|
||||
|
||||
| 字段名 | 类型 | 说明 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| dataScope | String | 数据权限范围 | 1-全部数据 2-自定义数据 3-本部门数据 4-本部门及以下数据 5-仅本人数据 |
|
||||
| deptCheckStrictly | String | 部门树选择项是否关联显示 | 0-父子不互相关联显示 1-父子互相关联显示 |
|
||||
| menuCheckStrictly | String | 菜单树选择项是否关联显示 | 0-父子不互相关联显示 1-父子互相关联显示 |
|
||||
|
||||
### 关联字段
|
||||
|
||||
| 字段名 | 类型 | 说明 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| menuIds | Long[] | 菜单ID数组 | [1, 2, 3, 4, 5] |
|
||||
| deptIds | Long[] | 部门ID数组 | [100, 101, 102] |
|
||||
|
||||
---
|
||||
|
||||
## 数据字典
|
||||
|
||||
### 角色状态 (roleStatus)
|
||||
|
||||
| 值 | 说明 |
|
||||
|----|------|
|
||||
| 0 | 正常 |
|
||||
| 1 | 停用 |
|
||||
|
||||
### 数据权限范围 (dataScope)
|
||||
|
||||
| 值 | 说明 |
|
||||
|----|------|
|
||||
| 1 | 全部数据权限 |
|
||||
| 2 | 自定数据权限 |
|
||||
| 3 | 本部门数据权限 |
|
||||
| 4 | 本部门及以下数据权限 |
|
||||
| 5 | 仅本人数据权限 |
|
||||
|
||||
### 树选择项关联显示
|
||||
|
||||
| 值 | 说明 |
|
||||
|----|------|
|
||||
| 0 | 父子不互相关联显示 |
|
||||
| 1 | 父子互相关联显示 |
|
||||
|
||||
---
|
||||
|
||||
## 权限验证机制
|
||||
|
||||
### 1. 角色权限验证
|
||||
|
||||
```java
|
||||
// Sa-Token权限验证
|
||||
@SaCheckPermission("system:role:list")
|
||||
public List<SysRole> list() {
|
||||
// 业务逻辑
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 数据权限控制
|
||||
|
||||
```java
|
||||
// 根据用户数据权限过滤数据
|
||||
public List<SysRole> getRolesByDataScope(Long userId) {
|
||||
SysUser user = getCurrentUser();
|
||||
String dataScope = user.getDataScope();
|
||||
|
||||
switch (dataScope) {
|
||||
case "1": // 全部数据权限
|
||||
return getAllRoles();
|
||||
case "2": // 自定义数据权限
|
||||
return getRolesByDeptIds(user.getDeptIds());
|
||||
case "3": // 本部门数据权限
|
||||
return getRolesByDept(user.getDeptId());
|
||||
case "4": // 本部门及以下数据权限
|
||||
return getRolesByDeptAndChildren(user.getDeptId());
|
||||
case "5": // 仅本人数据权限
|
||||
return getRolesByUserId(userId);
|
||||
default:
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 角色权限缓存
|
||||
|
||||
```java
|
||||
// 缓存用户角色权限
|
||||
@Cacheable(value = "userRoles", key = "#userId")
|
||||
public List<String> getRolesByUserId(Long userId) {
|
||||
return roleMapper.selectRolePermissionByUserId(userId);
|
||||
}
|
||||
|
||||
// 清除角色权限缓存
|
||||
@CacheEvict(value = "userRoles", key = "#userId")
|
||||
public void clearUserRoleCache(Long userId) {
|
||||
// 角色变更时清除缓存
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 错误码说明
|
||||
|
||||
| 错误码 | 错误信息 | 说明 |
|
||||
|--------|----------|------|
|
||||
| 400 | 角色名称不能为空 | 角色名称为空 |
|
||||
| 400 | 角色权限编码不能为空 | 角色编码为空 |
|
||||
| 400 | 显示顺序不能为空 | 排序值为空 |
|
||||
| 400 | 角色不存在 | 角色ID不存在 |
|
||||
| 400 | 角色编码已存在 | 角色编码重复 |
|
||||
| 400 | 角色已分配用户,不允许删除 | 角色已分配给用户时不能删除 |
|
||||
| 400 | 不能删除超级管理员角色 | 超级管理员角色不能删除 |
|
||||
| 400 | 不能停用超级管理员角色 | 超级管理员角色不能停用 |
|
||||
| 400 | 用户不存在 | 用户ID不存在 |
|
||||
| 400 | 角色ID列表不能为空 | 分配角色时角色ID为空 |
|
||||
| 401 | 当前会话未登录 | 未登录或Token无效 |
|
||||
| 403 | 权限不足 | 没有相应的操作权限 |
|
||||
| 500 | 系统异常 | 服务器内部错误 |
|
||||
|
||||
---
|
||||
|
||||
## RBAC权限模型
|
||||
|
||||
### 1. 基本概念
|
||||
|
||||
- **用户 (User)**: 系统的使用者
|
||||
- **角色 (Role)**: 权限的集合,连接用户和权限的桥梁
|
||||
- **权限 (Permission)**: 对系统资源的操作权限
|
||||
- **会话 (Session)**: 用户与系统的一次交互过程
|
||||
|
||||
### 2. 关系模型
|
||||
|
||||
```
|
||||
用户 ←→ 用户角色关系 ←→ 角色 ←→ 角色权限关系 ←→ 权限
|
||||
```
|
||||
|
||||
### 3. 数据库设计
|
||||
|
||||
```sql
|
||||
-- 用户表
|
||||
CREATE TABLE sys_login_user (
|
||||
user_id BIGINT PRIMARY KEY,
|
||||
login_name VARCHAR(32) UNIQUE NOT NULL,
|
||||
user_name VARCHAR(32) NOT NULL,
|
||||
-- 其他字段...
|
||||
);
|
||||
|
||||
-- 角色表
|
||||
CREATE TABLE sys_role (
|
||||
role_id BIGINT PRIMARY KEY,
|
||||
role_name VARCHAR(32) NOT NULL,
|
||||
role_code VARCHAR(32) UNIQUE NOT NULL,
|
||||
role_status CHAR(1) DEFAULT '0',
|
||||
-- 其他字段...
|
||||
);
|
||||
|
||||
-- 权限表(菜单表)
|
||||
CREATE TABLE sys_menu (
|
||||
menu_id BIGINT PRIMARY KEY,
|
||||
menu_name VARCHAR(64) NOT NULL,
|
||||
auth VARCHAR(128),
|
||||
-- 其他字段...
|
||||
);
|
||||
|
||||
-- 用户角色关联表
|
||||
CREATE TABLE sys_user_role (
|
||||
user_id BIGINT NOT NULL,
|
||||
role_id BIGINT NOT NULL,
|
||||
PRIMARY KEY (user_id, role_id)
|
||||
);
|
||||
|
||||
-- 角色权限关联表
|
||||
CREATE TABLE sys_role_menu (
|
||||
role_id BIGINT NOT NULL,
|
||||
menu_id BIGINT NOT NULL,
|
||||
PRIMARY KEY (role_id, menu_id)
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用建议
|
||||
|
||||
### 1. 角色设计原则
|
||||
|
||||
- **最小权限原则**: 每个角色只分配必要的权限
|
||||
- **职责分离**: 不同职责的操作分配给不同角色
|
||||
- **角色层次**: 建立角色层次结构,高级角色继承低级角色权限
|
||||
- **定期审查**: 定期审查角色权限,及时调整
|
||||
|
||||
### 2. 权限分配策略
|
||||
|
||||
- **默认无权限**: 新建角色默认无任何权限
|
||||
- **逐步授权**: 根据业务需要逐步授权
|
||||
- **权限组合**: 通过多个角色组合实现复杂权限需求
|
||||
- **临时授权**: 支持临时权限授权机制
|
||||
|
||||
### 3. 数据权限控制
|
||||
|
||||
- **部门数据权限**: 基于用户所属部门控制数据访问范围
|
||||
- **自定义数据权限**: 支持自定义数据访问范围
|
||||
- **数据隔离**: 确保不同用户只能访问授权范围内的数据
|
||||
- **审计日志**: 记录数据访问日志
|
||||
|
||||
### 4. 性能优化
|
||||
|
||||
- **权限缓存**: 将用户权限信息缓存到Redis
|
||||
- **批量操作**: 支持批量分配和撤销权限
|
||||
- **索引优化**: 对关联表建立合适的索引
|
||||
- **分页查询**: 大数据量时使用分页查询
|
||||
|
||||
---
|
||||
|
||||
## 安全考虑
|
||||
|
||||
### 1. 权限验证
|
||||
|
||||
- **双重验证**: 前后端都要进行权限验证
|
||||
- **接口鉴权**: 每个接口都要验证权限
|
||||
- **数据鉴权**: 数据层面的权限验证
|
||||
- **操作鉴权**: 敏感操作的二次验证
|
||||
|
||||
### 2. 权限变更
|
||||
|
||||
- **变更日志**: 记录所有权限变更操作
|
||||
- **审批流程**: 重要权限变更需要审批
|
||||
- **通知机制**: 权限变更及时通知相关人员
|
||||
- **回滚机制**: 支持权限变更回滚
|
||||
|
||||
### 3. 会话安全
|
||||
|
||||
- **会话超时**: 设置会话超时时间
|
||||
- **单点登录**: 防止重复登录
|
||||
- **强制下线**: 支持强制用户下线
|
||||
- **设备绑定**: 支持设备级别的访问控制
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **角色删除**: 删除角色前需要检查是否有用户关联
|
||||
2. **权限继承**: 考虑角色之间的权限继承关系
|
||||
3. **缓存一致性**: 角色权限变更后需要清理相关缓存
|
||||
4. **数据完整性**: 保证角色权限数据的完整性
|
||||
5. **超级管理员**: 超级管理员角色不能删除和停用
|
||||
6. **权限验证**: 前后端都需要进行权限验证
|
||||
7. **审计日志**: 记录所有角色操作日志
|
||||
8. **性能考虑**: 大量用户时注意权限验证性能
|
||||
9. **数据权限**: 正确配置数据权限范围
|
||||
10. **定期清理**: 定期清理无效的角色和权限关联
|
||||
871
api/system/图片管理API.md
Normal file
871
api/system/图片管理API.md
Normal file
@ -0,0 +1,871 @@
|
||||
# 图片管理API
|
||||
|
||||
## 概述
|
||||
|
||||
图片管理模块专门用于管理系统中的图片资源,提供图片的上传、存储、展示和管理功能。支持多种图片格式,具备图片压缩、水印、缩略图生成等高级功能。
|
||||
|
||||
## 权限说明
|
||||
|
||||
图片管理接口需要相应的权限才能访问:
|
||||
|
||||
| 操作 | 权限码 | 说明 |
|
||||
|------|--------|------|
|
||||
| 查询图片列表 | `system:picture:list` | 查看图片列表权限 |
|
||||
| 上传图片 | `system:picture:upload` | 上传图片权限 |
|
||||
| 删除图片 | `system:picture:remove` | 删除图片权限 |
|
||||
| 编辑图片 | `system:picture:edit` | 编辑图片信息权限 |
|
||||
|
||||
## 接口列表
|
||||
|
||||
### 1. 分页查询图片列表
|
||||
|
||||
**接口地址**: `GET /coder/sysPicture/listPage`
|
||||
|
||||
**接口描述**: 分页查询系统图片列表
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:picture:list`
|
||||
|
||||
**请求头**:
|
||||
```
|
||||
Authorization: Bearer your-token-value
|
||||
```
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
||||
|--------|------|------|------|------|
|
||||
| pageNo | Integer | 否 | 页码 | 1 |
|
||||
| pageSize | Integer | 否 | 每页大小 | 10 |
|
||||
| pictureName | String | 否 | 图片名称 | banner.jpg |
|
||||
| pictureType | String | 否 | 图片类型 | jpg |
|
||||
| albumName | String | 否 | 相册名称 | banner |
|
||||
| pictureStatus | String | 否 | 图片状态 | 0 |
|
||||
| beginTime | String | 否 | 开始时间 | 2024-01-01 |
|
||||
| endTime | String | 否 | 结束时间 | 2024-12-31 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"records": [
|
||||
{
|
||||
"pictureId": 1,
|
||||
"pictureName": "banner_20240705_001.jpg",
|
||||
"originalName": "主页横幅.jpg",
|
||||
"picturePath": "/upload/pictures/banner/2024/07/05/banner_20240705_001.jpg",
|
||||
"pictureUrl": "http://localhost:18099/upload/pictures/banner/2024/07/05/banner_20240705_001.jpg",
|
||||
"thumbnailUrl": "http://localhost:18099/upload/pictures/banner/2024/07/05/thumb_banner_20240705_001.jpg",
|
||||
"albumName": "banner",
|
||||
"pictureSize": 2048000,
|
||||
"pictureType": "jpg",
|
||||
"pictureFormat": "JPEG",
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"pictureStatus": "0",
|
||||
"isWatermark": "0",
|
||||
"uploadUserId": 1,
|
||||
"uploadUserName": "admin",
|
||||
"description": "主页横幅图片",
|
||||
"tags": "横幅,主页,展示",
|
||||
"viewCount": 100,
|
||||
"downloadCount": 10,
|
||||
"remark": "网站主页横幅",
|
||||
"createBy": "admin",
|
||||
"createTime": "2024-07-05 10:00:00",
|
||||
"updateBy": "admin",
|
||||
"updateTime": "2024-07-05 10:00:00"
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"size": 10,
|
||||
"current": 1,
|
||||
"pages": 1
|
||||
},
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
"http://localhost:18099/coder/sysPicture/listPage?pageNo=1&pageSize=10&albumName=banner" \
|
||||
-H "Authorization: Bearer your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 查询所有图片
|
||||
|
||||
**接口地址**: `GET /coder/sysPicture/list`
|
||||
|
||||
**接口描述**: 查询所有系统图片(不分页)
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:picture:list`
|
||||
|
||||
**请求参数**: 同分页查询(除pageNo、pageSize外)
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": [
|
||||
{
|
||||
"pictureId": 1,
|
||||
"pictureName": "banner_20240705_001.jpg",
|
||||
"originalName": "主页横幅.jpg",
|
||||
"picturePath": "/upload/pictures/banner/2024/07/05/banner_20240705_001.jpg",
|
||||
"pictureUrl": "http://localhost:18099/upload/pictures/banner/2024/07/05/banner_20240705_001.jpg",
|
||||
"thumbnailUrl": "http://localhost:18099/upload/pictures/banner/2024/07/05/thumb_banner_20240705_001.jpg",
|
||||
"albumName": "banner",
|
||||
"pictureSize": 2048000,
|
||||
"pictureType": "jpg",
|
||||
"pictureFormat": "JPEG",
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"pictureStatus": "0",
|
||||
"isWatermark": "0",
|
||||
"uploadUserId": 1,
|
||||
"uploadUserName": "admin",
|
||||
"description": "主页横幅图片",
|
||||
"tags": "横幅,主页,展示",
|
||||
"viewCount": 100,
|
||||
"downloadCount": 10,
|
||||
"remark": "网站主页横幅",
|
||||
"createBy": "admin",
|
||||
"createTime": "2024-07-05 10:00:00",
|
||||
"updateBy": "admin",
|
||||
"updateTime": "2024-07-05 10:00:00"
|
||||
}
|
||||
],
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
"http://localhost:18099/coder/sysPicture/list?albumName=banner" \
|
||||
-H "Authorization: Bearer your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 根据ID查询图片
|
||||
|
||||
**接口地址**: `GET /coder/sysPicture/getById/{id}`
|
||||
|
||||
**接口描述**: 根据图片ID查询图片详细信息
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:picture:list`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| id | Long | 是 | 图片ID |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"pictureId": 1,
|
||||
"pictureName": "banner_20240705_001.jpg",
|
||||
"originalName": "主页横幅.jpg",
|
||||
"picturePath": "/upload/pictures/banner/2024/07/05/banner_20240705_001.jpg",
|
||||
"pictureUrl": "http://localhost:18099/upload/pictures/banner/2024/07/05/banner_20240705_001.jpg",
|
||||
"thumbnailUrl": "http://localhost:18099/upload/pictures/banner/2024/07/05/thumb_banner_20240705_001.jpg",
|
||||
"albumName": "banner",
|
||||
"pictureSize": 2048000,
|
||||
"pictureType": "jpg",
|
||||
"pictureFormat": "JPEG",
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"pictureStatus": "0",
|
||||
"isWatermark": "0",
|
||||
"uploadUserId": 1,
|
||||
"uploadUserName": "admin",
|
||||
"description": "主页横幅图片",
|
||||
"tags": "横幅,主页,展示",
|
||||
"viewCount": 100,
|
||||
"downloadCount": 10,
|
||||
"remark": "网站主页横幅",
|
||||
"createBy": "admin",
|
||||
"createTime": "2024-07-05 10:00:00",
|
||||
"updateBy": "admin",
|
||||
"updateTime": "2024-07-05 10:00:00"
|
||||
},
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
http://localhost:18099/coder/sysPicture/getById/1 \
|
||||
-H "Authorization: Bearer your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 新增图片记录
|
||||
|
||||
**接口地址**: `POST /coder/sysPicture/add`
|
||||
|
||||
**接口描述**: 新增系统图片记录
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:picture:upload`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"pictureName": "product_20240705_001.jpg",
|
||||
"originalName": "产品展示图.jpg",
|
||||
"picturePath": "/upload/pictures/product/2024/07/05/product_20240705_001.jpg",
|
||||
"pictureUrl": "http://localhost:18099/upload/pictures/product/2024/07/05/product_20240705_001.jpg",
|
||||
"thumbnailUrl": "http://localhost:18099/upload/pictures/product/2024/07/05/thumb_product_20240705_001.jpg",
|
||||
"albumName": "product",
|
||||
"pictureSize": 1536000,
|
||||
"pictureType": "jpg",
|
||||
"pictureFormat": "JPEG",
|
||||
"width": 1200,
|
||||
"height": 800,
|
||||
"pictureStatus": "0",
|
||||
"isWatermark": "1",
|
||||
"uploadUserId": 1,
|
||||
"uploadUserName": "admin",
|
||||
"description": "产品展示图片",
|
||||
"tags": "产品,展示,商品",
|
||||
"remark": "商品详情页图片"
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|
||||
|--------|------|------|------|----------|
|
||||
| pictureName | String | 是 | 存储图片名 | 不能为空 |
|
||||
| originalName | String | 是 | 原始图片名 | 不能为空 |
|
||||
| picturePath | String | 是 | 图片相对路径 | 不能为空 |
|
||||
| pictureUrl | String | 是 | 图片访问URL | 不能为空 |
|
||||
| thumbnailUrl | String | 否 | 缩略图URL | 可为空 |
|
||||
| albumName | String | 是 | 相册名称 | 不能为空 |
|
||||
| pictureSize | Long | 是 | 图片大小 | 必须大于0 |
|
||||
| pictureType | String | 是 | 图片类型 | 不能为空 |
|
||||
| pictureFormat | String | 是 | 图片格式 | 不能为空 |
|
||||
| width | Integer | 否 | 图片宽度 | 大于0 |
|
||||
| height | Integer | 否 | 图片高度 | 大于0 |
|
||||
| pictureStatus | String | 是 | 图片状态 | 0-正常 1-删除 |
|
||||
| isWatermark | String | 否 | 是否水印 | 0-否 1-是 |
|
||||
| uploadUserId | Long | 否 | 上传用户ID | 有效的用户ID |
|
||||
| uploadUserName | String | 否 | 上传用户名 | 可为空 |
|
||||
| description | String | 否 | 图片描述 | 最长500字符 |
|
||||
| tags | String | 否 | 图片标签 | 逗号分隔 |
|
||||
| remark | String | 否 | 备注信息 | 最长200字符 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "新增成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysPicture/add \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer your-token-value" \
|
||||
-d '{
|
||||
"pictureName": "product_20240705_001.jpg",
|
||||
"originalName": "产品展示图.jpg",
|
||||
"picturePath": "/upload/pictures/product/2024/07/05/product_20240705_001.jpg",
|
||||
"pictureUrl": "http://localhost:18099/upload/pictures/product/2024/07/05/product_20240705_001.jpg",
|
||||
"albumName": "product",
|
||||
"pictureSize": 1536000,
|
||||
"pictureType": "jpg",
|
||||
"pictureFormat": "JPEG",
|
||||
"width": 1200,
|
||||
"height": 800,
|
||||
"pictureStatus": "0",
|
||||
"description": "产品展示图片",
|
||||
"tags": "产品,展示,商品"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. 修改图片信息
|
||||
|
||||
**接口地址**: `POST /coder/sysPicture/update`
|
||||
|
||||
**接口描述**: 修改系统图片记录信息
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:picture:edit`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"pictureId": 1,
|
||||
"pictureName": "product_20240705_001.jpg",
|
||||
"originalName": "产品展示图.jpg",
|
||||
"picturePath": "/upload/pictures/product/2024/07/05/product_20240705_001.jpg",
|
||||
"pictureUrl": "http://localhost:18099/upload/pictures/product/2024/07/05/product_20240705_001.jpg",
|
||||
"thumbnailUrl": "http://localhost:18099/upload/pictures/product/2024/07/05/thumb_product_20240705_001.jpg",
|
||||
"albumName": "product",
|
||||
"pictureSize": 1536000,
|
||||
"pictureType": "jpg",
|
||||
"pictureFormat": "JPEG",
|
||||
"width": 1200,
|
||||
"height": 800,
|
||||
"pictureStatus": "0",
|
||||
"isWatermark": "1",
|
||||
"uploadUserId": 1,
|
||||
"uploadUserName": "admin",
|
||||
"description": "产品展示图片(已更新)",
|
||||
"tags": "产品,展示,商品,新品",
|
||||
"remark": "商品详情页图片(已更新)"
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|
||||
|--------|------|------|------|----------|
|
||||
| pictureId | Long | 是 | 图片ID | 必须是有效的图片ID |
|
||||
| 其他参数 | - | - | 同新增图片记录 | - |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "修改成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysPicture/update \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer your-token-value" \
|
||||
-d '{
|
||||
"pictureId": 1,
|
||||
"description": "产品展示图片(已更新)",
|
||||
"tags": "产品,展示,商品,新品"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. 删除图片
|
||||
|
||||
**接口地址**: `POST /coder/sysPicture/deleteById/{id}`
|
||||
|
||||
**接口描述**: 根据ID删除图片记录和物理文件
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:picture:remove`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| id | Long | 是 | 图片ID |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "删除成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysPicture/deleteById/1 \
|
||||
-H "Authorization: Bearer your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. 批量删除图片
|
||||
|
||||
**接口地址**: `POST /coder/sysPicture/batchDelete`
|
||||
|
||||
**接口描述**: 批量删除图片记录和物理文件
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:picture:remove`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"ids": [1, 2, 3]
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| ids | Long[] | 是 | 图片ID数组 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "删除成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysPicture/batchDelete \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer your-token-value" \
|
||||
-d '{
|
||||
"ids": [1, 2, 3]
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 图片字段说明
|
||||
|
||||
### 基础字段
|
||||
|
||||
| 字段名 | 类型 | 说明 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| pictureId | Long | 图片ID | 1 |
|
||||
| pictureName | String | 存储图片名 | banner_20240705_001.jpg |
|
||||
| originalName | String | 原始图片名 | 主页横幅.jpg |
|
||||
| picturePath | String | 图片相对路径 | /upload/pictures/banner/2024/07/05/banner_20240705_001.jpg |
|
||||
| pictureUrl | String | 图片访问URL | http://localhost:18099/upload/pictures/banner/2024/07/05/banner_20240705_001.jpg |
|
||||
| thumbnailUrl | String | 缩略图URL | http://localhost:18099/upload/pictures/banner/2024/07/05/thumb_banner_20240705_001.jpg |
|
||||
|
||||
### 分类字段
|
||||
|
||||
| 字段名 | 类型 | 说明 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| albumName | String | 相册名称 | banner |
|
||||
| pictureType | String | 图片扩展名 | jpg |
|
||||
| pictureFormat | String | 图片格式 | JPEG |
|
||||
| pictureSize | Long | 图片大小(字节) | 2048000 |
|
||||
|
||||
### 尺寸字段
|
||||
|
||||
| 字段名 | 类型 | 说明 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| width | Integer | 图片宽度(像素) | 1920 |
|
||||
| height | Integer | 图片高度(像素) | 1080 |
|
||||
|
||||
### 状态字段
|
||||
|
||||
| 字段名 | 类型 | 说明 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| pictureStatus | String | 图片状态 | 0-正常 1-删除 |
|
||||
| isWatermark | String | 是否有水印 | 0-否 1-是 |
|
||||
| uploadUserId | Long | 上传用户ID | 1 |
|
||||
| uploadUserName | String | 上传用户名 | admin |
|
||||
|
||||
### 描述字段
|
||||
|
||||
| 字段名 | 类型 | 说明 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| description | String | 图片描述 | 主页横幅图片 |
|
||||
| tags | String | 图片标签 | 横幅,主页,展示 |
|
||||
| remark | String | 备注信息 | 网站主页横幅 |
|
||||
|
||||
### 统计字段
|
||||
|
||||
| 字段名 | 类型 | 说明 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| viewCount | Integer | 查看次数 | 100 |
|
||||
| downloadCount | Integer | 下载次数 | 10 |
|
||||
|
||||
---
|
||||
|
||||
## 数据字典
|
||||
|
||||
### 图片状态 (pictureStatus)
|
||||
|
||||
| 值 | 说明 |
|
||||
|----|------|
|
||||
| 0 | 正常 |
|
||||
| 1 | 已删除 |
|
||||
|
||||
### 是否水印 (isWatermark)
|
||||
|
||||
| 值 | 说明 |
|
||||
|----|------|
|
||||
| 0 | 无水印 |
|
||||
| 1 | 有水印 |
|
||||
|
||||
### 支持的图片格式
|
||||
|
||||
| 格式 | 扩展名 | MIME类型 | 说明 |
|
||||
|------|--------|----------|------|
|
||||
| JPEG | jpg, jpeg | image/jpeg | 有损压缩,适合照片 |
|
||||
| PNG | png | image/png | 无损压缩,支持透明 |
|
||||
| GIF | gif | image/gif | 支持动画 |
|
||||
| WebP | webp | image/webp | 现代格式,压缩率高 |
|
||||
| BMP | bmp | image/bmp | 位图格式 |
|
||||
| TIFF | tiff, tif | image/tiff | 高质量图像 |
|
||||
|
||||
### 相册分类
|
||||
|
||||
| 相册名称 | 说明 | 用途 |
|
||||
|----------|------|------|
|
||||
| banner | 横幅图片 | 网站横幅展示 |
|
||||
| product | 产品图片 | 商品展示 |
|
||||
| avatar | 头像图片 | 用户头像 |
|
||||
| gallery | 图库图片 | 相册展示 |
|
||||
| article | 文章图片 | 文章配图 |
|
||||
| icon | 图标图片 | 小图标 |
|
||||
| background | 背景图片 | 页面背景 |
|
||||
|
||||
---
|
||||
|
||||
## 图片处理功能
|
||||
|
||||
### 1. 图片上传处理
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class PictureUploadService {
|
||||
|
||||
/**
|
||||
* 上传图片
|
||||
*/
|
||||
public PictureUploadResult uploadPicture(MultipartFile file, String album) {
|
||||
// 1. 图片校验
|
||||
validatePicture(file);
|
||||
|
||||
// 2. 生成文件名
|
||||
String fileName = generatePictureName(file.getOriginalFilename());
|
||||
|
||||
// 3. 保存原图
|
||||
String originalPath = savePicture(file, album, fileName);
|
||||
|
||||
// 4. 生成缩略图
|
||||
String thumbnailPath = generateThumbnail(originalPath, album, fileName);
|
||||
|
||||
// 5. 添加水印
|
||||
if (needWatermark(album)) {
|
||||
addWatermark(originalPath);
|
||||
}
|
||||
|
||||
// 6. 获取图片信息
|
||||
BufferedImage image = ImageIO.read(new File(originalPath));
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
|
||||
// 7. 生成访问URL
|
||||
String pictureUrl = generatePictureUrl(album, fileName);
|
||||
String thumbnailUrl = generateThumbnailUrl(album, fileName);
|
||||
|
||||
// 8. 返回结果
|
||||
return PictureUploadResult.builder()
|
||||
.pictureName(fileName)
|
||||
.originalName(file.getOriginalFilename())
|
||||
.picturePath(originalPath)
|
||||
.pictureUrl(pictureUrl)
|
||||
.thumbnailUrl(thumbnailUrl)
|
||||
.pictureSize(file.getSize())
|
||||
.pictureType(getFileExtension(fileName))
|
||||
.pictureFormat(getImageFormat(file))
|
||||
.width(width)
|
||||
.height(height)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成缩略图
|
||||
*/
|
||||
private String generateThumbnail(String originalPath, String album, String fileName) {
|
||||
try {
|
||||
BufferedImage originalImage = ImageIO.read(new File(originalPath));
|
||||
|
||||
// 计算缩略图尺寸
|
||||
int thumbnailWidth = 200;
|
||||
int thumbnailHeight = 150;
|
||||
|
||||
// 保持宽高比
|
||||
double ratio = Math.min((double) thumbnailWidth / originalImage.getWidth(),
|
||||
(double) thumbnailHeight / originalImage.getHeight());
|
||||
int width = (int) (originalImage.getWidth() * ratio);
|
||||
int height = (int) (originalImage.getHeight() * ratio);
|
||||
|
||||
// 生成缩略图
|
||||
BufferedImage thumbnailImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g2d = thumbnailImage.createGraphics();
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2d.drawImage(originalImage, 0, 0, width, height, null);
|
||||
g2d.dispose();
|
||||
|
||||
// 保存缩略图
|
||||
String thumbnailPath = getThumbnailPath(album, fileName);
|
||||
ImageIO.write(thumbnailImage, "jpg", new File(thumbnailPath));
|
||||
|
||||
return thumbnailPath;
|
||||
} catch (IOException e) {
|
||||
throw new BusinessException("生成缩略图失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加水印
|
||||
*/
|
||||
private void addWatermark(String imagePath) {
|
||||
try {
|
||||
BufferedImage originalImage = ImageIO.read(new File(imagePath));
|
||||
Graphics2D g2d = originalImage.createGraphics();
|
||||
|
||||
// 设置水印属性
|
||||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
|
||||
g2d.setColor(Color.LIGHT_GRAY);
|
||||
g2d.setFont(new Font("Arial", Font.BOLD, 20));
|
||||
|
||||
// 计算水印位置
|
||||
FontMetrics fm = g2d.getFontMetrics();
|
||||
String watermarkText = "© Your Company";
|
||||
int x = originalImage.getWidth() - fm.stringWidth(watermarkText) - 10;
|
||||
int y = originalImage.getHeight() - fm.getHeight() + fm.getAscent() - 10;
|
||||
|
||||
// 绘制水印
|
||||
g2d.drawString(watermarkText, x, y);
|
||||
g2d.dispose();
|
||||
|
||||
// 保存带水印的图片
|
||||
ImageIO.write(originalImage, "jpg", new File(imagePath));
|
||||
} catch (IOException e) {
|
||||
throw new BusinessException("添加水印失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 图片压缩
|
||||
|
||||
```java
|
||||
/**
|
||||
* 图片压缩
|
||||
*/
|
||||
public void compressPicture(String inputPath, String outputPath, float quality) {
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(new File(inputPath));
|
||||
|
||||
// 创建输出流
|
||||
FileOutputStream fos = new FileOutputStream(outputPath);
|
||||
|
||||
// 获取JPEG写入器
|
||||
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
|
||||
ImageWriter writer = writers.next();
|
||||
|
||||
// 设置输出
|
||||
ImageOutputStream ios = ImageIO.createImageOutputStream(fos);
|
||||
writer.setOutput(ios);
|
||||
|
||||
// 设置压缩参数
|
||||
ImageWriteParam param = writer.getDefaultWriteParam();
|
||||
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||
param.setCompressionQuality(quality); // 压缩质量 0.0-1.0
|
||||
|
||||
// 写入图片
|
||||
writer.write(null, new IIOImage(image, null, null), param);
|
||||
|
||||
// 清理资源
|
||||
ios.close();
|
||||
fos.close();
|
||||
writer.dispose();
|
||||
} catch (IOException e) {
|
||||
throw new BusinessException("图片压缩失败", e);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 图片格式转换
|
||||
|
||||
```java
|
||||
/**
|
||||
* 图片格式转换
|
||||
*/
|
||||
public void convertPictureFormat(String inputPath, String outputPath, String format) {
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(new File(inputPath));
|
||||
|
||||
// 如果转换为JPEG,需要处理透明背景
|
||||
if ("jpg".equalsIgnoreCase(format) || "jpeg".equalsIgnoreCase(format)) {
|
||||
BufferedImage jpegImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g2d = jpegImage.createGraphics();
|
||||
g2d.setColor(Color.WHITE);
|
||||
g2d.fillRect(0, 0, image.getWidth(), image.getHeight());
|
||||
g2d.drawImage(image, 0, 0, null);
|
||||
g2d.dispose();
|
||||
image = jpegImage;
|
||||
}
|
||||
|
||||
// 保存转换后的图片
|
||||
ImageIO.write(image, format, new File(outputPath));
|
||||
} catch (IOException e) {
|
||||
throw new BusinessException("图片格式转换失败", e);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 错误码说明
|
||||
|
||||
| 错误码 | 错误信息 | 说明 |
|
||||
|--------|----------|------|
|
||||
| 400 | 图片不能为空 | 上传图片为空 |
|
||||
| 400 | 图片大小不能超过{size}MB | 图片大小超过限制 |
|
||||
| 400 | 不支持的图片格式 | 图片格式不支持 |
|
||||
| 400 | 图片尺寸超过限制 | 图片宽高超过限制 |
|
||||
| 400 | 图片名称不能为空 | 图片名称为空 |
|
||||
| 400 | 相册名称不能为空 | 相册名称为空 |
|
||||
| 400 | 图片不存在 | 图片ID不存在 |
|
||||
| 400 | 图片已被删除 | 图片状态为已删除 |
|
||||
| 401 | 当前会话未登录 | 未登录或Token无效 |
|
||||
| 403 | 权限不足 | 没有相应的操作权限 |
|
||||
| 500 | 图片上传失败 | 图片保存到磁盘失败 |
|
||||
| 500 | 缩略图生成失败 | 缩略图生成过程失败 |
|
||||
| 500 | 水印添加失败 | 水印添加过程失败 |
|
||||
| 500 | 图片压缩失败 | 图片压缩过程失败 |
|
||||
| 500 | 图片删除失败 | 物理文件删除失败 |
|
||||
|
||||
---
|
||||
|
||||
## 安全特性
|
||||
|
||||
### 1. 图片验证
|
||||
|
||||
- **格式验证**: 验证图片格式和MIME类型
|
||||
- **尺寸验证**: 验证图片宽高限制
|
||||
- **内容验证**: 验证图片内容安全性
|
||||
- **病毒扫描**: 对上传图片进行病毒扫描
|
||||
|
||||
### 2. 访问控制
|
||||
|
||||
- **权限验证**: 验证用户访问权限
|
||||
- **防盗链**: 防止图片被盗链
|
||||
- **水印保护**: 添加水印保护版权
|
||||
- **下载限制**: 限制图片下载次数
|
||||
|
||||
### 3. 存储安全
|
||||
|
||||
- **路径安全**: 防止路径遍历攻击
|
||||
- **文件重命名**: 重命名避免冲突
|
||||
- **备份策略**: 重要图片定期备份
|
||||
- **权限设置**: 设置合适的文件权限
|
||||
|
||||
---
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 1. 图片处理优化
|
||||
|
||||
- **异步处理**: 图片压缩和水印添加异步处理
|
||||
- **批量处理**: 支持批量图片处理
|
||||
- **缓存策略**: 缓存处理后的图片
|
||||
- **CDN加速**: 使用CDN加速图片访问
|
||||
|
||||
### 2. 存储优化
|
||||
|
||||
- **分目录存储**: 按相册和日期分目录存储
|
||||
- **格式优化**: 自动选择最优图片格式
|
||||
- **压缩存储**: 自动压缩降低存储空间
|
||||
- **重复检测**: 检测重复图片避免冗余
|
||||
|
||||
### 3. 访问优化
|
||||
|
||||
- **懒加载**: 图片懒加载技术
|
||||
- **响应式图片**: 根据设备提供不同尺寸
|
||||
- **WebP支持**: 支持WebP格式提高性能
|
||||
- **预加载**: 关键图片预加载
|
||||
|
||||
---
|
||||
|
||||
## 使用建议
|
||||
|
||||
### 1. 图片命名规范
|
||||
|
||||
- **唯一性**: 确保图片名唯一
|
||||
- **时间戳**: 包含时间戳信息
|
||||
- **分类标识**: 包含相册分类信息
|
||||
- **版本控制**: 支持图片版本管理
|
||||
|
||||
### 2. 相册管理
|
||||
|
||||
- **分类管理**: 按用途分类管理图片
|
||||
- **权限控制**: 不同相册设置不同权限
|
||||
- **容量限制**: 设置相册容量限制
|
||||
- **定期清理**: 定期清理无用图片
|
||||
|
||||
### 3. 图片优化
|
||||
|
||||
- **尺寸适配**: 根据用途选择合适尺寸
|
||||
- **格式选择**: 根据内容选择最优格式
|
||||
- **质量平衡**: 平衡图片质量和文件大小
|
||||
- **缓存策略**: 设置合适的缓存时间
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **图片安全**: 严格验证上传图片格式和内容
|
||||
2. **版权保护**: 添加水印保护图片版权
|
||||
3. **存储管理**: 定期清理无效和重复图片
|
||||
4. **性能考虑**: 大图片上传时注意性能影响
|
||||
5. **备份策略**: 重要图片需要备份
|
||||
6. **访问控制**: 合理设置图片访问权限
|
||||
7. **格式支持**: 根据需要支持不同图片格式
|
||||
8. **缩略图**: 为提高加载速度生成缩略图
|
||||
9. **压缩处理**: 适当压缩减少存储空间
|
||||
10. **监控告警**: 监控存储空间和访问异常
|
||||
867
api/system/文件管理API.md
Normal file
867
api/system/文件管理API.md
Normal file
@ -0,0 +1,867 @@
|
||||
# 文件管理API
|
||||
|
||||
## 概述
|
||||
|
||||
文件管理模块提供文件上传、下载、存储管理等功能。支持多种文件类型,提供文件记录管理和批量操作功能。分为通用文件上传接口和系统文件记录管理两部分。
|
||||
|
||||
## 权限说明
|
||||
|
||||
文件管理接口需要相应的权限才能访问:
|
||||
|
||||
| 操作 | 权限码 | 说明 |
|
||||
|------|--------|------|
|
||||
| 查询文件列表 | `system:file:list` | 查看文件列表权限 |
|
||||
| 上传文件 | `system:file:upload` | 上传文件权限 |
|
||||
| 下载文件 | `system:file:download` | 下载文件权限 |
|
||||
| 删除文件 | `system:file:remove` | 删除文件权限 |
|
||||
|
||||
## 文件上传接口
|
||||
|
||||
### 1. 上传文件(需认证)
|
||||
|
||||
**接口地址**: `POST /coder/file/uploadFile/{fileSize}/{folderName}/{fileParam}`
|
||||
|
||||
**接口描述**: 上传文件到服务器(需要登录认证)
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:file:upload`
|
||||
|
||||
**请求头**:
|
||||
```
|
||||
Authorization: Bearer your-token-value
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
||||
|--------|------|------|------|------|
|
||||
| fileSize | Integer | 是 | 文件大小限制(MB) | 10 |
|
||||
| folderName | String | 是 | 存储文件夹名称 | avatar |
|
||||
| fileParam | String | 是 | 文件参数名 | file |
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| file | File | 是 | 上传的文件 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"fileName": "avatar.jpg",
|
||||
"originalName": "my-avatar.jpg",
|
||||
"filePath": "/upload/avatar/2024/07/05/avatar_20240705_001.jpg",
|
||||
"fileUrl": "http://localhost:18099/upload/avatar/2024/07/05/avatar_20240705_001.jpg",
|
||||
"fileSize": 1024000,
|
||||
"fileType": "jpg",
|
||||
"uploadTime": "2024-07-05 10:00:00"
|
||||
},
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
|--------|------|------|
|
||||
| fileName | String | 存储文件名 |
|
||||
| originalName | String | 原始文件名 |
|
||||
| filePath | String | 文件相对路径 |
|
||||
| fileUrl | String | 文件访问URL |
|
||||
| fileSize | Long | 文件大小(字节) |
|
||||
| fileType | String | 文件类型 |
|
||||
| uploadTime | String | 上传时间 |
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/file/uploadFile/10/avatar/file \
|
||||
-H "Authorization: Bearer your-token-value" \
|
||||
-F "file=@avatar.jpg"
|
||||
```
|
||||
|
||||
**前端使用示例**:
|
||||
|
||||
```javascript
|
||||
// 使用FormData上传文件
|
||||
function uploadFile(file, folder, maxSize) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
fetch(`/coder/file/uploadFile/${maxSize}/${folder}/file`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + localStorage.getItem('token')
|
||||
},
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 200) {
|
||||
console.log('上传成功:', data.data.fileUrl);
|
||||
} else {
|
||||
console.error('上传失败:', data.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 匿名上传文件
|
||||
|
||||
**接口地址**: `POST /coder/file/uploadAnyFile/{fileSize}/{folderName}/{fileParam}`
|
||||
|
||||
**接口描述**: 匿名上传文件到服务器(无需登录认证)
|
||||
|
||||
**是否需要认证**: 否
|
||||
|
||||
**权限要求**: 无
|
||||
|
||||
**路径参数**: 同上传文件接口
|
||||
|
||||
**请求参数**: 同上传文件接口
|
||||
|
||||
**响应示例**: 同上传文件接口
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/file/uploadAnyFile/5/temp/file \
|
||||
-F "file=@document.pdf"
|
||||
```
|
||||
|
||||
**使用场景**:
|
||||
- 用户注册时上传头像
|
||||
- 访客上传临时文件
|
||||
- 公开资源上传
|
||||
|
||||
---
|
||||
|
||||
## 系统文件记录管理接口
|
||||
|
||||
### 1. 分页查询文件列表
|
||||
|
||||
**接口地址**: `GET /coder/sysFile/listPage`
|
||||
|
||||
**接口描述**: 分页查询系统文件记录列表
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:file:list`
|
||||
|
||||
**请求头**:
|
||||
```
|
||||
Authorization: Bearer your-token-value
|
||||
```
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
||||
|--------|------|------|------|------|
|
||||
| pageNo | Integer | 否 | 页码 | 1 |
|
||||
| pageSize | Integer | 否 | 每页大小 | 10 |
|
||||
| fileName | String | 否 | 文件名称 | avatar.jpg |
|
||||
| fileType | String | 否 | 文件类型 | jpg |
|
||||
| folderName | String | 否 | 文件夹名称 | avatar |
|
||||
| beginTime | String | 否 | 开始时间 | 2024-01-01 |
|
||||
| endTime | String | 否 | 结束时间 | 2024-12-31 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"records": [
|
||||
{
|
||||
"fileId": 1,
|
||||
"fileName": "avatar_20240705_001.jpg",
|
||||
"originalName": "my-avatar.jpg",
|
||||
"filePath": "/upload/avatar/2024/07/05/avatar_20240705_001.jpg",
|
||||
"fileUrl": "http://localhost:18099/upload/avatar/2024/07/05/avatar_20240705_001.jpg",
|
||||
"folderName": "avatar",
|
||||
"fileSize": 1024000,
|
||||
"fileType": "jpg",
|
||||
"contentType": "image/jpeg",
|
||||
"fileStatus": "0",
|
||||
"uploadUserId": 1,
|
||||
"uploadUserName": "admin",
|
||||
"remark": "用户头像",
|
||||
"createBy": "admin",
|
||||
"createTime": "2024-07-05 10:00:00",
|
||||
"updateBy": "admin",
|
||||
"updateTime": "2024-07-05 10:00:00"
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"size": 10,
|
||||
"current": 1,
|
||||
"pages": 1
|
||||
},
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
"http://localhost:18099/coder/sysFile/listPage?pageNo=1&pageSize=10&fileName=avatar" \
|
||||
-H "Authorization: Bearer your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 查询所有文件
|
||||
|
||||
**接口地址**: `GET /coder/sysFile/list`
|
||||
|
||||
**接口描述**: 查询所有系统文件记录(不分页)
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:file:list`
|
||||
|
||||
**请求参数**: 同分页查询(除pageNo、pageSize外)
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": [
|
||||
{
|
||||
"fileId": 1,
|
||||
"fileName": "avatar_20240705_001.jpg",
|
||||
"originalName": "my-avatar.jpg",
|
||||
"filePath": "/upload/avatar/2024/07/05/avatar_20240705_001.jpg",
|
||||
"fileUrl": "http://localhost:18099/upload/avatar/2024/07/05/avatar_20240705_001.jpg",
|
||||
"folderName": "avatar",
|
||||
"fileSize": 1024000,
|
||||
"fileType": "jpg",
|
||||
"contentType": "image/jpeg",
|
||||
"fileStatus": "0",
|
||||
"uploadUserId": 1,
|
||||
"uploadUserName": "admin",
|
||||
"remark": "用户头像",
|
||||
"createBy": "admin",
|
||||
"createTime": "2024-07-05 10:00:00",
|
||||
"updateBy": "admin",
|
||||
"updateTime": "2024-07-05 10:00:00"
|
||||
}
|
||||
],
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
"http://localhost:18099/coder/sysFile/list?fileType=jpg" \
|
||||
-H "Authorization: Bearer your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 根据ID查询文件
|
||||
|
||||
**接口地址**: `GET /coder/sysFile/getById/{id}`
|
||||
|
||||
**接口描述**: 根据文件ID查询文件详细信息
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:file:list`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| id | Long | 是 | 文件ID |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"fileId": 1,
|
||||
"fileName": "avatar_20240705_001.jpg",
|
||||
"originalName": "my-avatar.jpg",
|
||||
"filePath": "/upload/avatar/2024/07/05/avatar_20240705_001.jpg",
|
||||
"fileUrl": "http://localhost:18099/upload/avatar/2024/07/05/avatar_20240705_001.jpg",
|
||||
"folderName": "avatar",
|
||||
"fileSize": 1024000,
|
||||
"fileType": "jpg",
|
||||
"contentType": "image/jpeg",
|
||||
"fileStatus": "0",
|
||||
"uploadUserId": 1,
|
||||
"uploadUserName": "admin",
|
||||
"remark": "用户头像",
|
||||
"createBy": "admin",
|
||||
"createTime": "2024-07-05 10:00:00",
|
||||
"updateBy": "admin",
|
||||
"updateTime": "2024-07-05 10:00:00"
|
||||
},
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
http://localhost:18099/coder/sysFile/getById/1 \
|
||||
-H "Authorization: Bearer your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 新增文件记录
|
||||
|
||||
**接口地址**: `POST /coder/sysFile/add`
|
||||
|
||||
**接口描述**: 新增系统文件记录
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:file:upload`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"fileName": "document_20240705_001.pdf",
|
||||
"originalName": "重要文档.pdf",
|
||||
"filePath": "/upload/documents/2024/07/05/document_20240705_001.pdf",
|
||||
"fileUrl": "http://localhost:18099/upload/documents/2024/07/05/document_20240705_001.pdf",
|
||||
"folderName": "documents",
|
||||
"fileSize": 2048000,
|
||||
"fileType": "pdf",
|
||||
"contentType": "application/pdf",
|
||||
"fileStatus": "0",
|
||||
"uploadUserId": 1,
|
||||
"uploadUserName": "admin",
|
||||
"remark": "重要文档"
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|
||||
|--------|------|------|------|----------|
|
||||
| fileName | String | 是 | 存储文件名 | 不能为空 |
|
||||
| originalName | String | 是 | 原始文件名 | 不能为空 |
|
||||
| filePath | String | 是 | 文件相对路径 | 不能为空 |
|
||||
| fileUrl | String | 是 | 文件访问URL | 不能为空 |
|
||||
| folderName | String | 是 | 文件夹名称 | 不能为空 |
|
||||
| fileSize | Long | 是 | 文件大小 | 必须大于0 |
|
||||
| fileType | String | 是 | 文件类型 | 不能为空 |
|
||||
| contentType | String | 是 | MIME类型 | 不能为空 |
|
||||
| fileStatus | String | 是 | 文件状态 | 0-正常 1-删除 |
|
||||
| uploadUserId | Long | 否 | 上传用户ID | 有效的用户ID |
|
||||
| uploadUserName | String | 否 | 上传用户名 | 可为空 |
|
||||
| remark | String | 否 | 备注信息 | 最长200字符 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "新增成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysFile/add \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer your-token-value" \
|
||||
-d '{
|
||||
"fileName": "document_20240705_001.pdf",
|
||||
"originalName": "重要文档.pdf",
|
||||
"filePath": "/upload/documents/2024/07/05/document_20240705_001.pdf",
|
||||
"fileUrl": "http://localhost:18099/upload/documents/2024/07/05/document_20240705_001.pdf",
|
||||
"folderName": "documents",
|
||||
"fileSize": 2048000,
|
||||
"fileType": "pdf",
|
||||
"contentType": "application/pdf",
|
||||
"fileStatus": "0",
|
||||
"uploadUserId": 1,
|
||||
"uploadUserName": "admin",
|
||||
"remark": "重要文档"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. 修改文件信息
|
||||
|
||||
**接口地址**: `POST /coder/sysFile/update`
|
||||
|
||||
**接口描述**: 修改系统文件记录信息
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:file:edit`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"fileId": 1,
|
||||
"fileName": "document_20240705_001.pdf",
|
||||
"originalName": "重要文档.pdf",
|
||||
"filePath": "/upload/documents/2024/07/05/document_20240705_001.pdf",
|
||||
"fileUrl": "http://localhost:18099/upload/documents/2024/07/05/document_20240705_001.pdf",
|
||||
"folderName": "documents",
|
||||
"fileSize": 2048000,
|
||||
"fileType": "pdf",
|
||||
"contentType": "application/pdf",
|
||||
"fileStatus": "0",
|
||||
"uploadUserId": 1,
|
||||
"uploadUserName": "admin",
|
||||
"remark": "重要文档(已更新)"
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|
||||
|--------|------|------|------|----------|
|
||||
| fileId | Long | 是 | 文件ID | 必须是有效的文件ID |
|
||||
| 其他参数 | - | - | 同新增文件记录 | - |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "修改成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysFile/update \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer your-token-value" \
|
||||
-d '{
|
||||
"fileId": 1,
|
||||
"fileName": "document_20240705_001.pdf",
|
||||
"originalName": "重要文档.pdf",
|
||||
"remark": "重要文档(已更新)"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. 删除文件
|
||||
|
||||
**接口地址**: `POST /coder/sysFile/deleteById/{id}`
|
||||
|
||||
**接口描述**: 根据ID删除文件记录和物理文件
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:file:remove`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| id | Long | 是 | 文件ID |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "删除成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysFile/deleteById/1 \
|
||||
-H "Authorization: Bearer your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. 批量删除文件
|
||||
|
||||
**接口地址**: `POST /coder/sysFile/batchDelete`
|
||||
|
||||
**接口描述**: 批量删除文件记录和物理文件
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:file:remove`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"ids": [1, 2, 3]
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| ids | Long[] | 是 | 文件ID数组 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "删除成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysFile/batchDelete \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer your-token-value" \
|
||||
-d '{
|
||||
"ids": [1, 2, 3]
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 文件字段说明
|
||||
|
||||
### 基础字段
|
||||
|
||||
| 字段名 | 类型 | 说明 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| fileId | Long | 文件ID | 1 |
|
||||
| fileName | String | 存储文件名 | avatar_20240705_001.jpg |
|
||||
| originalName | String | 原始文件名 | my-avatar.jpg |
|
||||
| filePath | String | 文件相对路径 | /upload/avatar/2024/07/05/avatar_20240705_001.jpg |
|
||||
| fileUrl | String | 文件访问URL | http://localhost:18099/upload/avatar/2024/07/05/avatar_20240705_001.jpg |
|
||||
|
||||
### 分类字段
|
||||
|
||||
| 字段名 | 类型 | 说明 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| folderName | String | 文件夹名称 | avatar |
|
||||
| fileType | String | 文件扩展名 | jpg |
|
||||
| contentType | String | MIME类型 | image/jpeg |
|
||||
| fileSize | Long | 文件大小(字节) | 1024000 |
|
||||
|
||||
### 状态字段
|
||||
|
||||
| 字段名 | 类型 | 说明 | 示例 |
|
||||
|--------|------|------|------|
|
||||
| fileStatus | String | 文件状态 | 0-正常 1-删除 |
|
||||
| uploadUserId | Long | 上传用户ID | 1 |
|
||||
| uploadUserName | String | 上传用户名 | admin |
|
||||
| remark | String | 备注信息 | 用户头像 |
|
||||
|
||||
---
|
||||
|
||||
## 数据字典
|
||||
|
||||
### 文件状态 (fileStatus)
|
||||
|
||||
| 值 | 说明 |
|
||||
|----|------|
|
||||
| 0 | 正常 |
|
||||
| 1 | 已删除 |
|
||||
|
||||
### 常见文件类型
|
||||
|
||||
| 文件类型 | 扩展名 | MIME类型 | 说明 |
|
||||
|----------|--------|----------|------|
|
||||
| 图片 | jpg, jpeg, png, gif, bmp | image/* | 图片文件 |
|
||||
| 文档 | pdf, doc, docx, xls, xlsx, ppt, pptx | application/* | 办公文档 |
|
||||
| 音频 | mp3, wav, flac, aac | audio/* | 音频文件 |
|
||||
| 视频 | mp4, avi, mov, wmv | video/* | 视频文件 |
|
||||
| 压缩包 | zip, rar, 7z, tar, gz | application/* | 压缩文件 |
|
||||
| 文本 | txt, md, csv | text/* | 文本文件 |
|
||||
|
||||
---
|
||||
|
||||
## 文件配置
|
||||
|
||||
### 1. 文件存储配置
|
||||
|
||||
```yaml
|
||||
# application-dev.yml
|
||||
coder:
|
||||
# 文件存储路径
|
||||
filePath: /data/upload/
|
||||
|
||||
# 文件访问域名
|
||||
domain: http://localhost:18099
|
||||
|
||||
# 文件大小限制(MB)
|
||||
maxFileSize: 100
|
||||
|
||||
# 允许的文件类型
|
||||
allowedTypes:
|
||||
- jpg
|
||||
- jpeg
|
||||
- png
|
||||
- gif
|
||||
- pdf
|
||||
- doc
|
||||
- docx
|
||||
- xls
|
||||
- xlsx
|
||||
|
||||
# 禁止的文件类型
|
||||
forbiddenTypes:
|
||||
- exe
|
||||
- bat
|
||||
- sh
|
||||
- jsp
|
||||
- php
|
||||
```
|
||||
|
||||
### 2. 文件夹分类
|
||||
|
||||
```java
|
||||
// 文件夹类型枚举
|
||||
public enum FolderType {
|
||||
AVATAR("avatar", "用户头像"),
|
||||
DOCUMENT("document", "文档文件"),
|
||||
IMAGE("image", "图片文件"),
|
||||
TEMP("temp", "临时文件"),
|
||||
EXPORT("export", "导出文件");
|
||||
|
||||
private final String code;
|
||||
private final String name;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 文件上传工具类
|
||||
|
||||
```java
|
||||
@Component
|
||||
public class FileUploadUtil {
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*/
|
||||
public FileUploadResult uploadFile(MultipartFile file, String folder, int maxSize) {
|
||||
// 1. 文件校验
|
||||
validateFile(file, maxSize);
|
||||
|
||||
// 2. 生成文件名
|
||||
String fileName = generateFileName(file.getOriginalFilename());
|
||||
|
||||
// 3. 创建目录
|
||||
String datePath = DateUtil.format(new Date(), "yyyy/MM/dd");
|
||||
String dirPath = filePath + folder + "/" + datePath + "/";
|
||||
FileUtil.createDirs(dirPath);
|
||||
|
||||
// 4. 保存文件
|
||||
String filePath = dirPath + fileName;
|
||||
file.transferTo(new File(filePath));
|
||||
|
||||
// 5. 生成访问URL
|
||||
String fileUrl = domain + "/upload/" + folder + "/" + datePath + "/" + fileName;
|
||||
|
||||
// 6. 返回结果
|
||||
return FileUploadResult.builder()
|
||||
.fileName(fileName)
|
||||
.originalName(file.getOriginalFilename())
|
||||
.filePath(filePath)
|
||||
.fileUrl(fileUrl)
|
||||
.fileSize(file.getSize())
|
||||
.fileType(FileUtil.getExtension(fileName))
|
||||
.uploadTime(new Date())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件校验
|
||||
*/
|
||||
private void validateFile(MultipartFile file, int maxSize) {
|
||||
if (file.isEmpty()) {
|
||||
throw new BusinessException("文件不能为空");
|
||||
}
|
||||
|
||||
if (file.getSize() > maxSize * 1024 * 1024) {
|
||||
throw new BusinessException("文件大小不能超过" + maxSize + "MB");
|
||||
}
|
||||
|
||||
String extension = FileUtil.getExtension(file.getOriginalFilename());
|
||||
if (!allowedTypes.contains(extension.toLowerCase())) {
|
||||
throw new BusinessException("不支持的文件类型:" + extension);
|
||||
}
|
||||
|
||||
if (forbiddenTypes.contains(extension.toLowerCase())) {
|
||||
throw new BusinessException("禁止上传的文件类型:" + extension);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成文件名
|
||||
*/
|
||||
private String generateFileName(String originalName) {
|
||||
String extension = FileUtil.getExtension(originalName);
|
||||
String baseName = FileUtil.getBaseName(originalName);
|
||||
String timestamp = DateUtil.format(new Date(), "yyyyMMdd_HHmmss");
|
||||
String random = RandomUtil.randomString(3);
|
||||
return baseName + "_" + timestamp + "_" + random + "." + extension;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 错误码说明
|
||||
|
||||
| 错误码 | 错误信息 | 说明 |
|
||||
|--------|----------|------|
|
||||
| 400 | 文件不能为空 | 上传文件为空 |
|
||||
| 400 | 文件大小不能超过{size}MB | 文件大小超过限制 |
|
||||
| 400 | 不支持的文件类型 | 文件类型不在允许列表中 |
|
||||
| 400 | 禁止上传的文件类型 | 文件类型在禁止列表中 |
|
||||
| 400 | 文件名不能为空 | 文件名为空 |
|
||||
| 400 | 文件路径不能为空 | 文件路径为空 |
|
||||
| 400 | 文件不存在 | 文件ID不存在 |
|
||||
| 400 | 文件已被删除 | 文件状态为已删除 |
|
||||
| 401 | 当前会话未登录 | 未登录或Token无效 |
|
||||
| 403 | 权限不足 | 没有相应的操作权限 |
|
||||
| 500 | 文件上传失败 | 文件保存到磁盘失败 |
|
||||
| 500 | 文件删除失败 | 物理文件删除失败 |
|
||||
| 500 | 磁盘空间不足 | 服务器磁盘空间不足 |
|
||||
|
||||
---
|
||||
|
||||
## 安全特性
|
||||
|
||||
### 1. 文件类型验证
|
||||
|
||||
- **扩展名校验**: 验证文件扩展名
|
||||
- **MIME类型校验**: 验证文件MIME类型
|
||||
- **文件内容校验**: 验证文件真实类型
|
||||
- **黑名单过滤**: 禁止上传危险文件类型
|
||||
|
||||
### 2. 文件大小限制
|
||||
|
||||
- **单文件大小限制**: 限制单个文件大小
|
||||
- **总文件大小限制**: 限制用户总文件大小
|
||||
- **磁盘空间检查**: 检查服务器磁盘空间
|
||||
|
||||
### 3. 访问控制
|
||||
|
||||
- **权限验证**: 验证用户上传和访问权限
|
||||
- **路径防护**: 防止路径遍历攻击
|
||||
- **防盗链**: 防止文件被盗链
|
||||
|
||||
### 4. 病毒扫描
|
||||
|
||||
- **文件扫描**: 上传文件病毒扫描
|
||||
- **隔离机制**: 可疑文件隔离处理
|
||||
- **定期扫描**: 定期扫描存储文件
|
||||
|
||||
---
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 1. 文件存储
|
||||
|
||||
- **分目录存储**: 按日期和类型分目录存储
|
||||
- **CDN加速**: 使用CDN加速文件访问
|
||||
- **压缩存储**: 自动压缩图片文件
|
||||
- **缓存策略**: 设置合适的缓存策略
|
||||
|
||||
### 2. 上传优化
|
||||
|
||||
- **分片上传**: 大文件分片上传
|
||||
- **断点续传**: 支持断点续传功能
|
||||
- **并发上传**: 支持多文件并发上传
|
||||
- **进度显示**: 实时显示上传进度
|
||||
|
||||
### 3. 存储优化
|
||||
|
||||
- **重复文件检测**: 检测并避免重复文件
|
||||
- **文件压缩**: 自动压缩文件
|
||||
- **定期清理**: 清理临时文件和无效文件
|
||||
- **存储监控**: 监控存储空间使用情况
|
||||
|
||||
---
|
||||
|
||||
## 使用建议
|
||||
|
||||
### 1. 文件命名规范
|
||||
|
||||
- **唯一性**: 确保文件名唯一
|
||||
- **时间戳**: 包含时间戳信息
|
||||
- **随机性**: 添加随机字符
|
||||
- **可读性**: 保持一定的可读性
|
||||
|
||||
### 2. 文件分类管理
|
||||
|
||||
- **按类型分类**: 不同类型文件存储在不同目录
|
||||
- **按用户分类**: 不同用户文件分开存储
|
||||
- **按时间分类**: 按年月日创建目录结构
|
||||
- **按业务分类**: 按业务模块分类存储
|
||||
|
||||
### 3. 安全防护
|
||||
|
||||
- **输入验证**: 严格验证上传文件
|
||||
- **权限控制**: 细粒度的权限控制
|
||||
- **日志记录**: 记录所有文件操作
|
||||
- **备份策略**: 重要文件定期备份
|
||||
|
||||
### 4. 监控告警
|
||||
|
||||
- **存储监控**: 监控存储空间使用
|
||||
- **性能监控**: 监控上传下载性能
|
||||
- **安全监控**: 监控异常文件操作
|
||||
- **容量告警**: 存储空间不足告警
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **文件安全**: 严格验证上传文件类型和内容
|
||||
2. **存储路径**: 确保文件存储路径安全
|
||||
3. **权限控制**: 合理设置文件访问权限
|
||||
4. **磁盘空间**: 定期清理无效文件,避免磁盘满
|
||||
5. **备份策略**: 重要文件需要备份
|
||||
6. **访问控制**: 防止文件被恶意访问
|
||||
7. **性能考虑**: 大文件上传时注意性能影响
|
||||
8. **并发控制**: 处理并发上传时的文件冲突
|
||||
9. **错误处理**: 完善的错误处理和回滚机制
|
||||
10. **日志审计**: 记录所有文件操作日志
|
||||
1017
api/system/登录日志API.md
Normal file
1017
api/system/登录日志API.md
Normal file
File diff suppressed because it is too large
Load Diff
888
api/user/用户管理API.md
Normal file
888
api/user/用户管理API.md
Normal file
@ -0,0 +1,888 @@
|
||||
# 用户管理API
|
||||
|
||||
## 概述
|
||||
|
||||
用户管理模块提供系统用户的增删改查功能,包括用户信息管理、状态控制、密码管理、角色分配等核心功能。
|
||||
|
||||
## 权限说明
|
||||
|
||||
用户管理接口需要相应的权限才能访问:
|
||||
|
||||
| 操作 | 权限码 | 说明 |
|
||||
|------|--------|------|
|
||||
| 查询用户列表 | `system:user:list` | 查看用户列表权限 |
|
||||
| 新增用户 | `system:user:add` | 新增用户权限 |
|
||||
| 修改用户 | `system:user:edit` | 修改用户信息权限 |
|
||||
| 删除用户 | `system:user:remove` | 删除用户权限 |
|
||||
| 重置密码 | `system:user:resetPwd` | 重置用户密码权限 |
|
||||
| 修改状态 | `system:user:status` | 修改用户状态权限 |
|
||||
| 分配角色 | `system:user:role` | 分配用户角色权限 |
|
||||
| 导入用户 | `system:user:import` | 导入用户数据权限 |
|
||||
| 导出用户 | `system:user:export` | 导出用户数据权限 |
|
||||
|
||||
## 接口列表
|
||||
|
||||
### 1. 分页查询用户列表
|
||||
|
||||
**接口地址**: `GET /coder/sysLoginUser/listPage`
|
||||
|
||||
**接口描述**: 分页查询系统用户列表
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:user:list`
|
||||
|
||||
**请求头**:
|
||||
```
|
||||
Authorization: your-token-value
|
||||
```
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
||||
|--------|------|------|------|------|
|
||||
| pageNo | Integer | 否 | 页码 | 1 |
|
||||
| pageSize | Integer | 否 | 每页大小 | 10 |
|
||||
| loginName | String | 否 | 登录账号 | admin |
|
||||
| userName | String | 否 | 用户姓名 | 管理员 |
|
||||
| userType | String | 否 | 用户类型 | 1 |
|
||||
| phone | String | 否 | 手机号码 | 13800138000 |
|
||||
| sex | String | 否 | 用户性别 | 1 |
|
||||
| userStatus | String | 否 | 用户状态 | 0 |
|
||||
| beginTime | String | 否 | 开始时间 | 2024-01-01 |
|
||||
| endTime | String | 否 | 结束时间 | 2024-12-31 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"records": [
|
||||
{
|
||||
"userId": 1,
|
||||
"loginName": "admin",
|
||||
"userName": "管理员",
|
||||
"userType": "1",
|
||||
"email": "admin@example.com",
|
||||
"phone": "13800138000",
|
||||
"sex": "1",
|
||||
"avatar": "/upload/avatar/admin.jpg",
|
||||
"userStatus": "0",
|
||||
"loginIp": "127.0.0.1",
|
||||
"loginTime": "2024-01-01 10:00:00",
|
||||
"pwdUpdateTime": "2024-01-01 10:00:00",
|
||||
"remark": "系统管理员",
|
||||
"createBy": "admin",
|
||||
"createTime": "2024-01-01 10:00:00",
|
||||
"updateBy": "admin",
|
||||
"updateTime": "2024-01-01 10:00:00",
|
||||
"roleIds": [1, 2]
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
"size": 10,
|
||||
"current": 1,
|
||||
"pages": 1
|
||||
},
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
"http://localhost:18099/coder/sysLoginUser/listPage?pageNo=1&pageSize=10&loginName=admin" \
|
||||
-H "Authorization: your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 查询所有用户
|
||||
|
||||
**接口地址**: `GET /coder/sysLoginUser/list`
|
||||
|
||||
**接口描述**: 查询所有系统用户(不分页)
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:user:list`
|
||||
|
||||
**请求参数**: 同分页查询(除pageNo、pageSize外)
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": [
|
||||
{
|
||||
"userId": 1,
|
||||
"loginName": "admin",
|
||||
"userName": "管理员",
|
||||
// 其他字段...
|
||||
}
|
||||
],
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 根据ID查询用户
|
||||
|
||||
**接口地址**: `GET /coder/sysLoginUser/getById/{id}`
|
||||
|
||||
**接口描述**: 根据用户ID查询用户详细信息
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:user:list`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| id | Long | 是 | 用户ID |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"userId": 1,
|
||||
"loginName": "admin",
|
||||
"userName": "管理员",
|
||||
"userType": "1",
|
||||
"email": "admin@example.com",
|
||||
"phone": "13800138000",
|
||||
"sex": "1",
|
||||
"avatar": "/upload/avatar/admin.jpg",
|
||||
"userStatus": "0",
|
||||
"loginIp": "127.0.0.1",
|
||||
"loginTime": "2024-01-01 10:00:00",
|
||||
"pwdUpdateTime": "2024-01-01 10:00:00",
|
||||
"remark": "系统管理员",
|
||||
"createBy": "admin",
|
||||
"createTime": "2024-01-01 10:00:00",
|
||||
"updateBy": "admin",
|
||||
"updateTime": "2024-01-01 10:00:00",
|
||||
"roleIds": [1, 2]
|
||||
},
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
http://localhost:18099/coder/sysLoginUser/getById/1 \
|
||||
-H "Authorization: your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 新增用户
|
||||
|
||||
**接口地址**: `POST /coder/sysLoginUser/add`
|
||||
|
||||
**接口描述**: 新增系统用户
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:user:add`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"loginName": "newuser",
|
||||
"userName": "新用户",
|
||||
"password": "123456",
|
||||
"userType": "1",
|
||||
"email": "newuser@example.com",
|
||||
"phone": "13800138001",
|
||||
"sex": "1",
|
||||
"userStatus": "0",
|
||||
"remark": "新用户备注",
|
||||
"roleIds": [2]
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|
||||
|--------|------|------|------|----------|
|
||||
| loginName | String | 是 | 登录账号 | 3-16位字母数字 |
|
||||
| userName | String | 是 | 用户姓名 | 不能为空 |
|
||||
| password | String | 是 | 登录密码 | 不能为空 |
|
||||
| userType | String | 是 | 用户类型 | 1-系统用户 2-注册用户 |
|
||||
| email | String | 否 | 邮箱地址 | 邮箱格式验证 |
|
||||
| phone | String | 否 | 手机号码 | 手机号格式验证 |
|
||||
| sex | String | 否 | 用户性别 | 1-男 2-女 3-未知 |
|
||||
| userStatus | String | 是 | 用户状态 | 0-启用 1-停用 |
|
||||
| remark | String | 否 | 备注信息 | 最长200字符 |
|
||||
| roleIds | Long[] | 否 | 角色ID数组 | 有效的角色ID |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "新增成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysLoginUser/add \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: your-token-value" \
|
||||
-d '{
|
||||
"loginName": "newuser",
|
||||
"userName": "新用户",
|
||||
"password": "123456",
|
||||
"userType": "1",
|
||||
"email": "newuser@example.com",
|
||||
"phone": "13800138001",
|
||||
"sex": "1",
|
||||
"userStatus": "0",
|
||||
"remark": "新用户备注",
|
||||
"roleIds": [2]
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. 修改用户信息
|
||||
|
||||
**接口地址**: `POST /coder/sysLoginUser/update`
|
||||
|
||||
**接口描述**: 修改系统用户信息
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:user:edit`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"userId": 1,
|
||||
"loginName": "admin",
|
||||
"userName": "管理员",
|
||||
"userType": "1",
|
||||
"email": "admin@example.com",
|
||||
"phone": "13800138000",
|
||||
"sex": "1",
|
||||
"userStatus": "0",
|
||||
"remark": "系统管理员",
|
||||
"roleIds": [1, 2]
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|
||||
|--------|------|------|------|----------|
|
||||
| userId | Long | 是 | 用户ID | 必须是有效的用户ID |
|
||||
| loginName | String | 是 | 登录账号 | 3-16位字母数字 |
|
||||
| userName | String | 是 | 用户姓名 | 不能为空 |
|
||||
| userType | String | 是 | 用户类型 | 1-系统用户 2-注册用户 |
|
||||
| email | String | 否 | 邮箱地址 | 邮箱格式验证 |
|
||||
| phone | String | 否 | 手机号码 | 手机号格式验证 |
|
||||
| sex | String | 否 | 用户性别 | 1-男 2-女 3-未知 |
|
||||
| userStatus | String | 是 | 用户状态 | 0-启用 1-停用 |
|
||||
| remark | String | 否 | 备注信息 | 最长200字符 |
|
||||
| roleIds | Long[] | 否 | 角色ID数组 | 有效的角色ID |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "修改成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysLoginUser/update \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: your-token-value" \
|
||||
-d '{
|
||||
"userId": 1,
|
||||
"loginName": "admin",
|
||||
"userName": "管理员",
|
||||
"userType": "1",
|
||||
"email": "admin@example.com",
|
||||
"phone": "13800138000",
|
||||
"sex": "1",
|
||||
"userStatus": "0",
|
||||
"remark": "系统管理员",
|
||||
"roleIds": [1, 2]
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. 删除用户
|
||||
|
||||
**接口地址**: `POST /coder/sysLoginUser/deleteById/{id}`
|
||||
|
||||
**接口描述**: 根据ID删除用户
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:user:remove`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| id | Long | 是 | 用户ID |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "删除成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysLoginUser/deleteById/1 \
|
||||
-H "Authorization: your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. 批量删除用户
|
||||
|
||||
**接口地址**: `POST /coder/sysLoginUser/batchDelete`
|
||||
|
||||
**接口描述**: 批量删除用户
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:user:remove`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"ids": [1, 2, 3]
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| ids | Long[] | 是 | 用户ID数组 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "删除成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysLoginUser/batchDelete \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: your-token-value" \
|
||||
-d '{
|
||||
"ids": [1, 2, 3]
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8. 修改用户状态
|
||||
|
||||
**接口地址**: `POST /coder/sysLoginUser/updateStatus/{userId}/{userStatus}`
|
||||
|
||||
**接口描述**: 修改用户状态(启用/停用)
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:user:status`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| userId | Long | 是 | 用户ID |
|
||||
| userStatus | String | 是 | 用户状态(0-启用 1-停用) |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "修改成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysLoginUser/updateStatus/1/0 \
|
||||
-H "Authorization: your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 9. 获取登录用户信息
|
||||
|
||||
**接口地址**: `GET /coder/sysLoginUser/getLoginUserInformation`
|
||||
|
||||
**接口描述**: 获取当前登录用户的详细信息
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: 无(已登录用户可访问)
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"userId": 1,
|
||||
"loginName": "admin",
|
||||
"userName": "管理员",
|
||||
"userType": "1",
|
||||
"email": "admin@example.com",
|
||||
"phone": "13800138000",
|
||||
"sex": "1",
|
||||
"avatar": "/upload/avatar/admin.jpg",
|
||||
"userStatus": "0",
|
||||
"loginIp": "127.0.0.1",
|
||||
"loginTime": "2024-01-01 10:00:00",
|
||||
"pwdUpdateTime": "2024-01-01 10:00:00",
|
||||
"remark": "系统管理员",
|
||||
"createBy": "admin",
|
||||
"createTime": "2024-01-01 10:00:00",
|
||||
"updateBy": "admin",
|
||||
"updateTime": "2024-01-01 10:00:00",
|
||||
"roleIds": [1, 2],
|
||||
"roles": [
|
||||
{
|
||||
"roleId": 1,
|
||||
"roleName": "超级管理员",
|
||||
"roleCode": "admin"
|
||||
}
|
||||
],
|
||||
"permissions": [
|
||||
"*:*:*"
|
||||
]
|
||||
},
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
http://localhost:18099/coder/sysLoginUser/getLoginUserInformation \
|
||||
-H "Authorization: your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 10. 获取个人资料
|
||||
|
||||
**接口地址**: `GET /coder/sysLoginUser/getPersonalData`
|
||||
|
||||
**接口描述**: 获取当前用户的个人资料
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: 无(已登录用户可访问)
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"userId": 1,
|
||||
"loginName": "admin",
|
||||
"userName": "管理员",
|
||||
"email": "admin@example.com",
|
||||
"phone": "13800138000",
|
||||
"sex": "1",
|
||||
"avatar": "/upload/avatar/admin.jpg",
|
||||
"remark": "系统管理员"
|
||||
},
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
http://localhost:18099/coder/sysLoginUser/getPersonalData \
|
||||
-H "Authorization: your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 11. 修改个人资料
|
||||
|
||||
**接口地址**: `POST /coder/sysLoginUser/updateBasicData`
|
||||
|
||||
**接口描述**: 修改当前用户的个人资料
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: 无(已登录用户可访问)
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"userName": "管理员",
|
||||
"email": "admin@example.com",
|
||||
"phone": "13800138000",
|
||||
"sex": "1",
|
||||
"avatar": "/upload/avatar/admin.jpg",
|
||||
"remark": "系统管理员"
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|
||||
|--------|------|------|------|----------|
|
||||
| userName | String | 是 | 用户姓名 | 不能为空 |
|
||||
| email | String | 否 | 邮箱地址 | 邮箱格式验证 |
|
||||
| phone | String | 否 | 手机号码 | 手机号格式验证 |
|
||||
| sex | String | 否 | 用户性别 | 1-男 2-女 3-未知 |
|
||||
| avatar | String | 否 | 头像地址 | 有效的文件路径 |
|
||||
| remark | String | 否 | 备注信息 | 最长200字符 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "修改成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysLoginUser/updateBasicData \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: your-token-value" \
|
||||
-d '{
|
||||
"userName": "管理员",
|
||||
"email": "admin@example.com",
|
||||
"phone": "13800138000",
|
||||
"sex": "1",
|
||||
"avatar": "/upload/avatar/admin.jpg",
|
||||
"remark": "系统管理员"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 12. 修改登录密码
|
||||
|
||||
**接口地址**: `POST /coder/sysLoginUser/updateUserPwd`
|
||||
|
||||
**接口描述**: 修改当前用户的登录密码
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: 无(已登录用户可访问)
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"oldPassword": "123456",
|
||||
"newPassword": "654321",
|
||||
"confirmPassword": "654321"
|
||||
}
|
||||
```
|
||||
|
||||
**请求参数说明**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|
||||
|--------|------|------|------|----------|
|
||||
| oldPassword | String | 是 | 原密码 | 不能为空 |
|
||||
| newPassword | String | 是 | 新密码 | 不能为空 |
|
||||
| confirmPassword | String | 是 | 确认密码 | 必须与新密码一致 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "修改成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysLoginUser/updateUserPwd \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: your-token-value" \
|
||||
-d '{
|
||||
"oldPassword": "123456",
|
||||
"newPassword": "654321",
|
||||
"confirmPassword": "654321"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 13. 重置用户密码
|
||||
|
||||
**接口地址**: `POST /coder/sysLoginUser/resetPwd/{id}/{password}`
|
||||
|
||||
**接口描述**: 重置指定用户的密码
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:user:resetPwd`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| id | Long | 是 | 用户ID |
|
||||
| password | String | 是 | 新密码 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": "重置成功",
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysLoginUser/resetPwd/1/123456 \
|
||||
-H "Authorization: your-token-value"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 14. 下载用户导入模板
|
||||
|
||||
**接口地址**: `GET /coder/sysLoginUser/downloadExcelTemplate`
|
||||
|
||||
**接口描述**: 下载用户批量导入的Excel模板
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:user:import`
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应**: Excel文件下载
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
http://localhost:18099/coder/sysLoginUser/downloadExcelTemplate \
|
||||
-H "Authorization: your-token-value" \
|
||||
-o user_template.xlsx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 15. 导出用户数据
|
||||
|
||||
**接口地址**: `GET /coder/sysLoginUser/exportExcelData`
|
||||
|
||||
**接口描述**: 导出用户数据到Excel
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:user:export`
|
||||
|
||||
**请求参数**: 同查询参数
|
||||
|
||||
**响应**: Excel文件下载
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X GET \
|
||||
"http://localhost:18099/coder/sysLoginUser/exportExcelData?userStatus=0" \
|
||||
-H "Authorization: your-token-value" \
|
||||
-o users.xlsx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 16. 导入用户数据
|
||||
|
||||
**接口地址**: `POST /coder/sysLoginUser/importExcelData`
|
||||
|
||||
**接口描述**: 从Excel文件导入用户数据
|
||||
|
||||
**是否需要认证**: 是
|
||||
|
||||
**权限要求**: `system:user:import`
|
||||
|
||||
**请求参数**: 文件上传
|
||||
|
||||
**Content-Type**: `multipart/form-data`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| file | File | 是 | Excel文件 |
|
||||
| updateSupport | Boolean | 否 | 是否更新已存在的用户 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"total": 100,
|
||||
"success": 95,
|
||||
"failed": 5,
|
||||
"message": "导入成功95条,失败5条"
|
||||
},
|
||||
"traceId": "trace-123456"
|
||||
}
|
||||
```
|
||||
|
||||
**调用示例**:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:18099/coder/sysLoginUser/importExcelData \
|
||||
-H "Authorization: your-token-value" \
|
||||
-F "file=@users.xlsx" \
|
||||
-F "updateSupport=true"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 数据字典
|
||||
|
||||
### 用户类型 (userType)
|
||||
|
||||
| 值 | 说明 |
|
||||
|----|------|
|
||||
| 1 | 系统用户 |
|
||||
| 2 | 注册用户 |
|
||||
| 3 | 微信用户 |
|
||||
|
||||
### 用户性别 (sex)
|
||||
|
||||
| 值 | 说明 |
|
||||
|----|------|
|
||||
| 1 | 男 |
|
||||
| 2 | 女 |
|
||||
| 3 | 未知 |
|
||||
|
||||
### 用户状态 (userStatus)
|
||||
|
||||
| 值 | 说明 |
|
||||
|----|------|
|
||||
| 0 | 启用 |
|
||||
| 1 | 停用 |
|
||||
|
||||
---
|
||||
|
||||
## 错误码说明
|
||||
|
||||
| 错误码 | 错误信息 | 说明 |
|
||||
|--------|----------|------|
|
||||
| 400 | 账号长度为 3-16 位 | 登录账号长度不符合要求 |
|
||||
| 400 | 账号格式为数字以及字母 | 登录账号格式不正确 |
|
||||
| 400 | 登录账号不能为空 | 登录账号为空 |
|
||||
| 400 | 用户真实姓名不能为空 | 用户姓名为空 |
|
||||
| 400 | 用户不存在 | 用户ID不存在 |
|
||||
| 400 | 账号已存在 | 登录账号已被使用 |
|
||||
| 400 | 手机号已存在 | 手机号已被使用 |
|
||||
| 400 | 邮箱已存在 | 邮箱已被使用 |
|
||||
| 400 | 不能删除当前用户 | 不能删除自己的账号 |
|
||||
| 400 | 不能停用当前用户 | 不能停用自己的账号 |
|
||||
| 400 | 原密码错误 | 修改密码时原密码不正确 |
|
||||
| 400 | 新密码不能与原密码相同 | 新密码与原密码一致 |
|
||||
| 400 | 两次输入的密码不一致 | 确认密码与新密码不一致 |
|
||||
| 401 | 当前会话未登录 | 未登录或Token无效 |
|
||||
| 403 | 权限不足 | 没有相应的操作权限 |
|
||||
| 500 | 系统异常 | 服务器内部错误 |
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **权限验证**: 所有用户管理操作都需要相应的权限
|
||||
2. **数据校验**: 新增和修改用户时会进行数据格式校验
|
||||
3. **唯一性约束**: 登录账号、手机号、邮箱必须唯一
|
||||
4. **密码安全**: 密码使用盐值加密存储
|
||||
5. **操作日志**: 所有用户操作都会记录日志
|
||||
6. **批量操作**: 批量删除时会跳过超级管理员账号
|
||||
7. **Excel导入**: 支持Excel模板导入用户数据
|
||||
8. **角色关联**: 用户删除时会自动删除角色关联关系
|
||||
9. **会话管理**: 用户状态变更时会影响其登录会话
|
||||
10. **数据完整性**: 删除用户前会检查关联数据
|
||||
240
doc/oss/setup-env.sh
Executable file
240
doc/oss/setup-env.sh
Executable file
@ -0,0 +1,240 @@
|
||||
#!/bin/bash
|
||||
|
||||
# OSS环境变量快速设置脚本
|
||||
# 用于设置阿里云OSS所需的环境变量
|
||||
|
||||
echo "🔧 OSS环境变量设置工具"
|
||||
echo "=================================="
|
||||
|
||||
# 检测操作系统
|
||||
OS_TYPE=$(uname -s)
|
||||
SHELL_TYPE=$(basename "$SHELL")
|
||||
|
||||
echo "检测到操作系统: $OS_TYPE"
|
||||
echo "检测到Shell: $SHELL_TYPE"
|
||||
echo ""
|
||||
|
||||
# 获取当前配置文件中的值(仅用于显示)
|
||||
CURRENT_KEY_ID="LTAI5t982gXi7A72gAa9yugE"
|
||||
CURRENT_KEY_SECRET="Mi9ZsSWLGkvFoMiLNiZ71hHFzVso30"
|
||||
|
||||
echo "📋 配置文件中的当前值:"
|
||||
echo "OSS_ACCESS_KEY_ID: $CURRENT_KEY_ID"
|
||||
echo "OSS_ACCESS_KEY_SECRET: ${CURRENT_KEY_SECRET:0:8}..."
|
||||
echo ""
|
||||
|
||||
# 选择设置方式
|
||||
echo "请选择设置方式:"
|
||||
echo "1. 临时设置(当前终端会话有效)"
|
||||
echo "2. 永久设置(添加到Shell配置文件)"
|
||||
echo "3. 创建启动脚本"
|
||||
echo "4. 创建.env文件"
|
||||
echo "5. 显示手动设置命令"
|
||||
echo ""
|
||||
|
||||
read -p "请输入选项 (1-5): " choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
echo ""
|
||||
echo "🔧 临时设置环境变量..."
|
||||
export OSS_ACCESS_KEY_ID="$CURRENT_KEY_ID"
|
||||
export OSS_ACCESS_KEY_SECRET="$CURRENT_KEY_SECRET"
|
||||
|
||||
echo "✅ 环境变量已设置(当前终端会话有效)"
|
||||
echo ""
|
||||
echo "验证设置:"
|
||||
echo "OSS_ACCESS_KEY_ID: $OSS_ACCESS_KEY_ID"
|
||||
echo "OSS_ACCESS_KEY_SECRET: ${OSS_ACCESS_KEY_SECRET:0:8}..."
|
||||
echo ""
|
||||
echo "⚠️ 注意:这些变量只在当前终端会话中有效"
|
||||
echo " 关闭终端后需要重新设置"
|
||||
;;
|
||||
|
||||
2)
|
||||
echo ""
|
||||
echo "🔧 永久设置环境变量..."
|
||||
|
||||
# 根据Shell类型选择配置文件
|
||||
if [[ "$SHELL_TYPE" == "zsh" ]]; then
|
||||
CONFIG_FILE="$HOME/.zshrc"
|
||||
elif [[ "$SHELL_TYPE" == "bash" ]]; then
|
||||
CONFIG_FILE="$HOME/.bashrc"
|
||||
else
|
||||
CONFIG_FILE="$HOME/.profile"
|
||||
fi
|
||||
|
||||
echo "将添加到配置文件: $CONFIG_FILE"
|
||||
|
||||
# 检查是否已经存在
|
||||
if grep -q "OSS_ACCESS_KEY_ID" "$CONFIG_FILE"; then
|
||||
echo "⚠️ 配置文件中已存在OSS_ACCESS_KEY_ID,是否覆盖? (y/N)"
|
||||
read -p "" overwrite
|
||||
if [[ "$overwrite" != "y" && "$overwrite" != "Y" ]]; then
|
||||
echo "取消设置"
|
||||
exit 0
|
||||
fi
|
||||
# 移除现有配置
|
||||
sed -i.bak '/OSS_ACCESS_KEY_ID/d' "$CONFIG_FILE"
|
||||
sed -i.bak '/OSS_ACCESS_KEY_SECRET/d' "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
# 添加新配置
|
||||
echo "" >> "$CONFIG_FILE"
|
||||
echo "# OSS环境变量 - 由setup-env.sh添加" >> "$CONFIG_FILE"
|
||||
echo "export OSS_ACCESS_KEY_ID=\"$CURRENT_KEY_ID\"" >> "$CONFIG_FILE"
|
||||
echo "export OSS_ACCESS_KEY_SECRET=\"$CURRENT_KEY_SECRET\"" >> "$CONFIG_FILE"
|
||||
|
||||
echo "✅ 环境变量已添加到 $CONFIG_FILE"
|
||||
echo ""
|
||||
echo "请执行以下命令使配置生效:"
|
||||
echo "source $CONFIG_FILE"
|
||||
echo ""
|
||||
echo "或者重新打开终端"
|
||||
;;
|
||||
|
||||
3)
|
||||
echo ""
|
||||
echo "🔧 创建启动脚本..."
|
||||
|
||||
SCRIPT_FILE="start-app-with-oss.sh"
|
||||
|
||||
cat > "$SCRIPT_FILE" << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
# 应用程序启动脚本(包含OSS环境变量)
|
||||
# 自动生成于 $(date)
|
||||
|
||||
echo "🚀 启动应用程序(OSS模式)..."
|
||||
echo "=================================="
|
||||
|
||||
# 设置OSS环境变量
|
||||
export OSS_ACCESS_KEY_ID="LTAI5t982gXi7A72gAa9yugE"
|
||||
export OSS_ACCESS_KEY_SECRET="Mi9ZsSWLGkvFoMiLNiZ71hHFzVso30"
|
||||
|
||||
echo "✅ OSS环境变量已设置"
|
||||
echo "OSS_ACCESS_KEY_ID: $OSS_ACCESS_KEY_ID"
|
||||
echo "OSS_ACCESS_KEY_SECRET: ${OSS_ACCESS_KEY_SECRET:0:8}..."
|
||||
echo ""
|
||||
|
||||
# 切换到项目目录
|
||||
PROJECT_DIR="/Users/leocoder/leocoder/develop/templates/coder-common-thin/coder-common-thin-backend"
|
||||
if [ -d "$PROJECT_DIR" ]; then
|
||||
cd "$PROJECT_DIR"
|
||||
echo "📁 切换到项目目录: $PROJECT_DIR"
|
||||
else
|
||||
echo "❌ 项目目录不存在: $PROJECT_DIR"
|
||||
echo "请修改脚本中的PROJECT_DIR变量"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查JAR文件是否存在
|
||||
JAR_FILE="coder-common-thin-web/target/coder-common-thin-web-1.0.0.jar"
|
||||
if [ -f "$JAR_FILE" ]; then
|
||||
echo "📦 找到JAR文件: $JAR_FILE"
|
||||
else
|
||||
echo "❌ JAR文件不存在: $JAR_FILE"
|
||||
echo "请先编译项目: mvn clean package -DskipTests"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 启动应用程序
|
||||
echo ""
|
||||
echo "🚀 启动Spring Boot应用程序..."
|
||||
echo "访问地址: http://localhost:18099"
|
||||
echo "API文档: http://localhost:18099/swagger-ui.html"
|
||||
echo ""
|
||||
echo "按 Ctrl+C 停止应用程序"
|
||||
echo ""
|
||||
|
||||
java -jar "$JAR_FILE"
|
||||
EOF
|
||||
|
||||
chmod +x "$SCRIPT_FILE"
|
||||
|
||||
echo "✅ 启动脚本已创建: $SCRIPT_FILE"
|
||||
echo ""
|
||||
echo "使用方法:"
|
||||
echo "./$SCRIPT_FILE"
|
||||
;;
|
||||
|
||||
4)
|
||||
echo ""
|
||||
echo "🔧 创建.env文件..."
|
||||
|
||||
ENV_FILE=".env"
|
||||
|
||||
cat > "$ENV_FILE" << EOF
|
||||
# OSS环境变量配置
|
||||
# 创建时间: $(date)
|
||||
# 注意: 此文件包含敏感信息,不要提交到版本控制
|
||||
|
||||
OSS_ACCESS_KEY_ID=$CURRENT_KEY_ID
|
||||
OSS_ACCESS_KEY_SECRET=$CURRENT_KEY_SECRET
|
||||
EOF
|
||||
|
||||
echo "✅ .env文件已创建: $ENV_FILE"
|
||||
echo ""
|
||||
echo "使用方法:"
|
||||
echo "1. 使用dotenv工具: dotenv java -jar app.jar"
|
||||
echo "2. 手动加载: source .env && java -jar app.jar"
|
||||
echo "3. 在IDE中配置Environment Variables"
|
||||
echo ""
|
||||
echo "⚠️ 重要提醒:"
|
||||
echo "• 将.env添加到.gitignore文件中"
|
||||
echo "• 不要将.env文件提交到版本控制"
|
||||
|
||||
# 检查并添加到.gitignore
|
||||
if [ -f ".gitignore" ]; then
|
||||
if ! grep -q "\.env" .gitignore; then
|
||||
echo ".env" >> .gitignore
|
||||
echo "✅ .env已添加到.gitignore"
|
||||
fi
|
||||
else
|
||||
echo ".env" > .gitignore
|
||||
echo "✅ 已创建.gitignore并添加.env"
|
||||
fi
|
||||
;;
|
||||
|
||||
5)
|
||||
echo ""
|
||||
echo "📋 手动设置命令:"
|
||||
echo "=================================="
|
||||
echo ""
|
||||
echo "🐧 Linux/macOS (Bash):"
|
||||
echo "export OSS_ACCESS_KEY_ID=\"$CURRENT_KEY_ID\""
|
||||
echo "export OSS_ACCESS_KEY_SECRET=\"$CURRENT_KEY_SECRET\""
|
||||
echo ""
|
||||
echo "🐧 Linux/macOS (Zsh):"
|
||||
echo "export OSS_ACCESS_KEY_ID=\"$CURRENT_KEY_ID\""
|
||||
echo "export OSS_ACCESS_KEY_SECRET=\"$CURRENT_KEY_SECRET\""
|
||||
echo ""
|
||||
echo "🪟 Windows (CMD):"
|
||||
echo "set OSS_ACCESS_KEY_ID=$CURRENT_KEY_ID"
|
||||
echo "set OSS_ACCESS_KEY_SECRET=$CURRENT_KEY_SECRET"
|
||||
echo ""
|
||||
echo "🪟 Windows (PowerShell):"
|
||||
echo "\$env:OSS_ACCESS_KEY_ID=\"$CURRENT_KEY_ID\""
|
||||
echo "\$env:OSS_ACCESS_KEY_SECRET=\"$CURRENT_KEY_SECRET\""
|
||||
echo ""
|
||||
echo "🐳 Docker:"
|
||||
echo "docker run -e OSS_ACCESS_KEY_ID=\"$CURRENT_KEY_ID\" -e OSS_ACCESS_KEY_SECRET=\"$CURRENT_KEY_SECRET\" your-app"
|
||||
echo ""
|
||||
echo "☕ Java启动命令:"
|
||||
echo "OSS_ACCESS_KEY_ID=\"$CURRENT_KEY_ID\" OSS_ACCESS_KEY_SECRET=\"$CURRENT_KEY_SECRET\" java -jar app.jar"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "❌ 无效选项,请选择1-5"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo "🧪 验证环境变量设置:"
|
||||
echo "--------------------------------"
|
||||
echo "echo \$OSS_ACCESS_KEY_ID"
|
||||
echo "echo \$OSS_ACCESS_KEY_SECRET"
|
||||
echo ""
|
||||
echo "📖 详细文档: doc/oss/环境变量设置指南.md"
|
||||
echo "🚀 测试脚本: doc/oss/修复后验证测试.sh"
|
||||
271
doc/oss/环境变量设置指南.md
Normal file
271
doc/oss/环境变量设置指南.md
Normal file
@ -0,0 +1,271 @@
|
||||
# 环境变量设置指南
|
||||
|
||||
## 📋 需要设置的环境变量
|
||||
|
||||
根据你的配置文件,需要设置以下环境变量:
|
||||
|
||||
```bash
|
||||
OSS_ACCESS_KEY_ID=your_access_key_id
|
||||
OSS_ACCESS_KEY_SECRET=your_access_key_secret
|
||||
```
|
||||
|
||||
## 🖥️ 不同操作系统的设置方法
|
||||
|
||||
### 1. macOS / Linux
|
||||
|
||||
#### 方法1:临时设置(当前终端会话有效)
|
||||
```bash
|
||||
export OSS_ACCESS_KEY_ID=LTAI5t982gXi7A72gAa9yugE
|
||||
export OSS_ACCESS_KEY_SECRET=Mi9ZsSWLGkvFoMiLNiZ71hHFzVso30
|
||||
```
|
||||
|
||||
#### 方法2:永久设置(添加到配置文件)
|
||||
|
||||
**对于 Bash 用户:**
|
||||
```bash
|
||||
# 编辑 ~/.bashrc 或 ~/.bash_profile
|
||||
echo 'export OSS_ACCESS_KEY_ID=LTAI5t982gXi7A72gAa9yugE' >> ~/.bashrc
|
||||
echo 'export OSS_ACCESS_KEY_SECRET=Mi9ZsSWLGkvFoMiLNiZ71hHFzVso30' >> ~/.bashrc
|
||||
|
||||
# 重新加载配置
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
**对于 Zsh 用户(macOS 默认):**
|
||||
```bash
|
||||
# 编辑 ~/.zshrc
|
||||
echo 'export OSS_ACCESS_KEY_ID=LTAI5t982gXi7A72gAa9yugE' >> ~/.zshrc
|
||||
echo 'export OSS_ACCESS_KEY_SECRET=Mi9ZsSWLGkvFoMiLNiZ71hHFzVso30' >> ~/.zshrc
|
||||
|
||||
# 重新加载配置
|
||||
source ~/.zshrc
|
||||
```
|
||||
|
||||
### 2. Windows
|
||||
|
||||
#### 方法1:命令行临时设置
|
||||
|
||||
**CMD:**
|
||||
```cmd
|
||||
set OSS_ACCESS_KEY_ID=LTAI5t982gXi7A72gAa9yugE
|
||||
set OSS_ACCESS_KEY_SECRET=Mi9ZsSWLGkvFoMiLNiZ71hHFzVso30
|
||||
```
|
||||
|
||||
**PowerShell:**
|
||||
```powershell
|
||||
$env:OSS_ACCESS_KEY_ID="LTAI5t982gXi7A72gAa9yugE"
|
||||
$env:OSS_ACCESS_KEY_SECRET="Mi9ZsSWLGkvFoMiLNiZ71hHFzVso30"
|
||||
```
|
||||
|
||||
#### 方法2:系统环境变量设置
|
||||
|
||||
1. 右击"此电脑" → "属性"
|
||||
2. 点击"高级系统设置"
|
||||
3. 点击"环境变量"
|
||||
4. 在"系统变量"中点击"新建"
|
||||
5. 变量名:`OSS_ACCESS_KEY_ID`,变量值:`LTAI5t982gXi7A72gAa9yugE`
|
||||
6. 重复步骤4-5,设置 `OSS_ACCESS_KEY_SECRET`
|
||||
|
||||
## 🚀 启动应用程序的方法
|
||||
|
||||
### 1. 直接在终端启动
|
||||
|
||||
```bash
|
||||
# 设置环境变量
|
||||
export OSS_ACCESS_KEY_ID=LTAI5t982gXi7A72gAa9yugE
|
||||
export OSS_ACCESS_KEY_SECRET=Mi9ZsSWLGkvFoMiLNiZ71hHFzVso30
|
||||
|
||||
# 启动应用程序
|
||||
cd /Users/leocoder/leocoder/develop/templates/coder-common-thin/coder-common-thin-backend
|
||||
java -jar coder-common-thin-web/target/coder-common-thin-web-1.0.0.jar
|
||||
```
|
||||
|
||||
### 2. 一行命令启动
|
||||
|
||||
```bash
|
||||
OSS_ACCESS_KEY_ID=LTAI5t982gXi7A72gAa9yugE OSS_ACCESS_KEY_SECRET=Mi9ZsSWLGkvFoMiLNiZ71hHFzVso30 java -jar coder-common-thin-web/target/coder-common-thin-web-1.0.0.jar
|
||||
```
|
||||
|
||||
### 3. 使用启动脚本
|
||||
|
||||
创建一个启动脚本:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 文件名: start-app.sh
|
||||
|
||||
# 设置环境变量
|
||||
export OSS_ACCESS_KEY_ID=LTAI5t982gXi7A72gAa9yugE
|
||||
export OSS_ACCESS_KEY_SECRET=Mi9ZsSWLGkvFoMiLNiZ71hHFzVso30
|
||||
|
||||
# 切换到项目目录
|
||||
cd /Users/leocoder/leocoder/develop/templates/coder-common-thin/coder-common-thin-backend
|
||||
|
||||
# 启动应用程序
|
||||
java -jar coder-common-thin-web/target/coder-common-thin-web-1.0.0.jar
|
||||
```
|
||||
|
||||
给脚本添加执行权限并运行:
|
||||
```bash
|
||||
chmod +x start-app.sh
|
||||
./start-app.sh
|
||||
```
|
||||
|
||||
## 🐳 Docker 环境
|
||||
|
||||
### 1. docker run 命令
|
||||
```bash
|
||||
docker run -d \
|
||||
-e OSS_ACCESS_KEY_ID=LTAI5t982gXi7A72gAa9yugE \
|
||||
-e OSS_ACCESS_KEY_SECRET=Mi9ZsSWLGkvFoMiLNiZ71hHFzVso30 \
|
||||
-p 18099:18099 \
|
||||
your-app-image
|
||||
```
|
||||
|
||||
### 2. docker-compose.yml
|
||||
```yaml
|
||||
version: '3.8'
|
||||
services:
|
||||
app:
|
||||
image: your-app-image
|
||||
ports:
|
||||
- "18099:18099"
|
||||
environment:
|
||||
- OSS_ACCESS_KEY_ID=LTAI5t982gXi7A72gAa9yugE
|
||||
- OSS_ACCESS_KEY_SECRET=Mi9ZsSWLGkvFoMiLNiZ71hHFzVso30
|
||||
```
|
||||
|
||||
### 3. 使用 .env 文件
|
||||
```bash
|
||||
# 创建 .env 文件
|
||||
echo "OSS_ACCESS_KEY_ID=LTAI5t982gXi7A72gAa9yugE" > .env
|
||||
echo "OSS_ACCESS_KEY_SECRET=Mi9ZsSWLGkvFoMiLNiZ71hHFzVso30" >> .env
|
||||
|
||||
# docker-compose 会自动读取
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## 🔧 IDE 环境配置
|
||||
|
||||
### 1. IntelliJ IDEA
|
||||
|
||||
1. 打开 Run Configuration
|
||||
2. 选择你的 Spring Boot 应用
|
||||
3. 在 "Environment Variables" 中添加:
|
||||
- `OSS_ACCESS_KEY_ID=LTAI5t982gXi7A72gAa9yugE`
|
||||
- `OSS_ACCESS_KEY_SECRET=Mi9ZsSWLGkvFoMiLNiZ71hHFzVso30`
|
||||
|
||||
### 2. VS Code
|
||||
|
||||
在 `.vscode/launch.json` 中配置:
|
||||
```json
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Spring Boot App",
|
||||
"request": "launch",
|
||||
"mainClass": "org.leocoder.thin.web.CoderApplication",
|
||||
"env": {
|
||||
"OSS_ACCESS_KEY_ID": "LTAI5t982gXi7A72gAa9yugE",
|
||||
"OSS_ACCESS_KEY_SECRET": "Mi9ZsSWLGkvFoMiLNiZ71hHFzVso30"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Eclipse
|
||||
|
||||
1. 右击项目 → Run As → Run Configurations
|
||||
2. 选择你的 Java Application
|
||||
3. 在 "Environment" 选项卡中添加变量
|
||||
|
||||
## 🔐 安全最佳实践
|
||||
|
||||
### 1. 使用 .env 文件(推荐)
|
||||
|
||||
创建 `.env` 文件(不要提交到版本控制):
|
||||
```bash
|
||||
# .env 文件
|
||||
OSS_ACCESS_KEY_ID=LTAI5t982gXi7A72gAa9yugE
|
||||
OSS_ACCESS_KEY_SECRET=Mi9ZsSWLGkvFoMiLNiZ71hHFzVso30
|
||||
```
|
||||
|
||||
在 `.gitignore` 中添加:
|
||||
```
|
||||
.env
|
||||
```
|
||||
|
||||
### 2. 使用系统密钥管理
|
||||
|
||||
**macOS Keychain:**
|
||||
```bash
|
||||
# 存储到 Keychain
|
||||
security add-generic-password -s "oss-access-key" -a "your-app" -w "LTAI5t982gXi7A72gAa9yugE"
|
||||
|
||||
# 从 Keychain 读取
|
||||
OSS_ACCESS_KEY_ID=$(security find-generic-password -s "oss-access-key" -a "your-app" -w)
|
||||
```
|
||||
|
||||
**Linux Secret Service:**
|
||||
```bash
|
||||
# 使用 secret-tool
|
||||
secret-tool store --label="OSS Access Key" service oss-access-key LTAI5t982gXi7A72gAa9yugE
|
||||
```
|
||||
|
||||
### 3. 配置文件最佳实践
|
||||
|
||||
修改 `application-dev.yml`:
|
||||
```yaml
|
||||
coder:
|
||||
oss:
|
||||
# 只从环境变量读取,不设置默认值
|
||||
access-key-id: ${OSS_ACCESS_KEY_ID}
|
||||
access-key-secret: ${OSS_ACCESS_KEY_SECRET}
|
||||
```
|
||||
|
||||
## ✅ 验证环境变量
|
||||
|
||||
### 1. 检查环境变量是否设置成功
|
||||
```bash
|
||||
# Linux/macOS
|
||||
echo $OSS_ACCESS_KEY_ID
|
||||
echo $OSS_ACCESS_KEY_SECRET
|
||||
|
||||
# Windows CMD
|
||||
echo %OSS_ACCESS_KEY_ID%
|
||||
echo %OSS_ACCESS_KEY_SECRET%
|
||||
|
||||
# Windows PowerShell
|
||||
echo $env:OSS_ACCESS_KEY_ID
|
||||
echo $env:OSS_ACCESS_KEY_SECRET
|
||||
```
|
||||
|
||||
### 2. 在应用程序中验证
|
||||
|
||||
在 Java 代码中临时添加日志:
|
||||
```java
|
||||
@PostConstruct
|
||||
public void logOssConfig() {
|
||||
log.info("OSS_ACCESS_KEY_ID: {}", System.getenv("OSS_ACCESS_KEY_ID"));
|
||||
log.info("OSS_ACCESS_KEY_SECRET: {}",
|
||||
System.getenv("OSS_ACCESS_KEY_SECRET") != null ? "已设置" : "未设置");
|
||||
}
|
||||
```
|
||||
|
||||
## 🚨 注意事项
|
||||
|
||||
1. **不要在版本控制中提交真实的密钥**
|
||||
2. **定期轮换访问密钥**
|
||||
3. **使用最小权限原则配置OSS权限**
|
||||
4. **在生产环境中使用更安全的密钥管理方案**
|
||||
5. **重启应用程序后环境变量才会生效**
|
||||
|
||||
## 📝 快速设置脚本
|
||||
|
||||
我已经为你准备了一个快速设置脚本,运行即可:
|
||||
|
||||
```bash
|
||||
# 当前目录下创建 setup-env.sh
|
||||
chmod +x setup-env.sh
|
||||
./setup-env.sh
|
||||
```
|
||||
|
||||
按照这个指南设置环境变量后,你的OSS功能就可以正常使用了!
|
||||
1855
doc/oss/阿里云OSS文件上传系统设计方案.md
Normal file
1855
doc/oss/阿里云OSS文件上传系统设计方案.md
Normal file
File diff suppressed because it is too large
Load Diff
727
doc/websocket/WebSocket权限实时推送技术方案设计.md
Normal file
727
doc/websocket/WebSocket权限实时推送技术方案设计.md
Normal file
@ -0,0 +1,727 @@
|
||||
# WebSocket权限实时推送技术方案设计
|
||||
|
||||
## 📋 项目背景
|
||||
|
||||
在当前的权限管理系统中,管理员修改用户权限后,用户需要重新登录才能获得最新权限,这严重影响了用户体验。为了解决这个问题,我们设计了基于WebSocket的权限实时推送方案,让权限变更能够立即生效。
|
||||
|
||||
## 🎯 整体架构设计
|
||||
|
||||
### 系统架构图
|
||||
```
|
||||
┌─────────────────┐ WebSocket ┌─────────────────┐ 数据库操作 ┌─────────────────┐
|
||||
│ 前端应用 │ ←──────────→ │ Spring Boot │ ←──────────→ │ MySQL数据库 │
|
||||
│ │ │ 后端服务 │ │ │
|
||||
│ - 权限缓存 │ │ - WebSocket服务 │ │ - 用户权限表 │
|
||||
│ - 实时更新 │ │ - 权限管理 │ │ - 权限变更记录 │
|
||||
│ - 用户界面 │ │ - 消息推送 │ │ │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
### 核心目标
|
||||
- ✅ 权限变更实时生效,无需重新登录
|
||||
- ✅ 支持多标签页同步更新
|
||||
- ✅ 安全可靠的消息推送机制
|
||||
- ✅ 良好的用户体验和性能表现
|
||||
|
||||
## 🔧 后端技术方案设计
|
||||
|
||||
### 1. WebSocket服务架构
|
||||
|
||||
#### 1.1 技术栈选择
|
||||
```
|
||||
Spring Boot + Spring WebSocket + SaToken + Redis + MySQL
|
||||
```
|
||||
|
||||
#### 1.2 核心组件设计
|
||||
```
|
||||
┌── WebSocket管理层
|
||||
│ ├── WebSocketConfig (配置)
|
||||
│ ├── WebSocketHandler (连接处理)
|
||||
│ └── WebSocketInterceptor (权限验证)
|
||||
│
|
||||
├── 权限推送服务层
|
||||
│ ├── PermissionPushService (权限推送核心服务)
|
||||
│ ├── UserSessionManager (用户会话管理)
|
||||
│ └── MessageBroadcaster (消息广播器)
|
||||
│
|
||||
├── 权限监听层
|
||||
│ ├── PermissionChangeListener (权限变更监听)
|
||||
│ ├── RoleChangeListener (角色变更监听)
|
||||
│ └── MenuChangeListener (菜单变更监听)
|
||||
│
|
||||
└── 数据存储层
|
||||
├── Redis (会话存储 + 消息队列)
|
||||
└── MySQL (权限数据 + 变更记录)
|
||||
```
|
||||
|
||||
### 2. 核心服务设计
|
||||
|
||||
#### 2.1 WebSocket连接管理
|
||||
```java
|
||||
// 连接管理策略
|
||||
- 用户登录后自动建立WebSocket连接
|
||||
- 一个用户可以有多个连接(多标签页支持)
|
||||
- 连接断开后自动重连机制
|
||||
- 连接状态持久化到Redis
|
||||
|
||||
// 会话存储结构
|
||||
Key: "websocket:user:{userId}"
|
||||
Value: {
|
||||
"connections": [
|
||||
{
|
||||
"sessionId": "session-123",
|
||||
"connectTime": 1640995200000,
|
||||
"lastHeartbeat": 1640995800000,
|
||||
"browser": "Chrome",
|
||||
"ip": "192.168.1.100"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 权限变更监听机制
|
||||
```java
|
||||
// 监听触发点
|
||||
1. 用户角色分配/取消
|
||||
2. 角色权限修改
|
||||
3. 菜单权限调整
|
||||
4. 用户状态变更
|
||||
|
||||
// 变更事件设计
|
||||
@EventListener
|
||||
public class PermissionChangeListener {
|
||||
// 用户角色变更
|
||||
@Async
|
||||
public void handleUserRoleChange(UserRoleChangeEvent event)
|
||||
|
||||
// 角色权限变更
|
||||
@Async
|
||||
public void handleRolePermissionChange(RolePermissionChangeEvent event)
|
||||
|
||||
// 菜单权限变更
|
||||
@Async
|
||||
public void handleMenuPermissionChange(MenuPermissionChangeEvent event)
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3 消息推送策略
|
||||
```java
|
||||
// 消息类型设计
|
||||
enum MessageType {
|
||||
PERMISSION_UPDATE, // 权限更新
|
||||
ROLE_CHANGE, // 角色变更
|
||||
FORCE_LOGOUT, // 强制退出
|
||||
SYSTEM_NOTICE // 系统通知
|
||||
}
|
||||
|
||||
// 推送策略
|
||||
1. 精准推送:只推送给受影响的用户
|
||||
2. 批量推送:角色权限变更时推送给该角色的所有用户
|
||||
3. 广播推送:系统级权限调整时推送给所有在线用户
|
||||
```
|
||||
|
||||
### 3. 数据库设计扩展
|
||||
|
||||
#### 3.1 权限变更记录表
|
||||
```sql
|
||||
CREATE TABLE sys_permission_change_log (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
change_type VARCHAR(50) NOT NULL, -- USER_ROLE, ROLE_PERMISSION, MENU_PERMISSION
|
||||
target_user_id BIGINT, -- 目标用户ID(如果是用户级变更)
|
||||
target_role_id BIGINT, -- 目标角色ID(如果是角色级变更)
|
||||
operator_id BIGINT NOT NULL, -- 操作者ID
|
||||
change_detail JSON, -- 变更详情
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_user_time (target_user_id, create_time),
|
||||
INDEX idx_role_time (target_role_id, create_time)
|
||||
);
|
||||
```
|
||||
|
||||
#### 3.2 WebSocket会话表
|
||||
```sql
|
||||
CREATE TABLE sys_websocket_session (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
user_id BIGINT NOT NULL,
|
||||
session_id VARCHAR(100) NOT NULL UNIQUE,
|
||||
connect_time DATETIME NOT NULL,
|
||||
disconnect_time DATETIME,
|
||||
client_info JSON, -- 客户端信息
|
||||
status TINYINT DEFAULT 1, -- 1:连接中 0:已断开
|
||||
INDEX idx_user_status (user_id, status)
|
||||
);
|
||||
```
|
||||
|
||||
### 4. 后端核心接口设计
|
||||
|
||||
#### 4.1 WebSocket端点配置
|
||||
```java
|
||||
@Configuration
|
||||
@EnableWebSocket
|
||||
public class WebSocketConfig implements WebSocketConfigurer {
|
||||
|
||||
@Override
|
||||
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
|
||||
registry.addHandler(new PermissionWebSocketHandler(), "/ws/permission")
|
||||
.setAllowedOrigins("*")
|
||||
.addInterceptors(new WebSocketAuthInterceptor());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.2 权限推送服务接口
|
||||
```java
|
||||
@Component
|
||||
public class PermissionPushService {
|
||||
|
||||
/**
|
||||
* 推送权限更新消息给指定用户
|
||||
*/
|
||||
public void pushPermissionUpdate(Long userId, List<String> newPermissions);
|
||||
|
||||
/**
|
||||
* 推送角色变更消息给指定用户
|
||||
*/
|
||||
public void pushRoleChange(Long userId, List<String> newRoles);
|
||||
|
||||
/**
|
||||
* 批量推送权限更新(角色权限变更时)
|
||||
*/
|
||||
public void batchPushPermissionUpdate(Long roleId, List<String> newPermissions);
|
||||
|
||||
/**
|
||||
* 强制用户下线
|
||||
*/
|
||||
public void forceUserLogout(Long userId, String reason);
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.3 消息格式定义
|
||||
```java
|
||||
@Data
|
||||
public class WebSocketMessage {
|
||||
private String type; // 消息类型
|
||||
private Long userId; // 目标用户ID
|
||||
private Long timestamp; // 时间戳
|
||||
private Object data; // 消息数据
|
||||
private String messageId; // 消息ID
|
||||
private String operator; // 操作者
|
||||
}
|
||||
|
||||
@Data
|
||||
public class PermissionUpdateData {
|
||||
private List<String> permissions; // 新权限列表
|
||||
private List<String> roles; // 新角色列表
|
||||
private String updateType; // 更新类型:ADD, REMOVE, REPLACE
|
||||
private String reason; // 变更原因
|
||||
}
|
||||
```
|
||||
|
||||
## 🌐 前端技术方案设计
|
||||
|
||||
### 1. WebSocket客户端架构
|
||||
|
||||
#### 1.1 技术栈
|
||||
```
|
||||
Vue3 + TypeScript + Pinia + WebSocket API
|
||||
```
|
||||
|
||||
#### 1.2 组件设计
|
||||
```
|
||||
┌── WebSocket管理层
|
||||
│ ├── WebSocketService (核心WebSocket服务)
|
||||
│ ├── ConnectionManager (连接管理器)
|
||||
│ └── MessageHandler (消息处理器)
|
||||
│
|
||||
├── 权限更新层
|
||||
│ ├── PermissionUpdateService (权限更新服务)
|
||||
│ ├── PermissionSyncManager (权限同步管理)
|
||||
│ └── PermissionNotification (权限通知)
|
||||
│
|
||||
└── 用户界面层
|
||||
├── PermissionUpdateNotify (权限更新提示组件)
|
||||
├── ConnectionStatus (连接状态组件)
|
||||
└── WebSocketDebugPanel (调试面板)
|
||||
```
|
||||
|
||||
### 2. WebSocket服务设计
|
||||
|
||||
#### 2.1 连接管理策略
|
||||
```typescript
|
||||
class WebSocketService {
|
||||
// 连接策略
|
||||
- 用户登录成功后自动连接
|
||||
- 连接断开后指数退避重连
|
||||
- 页面可见性变化时管理连接
|
||||
- 网络状态变化时重连
|
||||
|
||||
// 连接状态管理
|
||||
enum ConnectionState {
|
||||
DISCONNECTED = 'disconnected',
|
||||
CONNECTING = 'connecting',
|
||||
CONNECTED = 'connected',
|
||||
RECONNECTING = 'reconnecting',
|
||||
ERROR = 'error'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 消息处理机制
|
||||
```typescript
|
||||
// 消息类型定义
|
||||
interface WebSocketMessage {
|
||||
type: 'PERMISSION_UPDATE' | 'ROLE_CHANGE' | 'FORCE_LOGOUT' | 'SYSTEM_NOTICE'
|
||||
userId: number
|
||||
timestamp: number
|
||||
data: any
|
||||
messageId: string
|
||||
}
|
||||
|
||||
// 消息处理器
|
||||
class MessageHandler {
|
||||
handlePermissionUpdate() // 处理权限更新
|
||||
handleRoleChange() // 处理角色变更
|
||||
handleForceLogout() // 处理强制退出
|
||||
handleSystemNotice() // 处理系统通知
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 权限同步机制
|
||||
|
||||
#### 3.1 同步策略
|
||||
```typescript
|
||||
class PermissionSyncManager {
|
||||
// 同步时机
|
||||
1. 收到权限更新消息时立即同步
|
||||
2. 连接重建后检查权限版本
|
||||
3. 页面激活时检查权限一致性
|
||||
|
||||
// 同步方式
|
||||
async syncPermissions(updateType: string) {
|
||||
// 1. 请求最新权限数据
|
||||
// 2. 更新本地权限缓存
|
||||
// 3. 触发UI重新渲染
|
||||
// 4. 显示权限更新通知
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 冲突处理
|
||||
```typescript
|
||||
// 权限冲突处理策略
|
||||
1. 权限被收回:立即隐藏相关UI,显示权限不足提示
|
||||
2. 权限被授予:立即显示新的功能按钮,显示权限获得提示
|
||||
3. 强制退出:清理本地数据,跳转到登录页
|
||||
4. 操作中断:保存用户操作状态,权限恢复后继续
|
||||
```
|
||||
|
||||
### 4. 前端核心服务设计
|
||||
|
||||
#### 4.1 WebSocket服务接口
|
||||
```typescript
|
||||
export interface IWebSocketService {
|
||||
// 连接管理
|
||||
connect(): Promise<void>
|
||||
disconnect(): void
|
||||
reconnect(): Promise<void>
|
||||
|
||||
// 消息发送
|
||||
sendMessage(message: WebSocketMessage): void
|
||||
|
||||
// 事件监听
|
||||
onMessage(callback: (message: WebSocketMessage) => void): void
|
||||
onConnected(callback: () => void): void
|
||||
onDisconnected(callback: () => void): void
|
||||
onError(callback: (error: Error) => void): void
|
||||
|
||||
// 状态查询
|
||||
isConnected(): boolean
|
||||
getConnectionState(): ConnectionState
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.2 权限更新服务接口
|
||||
```typescript
|
||||
export interface IPermissionUpdateService {
|
||||
// 权限同步
|
||||
syncPermissions(): Promise<void>
|
||||
updateLocalPermissions(permissions: string[]): void
|
||||
|
||||
// 通知管理
|
||||
showPermissionUpdateNotification(updateInfo: PermissionUpdateInfo): void
|
||||
showPermissionRevokedWarning(revokedPermissions: string[]): void
|
||||
|
||||
// 权限检查
|
||||
checkPermissionChange(): Promise<boolean>
|
||||
validateCurrentPermissions(): boolean
|
||||
}
|
||||
```
|
||||
|
||||
## 🔐 安全性设计
|
||||
|
||||
### 1. 连接安全
|
||||
```java
|
||||
// Token验证
|
||||
- WebSocket握手时验证JWT Token
|
||||
- 定期验证Token有效性
|
||||
- Token过期时自动断开连接
|
||||
|
||||
// 权限验证
|
||||
- 连接建立时验证用户权限
|
||||
- 消息发送前验证操作权限
|
||||
- 防止权限越权操作
|
||||
|
||||
// 示例:WebSocket拦截器
|
||||
@Component
|
||||
public class WebSocketAuthInterceptor implements HandshakeInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean beforeHandshake(ServerHttpRequest request,
|
||||
ServerHttpResponse response,
|
||||
WebSocketHandler wsHandler,
|
||||
Map<String, Object> attributes) {
|
||||
// 1. 提取Token
|
||||
String token = extractTokenFromRequest(request);
|
||||
|
||||
// 2. 验证Token有效性
|
||||
if (!saTokenUtil.isValidToken(token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. 获取用户信息
|
||||
Long userId = saTokenUtil.getUserIdFromToken(token);
|
||||
attributes.put("userId", userId);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 消息安全
|
||||
```java
|
||||
// 消息加密
|
||||
- 敏感消息内容加密传输
|
||||
- 消息完整性校验
|
||||
- 防止消息重放攻击
|
||||
|
||||
// 频率限制
|
||||
- 连接频率限制
|
||||
- 消息发送频率限制
|
||||
- 异常连接自动断开
|
||||
|
||||
// 示例:消息安全处理
|
||||
@Component
|
||||
public class MessageSecurityHandler {
|
||||
|
||||
public WebSocketMessage encryptMessage(WebSocketMessage message) {
|
||||
// 对敏感数据进行加密
|
||||
if (message.getType().equals("PERMISSION_UPDATE")) {
|
||||
String encryptedData = aesUtil.encrypt(message.getData().toString());
|
||||
message.setData(encryptedData);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
public boolean validateMessageIntegrity(WebSocketMessage message) {
|
||||
// 验证消息完整性
|
||||
String expectedHash = calculateMessageHash(message);
|
||||
return expectedHash.equals(message.getHash());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 访问控制
|
||||
```java
|
||||
// 用户隔离
|
||||
- 确保用户只能接收自己的权限变更消息
|
||||
- 防止跨用户信息泄露
|
||||
- 管理员权限特殊处理
|
||||
|
||||
// 示例:消息权限验证
|
||||
@Component
|
||||
public class MessagePermissionValidator {
|
||||
|
||||
public boolean canReceiveMessage(Long userId, WebSocketMessage message) {
|
||||
// 1. 检查消息是否发给该用户
|
||||
if (!message.getUserId().equals(userId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 检查用户是否有权限接收该类型消息
|
||||
return hasPermissionToReceiveMessageType(userId, message.getType());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🚀 性能优化方案
|
||||
|
||||
### 1. 连接优化
|
||||
```java
|
||||
// 连接池管理
|
||||
- 合理设置连接数上限
|
||||
- 空闲连接自动清理
|
||||
- 连接状态监控
|
||||
|
||||
// 内存优化
|
||||
- 及时清理断开的连接
|
||||
- 消息队列大小限制
|
||||
- 定期清理过期数据
|
||||
|
||||
// 示例:连接池配置
|
||||
@Configuration
|
||||
public class WebSocketPoolConfig {
|
||||
|
||||
@Bean
|
||||
public WebSocketConnectionPool connectionPool() {
|
||||
return WebSocketConnectionPool.builder()
|
||||
.maxConnections(10000) // 最大连接数
|
||||
.maxConnectionsPerUser(5) // 每用户最大连接数
|
||||
.idleTimeout(Duration.ofMinutes(30)) // 空闲超时
|
||||
.cleanupInterval(Duration.ofMinutes(5)) // 清理间隔
|
||||
.build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 推送优化
|
||||
```java
|
||||
// 批量推送
|
||||
- 相同类型消息合并推送
|
||||
- 延迟推送策略
|
||||
- 推送优先级管理
|
||||
|
||||
// 缓存优化
|
||||
- Redis缓存权限数据
|
||||
- 权限变更增量推送
|
||||
- 本地权限缓存
|
||||
|
||||
// 示例:批量推送实现
|
||||
@Component
|
||||
public class BatchMessagePusher {
|
||||
|
||||
private final Map<String, List<WebSocketMessage>> messageBatches = new ConcurrentHashMap<>();
|
||||
|
||||
@Scheduled(fixedDelay = 1000) // 每秒批量推送一次
|
||||
public void flushMessageBatches() {
|
||||
messageBatches.forEach((batchKey, messages) -> {
|
||||
WebSocketMessage batchMessage = mergeMess
|
||||
ages(messages);
|
||||
webSocketHandler.broadcast(batchMessage);
|
||||
});
|
||||
messageBatches.clear();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 数据库优化
|
||||
```sql
|
||||
-- 权限查询优化
|
||||
CREATE INDEX idx_user_permission_version ON sys_login_user(user_id, permission_version);
|
||||
CREATE INDEX idx_role_permission_update ON sys_role_menu(role_id, update_time);
|
||||
|
||||
-- 会话查询优化
|
||||
CREATE INDEX idx_websocket_user_status ON sys_websocket_session(user_id, status, connect_time);
|
||||
|
||||
-- 变更日志查询优化
|
||||
CREATE INDEX idx_permission_log_target ON sys_permission_change_log(target_user_id, target_role_id, create_time);
|
||||
```
|
||||
|
||||
## 📊 监控和日志
|
||||
|
||||
### 1. 连接监控
|
||||
```java
|
||||
// 监控指标
|
||||
- 当前连接数
|
||||
- 连接成功率
|
||||
- 连接断开原因统计
|
||||
- 消息推送成功率
|
||||
|
||||
// 示例:监控服务
|
||||
@Component
|
||||
public class WebSocketMonitorService {
|
||||
|
||||
private final MeterRegistry meterRegistry;
|
||||
|
||||
public void recordConnection(String result) {
|
||||
Counter.builder("websocket.connections")
|
||||
.tag("result", result)
|
||||
.register(meterRegistry)
|
||||
.increment();
|
||||
}
|
||||
|
||||
public void recordMessagePush(String type, String result) {
|
||||
Counter.builder("websocket.messages")
|
||||
.tag("type", type)
|
||||
.tag("result", result)
|
||||
.register(meterRegistry)
|
||||
.increment();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 错误处理和日志
|
||||
```java
|
||||
// 日志记录
|
||||
- 连接建立/断开日志
|
||||
- 消息推送日志
|
||||
- 错误异常日志
|
||||
- 性能统计日志
|
||||
|
||||
// 示例:日志配置
|
||||
@Slf4j
|
||||
@Component
|
||||
public class WebSocketLogger {
|
||||
|
||||
public void logConnection(Long userId, String action, String result) {
|
||||
log.info("WebSocket连接 - 用户:{}, 操作:{}, 结果:{}", userId, action, result);
|
||||
}
|
||||
|
||||
public void logMessagePush(Long userId, String messageType, String result) {
|
||||
log.info("消息推送 - 用户:{}, 类型:{}, 结果:{}", userId, messageType, result);
|
||||
}
|
||||
|
||||
public void logError(String operation, Exception e) {
|
||||
log.error("WebSocket错误 - 操作:{}, 异常:", operation, e);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📋 实施步骤
|
||||
|
||||
### 第一阶段:基础WebSocket服务(1-2周)
|
||||
1. **后端WebSocket服务搭建**
|
||||
- 创建WebSocket配置类
|
||||
- 实现WebSocket处理器
|
||||
- 添加权限验证拦截器
|
||||
- 基础连接管理功能
|
||||
|
||||
2. **前端WebSocket客户端**
|
||||
- 创建WebSocket服务类
|
||||
- 实现连接管理逻辑
|
||||
- 添加重连机制
|
||||
- 基础消息收发功能
|
||||
|
||||
3. **基础测试**
|
||||
- 连接建立测试
|
||||
- 消息收发测试
|
||||
- 断线重连测试
|
||||
|
||||
### 第二阶段:权限推送核心功能(2-3周)
|
||||
1. **权限变更监听**
|
||||
- 实现用户角色变更监听
|
||||
- 实现角色权限变更监听
|
||||
- 实现菜单权限变更监听
|
||||
- 创建权限变更事件
|
||||
|
||||
2. **消息推送逻辑**
|
||||
- 实现权限更新消息推送
|
||||
- 实现角色变更消息推送
|
||||
- 实现批量推送逻辑
|
||||
- 添加消息去重机制
|
||||
|
||||
3. **前端权限同步**
|
||||
- 实现权限数据更新
|
||||
- 实现UI实时刷新
|
||||
- 添加权限变更通知
|
||||
- 处理权限冲突场景
|
||||
|
||||
### 第三阶段:高级功能和优化(2-3周)
|
||||
1. **安全性增强**
|
||||
- 添加消息加密
|
||||
- 实现访问控制
|
||||
- 添加频率限制
|
||||
- 防止攻击机制
|
||||
|
||||
2. **性能优化**
|
||||
- 实现连接池管理
|
||||
- 添加消息批量处理
|
||||
- 优化数据库查询
|
||||
- 添加缓存机制
|
||||
|
||||
3. **用户体验优化**
|
||||
- 完善重连策略
|
||||
- 优化通知交互
|
||||
- 添加调试面板
|
||||
- 处理边界情况
|
||||
|
||||
### 第四阶段:生产环境适配(1-2周)
|
||||
1. **集群部署支持**
|
||||
- Redis消息队列
|
||||
- 负载均衡配置
|
||||
- 会话共享机制
|
||||
|
||||
2. **监控和运维**
|
||||
- 添加监控指标
|
||||
- 完善日志记录
|
||||
- 配置告警机制
|
||||
- 制定运维手册
|
||||
|
||||
3. **测试和部署**
|
||||
- 压力测试
|
||||
- 兼容性测试
|
||||
- 生产环境部署
|
||||
- 回滚方案准备
|
||||
|
||||
## 🔍 风险评估和应对方案
|
||||
|
||||
### 1. 技术风险
|
||||
| 风险项 | 影响度 | 概率 | 应对方案 |
|
||||
|--------|--------|------|----------|
|
||||
| WebSocket连接不稳定 | 高 | 中 | 完善重连机制,降级到轮询 |
|
||||
| 消息推送延迟 | 中 | 低 | 优化推送逻辑,添加超时机制 |
|
||||
| 内存泄漏 | 高 | 低 | 定期清理,添加监控 |
|
||||
| 安全漏洞 | 高 | 低 | 安全审计,权限校验 |
|
||||
|
||||
### 2. 业务风险
|
||||
| 风险项 | 影响度 | 概率 | 应对方案 |
|
||||
|--------|--------|------|----------|
|
||||
| 权限同步失败 | 高 | 中 | 手动刷新机制,错误提示 |
|
||||
| 用户体验下降 | 中 | 低 | 渐进式升级,用户反馈 |
|
||||
| 系统复杂度增加 | 中 | 高 | 完善文档,团队培训 |
|
||||
|
||||
### 3. 运维风险
|
||||
| 风险项 | 影响度 | 概率 | 应对方案 |
|
||||
|--------|--------|------|----------|
|
||||
| 服务器压力增加 | 中 | 中 | 性能监控,扩容预案 |
|
||||
| 故障排查困难 | 中 | 中 | 详细日志,监控告警 |
|
||||
| 部署复杂度增加 | 低 | 高 | 自动化部署,回滚机制 |
|
||||
|
||||
## 📈 预期效果
|
||||
|
||||
### 1. 用户体验提升
|
||||
- ✅ 权限变更即时生效,无需重新登录
|
||||
- ✅ 多标签页权限状态同步
|
||||
- ✅ 清晰的权限变更通知
|
||||
|
||||
### 2. 系统性能
|
||||
- ✅ 减少不必要的接口调用
|
||||
- ✅ 提高权限检查效率
|
||||
- ✅ 降低服务器负载
|
||||
|
||||
### 3. 管理效率
|
||||
- ✅ 权限管理操作即时生效
|
||||
- ✅ 减少用户投诉和支持工作
|
||||
- ✅ 提高系统管理效率
|
||||
|
||||
## 📚 相关技术文档
|
||||
|
||||
1. [Spring WebSocket官方文档](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#websocket)
|
||||
2. [Vue3 WebSocket最佳实践](https://vuejs.org/guide/extras/web-components.html)
|
||||
3. [SaToken权限认证文档](https://sa-token.dev33.cn/)
|
||||
4. [Redis消息队列使用指南](https://redis.io/docs/manual/pubsub/)
|
||||
|
||||
## 👥 团队分工建议
|
||||
|
||||
| 角色 | 职责 | 时间投入 |
|
||||
|------|------|----------|
|
||||
| 后端开发 | WebSocket服务、权限监听、消息推送 | 60% |
|
||||
| 前端开发 | WebSocket客户端、权限同步、UI更新 | 40% |
|
||||
| 测试工程师 | 功能测试、性能测试、安全测试 | 全程参与 |
|
||||
| 运维工程师 | 部署配置、监控告警、性能调优 | 后期参与 |
|
||||
|
||||
---
|
||||
|
||||
**文档版本**: v1.0
|
||||
**创建时间**: 2025-01-07
|
||||
**更新时间**: 2025-01-07
|
||||
**负责人**: 系统架构团队
|
||||
**审核人**: 技术负责人
|
||||
242
script/README.md
Normal file
242
script/README.md
Normal file
@ -0,0 +1,242 @@
|
||||
# 项目模板重构脚本
|
||||
|
||||
这是一个用于将 `heritage-backend` 项目重构为新项目模板的自动化脚本。
|
||||
|
||||
## 功能特性
|
||||
|
||||
🚀 **全自动重构**:一键完成整个项目的重构过程
|
||||
📁 **目录重命名**:自动重命名所有模块目录和子模块目录
|
||||
📦 **Maven配置**:更新所有 pom.xml 文件中的 groupId、artifactId 和模块名
|
||||
☕ **Java包结构**:重构所有 Java 文件的包名和 import 语句
|
||||
⚙️ **配置文件**:更新所有配置文件中的包名引用
|
||||
🗄️ **数据库配置**:重命名 SQL 文件和更新数据库名引用
|
||||
🔄 **多层级支持**:支持插件模块等多层级子模块结构
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 1. 进入脚本目录
|
||||
```bash
|
||||
cd /Users/leocoder/leocoder/develop/frameworks/heritage/heritage-backend/script
|
||||
```
|
||||
|
||||
### 2. 执行重构脚本
|
||||
```bash
|
||||
./project-template-refactor.sh
|
||||
```
|
||||
|
||||
### 3. 按提示输入新项目信息
|
||||
脚本会提示你输入以下信息:
|
||||
- **新项目名称**:例如 `my-project-backend`
|
||||
- **新的GroupId**:例如 `com.company.project`
|
||||
- **新的包名**:例如 `com.company.project`
|
||||
- **新的模块前缀**:例如 `my-project`
|
||||
- **新的数据库名**:例如 `my_project_db`
|
||||
|
||||
### 4. 确认配置并开始重构
|
||||
脚本会显示所有配置信息供你确认,输入 `y` 开始重构。
|
||||
|
||||
## 重构内容详解
|
||||
|
||||
### 📁 目录结构重构
|
||||
**原始结构:**
|
||||
```
|
||||
/parent-directory/
|
||||
└── heritage-backend/ ← 项目根目录
|
||||
├── heritage-web/
|
||||
├── heritage-common/
|
||||
├── heritage-model/
|
||||
├── heritage-mybatisplus/
|
||||
├── heritage-modules/
|
||||
├── heritage-plugins/
|
||||
│ ├── heritage-easyexcel/
|
||||
│ ├── heritage-oss/
|
||||
│ ├── heritage-sa-token/
|
||||
│ └── ...其他插件模块
|
||||
└── sql/
|
||||
└── heritage.sql
|
||||
```
|
||||
|
||||
**重构后结构(以 my-project-backend 为例):**
|
||||
```
|
||||
/parent-directory/
|
||||
├── heritage-backend_backup_* ← 自动创建的备份
|
||||
└── my-project-backend/ ← 重命名后的项目根目录
|
||||
├── my-project-web/
|
||||
├── my-project-common/
|
||||
├── my-project-model/
|
||||
├── my-project-mybatisplus/
|
||||
├── my-project-modules/
|
||||
├── my-project-plugins/
|
||||
│ ├── my-project-easyexcel/
|
||||
│ ├── my-project-oss/
|
||||
│ ├── my-project-sa-token/
|
||||
│ └── ...其他插件模块
|
||||
└── sql/
|
||||
└── my_project_db.sql
|
||||
```
|
||||
|
||||
⭐ **v1.2.0 新特性**:脚本现在会自动重命名项目根目录,无需手动操作!
|
||||
|
||||
### 📦 Maven 配置重构
|
||||
- **groupId**:`org.leocoder.heritage` → `com.company.project`
|
||||
- **artifactId**:`heritage-*` → `my-project-*`
|
||||
- **name**:相应模块名称更新
|
||||
- **依赖引用**:所有模块间依赖的 groupId 和 artifactId
|
||||
|
||||
### ☕ Java 包结构重构
|
||||
- **包声明**:`package org.leocoder.heritage.*` → `package com.company.project.*`
|
||||
- **import语句**:`import org.leocoder.heritage.*` → `import com.company.project.*`
|
||||
- **目录结构**:`src/main/java/org.leocoder.heritage/` → `src/main/java/com/company/project/`
|
||||
|
||||
### ⚙️ 配置文件重构
|
||||
- **application.yml / application-dev.yml / application-local.yml**:
|
||||
- `packages-to-scan: org.leocoder.heritage` → `packages-to-scan: com.company.project`
|
||||
- `projectName: CORDER-ADMIN-THIN` → `projectName: MY-PROJECT-ADMIN`
|
||||
- `pool-name: CORDER-HIKARI-DEV` → `pool-name: MY-PROJECT-HIKARI-DEV`
|
||||
- `name: coder-web` → `name: my-project-web`
|
||||
- `jdbc:mysql://localhost:3306/heritage` → `jdbc:mysql://localhost:3306/new-db-name`
|
||||
- `jdbc:mysql://localhost:3306/heritage-backup` → `jdbc:mysql://localhost:3306/new-db-name-backup`
|
||||
- `filePath: /path/heritage-backend/` → `filePath: /path/new-project-backend/`
|
||||
- **logback配置文件**(logback-spring*.xml):
|
||||
- `<contextName>heritage-logback</contextName>` → `<contextName>my-project-logback</contextName>`
|
||||
- `<property name="CORDER_ADMIN_LOGS" value="./logs"/>` → `<property name="MY_PROJECT_ADMIN_LOGS" value="./logs"/>`
|
||||
- 所有日志路径引用:`${CORDER_ADMIN_LOGS}` → `${MY_PROJECT_ADMIN_LOGS}`
|
||||
- **其他配置文件**:包名和项目相关引用更新
|
||||
|
||||
### 🗄️ SQL 文件重构
|
||||
- **文件重命名**:`heritage.sql` → `my_project_db.sql`
|
||||
- **数据库名更新**:SQL文件中的数据库名引用更新
|
||||
|
||||
## 安全特性
|
||||
|
||||
### 🔒 自动备份
|
||||
脚本执行前会自动创建项目备份:
|
||||
```
|
||||
heritage-backend_backup_20250922_143022/
|
||||
```
|
||||
|
||||
### ✅ 验证检查
|
||||
重构完成后自动验证是否还有旧的引用残留
|
||||
|
||||
### 🚫 错误处理
|
||||
遇到错误时立即停止执行,保护项目完整性
|
||||
|
||||
## 重构后操作
|
||||
|
||||
重构完成后请执行以下步骤:
|
||||
|
||||
### 1. 验证编译
|
||||
```bash
|
||||
mvn clean compile
|
||||
```
|
||||
|
||||
### 2. 更新数据库配置
|
||||
手动检查并更新以下配置文件中的数据库连接信息:
|
||||
- `*/src/main/resources/application-dev.yml`
|
||||
- `*/src/main/resources/application-local.yml`
|
||||
|
||||
### 3. 验证功能
|
||||
- 启动应用程序验证功能正常
|
||||
- 检查所有插件模块是否正常加载
|
||||
|
||||
### 4. IDE配置
|
||||
- 重新导入Maven项目
|
||||
- 检查项目结构和依赖
|
||||
|
||||
### 5. Git管理
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "refactor: 重构项目为新模板"
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
⚠️ **重要提醒:**
|
||||
1. 重构前请确保代码已提交到Git仓库
|
||||
2. 脚本会自动创建备份,但建议额外手动备份重要数据
|
||||
3. 重构过程不可逆,请谨慎操作
|
||||
4. 重构后需要手动更新数据库连接配置
|
||||
5. 如果项目中有自定义的特殊配置,可能需要手动调整
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
1. **权限错误**:确保脚本有可执行权限 `chmod +x project-template-refactor.sh`
|
||||
2. **路径错误**:确保在正确的项目根目录下执行脚本
|
||||
3. **Maven错误**:重构后如果编译失败,检查依赖配置是否正确
|
||||
4. **包名冲突**:如果新包名已存在,选择不同的包名
|
||||
5. **Bash版本兼容性**:脚本已修复了bash版本兼容性问题
|
||||
6. **包名未正确替换**:使用修复脚本 `./fix-existing-project.sh`
|
||||
7. **多余src目录**:使用修复脚本自动清理
|
||||
|
||||
### 项目修复工具
|
||||
|
||||
#### 1. 快速修复目录名反斜杠问题
|
||||
如果目录名中出现反斜杠(如 `leocoder\`):
|
||||
```bash
|
||||
./quick-fix-backslash.sh
|
||||
```
|
||||
|
||||
#### 2. 修复遗漏的modules子模块
|
||||
如果modules目录下的子模块没有被重命名:
|
||||
```bash
|
||||
./fix-missing-modules.sh
|
||||
```
|
||||
|
||||
#### 3. 修复已有问题的项目
|
||||
如果重构过程中出现包名未正确替换或目录结构问题:
|
||||
```bash
|
||||
./fix-existing-project.sh
|
||||
```
|
||||
|
||||
#### 4. 检查项目状态
|
||||
检查项目重构后的整体状态:
|
||||
```bash
|
||||
./project-status-check.sh
|
||||
```
|
||||
|
||||
#### 5. 演示logback配置处理
|
||||
查看logback配置处理效果:
|
||||
```bash
|
||||
./logback-demo.sh
|
||||
```
|
||||
|
||||
#### 6. 测试反斜杠修复功能
|
||||
验证反斜杠问题修复效果:
|
||||
```bash
|
||||
./test-backslash-fix.sh
|
||||
```
|
||||
|
||||
#### 7. 测试配置文件修复功能
|
||||
验证数据库连接字符串和文件路径配置修复效果:
|
||||
```bash
|
||||
./test-config-fix.sh
|
||||
```
|
||||
|
||||
### 回滚操作
|
||||
如果重构出现问题,可以:
|
||||
1. 删除重构后的项目目录
|
||||
2. 从备份目录恢复:`cp -r heritage-backend_backup_* heritage-backend`
|
||||
3. 或使用修复脚本进行增量修复
|
||||
|
||||
### 脚本版本信息
|
||||
- **v1.0.0**:初始版本
|
||||
- **v1.1.0**:修复bash兼容性问题,改进Java包结构迁移逻辑,增加修复工具
|
||||
- **v1.1.1**:修复sed正则表达式导致的目录名反斜杠问题,增加快速修复工具
|
||||
- **v1.2.0**:**重大更新** - 完善反斜杠自动清理,增加项目根目录自动重命名功能
|
||||
- **v1.2.1**:**关键修复** - 彻底解决反斜杠问题,增强MyBatis XML处理,改进验证逻辑
|
||||
- **v1.2.2**:**模块修复** - 修复modules子模块重命名遗漏问题,支持多层级子模块处理
|
||||
- **v1.2.3**:**配置修复** - 修复数据库连接字符串和文件路径配置未更新问题
|
||||
|
||||
## 技术支持
|
||||
|
||||
如果在使用过程中遇到问题,请:
|
||||
1. 检查脚本执行日志
|
||||
2. 查看备份目录是否完整
|
||||
3. 联系开发团队获取支持
|
||||
|
||||
---
|
||||
|
||||
**版本**:v1.2.3
|
||||
**作者**:Leocoder
|
||||
**更新时间**:2025-09-22
|
||||
567
script/project-template-refactor.sh
Executable file
567
script/project-template-refactor.sh
Executable file
@ -0,0 +1,567 @@
|
||||
#!/bin/bash
|
||||
|
||||
#=============================================================================
|
||||
# 项目模板重构脚本
|
||||
# 用于将coder-common-thin-backend项目重构为新的项目模板
|
||||
#
|
||||
# 功能:
|
||||
# 1. 重构项目名称和模块名
|
||||
# 2. 修改包名和groupId
|
||||
# 3. 更新配置文件中的包名引用
|
||||
# 4. 重命名SQL文件和数据库名
|
||||
# 5. 处理多层级子模块结构
|
||||
# 6. 更新数据库连接字符串
|
||||
# 7. 更新文件路径配置
|
||||
#
|
||||
# 作者: Leocoder
|
||||
# 版本: 1.2.3
|
||||
#=============================================================================
|
||||
|
||||
set -e # 遇到错误立即退出
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $1"
|
||||
}
|
||||
|
||||
# 当前项目信息
|
||||
CURRENT_PROJECT_NAME="coder-common-thin-backend"
|
||||
CURRENT_GROUP_ID="org.leocoder.thin"
|
||||
CURRENT_PACKAGE_NAME="org.leocoder.thin"
|
||||
CURRENT_MODULE_PREFIX="coder-common-thin"
|
||||
CURRENT_DB_NAME="coder-common-thin"
|
||||
|
||||
# 脚本目录
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
log_info "=== 项目模板重构脚本 v1.2.3 ==="
|
||||
log_info "项目根目录: $PROJECT_ROOT"
|
||||
|
||||
# 验证当前目录
|
||||
if [[ ! -f "$PROJECT_ROOT/pom.xml" ]]; then
|
||||
log_error "当前目录不是有效的Maven项目根目录"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 获取用户输入
|
||||
echo ""
|
||||
log_step "请输入新项目的配置信息:"
|
||||
|
||||
read -p "新项目名称 (例如: my-project-backend): " NEW_PROJECT_NAME
|
||||
read -p "新的GroupId (例如: com.company.project): " NEW_GROUP_ID
|
||||
read -p "新的包名 (例如: com.company.project): " NEW_PACKAGE_NAME
|
||||
read -p "新的模块前缀 (例如: my-project): " NEW_MODULE_PREFIX
|
||||
read -p "新的数据库名 (例如: my_project_db): " NEW_DB_NAME
|
||||
|
||||
# 验证输入
|
||||
if [[ -z "$NEW_PROJECT_NAME" || -z "$NEW_GROUP_ID" || -z "$NEW_PACKAGE_NAME" || -z "$NEW_MODULE_PREFIX" || -z "$NEW_DB_NAME" ]]; then
|
||||
log_error "所有参数都不能为空"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 显示配置信息确认
|
||||
echo ""
|
||||
log_info "=== 重构配置确认 ==="
|
||||
echo "项目名称: $CURRENT_PROJECT_NAME -> $NEW_PROJECT_NAME"
|
||||
echo "GroupId: $CURRENT_GROUP_ID -> $NEW_GROUP_ID"
|
||||
echo "包名: $CURRENT_PACKAGE_NAME -> $NEW_PACKAGE_NAME"
|
||||
echo "模块前缀: $CURRENT_MODULE_PREFIX -> $NEW_MODULE_PREFIX"
|
||||
echo "数据库名: $CURRENT_DB_NAME -> $NEW_DB_NAME"
|
||||
echo ""
|
||||
|
||||
read -p "确认开始重构?[y/N]: " CONFIRM
|
||||
if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then
|
||||
log_info "重构已取消"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 备份项目
|
||||
BACKUP_DIR="${PROJECT_ROOT}_backup_$(date +%Y%m%d_%H%M%S)"
|
||||
log_step "创建项目备份: $BACKUP_DIR"
|
||||
cp -r "$PROJECT_ROOT" "$BACKUP_DIR"
|
||||
log_info "备份完成"
|
||||
|
||||
# 重构函数
|
||||
|
||||
# 1. 重命名模块目录
|
||||
rename_module_directories() {
|
||||
log_step "重命名模块目录..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# 主模块重命名
|
||||
local modules=(
|
||||
"$CURRENT_MODULE_PREFIX-web"
|
||||
"$CURRENT_MODULE_PREFIX-common"
|
||||
"$CURRENT_MODULE_PREFIX-model"
|
||||
"$CURRENT_MODULE_PREFIX-mybatisplus"
|
||||
"$CURRENT_MODULE_PREFIX-modules"
|
||||
"$CURRENT_MODULE_PREFIX-plugins"
|
||||
)
|
||||
|
||||
for module in "${modules[@]}"; do
|
||||
if [[ -d "$module" ]]; then
|
||||
new_module="${module/$CURRENT_MODULE_PREFIX/$NEW_MODULE_PREFIX}"
|
||||
log_info "重命名: $module -> $new_module"
|
||||
mv "$module" "$new_module"
|
||||
fi
|
||||
done
|
||||
|
||||
# 递归重命名所有子模块(modules、plugins等)
|
||||
local parent_modules=(
|
||||
"$NEW_MODULE_PREFIX-modules"
|
||||
"$NEW_MODULE_PREFIX-plugins"
|
||||
)
|
||||
|
||||
for parent_module in "${parent_modules[@]}"; do
|
||||
if [[ -d "$parent_module" ]]; then
|
||||
log_info "处理子模块目录: $parent_module"
|
||||
cd "$parent_module"
|
||||
|
||||
# 重命名当前目录下的所有子模块
|
||||
for sub_dir in $CURRENT_MODULE_PREFIX-*; do
|
||||
if [[ -d "$sub_dir" ]]; then
|
||||
new_sub_dir="${sub_dir/$CURRENT_MODULE_PREFIX/$NEW_MODULE_PREFIX}"
|
||||
log_info "重命名子模块: $sub_dir -> $new_sub_dir"
|
||||
mv "$sub_dir" "$new_sub_dir"
|
||||
|
||||
# 递归处理子模块中的子子模块(如果存在)
|
||||
if [[ -d "$new_sub_dir" ]]; then
|
||||
cd "$new_sub_dir"
|
||||
for sub_sub_dir in $CURRENT_MODULE_PREFIX-*; do
|
||||
if [[ -d "$sub_sub_dir" ]]; then
|
||||
new_sub_sub_dir="${sub_sub_dir/$CURRENT_MODULE_PREFIX/$NEW_MODULE_PREFIX}"
|
||||
log_info "重命名子子模块: $sub_sub_dir -> $new_sub_sub_dir"
|
||||
mv "$sub_sub_dir" "$new_sub_sub_dir"
|
||||
fi
|
||||
done
|
||||
cd ".."
|
||||
fi
|
||||
fi
|
||||
done
|
||||
cd "$PROJECT_ROOT"
|
||||
fi
|
||||
done
|
||||
|
||||
log_info "模块目录重命名完成"
|
||||
}
|
||||
|
||||
# 2. 更新POM文件
|
||||
update_pom_files() {
|
||||
log_step "更新POM文件..."
|
||||
|
||||
# 查找所有pom.xml文件
|
||||
find "$PROJECT_ROOT" -name "pom.xml" -type f | while read pom_file; do
|
||||
log_info "更新POM: $pom_file"
|
||||
|
||||
# 使用sed进行替换
|
||||
sed -i.bak \
|
||||
-e "s|<groupId>$CURRENT_GROUP_ID</groupId>|<groupId>$NEW_GROUP_ID</groupId>|g" \
|
||||
-e "s|<artifactId>$CURRENT_PROJECT_NAME</artifactId>|<artifactId>$NEW_PROJECT_NAME</artifactId>|g" \
|
||||
-e "s|<name>$CURRENT_PROJECT_NAME</name>|<name>$NEW_PROJECT_NAME</name>|g" \
|
||||
-e "s|$CURRENT_MODULE_PREFIX-|$NEW_MODULE_PREFIX-|g" \
|
||||
"$pom_file"
|
||||
|
||||
# 删除备份文件
|
||||
rm -f "${pom_file}.bak"
|
||||
done
|
||||
|
||||
log_info "POM文件更新完成"
|
||||
}
|
||||
|
||||
# 3. 更新Java包结构
|
||||
update_java_packages() {
|
||||
log_step "更新Java包结构..."
|
||||
|
||||
# 转换包名路径
|
||||
local current_package_path="${CURRENT_PACKAGE_NAME//./\/}"
|
||||
local new_package_path="${NEW_PACKAGE_NAME//./\/}"
|
||||
|
||||
log_info "包路径转换: $current_package_path -> $new_package_path"
|
||||
|
||||
# 查找所有Java文件并更新包名和import语句
|
||||
find "$PROJECT_ROOT" -name "*.java" -type f | while read java_file; do
|
||||
log_info "更新Java文件内容: $java_file"
|
||||
|
||||
# 更新package声明和import语句,使用更精确的匹配(避免转义字符问题)
|
||||
sed -i.bak \
|
||||
-e "s|^package ${CURRENT_PACKAGE_NAME};|package ${NEW_PACKAGE_NAME};|g" \
|
||||
-e "s|^package ${CURRENT_PACKAGE_NAME}[.]|package ${NEW_PACKAGE_NAME}.|g" \
|
||||
-e "s|^import ${CURRENT_PACKAGE_NAME};|import ${NEW_PACKAGE_NAME};|g" \
|
||||
-e "s|^import ${CURRENT_PACKAGE_NAME}[.]|import ${NEW_PACKAGE_NAME}.|g" \
|
||||
-e "s|${CURRENT_PACKAGE_NAME}[.]|${NEW_PACKAGE_NAME}.|g" \
|
||||
"$java_file"
|
||||
|
||||
rm -f "${java_file}.bak"
|
||||
done
|
||||
|
||||
# 重构Java包目录结构
|
||||
log_info "开始重构Java包目录结构..."
|
||||
|
||||
# 查找所有包含当前包路径的java源码目录
|
||||
find "$PROJECT_ROOT" -path "*/src/main/java/$current_package_path" -type d | while read old_package_dir; do
|
||||
log_info "处理包目录: $old_package_dir"
|
||||
|
||||
# 获取java源码根目录
|
||||
local java_root="${old_package_dir%/$current_package_path}"
|
||||
local new_package_dir="$java_root/$new_package_path"
|
||||
|
||||
log_info " 源目录: $old_package_dir"
|
||||
log_info " 目标目录: $new_package_dir"
|
||||
|
||||
# 创建新的包目录结构
|
||||
mkdir -p "$(dirname "$new_package_dir")"
|
||||
|
||||
# 移动整个包目录到新位置
|
||||
if [[ -d "$old_package_dir" && "$old_package_dir" != "$new_package_dir" ]]; then
|
||||
mv "$old_package_dir" "$new_package_dir"
|
||||
log_info " 已移动包目录"
|
||||
|
||||
# 清理空的父级目录
|
||||
local old_parent="$(dirname "$old_package_dir")"
|
||||
while [[ "$old_parent" != "$java_root" && -d "$old_parent" ]]; do
|
||||
if [[ -z "$(ls -A "$old_parent" 2>/dev/null)" ]]; then
|
||||
rmdir "$old_parent" 2>/dev/null && log_info " 清理空目录: $old_parent"
|
||||
old_parent="$(dirname "$old_parent")"
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
log_info "Java包结构更新完成"
|
||||
}
|
||||
|
||||
# 4. 清理目录名中的反斜杠(独立函数)
|
||||
cleanup_backslash_directories() {
|
||||
log_step "清理目录名中的反斜杠..."
|
||||
|
||||
local fixed_count=0
|
||||
local attempts=0
|
||||
|
||||
# 重复检查和修复,直到没有反斜杠目录或达到最大尝试次数
|
||||
while [[ $attempts -lt 5 ]]; do
|
||||
local found_backslash=false
|
||||
|
||||
# 查找包含反斜杠的目录
|
||||
for dir_path in $(find "$PROJECT_ROOT" -path "*/src/main/java/*" -type d 2>/dev/null); do
|
||||
if [[ ! -d "$dir_path" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
dir_name=$(basename "$dir_path")
|
||||
|
||||
# 检查目录名是否包含反斜杠
|
||||
if [[ "$dir_name" == *"\\"* ]]; then
|
||||
found_backslash=true
|
||||
|
||||
# 清理反斜杠,获取正确的目录名
|
||||
clean_dir_name="${dir_name//\\/}"
|
||||
parent_dir=$(dirname "$dir_path")
|
||||
new_dir_path="$parent_dir/$clean_dir_name"
|
||||
|
||||
log_info "修复目录名: '$dir_name' -> '$clean_dir_name'"
|
||||
|
||||
# 重命名目录
|
||||
if [[ "$dir_path" != "$new_dir_path" && ! -e "$new_dir_path" ]]; then
|
||||
if mv "$dir_path" "$new_dir_path" 2>/dev/null; then
|
||||
log_info " ✓ 目录重命名成功: $(basename "$new_dir_path")"
|
||||
((fixed_count++))
|
||||
else
|
||||
log_warn " ✗ 目录重命名失败: $dir_path"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# 如果没有找到反斜杠目录,退出循环
|
||||
if [[ "$found_backslash" == false ]]; then
|
||||
break
|
||||
fi
|
||||
|
||||
((attempts++))
|
||||
sleep 0.1 # 短暂等待,确保文件系统操作完成
|
||||
done
|
||||
|
||||
if [[ $fixed_count -gt 0 ]]; then
|
||||
log_info "总共修复了 $fixed_count 个包含反斜杠的目录(用了 $attempts 轮处理)"
|
||||
else
|
||||
log_info "未发现包含反斜杠的目录"
|
||||
fi
|
||||
|
||||
log_info "反斜杠清理完成"
|
||||
}
|
||||
|
||||
# 5. 更新配置文件
|
||||
update_config_files() {
|
||||
log_step "更新配置文件..."
|
||||
|
||||
# 更新application配置文件
|
||||
find "$PROJECT_ROOT" -name "application*.yml" -o -name "application*.yaml" -o -name "application*.properties" | while read config_file; do
|
||||
log_info "更新配置文件: $config_file"
|
||||
|
||||
# 计算新的配置名称(兼容各种bash版本)
|
||||
local new_pool_name="$(echo "$NEW_MODULE_PREFIX" | tr '[:lower:]' '[:upper:]')-HIKARI-DEV"
|
||||
local new_project_name="$(echo "$NEW_MODULE_PREFIX" | tr '[:lower:]' '[:upper:]')-ADMIN"
|
||||
|
||||
sed -i.bak \
|
||||
-e "s|packages-to-scan: $CURRENT_PACKAGE_NAME|packages-to-scan: $NEW_PACKAGE_NAME|g" \
|
||||
-e "s|$CURRENT_PACKAGE_NAME|$NEW_PACKAGE_NAME|g" \
|
||||
-e "s|name: coder-web|name: ${NEW_MODULE_PREFIX}-web|g" \
|
||||
-e "s|projectName: CORDER-ADMIN-THIN|projectName: $new_project_name|g" \
|
||||
-e "s|pool-name: CORDER-HIKARI-DEV|pool-name: $new_pool_name|g" \
|
||||
-e "s|jdbc:mysql://localhost:3306/$CURRENT_DB_NAME|jdbc:mysql://localhost:3306/$NEW_DB_NAME|g" \
|
||||
-e "s|jdbc:mysql://localhost:3306/$CURRENT_DB_NAME-backup|jdbc:mysql://localhost:3306/$NEW_DB_NAME-backup|g" \
|
||||
-e "s|/$CURRENT_PROJECT_NAME/|/$NEW_PROJECT_NAME/|g" \
|
||||
-e "s|$CURRENT_MODULE_PREFIX|$NEW_MODULE_PREFIX|g" \
|
||||
"$config_file"
|
||||
|
||||
rm -f "${config_file}.bak"
|
||||
done
|
||||
|
||||
# 更新logback配置文件(专门处理logback特有的配置)
|
||||
find "$PROJECT_ROOT" -name "logback*.xml" | while read logback_file; do
|
||||
log_info "更新Logback配置: $logback_file"
|
||||
|
||||
# 计算新的日志配置名称(兼容各种bash版本)
|
||||
local new_context_name="${NEW_MODULE_PREFIX}-logback"
|
||||
local new_property_name="$(echo "$NEW_MODULE_PREFIX" | tr '[:lower:]' '[:upper:]')_ADMIN_LOGS"
|
||||
|
||||
sed -i.bak \
|
||||
-e "s|$CURRENT_PACKAGE_NAME|$NEW_PACKAGE_NAME|g" \
|
||||
-e "s|<contextName>$CURRENT_MODULE_PREFIX-logback</contextName>|<contextName>$new_context_name</contextName>|g" \
|
||||
-e "s|CORDER_ADMIN_LOGS|$new_property_name|g" \
|
||||
-e "s|name=\"CORDER_ADMIN_LOGS\"|name=\"$new_property_name\"|g" \
|
||||
"$logback_file"
|
||||
|
||||
rm -f "${logback_file}.bak"
|
||||
done
|
||||
|
||||
# 更新MyBatis XML文件(处理namespace和resultType)
|
||||
find "$PROJECT_ROOT" -name "*.xml" -path "*/mapper/*" | while read mapper_file; do
|
||||
log_info "更新MyBatis配置: $mapper_file"
|
||||
|
||||
sed -i.bak \
|
||||
-e "s|namespace=\"$CURRENT_PACKAGE_NAME|namespace=\"$NEW_PACKAGE_NAME|g" \
|
||||
-e "s|resultType=\"$CURRENT_PACKAGE_NAME|resultType=\"$NEW_PACKAGE_NAME|g" \
|
||||
-e "s|parameterType=\"$CURRENT_PACKAGE_NAME|parameterType=\"$NEW_PACKAGE_NAME|g" \
|
||||
"$mapper_file"
|
||||
|
||||
rm -f "${mapper_file}.bak"
|
||||
done
|
||||
|
||||
log_info "配置文件更新完成"
|
||||
}
|
||||
|
||||
# 6. 更新SQL文件
|
||||
update_sql_files() {
|
||||
log_step "更新SQL文件..."
|
||||
|
||||
local sql_dir="$PROJECT_ROOT/sql"
|
||||
if [[ -d "$sql_dir" ]]; then
|
||||
# 重命名SQL文件
|
||||
local current_sql_file="$sql_dir/$CURRENT_DB_NAME.sql"
|
||||
local new_sql_file="$sql_dir/$NEW_DB_NAME.sql"
|
||||
|
||||
if [[ -f "$current_sql_file" ]]; then
|
||||
log_info "重命名SQL文件: $CURRENT_DB_NAME.sql -> $NEW_DB_NAME.sql"
|
||||
mv "$current_sql_file" "$new_sql_file"
|
||||
|
||||
# 更新SQL文件内容中的数据库名引用
|
||||
sed -i.bak \
|
||||
-e "s|Source Schema.*: $CURRENT_DB_NAME|Source Schema : $NEW_DB_NAME|g" \
|
||||
-e "s|$CURRENT_DB_NAME|$NEW_DB_NAME|g" \
|
||||
"$new_sql_file"
|
||||
|
||||
rm -f "${new_sql_file}.bak"
|
||||
fi
|
||||
fi
|
||||
|
||||
log_info "SQL文件更新完成"
|
||||
}
|
||||
|
||||
# 7. 更新其他文件
|
||||
update_other_files() {
|
||||
log_step "更新其他相关文件..."
|
||||
|
||||
# 更新README文件
|
||||
find "$PROJECT_ROOT" -name "README*" -type f | while read readme_file; do
|
||||
log_info "更新README: $readme_file"
|
||||
|
||||
sed -i.bak \
|
||||
-e "s|$CURRENT_PROJECT_NAME|$NEW_PROJECT_NAME|g" \
|
||||
-e "s|$CURRENT_MODULE_PREFIX|$NEW_MODULE_PREFIX|g" \
|
||||
-e "s|$CURRENT_PACKAGE_NAME|$NEW_PACKAGE_NAME|g" \
|
||||
"$readme_file"
|
||||
|
||||
rm -f "${readme_file}.bak"
|
||||
done
|
||||
|
||||
# 更新CLAUDE.md文件
|
||||
if [[ -f "$PROJECT_ROOT/CLAUDE.md" ]]; then
|
||||
log_info "更新CLAUDE.md"
|
||||
|
||||
sed -i.bak \
|
||||
-e "s|$CURRENT_PROJECT_NAME|$NEW_PROJECT_NAME|g" \
|
||||
-e "s|$CURRENT_MODULE_PREFIX|$NEW_MODULE_PREFIX|g" \
|
||||
-e "s|$CURRENT_PACKAGE_NAME|$NEW_PACKAGE_NAME|g" \
|
||||
"$PROJECT_ROOT/CLAUDE.md"
|
||||
|
||||
rm -f "$PROJECT_ROOT/CLAUDE.md.bak"
|
||||
fi
|
||||
|
||||
# 更新其他配置文件(如果存在)
|
||||
find "$PROJECT_ROOT" -name "*.properties" -o -name "*.yml" -o -name "*.yaml" | while read prop_file; do
|
||||
if [[ "$prop_file" != *"application"* ]]; then
|
||||
log_info "更新属性文件: $prop_file"
|
||||
|
||||
sed -i.bak \
|
||||
-e "s|$CURRENT_PACKAGE_NAME|$NEW_PACKAGE_NAME|g" \
|
||||
-e "s|$CURRENT_MODULE_PREFIX|$NEW_MODULE_PREFIX|g" \
|
||||
"$prop_file"
|
||||
|
||||
rm -f "${prop_file}.bak"
|
||||
fi
|
||||
done
|
||||
|
||||
log_info "其他文件更新完成"
|
||||
}
|
||||
|
||||
# 8. 清理和验证
|
||||
cleanup_and_verify() {
|
||||
log_step "清理临时文件和验证..."
|
||||
|
||||
# 清理备份文件
|
||||
find "$PROJECT_ROOT" -name "*.bak" -delete
|
||||
|
||||
# 验证重构结果
|
||||
log_info "验证重构结果..."
|
||||
|
||||
# 检查是否还有旧的引用(排除脚本文件、日志文件、备份等)
|
||||
local old_refs=$(grep -r "$CURRENT_PACKAGE_NAME" "$PROJECT_ROOT" \
|
||||
--exclude-dir=".git" \
|
||||
--exclude-dir="target" \
|
||||
--exclude-dir=".idea" \
|
||||
--exclude-dir="script" \
|
||||
--exclude="*.log" \
|
||||
--exclude="spy.log" \
|
||||
--exclude="*.bak" \
|
||||
--exclude="*.sh" \
|
||||
2>/dev/null | wc -l)
|
||||
|
||||
if [[ $old_refs -gt 0 ]]; then
|
||||
log_warn "发现 $old_refs 处旧包名引用,请手动检查"
|
||||
grep -r "$CURRENT_PACKAGE_NAME" "$PROJECT_ROOT" \
|
||||
--exclude-dir=".git" \
|
||||
--exclude-dir="target" \
|
||||
--exclude-dir=".idea" \
|
||||
--exclude-dir="script" \
|
||||
--exclude="*.log" \
|
||||
--exclude="spy.log" \
|
||||
--exclude="*.bak" \
|
||||
--exclude="*.sh" \
|
||||
2>/dev/null | head -10
|
||||
else
|
||||
log_info "✓ 验证通过:未发现旧包名引用"
|
||||
fi
|
||||
|
||||
log_info "清理完成"
|
||||
}
|
||||
|
||||
# 9. 重命名项目根目录
|
||||
rename_project_directory() {
|
||||
log_step "重命名项目根目录..."
|
||||
|
||||
# 获取当前项目目录名
|
||||
local current_dir_name=$(basename "$PROJECT_ROOT")
|
||||
local parent_dir=$(dirname "$PROJECT_ROOT")
|
||||
local new_project_dir="$parent_dir/$NEW_PROJECT_NAME"
|
||||
|
||||
log_info "项目目录重命名: $current_dir_name -> $NEW_PROJECT_NAME"
|
||||
|
||||
# 检查目标目录是否已存在
|
||||
if [[ -d "$new_project_dir" && "$PROJECT_ROOT" != "$new_project_dir" ]]; then
|
||||
log_warn "目标目录已存在: $new_project_dir"
|
||||
log_warn "跳过项目目录重命名"
|
||||
return
|
||||
fi
|
||||
|
||||
# 重命名项目目录
|
||||
if [[ "$PROJECT_ROOT" != "$new_project_dir" ]]; then
|
||||
log_info "移动项目目录: $PROJECT_ROOT -> $new_project_dir"
|
||||
|
||||
# 切换到父目录
|
||||
cd "$parent_dir"
|
||||
|
||||
# 重命名目录
|
||||
if mv "$current_dir_name" "$NEW_PROJECT_NAME" 2>/dev/null; then
|
||||
log_info "✓ 项目目录重命名成功"
|
||||
log_info "新的项目路径: $new_project_dir"
|
||||
|
||||
# 更新PROJECT_ROOT变量为新路径
|
||||
PROJECT_ROOT="$new_project_dir"
|
||||
else
|
||||
log_warn "✗ 项目目录重命名失败,请手动重命名"
|
||||
fi
|
||||
else
|
||||
log_info "项目目录名已正确,无需重命名"
|
||||
fi
|
||||
|
||||
log_info "项目目录处理完成"
|
||||
}
|
||||
|
||||
# 执行重构
|
||||
echo ""
|
||||
log_info "=== 开始执行重构 ==="
|
||||
|
||||
rename_module_directories
|
||||
update_pom_files
|
||||
update_java_packages
|
||||
cleanup_backslash_directories
|
||||
update_config_files
|
||||
update_sql_files
|
||||
update_other_files
|
||||
cleanup_and_verify
|
||||
rename_project_directory
|
||||
|
||||
echo ""
|
||||
log_info "=== 重构完成 ==="
|
||||
log_info "新项目名称: $NEW_PROJECT_NAME"
|
||||
log_info "新的GroupId: $NEW_GROUP_ID"
|
||||
log_info "新的包名: $NEW_PACKAGE_NAME"
|
||||
log_info "新的模块前缀: $NEW_MODULE_PREFIX"
|
||||
log_info "新的数据库名: $NEW_DB_NAME"
|
||||
echo ""
|
||||
log_info "项目备份位置: $BACKUP_DIR"
|
||||
log_info "新项目位置: $PROJECT_ROOT"
|
||||
echo ""
|
||||
log_warn "重构完成后请执行以下操作:"
|
||||
echo "1. 进入新项目目录: cd \"$PROJECT_ROOT\""
|
||||
echo "2. 验证项目能否正常编译: mvn clean compile"
|
||||
echo "3. 更新数据库连接配置中的数据库名"
|
||||
echo "4. 在IDE中重新导入项目,检查包结构"
|
||||
echo "5. 提交代码到新的Git仓库"
|
||||
echo ""
|
||||
|
||||
log_info "感谢使用项目模板重构脚本!"
|
||||
Loading…
Reference in New Issue
Block a user