docs(plan): 用户聚合查询实施计划
- 20个详细任务分解 - 包含依赖关系图 - 数据库变更、代码修改、缓存、测试全覆盖 - 风险评估和回滚计划
This commit is contained in:
@@ -0,0 +1,725 @@
|
|||||||
|
# 用户聚合查询实施计划
|
||||||
|
|
||||||
|
> **日期**: 2026-06-06
|
||||||
|
> **状态**: 已批准
|
||||||
|
> **关联 Spec**: `docs/superpowers/specs/2026-06-06-user-aggregate-query-design.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 任务总览
|
||||||
|
|
||||||
|
### 1.1 任务清单
|
||||||
|
|
||||||
|
| 编号 | 任务名称 | 优先级 | 预估时间 | 依赖 |
|
||||||
|
|------|---------|--------|---------|------|
|
||||||
|
| T1 | 数据库变更:uc_user 添加 phone 字段 | 高 | 20分钟 | 无 |
|
||||||
|
| T2 | 数据库变更:uc_user_detail 移除 phone 字段 | 高 | 15分钟 | T1 |
|
||||||
|
| T3 | 修改 User 实体:添加 phone 字段 | 高 | 15分钟 | T1 |
|
||||||
|
| T4 | 修改 UserDetail 实体:移除 phone 字段 | 高 | 10分钟 | T2 |
|
||||||
|
| T5 | 新增 VO 对象:UserAggregateVO, UserDeptVO, UserPostVO | 高 | 20分钟 | 无 |
|
||||||
|
| T6 | 新增枚举:AccountType | 高 | 10分钟 | 无 |
|
||||||
|
| T7 | 新增 DTO:LoginAccountDTO | 高 | 10分钟 | T6 |
|
||||||
|
| T8 | 修改 UserDeptMapper:添加批量查询方法 | 高 | 20分钟 | 无 |
|
||||||
|
| T9 | 修改 UserPostMapper:添加批量查询方法 | 高 | 20分钟 | 无 |
|
||||||
|
| T10 | 修改 UserService:添加聚合查询方法 | 高 | 30分钟 | T3, T5, T8, T9 |
|
||||||
|
| T11 | 修改 UserController:添加聚合接口 | 高 | 20分钟 | T10 |
|
||||||
|
| T12 | 修改 UserInnerController:添加统一认证接口 | 高 | 25分钟 | T3, T7 |
|
||||||
|
| T13 | 修改 UserAuthFeign:添加统一认证方法 | 高 | 15分钟 | T12 |
|
||||||
|
| T14 | 修改 RemoteUserDetailsService:支持新接口 | 高 | 20分钟 | T13 |
|
||||||
|
| T15 | 添加缓存:Redis 缓存用户聚合数据 | 中 | 25分钟 | T10 |
|
||||||
|
| T16 | 缓存失效:数据变更时清除缓存 | 中 | 20分钟 | T15 |
|
||||||
|
| T17 | 编写 SQL 升级脚本 | 高 | 15分钟 | T1, T2 |
|
||||||
|
| T18 | 单元测试 | 中 | 40分钟 | T10, T12 |
|
||||||
|
| T19 | 集成测试 | 中 | 30分钟 | T18 |
|
||||||
|
| T20 | 文档更新 | 低 | 15分钟 | 全部 |
|
||||||
|
|
||||||
|
### 1.2 依赖关系图
|
||||||
|
|
||||||
|
```
|
||||||
|
T1 (数据库添加phone)
|
||||||
|
├── T3 (User实体添加phone)
|
||||||
|
│ ├── T10 (UserService聚合查询)
|
||||||
|
│ │ ├── T11 (UserController聚合接口)
|
||||||
|
│ │ ├── T15 (Redis缓存)
|
||||||
|
│ │ │ └── T16 (缓存失效)
|
||||||
|
│ │ └── T18 (单元测试)
|
||||||
|
│ └── T12 (统一认证接口)
|
||||||
|
│ ├── T13 (UserAuthFeign)
|
||||||
|
│ │ └── T14 (RemoteUserDetailsService)
|
||||||
|
│ └── T18 (单元测试)
|
||||||
|
├── T7 (LoginAccountDTO)
|
||||||
|
│ └── T12 (统一认证接口)
|
||||||
|
└── T17 (SQL脚本)
|
||||||
|
|
||||||
|
T2 (数据库移除phone)
|
||||||
|
└── T4 (UserDetail实体移除phone)
|
||||||
|
|
||||||
|
T5 (VO对象)
|
||||||
|
└── T10 (UserService聚合查询)
|
||||||
|
|
||||||
|
T6 (AccountType枚举)
|
||||||
|
├── T7 (LoginAccountDTO)
|
||||||
|
└── T12 (统一认证接口)
|
||||||
|
|
||||||
|
T8 (UserDeptMapper批量查询)
|
||||||
|
└── T10 (UserService聚合查询)
|
||||||
|
|
||||||
|
T9 (UserPostMapper批量查询)
|
||||||
|
└── T10 (UserService聚合查询)
|
||||||
|
|
||||||
|
T18 (单元测试)
|
||||||
|
└── T19 (集成测试)
|
||||||
|
|
||||||
|
T20 (文档更新)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 详细任务
|
||||||
|
|
||||||
|
### T1: 数据库变更 - uc_user 添加 phone 字段
|
||||||
|
|
||||||
|
**目标**: 在 `uc_user` 表添加 `phone` 字段并创建索引
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 编写 SQL:
|
||||||
|
```sql
|
||||||
|
ALTER TABLE rui_uc_user
|
||||||
|
ADD COLUMN phone VARCHAR(20) DEFAULT NULL COMMENT '手机号' AFTER username;
|
||||||
|
|
||||||
|
ALTER TABLE rui_uc_user
|
||||||
|
ADD UNIQUE KEY uk_phone (tenant_id, phone);
|
||||||
|
|
||||||
|
ALTER TABLE rui_uc_user
|
||||||
|
ADD INDEX idx_phone (phone);
|
||||||
|
```
|
||||||
|
2. [ ] 在开发环境执行 SQL
|
||||||
|
3. [ ] 验证表结构:`DESCRIBE rui_uc_user;`
|
||||||
|
4. [ ] 验证索引:`SHOW INDEX FROM rui_uc_user;`
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- `phone` 字段存在
|
||||||
|
- `uk_phone` 唯一索引存在
|
||||||
|
- `idx_phone` 普通索引存在
|
||||||
|
|
||||||
|
**风险**: 生产环境需要谨慎,建议在低峰期执行
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T2: 数据库变更 - uc_user_detail 移除 phone 字段
|
||||||
|
|
||||||
|
**目标**: 从 `uc_user_detail` 表移除 `phone` 字段
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 备份数据(可选)
|
||||||
|
2. [ ] 编写 SQL:
|
||||||
|
```sql
|
||||||
|
-- 先迁移数据(如果有)
|
||||||
|
-- UPDATE rui_uc_user u
|
||||||
|
-- JOIN rui_uc_user_detail d ON u.id = d.user_id
|
||||||
|
-- SET u.phone = d.phone
|
||||||
|
-- WHERE u.phone IS NULL AND d.phone IS NOT NULL;
|
||||||
|
|
||||||
|
ALTER TABLE rui_uc_user_detail
|
||||||
|
DROP COLUMN phone;
|
||||||
|
```
|
||||||
|
3. [ ] 在开发环境执行 SQL
|
||||||
|
4. [ ] 验证表结构
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- `phone` 字段已移除
|
||||||
|
- 其他字段不受影响
|
||||||
|
|
||||||
|
**风险**: 确保数据已迁移或不再使用
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T3: 修改 User 实体 - 添加 phone 字段
|
||||||
|
|
||||||
|
**目标**: 在 `User.java` 实体中添加 `phone` 字段
|
||||||
|
|
||||||
|
**文件**: `rui-service/rui-service-user/src/main/java/com/rui/service/user/entity/User.java`
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 添加字段:
|
||||||
|
```java
|
||||||
|
@Schema(description = "手机号")
|
||||||
|
@SearchField(alias = "phone")
|
||||||
|
private String phone;
|
||||||
|
```
|
||||||
|
2. [ ] 确保字段位置在 `username` 之后
|
||||||
|
3. [ ] 编译验证
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- `User` 实体可以正常编译
|
||||||
|
- `phone` 字段有 getter/setter(@Data 自动生成)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T4: 修改 UserDetail 实体 - 移除 phone 字段
|
||||||
|
|
||||||
|
**目标**: 从 `UserDetail.java` 实体中移除 `phone` 字段
|
||||||
|
|
||||||
|
**文件**: `rui-service/rui-service-user/src/main/java/com/rui/service/user/entity/UserDetail.java`
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 移除字段:
|
||||||
|
```java
|
||||||
|
// 移除以下代码
|
||||||
|
@Schema(description = "手机号")
|
||||||
|
@SearchField(alias = "phone")
|
||||||
|
private String phone;
|
||||||
|
```
|
||||||
|
2. [ ] 编译验证
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- `UserDetail` 实体可以正常编译
|
||||||
|
- `phone` 字段已移除
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T5: 新增 VO 对象
|
||||||
|
|
||||||
|
**目标**: 创建用户聚合查询的 VO 对象
|
||||||
|
|
||||||
|
**文件**:
|
||||||
|
- `rui-service/rui-service-user/src/main/java/com/rui/service/user/vo/UserAggregateVO.java`
|
||||||
|
- `rui-service/rui-service-user/src/main/java/com/rui/service/user/vo/UserDeptVO.java`
|
||||||
|
- `rui-service/rui-service-user/src/main/java/com/rui/service/user/vo/UserPostVO.java`
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 创建 `vo` 包
|
||||||
|
2. [ ] 创建 `UserAggregateVO.java`:
|
||||||
|
```java
|
||||||
|
package com.rui.service.user.vo;
|
||||||
|
|
||||||
|
import com.rui.service.user.entity.User;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Schema(description = "用户聚合信息")
|
||||||
|
public class UserAggregateVO extends User {
|
||||||
|
|
||||||
|
@Schema(description = "部门列表")
|
||||||
|
private List<UserDeptVO> depts;
|
||||||
|
|
||||||
|
@Schema(description = "岗位列表")
|
||||||
|
private List<UserPostVO> posts;
|
||||||
|
|
||||||
|
@Schema(description = "主部门ID")
|
||||||
|
private Long mainDeptId;
|
||||||
|
|
||||||
|
@Schema(description = "主部门名称")
|
||||||
|
private String mainDeptName;
|
||||||
|
|
||||||
|
@Schema(description = "部门编码")
|
||||||
|
private String deptCode;
|
||||||
|
|
||||||
|
@Schema(description = "岗位编码")
|
||||||
|
private String postCode;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. [ ] 创建 `UserDeptVO.java`
|
||||||
|
4. [ ] 创建 `UserPostVO.java`
|
||||||
|
5. [ ] 编译验证
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- 所有 VO 类可以正常编译
|
||||||
|
- 字段和类型正确
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T6: 新增枚举 - AccountType
|
||||||
|
|
||||||
|
**目标**: 创建账号类型枚举
|
||||||
|
|
||||||
|
**文件**: `rui-service/rui-service-user/src/main/java/com/rui/service/user/enums/AccountType.java`
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 创建 `enums` 包
|
||||||
|
2. [ ] 创建 `AccountType.java`:
|
||||||
|
```java
|
||||||
|
package com.rui.service.user.enums;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum AccountType {
|
||||||
|
USERNAME("用户名"),
|
||||||
|
PHONE("手机号"),
|
||||||
|
EMAIL("邮箱");
|
||||||
|
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
AccountType(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. [ ] 编译验证
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- 枚举可以正常编译
|
||||||
|
- 包含 USERNAME, PHONE, EMAIL 三个值
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T7: 新增 DTO - LoginAccountDTO
|
||||||
|
|
||||||
|
**目标**: 创建登录账号 DTO
|
||||||
|
|
||||||
|
**文件**: `rui-service/rui-service-user/src/main/java/com/rui/service/user/dto/LoginAccountDTO.java`
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 创建 `dto` 包
|
||||||
|
2. [ ] 创建 `LoginAccountDTO.java`:
|
||||||
|
```java
|
||||||
|
package com.rui.service.user.dto;
|
||||||
|
|
||||||
|
import com.rui.service.user.enums.AccountType;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "登录账号信息")
|
||||||
|
public class LoginAccountDTO {
|
||||||
|
|
||||||
|
@Schema(description = "账号")
|
||||||
|
private String account;
|
||||||
|
|
||||||
|
@Schema(description = "账号类型")
|
||||||
|
private AccountType accountType;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. [ ] 编译验证
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- DTO 可以正常编译
|
||||||
|
- 包含 account 和 accountType 字段
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T8: 修改 UserDeptMapper - 添加批量查询方法
|
||||||
|
|
||||||
|
**目标**: 添加根据用户ID列表批量查询部门的方法
|
||||||
|
|
||||||
|
**文件**:
|
||||||
|
- `rui-service/rui-service-user/src/main/java/com/rui/service/user/mapper/UserDeptMapper.java`
|
||||||
|
- `rui-service/rui-service-user/src/main/resources/mapper/UserDeptMapper.xml`
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 在 `UserDeptMapper.java` 添加方法:
|
||||||
|
```java
|
||||||
|
List<UserDeptVO> selectDeptListByUserIds(@Param("userIds") List<Long> userIds);
|
||||||
|
```
|
||||||
|
2. [ ] 在 `UserDeptMapper.xml` 添加 SQL:
|
||||||
|
```xml
|
||||||
|
<select id="selectDeptListByUserIds" resultType="com.rui.service.user.vo.UserDeptVO">
|
||||||
|
SELECT
|
||||||
|
ud.user_id as userId,
|
||||||
|
ud.dept_id as deptId,
|
||||||
|
d.dept_code as deptCode,
|
||||||
|
d.name as deptName,
|
||||||
|
ud.is_main as main
|
||||||
|
FROM uc_user_dept ud
|
||||||
|
INNER JOIN uc_dept d ON ud.dept_id = d.id
|
||||||
|
WHERE ud.user_id IN
|
||||||
|
<foreach collection="userIds" item="id" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
AND ud.deleted = 0
|
||||||
|
AND d.deleted = 0
|
||||||
|
</select>
|
||||||
|
```
|
||||||
|
3. [ ] 编译验证
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- Mapper 接口可以正常编译
|
||||||
|
- XML 语法正确
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T9: 修改 UserPostMapper - 添加批量查询方法
|
||||||
|
|
||||||
|
**目标**: 添加根据用户ID列表批量查询岗位的方法
|
||||||
|
|
||||||
|
**文件**:
|
||||||
|
- `rui-service/rui-service-user/src/main/java/com/rui/service/user/mapper/UserPostMapper.java`
|
||||||
|
- `rui-service/rui-service-user/src/main/resources/mapper/UserPostMapper.xml`
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 在 `UserPostMapper.java` 添加方法:
|
||||||
|
```java
|
||||||
|
List<UserPostVO> selectPostListByUserIds(@Param("userIds") List<Long> userIds);
|
||||||
|
```
|
||||||
|
2. [ ] 在 `UserPostMapper.xml` 添加 SQL
|
||||||
|
3. [ ] 编译验证
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- Mapper 接口可以正常编译
|
||||||
|
- XML 语法正确
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T10: 修改 UserService - 添加聚合查询方法
|
||||||
|
|
||||||
|
**目标**: 在 UserService 中添加聚合查询逻辑
|
||||||
|
|
||||||
|
**文件**:
|
||||||
|
- `rui-service/rui-service-user/src/main/java/com/rui/service/user/service/IUserService.java`
|
||||||
|
- `rui-service/rui-service-user/src/main/java/com/rui/service/user/service/impl/UserServiceImpl.java`
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 在 `IUserService.java` 添加方法:
|
||||||
|
```java
|
||||||
|
UserAggregateVO getUserAggregate(Long userId);
|
||||||
|
|
||||||
|
Page<UserAggregateVO> listUserAggregate(Page<User> page, QueryWrapper<User> query);
|
||||||
|
```
|
||||||
|
2. [ ] 在 `UserServiceImpl.java` 实现方法
|
||||||
|
3. [ ] 注入 `UserDeptMapper` 和 `UserPostMapper`
|
||||||
|
4. [ ] 实现单用户聚合查询
|
||||||
|
5. [ ] 实现批量列表查询(使用 IN 批量查询)
|
||||||
|
6. [ ] 编译验证
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- Service 接口可以正常编译
|
||||||
|
- 实现类可以正常编译
|
||||||
|
- 聚合查询逻辑正确
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T11: 修改 UserController - 添加聚合接口
|
||||||
|
|
||||||
|
**目标**: 添加用户聚合查询接口
|
||||||
|
|
||||||
|
**文件**: `rui-service/rui-service-user/src/main/java/com/rui/service/user/controller/UserController.java`
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 添加方法:
|
||||||
|
```java
|
||||||
|
@Operation(summary = "获取用户聚合信息")
|
||||||
|
@GetMapping("/{id}/aggregate")
|
||||||
|
public Result<UserAggregateVO> getUserAggregate(@PathVariable Long id) {
|
||||||
|
return Result.ok(service.getUserAggregate(id));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
2. [ ] 编译验证
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- Controller 可以正常编译
|
||||||
|
- 接口路径正确
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T12: 修改 UserInnerController - 添加统一认证接口
|
||||||
|
|
||||||
|
**目标**: 添加统一认证查询接口
|
||||||
|
|
||||||
|
**文件**: `rui-service/rui-service-user/src/main/java/com/rui/service/user/controller/inner/UserInnerController.java`
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 添加新方法:
|
||||||
|
```java
|
||||||
|
@PostMapping("/auth/load")
|
||||||
|
public Result<JSONObject> loadUser(@RequestBody LoginAccountDTO loginAccount) {
|
||||||
|
User user;
|
||||||
|
|
||||||
|
switch (loginAccount.getAccountType()) {
|
||||||
|
case PHONE:
|
||||||
|
user = userService.lambdaQuery()
|
||||||
|
.eq(User::getPhone, loginAccount.getAccount())
|
||||||
|
.one();
|
||||||
|
break;
|
||||||
|
case EMAIL:
|
||||||
|
// 如果 User 实体有 email 字段
|
||||||
|
user = userService.lambdaQuery()
|
||||||
|
.eq(User::getEmail, loginAccount.getAccount())
|
||||||
|
.one();
|
||||||
|
break;
|
||||||
|
case USERNAME:
|
||||||
|
default:
|
||||||
|
user = userService.lambdaQuery()
|
||||||
|
.eq(User::getUsername, loginAccount.getAccount())
|
||||||
|
.one();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user == null) {
|
||||||
|
return Result.ok(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组装认证信息(复用现有逻辑)
|
||||||
|
JSONObject info = buildAuthInfo(user);
|
||||||
|
return Result.ok(info);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
2. [ ] 将原有 `loadByUsername` 标记为 `@Deprecated`
|
||||||
|
3. [ ] 编译验证
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- Controller 可以正常编译
|
||||||
|
- 新接口可以处理不同账号类型
|
||||||
|
- 旧接口仍然可用但标记为弃用
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T13: 修改 UserAuthFeign - 添加统一认证方法
|
||||||
|
|
||||||
|
**目标**: 在 Feign 客户端添加新方法
|
||||||
|
|
||||||
|
**文件**: `rui-common/rui-common-oauth2/src/main/java/com/rui/common/oauth2/feign/UserAuthFeign.java`
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 添加新方法:
|
||||||
|
```java
|
||||||
|
@PostMapping("/auth/load")
|
||||||
|
Result<JSONObject> loadUser(@RequestBody LoginAccountDTO loginAccount);
|
||||||
|
```
|
||||||
|
2. [ ] 将原有 `loadUser` 方法(基于 username)标记为 `@Deprecated`
|
||||||
|
3. [ ] 编译验证
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- Feign 接口可以正常编译
|
||||||
|
- 新方法参数正确
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T14: 修改 RemoteUserDetailsService - 支持新接口
|
||||||
|
|
||||||
|
**目标**: 修改认证服务以支持新的统一认证接口
|
||||||
|
|
||||||
|
**文件**: `rui-common/rui-common-oauth2/src/main/java/com/rui/common/oauth2/service/RemoteUserDetailsService.java`
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 修改 `loadUserByUsername` 方法,改为调用新的 `loadUser` 方法
|
||||||
|
2. [ ] 或者新增方法 `loadUserByAccount`
|
||||||
|
3. [ ] 确保兼容性
|
||||||
|
4. [ ] 编译验证
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- 认证服务可以正常编译
|
||||||
|
- 支持用户名和手机号登录
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T15: 添加 Redis 缓存
|
||||||
|
|
||||||
|
**目标**: 为用户聚合数据添加 Redis 缓存
|
||||||
|
|
||||||
|
**文件**: `rui-service/rui-service-user/src/main/java/com/rui/service/user/service/impl/UserServiceImpl.java`
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 注入 `RedisUtil`
|
||||||
|
2. [ ] 在 `getUserAggregate` 方法中添加缓存逻辑:
|
||||||
|
```java
|
||||||
|
public UserAggregateVO getUserAggregate(Long userId) {
|
||||||
|
String cacheKey = String.format("user:agg:%s:%s", tenantId, userId);
|
||||||
|
|
||||||
|
// 尝试从缓存获取
|
||||||
|
UserAggregateVO cached = redisUtil.getObj(cacheKey, UserAggregateVO.class);
|
||||||
|
if (cached != null) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询数据库...
|
||||||
|
UserAggregateVO vo = // ... 查询逻辑
|
||||||
|
|
||||||
|
// 写入缓存(10分钟)
|
||||||
|
redisUtil.set(cacheKey, vo, Duration.ofMinutes(10));
|
||||||
|
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. [ ] 编译验证
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- 缓存可以正常读写
|
||||||
|
- TTL 设置正确
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T16: 缓存失效
|
||||||
|
|
||||||
|
**目标**: 在用户数据变更时清除缓存
|
||||||
|
|
||||||
|
**文件**:
|
||||||
|
- `rui-service/rui-service-user/src/main/java/com/rui/service/user/service/impl/UserDeptServiceImpl.java`
|
||||||
|
- `rui-service/rui-service-user/src/main/java/com/rui/service/user/service/impl/UserPostServiceImpl.java`
|
||||||
|
- `rui-service/rui-service-user/src/main/java/com/rui/service/user/service/impl/UserServiceImpl.java`
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 在 `UserDeptServiceImpl` 的 `assignDepts` 和 `setMainDept` 方法中添加缓存清除
|
||||||
|
2. [ ] 在 `UserPostServiceImpl` 的 `assignPosts` 方法中添加缓存清除
|
||||||
|
3. [ ] 在 `UserServiceImpl` 的 `update` 方法中添加缓存清除
|
||||||
|
4. [ ] 编写通用的缓存清除方法
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- 数据变更后缓存被清除
|
||||||
|
- 下次查询会重新加载数据
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T17: 编写 SQL 升级脚本
|
||||||
|
|
||||||
|
**目标**: 创建数据库升级脚本
|
||||||
|
|
||||||
|
**文件**: `sql/upgrade-v2.x-add-phone-to-user.sql`
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 创建 SQL 文件
|
||||||
|
2. [ ] 编写升级脚本:
|
||||||
|
```sql
|
||||||
|
-- 升级脚本:将 phone 从 uc_user_detail 迁移到 uc_user
|
||||||
|
|
||||||
|
-- 1. 在 uc_user 表添加 phone 字段
|
||||||
|
ALTER TABLE rui_uc_user
|
||||||
|
ADD COLUMN phone VARCHAR(20) DEFAULT NULL COMMENT '手机号' AFTER username;
|
||||||
|
|
||||||
|
-- 2. 添加唯一索引
|
||||||
|
ALTER TABLE rui_uc_user
|
||||||
|
ADD UNIQUE KEY uk_phone (tenant_id, phone);
|
||||||
|
|
||||||
|
-- 3. 添加普通索引
|
||||||
|
ALTER TABLE rui_uc_user
|
||||||
|
ADD INDEX idx_phone (phone);
|
||||||
|
|
||||||
|
-- 4. 迁移数据(如果有)
|
||||||
|
-- UPDATE rui_uc_user u
|
||||||
|
-- JOIN rui_uc_user_detail d ON u.id = d.user_id
|
||||||
|
-- SET u.phone = d.phone
|
||||||
|
-- WHERE u.phone IS NULL AND d.phone IS NOT NULL;
|
||||||
|
|
||||||
|
-- 5. 从 uc_user_detail 移除 phone 字段
|
||||||
|
ALTER TABLE rui_uc_user_detail
|
||||||
|
DROP COLUMN phone;
|
||||||
|
```
|
||||||
|
3. [ ] 验证 SQL 语法
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- SQL 语法正确
|
||||||
|
- 可以在开发环境正常执行
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T18: 单元测试
|
||||||
|
|
||||||
|
**目标**: 编写单元测试
|
||||||
|
|
||||||
|
**文件**:
|
||||||
|
- `rui-service/rui-service-user/src/test/java/com/rui/service/user/service/UserServiceTest.java`
|
||||||
|
- `rui-service/rui-service-user/src/test/java/com/rui/service/user/controller/UserControllerTest.java`
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 测试 `getUserAggregate` 方法
|
||||||
|
2. [ ] 测试 `listUserAggregate` 方法
|
||||||
|
3. [ ] 测试缓存命中和失效
|
||||||
|
4. [ ] 测试统一认证接口
|
||||||
|
5. [ ] 运行测试
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- 所有测试通过
|
||||||
|
- 覆盖主要业务场景
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T19: 集成测试
|
||||||
|
|
||||||
|
**目标**: 进行集成测试
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 启动服务
|
||||||
|
2. [ ] 测试聚合接口
|
||||||
|
3. [ ] 测试统一认证接口
|
||||||
|
4. [ ] 测试缓存
|
||||||
|
5. [ ] 验证旧接口仍然可用
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- 所有接口正常响应
|
||||||
|
- 数据正确
|
||||||
|
- 缓存有效
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### T20: 文档更新
|
||||||
|
|
||||||
|
**目标**: 更新相关文档
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
1. [ ] 更新 API 文档(Swagger)
|
||||||
|
2. [ ] 更新数据库文档
|
||||||
|
3. [ ] 更新接口文档
|
||||||
|
|
||||||
|
**验证标准**:
|
||||||
|
- 文档与实际代码一致
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 实施顺序建议
|
||||||
|
|
||||||
|
### 阶段 1:数据库变更(T1, T2, T17)
|
||||||
|
先执行数据库变更,为后续代码修改做准备。
|
||||||
|
|
||||||
|
### 阶段 2:基础代码(T3, T4, T5, T6, T7, T8, T9)
|
||||||
|
修改实体、新增 VO/DTO/枚举、修改 Mapper。
|
||||||
|
|
||||||
|
### 阶段 3:核心业务(T10, T11, T12, T13, T14)
|
||||||
|
实现聚合查询和统一认证接口。
|
||||||
|
|
||||||
|
### 阶段 4:缓存优化(T15, T16)
|
||||||
|
添加缓存和失效机制。
|
||||||
|
|
||||||
|
### 阶段 5:测试(T18, T19)
|
||||||
|
编写和运行测试。
|
||||||
|
|
||||||
|
### 阶段 6:文档(T20)
|
||||||
|
更新文档。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 风险评估
|
||||||
|
|
||||||
|
| 风险 | 概率 | 影响 | 缓解措施 |
|
||||||
|
|------|------|------|----------|
|
||||||
|
| 数据库变更失败 | 低 | 高 | 备份数据,先在开发环境测试 |
|
||||||
|
| 缓存数据不一致 | 中 | 中 | 完善缓存失效机制 |
|
||||||
|
| 旧接口不兼容 | 低 | 高 | 保留旧接口,标记为弃用 |
|
||||||
|
| 手机号唯一性冲突 | 中 | 中 | 数据迁移时处理重复数据 |
|
||||||
|
| 性能问题 | 低 | 中 | 批量查询优化,添加索引 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 回滚计划
|
||||||
|
|
||||||
|
如果实施过程中出现问题,可以按以下顺序回滚:
|
||||||
|
|
||||||
|
1. **代码回滚**:使用 git 回滚到上一个版本
|
||||||
|
2. **数据库回滚**:
|
||||||
|
```sql
|
||||||
|
-- 移除 uc_user 的 phone 字段
|
||||||
|
ALTER TABLE rui_uc_user DROP COLUMN phone;
|
||||||
|
ALTER TABLE rui_uc_user DROP INDEX uk_phone;
|
||||||
|
ALTER TABLE rui_uc_user DROP INDEX idx_phone;
|
||||||
|
|
||||||
|
-- 在 uc_user_detail 添加 phone 字段
|
||||||
|
ALTER TABLE rui_uc_user_detail
|
||||||
|
ADD COLUMN phone VARCHAR(20) DEFAULT NULL COMMENT '手机号' AFTER email;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 验收标准
|
||||||
|
|
||||||
|
- [ ] 所有任务完成
|
||||||
|
- [ ] 单元测试通过率 100%
|
||||||
|
- [ ] 集成测试通过
|
||||||
|
- [ ] 代码审查通过
|
||||||
|
- [ ] 文档更新完成
|
||||||
|
- [ ] 数据库变更成功
|
||||||
|
- [ ] 缓存正常工作
|
||||||
|
- [ ] 旧接口仍然可用
|
||||||
Reference in New Issue
Block a user