368 lines
10 KiB
Markdown
368 lines
10 KiB
Markdown
# 修改密码功能实现计划
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
|
||
**Goal:** 为碳信网平台添加普通用户自助修改密码功能(txw-sso后端 + txw-mhzc-web前端)
|
||
|
||
**Architecture:**
|
||
- 后端:SSO服务新增 `POST /sso/auth/changePassword` 接口,用户通过 authenticate 验证旧密码,通过 mhzc-service 的 Feign 接口更新用户密码
|
||
- 前端:mhzc-web 新增修改密码页面(账号管理),工作台增加快捷入口,用户中心左侧菜单增加"账号管理"菜单项
|
||
|
||
**Tech Stack:** Java Spring Boot / Vue 2 / TDesign / Feign
|
||
|
||
---
|
||
|
||
## 涉及的变更文件
|
||
|
||
### 后端(txw-sso)
|
||
| 文件 | 改动 |
|
||
|------|------|
|
||
| `AuthService.java` | 新增 `changePassword` 接口声明 |
|
||
| `AuthServiceImpl.java` | 实现 `changePassword` 逻辑 |
|
||
| `AuthController.java` | 新增 `/auth/changePassword` 路由 |
|
||
| `ChangePasswordReqVO.java` | 新建请求 VO |
|
||
| `ChangePasswordRespVO.java` | 新建响应 VO |
|
||
| `YhxxApi.java` (txw-sso-api) | 新增 Feign 方法 `updatePassword` |
|
||
| `TxwMhzcYhxxbService.java` (txw-mhzc) | 新增 `updatePassword` 方法声明 |
|
||
| `TxwMhzcYhxxbServiceImpl.java` (txw-mhzc) | 实现 `updatePassword` |
|
||
| `TxwMhzcYhxxbMapper.xml` | 新增 updatePassword SQL |
|
||
| `ISsoApi.java` | FeignClient name 常量(需确认mhzc服务名) |
|
||
|
||
### 前端(txw-mhzc-web)
|
||
| 文件 | 改动 |
|
||
|------|------|
|
||
| `views/gzt/index.vue` | 新增修改密码快捷入口按钮 |
|
||
| `views/yhzx/zhanghugl/index.vue` | 新建修改密码页面 |
|
||
| `router/routes.js` | 新增 `zhanghugl` 路由 |
|
||
| `views/glxtSy/config.js` | 菜单配置新增"账号管理" |
|
||
| `api/sso.js` | 新建,封装 changePassword API |
|
||
|
||
---
|
||
|
||
## 后端任务拆分
|
||
|
||
### Task 1: 后端 - 新建 ChangePasswordReqVO
|
||
|
||
**文件:** `txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/pojo/vo/ChangePasswordReqVO.java`
|
||
|
||
- [ ] **Step 1: 创建文件**
|
||
|
||
```java
|
||
package com.css.txw.sso.pojo.vo;
|
||
|
||
import io.swagger.v3.oas.annotations.media.Schema;
|
||
import lombok.AllArgsConstructor;
|
||
import lombok.Builder;
|
||
import lombok.Data;
|
||
import lombok.NoArgsConstructor;
|
||
|
||
import javax.validation.constraints.NotBlank;
|
||
|
||
@Schema(description = "修改密码 Request VO")
|
||
@Data
|
||
@NoArgsConstructor
|
||
@AllArgsConstructor
|
||
@Builder
|
||
public class ChangePasswordReqVO {
|
||
|
||
@Schema(description = "旧密码(MD5加密)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||
@NotBlank(message = "旧密码不能为空")
|
||
private String oldPassword;
|
||
|
||
@Schema(description = "新密码(MD5加密)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||
@NotBlank(message = "新密码不能为空")
|
||
private String newPassword;
|
||
|
||
@Schema(description = "确认密码(MD5加密)", requiredMode = Schema.RequiredMode.REQUIRED)
|
||
@NotBlank(message = "确认密码不能为空")
|
||
private String confirmPassword;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Task 2: 后端 - AuthService 新增 changePassword 接口声明
|
||
|
||
**文件:** `txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/AuthService.java`
|
||
|
||
在接口末尾添加:
|
||
```java
|
||
/**
|
||
* 修改密码
|
||
*
|
||
* @param reqVO 修改密码请求
|
||
* @return 成功返回 null
|
||
*/
|
||
void changePassword(ChangePasswordReqVO reqVO);
|
||
```
|
||
|
||
引入 import:
|
||
```java
|
||
import com.css.txw.sso.pojo.vo.ChangePasswordReqVO;
|
||
```
|
||
|
||
---
|
||
|
||
### Task 3: 后端 - AuthServiceImpl 实现 changePassword
|
||
|
||
**文件:** `txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/auth/impl/AuthServiceImpl.java`
|
||
|
||
在类末尾添加实现方法:
|
||
|
||
```java
|
||
@Override
|
||
public void changePassword(ChangePasswordReqVO reqVO) {
|
||
// 1. 校验新密码与确认密码一致性
|
||
if (!reqVO.getNewPassword().equals(reqVO.getConfirmPassword())) {
|
||
throw exception(AUTH_PASSWORD_CONFIRM_MISMATCH);
|
||
}
|
||
|
||
// 2. 密码复杂度校验(6-20位,字母+数字)
|
||
if (!isValidPasswordComplexity(reqVO.getNewPassword())) {
|
||
throw exception(AUTH_PASSWORD_COMPLEXITY_INVALID);
|
||
}
|
||
|
||
// 3. 获取当前登录用户
|
||
String userId = SecurityFrameworkUtils.getLoginUserId();
|
||
YhxxbDTO yhxx = yhxxService.getYhxx(userId);
|
||
|
||
// 4. 校验新旧密码不能相同
|
||
if (reqVO.getOldPassword().equals(reqVO.getNewPassword())) {
|
||
throw exception(AUTH_PASSWORD_SAME_AS_OLD);
|
||
}
|
||
|
||
// 5. 旧密码正确性校验
|
||
authenticate(yhxx.getDlzh(), reqVO.getOldPassword());
|
||
|
||
// 6. 更新密码
|
||
yhxxService.updatePassword(userId, reqVO.getNewPassword());
|
||
}
|
||
|
||
private boolean isValidPasswordComplexity(String password) {
|
||
if (password == null || password.length() < 6 || password.length() > 20) {
|
||
return false;
|
||
}
|
||
boolean hasLetter = password.matches(".*[A-Za-z]+.*");
|
||
boolean hasDigit = password.matches(".*[0-9]+.*");
|
||
return hasLetter && hasDigit;
|
||
}
|
||
```
|
||
|
||
新增引入:
|
||
```java
|
||
import com.css.txw.sso.pojo.vo.ChangePasswordReqVO;
|
||
import com.css.txw.sso.util.SecurityFrameworkUtils;
|
||
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_PASSWORD_CONFIRM_MISMATCH;
|
||
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_PASSWORD_COMPLEXITY_INVALID;
|
||
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_PASSWORD_SAME_AS_OLD;
|
||
```
|
||
|
||
---
|
||
|
||
### Task 4: 后端 - ErrorCodeConstants 新增错误码
|
||
|
||
**文件:** `txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/constants/ErrorCodeConstants.java`
|
||
|
||
新增三个错误码:
|
||
```java
|
||
AUTH_PASSWORD_CONFIRM_MISMATCH(400, "两次输入的新密码不一致"),
|
||
AUTH_PASSWORD_COMPLEXITY_INVALID(400, "密码长度6-20位,需包含字母和数字"),
|
||
AUTH_PASSWORD_SAME_AS_OLD(400, "新密码不能与原密码相同"),
|
||
```
|
||
|
||
---
|
||
|
||
### Task 5: 后端 - AuthController 新增路由
|
||
|
||
**文件:** `txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/controller/auth/AuthController.java`
|
||
|
||
在类末尾添加:
|
||
|
||
```java
|
||
@PostMapping("/changePassword")
|
||
@Operation(summary = "修改密码")
|
||
public CommonResult<Void> changePassword(@RequestBody @Valid ChangePasswordReqVO reqVO) {
|
||
authService.changePassword(reqVO);
|
||
return success(null);
|
||
}
|
||
```
|
||
|
||
新增引入:
|
||
```java
|
||
import com.css.txw.sso.pojo.vo.ChangePasswordReqVO;
|
||
```
|
||
|
||
---
|
||
|
||
### Task 6: 后端 - YhxxService 新增 updatePassword 方法
|
||
|
||
**文件:** `txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/YhxxService.java`
|
||
|
||
末尾新增方法声明:
|
||
```java
|
||
void updatePassword(String yhUuid, String newPassword);
|
||
```
|
||
|
||
---
|
||
|
||
### Task 7: 后端 - YhxxServiceImpl 实现 updatePassword(Feign调用mhzc)
|
||
|
||
**文件:** `txw-sso/txw-sso-service-biz/src/main/java/com/css/txw/sso/service/yhxx/impl/YhxxServiceImpl.java`
|
||
|
||
首先需要确认 mhzc 服务的 Feign 接口存在。如果 YhxxApi 不支持更新密码,则需要在 txw-sso-api 中新增 Feign 方法指向 mhzc 的更新密码接口。
|
||
|
||
步骤:
|
||
1. 在 `txw-sso-api` 的 `YhxxApi.java` 中新增 `updatePassword` Feign 方法
|
||
2. 在 `txw-mhzc` 的 `TxwMhzcYhxxbService` 新增 `updatePassword` 方法
|
||
3. 在 `TxwMhzcYhxxbServiceImpl` 实现该方法
|
||
4. 在 mapper XML 中新增 update SQL
|
||
5. 在 `YhxxServiceImpl` 中调用该 Feign 接口
|
||
|
||
**YhxxApi.java (txw-sso-api) 新增:**
|
||
```java
|
||
@PostMapping("/mhzc/user/updatePassword")
|
||
@Operation(summary = "更新用户密码")
|
||
CommonResult<String> updatePassword(@RequestParam("yhUuid") String yhUuid, @RequestParam("dlmm") String dlmm);
|
||
```
|
||
|
||
**TxwMhzcYhxxbService.java 新增:**
|
||
```java
|
||
String updatePassword(String yhUuid, String dlmm);
|
||
```
|
||
|
||
**TxwMhzcYhxxbServiceImpl.java 新增:**
|
||
```java
|
||
@Override
|
||
public String updatePassword(String yhUuid, String dlmm) {
|
||
TxwMhzcYhxxbDO yhxxbDO = new TxwMhzcYhxxbDO();
|
||
yhxxbDO.setYhUuid(yhUuid);
|
||
yhxxbDO.setDlmm(dlmm);
|
||
this.updateById(yhxxbDO);
|
||
return "success";
|
||
}
|
||
```
|
||
|
||
**TxwMhzcYhxxbMapper.xml 新增:**
|
||
```xml
|
||
<update id="updatePassword">
|
||
UPDATE txw_mhzc_yhxxb SET dlmm = #{dlmm} WHERE yh_uuid = #{yhUuid}
|
||
</update>
|
||
```
|
||
|
||
**YhxxServiceImpl.java 实现:**
|
||
```java
|
||
@Resource
|
||
private YhxxApi yhxxApi;
|
||
|
||
@Override
|
||
public void updatePassword(String yhUuid, String newPassword) {
|
||
yhxxApi.updatePassword(yhUuid, newPassword);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 前端任务拆分(txw-mhzc-web)
|
||
|
||
### Task 8: 前端 - 新建 api/sso.js
|
||
|
||
**文件:** `txw-mhzc-web/src/pages/index/api/sso.js`
|
||
|
||
```js
|
||
import { fetchSso } from '@/core/request';
|
||
|
||
export const changePassword = (params) => {
|
||
return fetchSso({
|
||
url: '/sso/auth/changePassword',
|
||
data: JSON.stringify(params),
|
||
method: 'post',
|
||
loading: true,
|
||
});
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### Task 9: 前端 - 新建修改密码页面
|
||
|
||
**文件:** `txw-mhzc-web/src/pages/index/views/yhzx/zhanghugl/index.vue`
|
||
|
||
页面元素:
|
||
- 三个密码输入框(旧密码、新密码、确认密码)
|
||
- 确认按钮
|
||
- 密码格式提示文字
|
||
|
||
校验逻辑:
|
||
1. 旧密码、新密码、确认密码不能为空
|
||
2. 新密码长度6-20位,需包含字母和数字(提示:密码长度6-20位,需包含字母和数字)
|
||
3. 新密码与确认密码一致性(提示:两次输入的新密码不一致)
|
||
4. 提交后调用 changePassword API
|
||
5. 成功提示"密码修改成功",可跳转回工作台
|
||
|
||
样式参考现有的登录页面,使用 TDesign 组件库。
|
||
|
||
---
|
||
|
||
### Task 10: 前端 - 路由配置新增 zhanghugl
|
||
|
||
**文件:** `txw-mhzc-web/src/pages/index/router/routes.js`
|
||
|
||
在 `yhzx` 的 children 数组中新增:
|
||
```js
|
||
{ path: 'zhanghugl', component: zhanghugl, name: 'zhanghugl', meta: { title: '账号管理' } }
|
||
```
|
||
|
||
新增 zhanghugl 路由函数:
|
||
```js
|
||
function zhanghugl() {
|
||
return import('@/pages/index/views/yhzx/zhanghugl/index.vue');
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Task 11: 前端 - 菜单配置新增"账号管理"
|
||
|
||
**文件:** `txw-mhzc-web/src/pages/index/views/glxtSy/config.js`
|
||
|
||
在 `menuList` 数组中新增一项:
|
||
```js
|
||
{
|
||
value: 'zhanghugl',
|
||
title: '账号管理',
|
||
icon: 'password',
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Task 12: 前端 - 工作台新增修改密码快捷入口
|
||
|
||
**文件:** `txw-mhzc-web/src/pages/index/views/gzt/index.vue`
|
||
|
||
在快捷操作区域新增按钮:
|
||
```html
|
||
<t-button theme="primary" variant="outline" @click="$router.push('/yhzx/zhanghugl')">
|
||
修改密码
|
||
</t-button>
|
||
```
|
||
|
||
---
|
||
|
||
## 任务执行顺序
|
||
|
||
建议按以下顺序执行:
|
||
|
||
1. Task 1 - 新建 ChangePasswordReqVO
|
||
2. Task 4 - 新增错误码
|
||
3. Task 6 - YhxxService 新增 updatePassword
|
||
4. Task 7 - YhxxServiceImpl + Feign + MHZC 实现
|
||
5. Task 2 - AuthService 新增 changePassword
|
||
6. Task 3 - AuthServiceImpl 实现
|
||
7. Task 5 - AuthController 新增路由
|
||
8. Task 8 - 前端 API 新建
|
||
9. Task 9 - 前端页面新建
|
||
10. Task 10 - 路由配置
|
||
11. Task 11 - 菜单配置
|
||
12. Task 12 - 工作台入口
|