This commit is contained in:
33
openspec/changes/add-self-service-password-change/design.md
Normal file
33
openspec/changes/add-self-service-password-change/design.md
Normal file
@@ -0,0 +1,33 @@
|
||||
## Context
|
||||
|
||||
头像菜单位于 `src/components/core/layouts/art-header-bar/index.vue`,当前“个人中心”通过路由跳转,“锁定屏幕”通过事件打开。`AuthService.changePassword` 已封装 `PUT /api/auth/password`,但当前 TypeScript 参数 `ChangePasswordParams` 使用 `oldPassword`、`newPassword`、`confirmPassword`,与 OpenAPI 的 `old_password`、`new_password` 不一致。HTTP 层只序列化 JSON,不会将 camelCase 自动转换成 snake_case。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
- Goals: 提供头像菜单入口和简单的自助修改密码流程;遵循给定 OpenAPI 请求契约;不增加权限编码或权限判断。
|
||||
- Non-Goals: 修改后端接口、增加路由、改变锁屏行为、增加权限管理配置。
|
||||
|
||||
## Decisions
|
||||
|
||||
- Decision: 使用头像菜单中的对话框承载表单,不为此功能新增页面路由。入口依次位于个人中心、修改密码、锁定屏幕。
|
||||
- Decision: 表单收集当前密码、新密码和确认密码;确认密码仅用于客户端一致性校验,提交时仅构造 `{ old_password, new_password }`。
|
||||
- Decision: 将 API DTO 类型调整为精确表达 OpenAPI 的 snake_case 字段,避免依赖 HTTP 层不存在的字段转换。
|
||||
- Decision: 所有已登录用户均可见并可操作该入口,不使用 RBAC、按钮权限或菜单权限配置进行隐藏/拦截;API 请求仍携带现有 Bearer Token。
|
||||
- Decision: 接口成功时清空敏感表单数据、提示成功,再调用 `userStore.clearLocalState()` 清除旧 token 并跳转登录页;接口失败时展示错误反馈,不记录密码值。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 当前类型与接口字段不一致,直接复用旧 camelCase 参数会发送错误字段;以 DTO 对齐及请求验证覆盖此风险。
|
||||
- 密码策略细节未在给定 OpenAPI 中定义;本次不自行新增密码复杂度规则,沿用后端校验结果并反馈错误。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
无需数据迁移或后端变更。前端发布后,已登录用户可从头像菜单使用入口;回滚时可移除菜单项及对话框实现。
|
||||
|
||||
## Open Questions
|
||||
|
||||
- None.
|
||||
|
||||
## Session Handling
|
||||
|
||||
After a successful password change, clear the current client authentication state with `userStore.clearLocalState()` and redirect to the login route. Do not attempt the logout API after changing the password because the old token may already be invalid.
|
||||
@@ -0,0 +1,21 @@
|
||||
# Change: 添加自助修改密码功能
|
||||
|
||||
## Why
|
||||
|
||||
当前头像菜单提供个人中心和锁定屏幕入口,但用户无法从该处修改自己的登录密码。认证服务已提供 `PUT /api/auth/password`,需要补齐可发现的前端入口和修改流程。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 在头像菜单的“个人中心”和“锁定屏幕”之间增加“修改密码”入口。
|
||||
- 点击后打开修改密码表单,提交当前密码和新密码至 `PUT /api/auth/password`。
|
||||
- 按 OpenAPI 使用 `old_password`、`new_password` 请求字段;确认密码(如表单提供)只在前端校验,不发送给接口。
|
||||
- 修改密码入口面向所有已登录用户,不新增权限编码、路由权限或按钮权限。
|
||||
- 修改成功后清理本地认证状态并跳转登录页,要求用户使用新密码重新登录。
|
||||
- 为新增文案提供中英文国际化。
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected specs: `admin-authentication`
|
||||
- Affected code: `src/components/core/layouts/art-header-bar/index.vue`、`src/components/core/layouts/art-header-bar/ChangePasswordDialog.vue`、`src/components/core/layouts/art-header-bar/style.scss`、`src/api/modules/auth.ts`、`src/types/api/auth.ts`、`src/locales/langs/zh.json`、`src/locales/langs/en.json`
|
||||
- API: 已有 `PUT /api/auth/password`;请求体按 OpenAPI 发送 `old_password` 和 `new_password`,使用当前登录 Bearer Token。
|
||||
- Authorization: 不新增前端权限配置;接口仍按认证 API 的 Bearer 认证要求调用。
|
||||
@@ -0,0 +1,33 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Self-Service Password Change Entry
|
||||
|
||||
The authenticated admin interface SHALL provide a password-change entry in the avatar menu between “个人中心” and “锁定屏幕”. The entry SHALL be available to every authenticated user without a frontend route, button, or menu permission check.
|
||||
|
||||
#### Scenario: User opens password-change form from avatar menu
|
||||
|
||||
- **GIVEN** the user is authenticated in the admin interface
|
||||
- **WHEN** the user opens the avatar menu
|
||||
- **THEN** the menu shows “个人中心”, “修改密码”, and “锁定屏幕” in that order
|
||||
- **AND** selecting “修改密码” opens the password-change form
|
||||
- **AND** the entry is not hidden or disabled based on a permission code
|
||||
|
||||
### Requirement: Change Current User Password
|
||||
|
||||
The password-change form SHALL collect the current password and new password, and SHALL submit them through `PUT /api/auth/password` using the current Bearer token. The request body MUST contain exactly the API fields `old_password` and `new_password`; any confirmation value used for client-side validation MUST NOT be sent. Password values MUST NOT be written to application logs.
|
||||
|
||||
#### Scenario: Password change succeeds
|
||||
|
||||
- **GIVEN** the user has entered a current password and matching new-password values
|
||||
- **WHEN** the user submits the form and the API returns success
|
||||
- **THEN** the frontend sends `old_password` and `new_password` to `PUT /api/auth/password`
|
||||
- **AND** the frontend displays success feedback and clears the password form values
|
||||
- **AND** the frontend clears the local authentication state and redirects the user to the login page
|
||||
- **AND** the user must authenticate again with the new password
|
||||
|
||||
#### Scenario: Password change is rejected
|
||||
|
||||
- **GIVEN** the user submits the password-change form
|
||||
- **WHEN** the API rejects the request
|
||||
- **THEN** the frontend displays error feedback and does not report success
|
||||
- **AND** the frontend does not log any password value
|
||||
13
openspec/changes/add-self-service-password-change/tasks.md
Normal file
13
openspec/changes/add-self-service-password-change/tasks.md
Normal file
@@ -0,0 +1,13 @@
|
||||
## 1. 头像菜单与修改密码表单
|
||||
|
||||
- [x] 1.1 在头像菜单“个人中心”和“锁定屏幕”之间添加“修改密码”入口,不设置权限判断。
|
||||
- [x] 1.2 实现修改密码表单,收集当前密码、新密码及确认密码,并在前端校验必填项与两次新密码一致。
|
||||
- [x] 1.3 调用认证 API;请求仅包含 `old_password` 和 `new_password`,成功后清空表单并提示结果,失败时显示错误反馈。
|
||||
- [x] 1.4 添加中英文菜单、表单、校验及结果提示文案。
|
||||
|
||||
## 2. API 契约与验证
|
||||
|
||||
- [x] 2.1 对齐 `ChangePasswordParams` 与 `AuthService.changePassword` 的请求 DTO,确保网络请求字段为 OpenAPI 定义的 snake_case,且不发送确认密码。
|
||||
- [x] 2.2 验证菜单顺序、表单校验、成功与失败反馈,以及入口不依赖任何权限编码。
|
||||
- [x] 2.3 运行相关类型检查和构建,处理本次改动引入的问题。
|
||||
- [x] 2.4 密码修改成功后清理本地认证状态并跳转登录页。
|
||||
Reference in New Issue
Block a user