Files
StoreWeb/Modules/Store.Users/README.md
T
2026-10-23 18:16:05 +08:00

100 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 用户模块迁移与运行
本模块参考 `D:/Works/KJD_WebService/KJD_WebService/BLL/WXXD/WXUserManage.cs` 重写,保留旧表映射,采用 ASP.NET Core 控制器、DTO、服务、FreeSql 仓储和现有 JWT 身份体系。所有新代码均含中文用途或关键规则注释。
## 文件与职责
- 文件:`Controllers/UserController.cs`:`/user/[action]` 接口,统一要求 Bearer Token。
- 文件:`Services/UserManagementService.cs`:主子账号管理、权限检查、分页、密码修改和流水查询。
- 文件:`Services/UserManagementService.TopUp.cs`:充值备注与代理二维码。
- 文件:`Services/UserFundsService.cs`:积分充值、积分转移、余额充值/消费;事务、行锁、请求去重与流水写入。
- 文件:`Services/UserQueryService.cs`、`Repositories/UserRepository.cs`:认证和查询、主子账号有效配置继承。
- 文件:`Services/UserPassword.cs`:带盐哈希密码与旧明文密码兼容。
- 文件:`../Store.Users.Contracts/Dtos/UserManagementRequests.cs`、`UserFundsRequest.cs`、`UserTopUpRequests.cs`:输入验证与响应契约。
- 文件:`Database/prepare-user-module.sql`:上线前密码列、唯一用户名、事务引擎及请求号列检查。
- 文件:`../../Store.API/Controllers/AuthController.cs`:登录签发 JWT 前调用真实密码验证,不再接受任意密码。
- 文件:`../../Tests/UserManagementSmoke/Program.cs`:临时 SQLite 集成验证,避免访问业务数据库。
## 接口
HTTP 方法约定:控制器通过 [DefaultHttpPost] 启用默认 POST。所有操作只接受 POST,无需逐个标记 [HttpPost]。该约定同时生成路由约束和端点元数据,接口文档也显示 POST。
| 方法 | 路径 | 访问与行为 |
| --- | --- | --- |
| POST | /user/GetUser?userName=... | 本人、直属主账号或管理员;省略用户名读取本人 |
| POST | /user/GetAllUser | 管理员查询全部;普通主账号查询本人及直属子账号 |
| POST | /user/GetSubUser | 管理员查询所有子账号,可用 ParentUserName 按父账号筛选;普通主账号仅能查询自己的子账号 |
| POST | /user/AddUser | 管理员新增主账号 |
| POST | /user/AddSubUser | 当前主账号新增直属子账号 |
| POST | /user/UpdateUser | 管理员修改任意账户;普通主账号修改本人及直属子账号 |
| POST | /user/UpdateSubUser | 管理员修改任意子账号;普通主账号修改直属子账号 |
| POST | /user/DelUser?id=... | 管理员或直属主账号删除空账户 |
| POST | /user/ChangePassword | 本人改密校验原密码;管理员/主账号可重置授权账户密码 |
| POST | /user/GetUserPoints | 授权账户积分流水,支持起止时间、操作类型、店铺过滤 |
| POST | /user/GetUserBalance | 授权账户余额流水,支持起止时间、支付类型、订单过滤 |
| POST | /user/UpdateUserPoints | 管理员充值;本人向管理范围内账户转移自己的积分 |
| POST | /user/UpdateUserBalance | 管理员充值或消费调整 |
| POST | /user/AddUserTopUpMsg | 本人或授权账户提交充值备注 |
| POST | /user/UpdateUserTopUpMsg | 管理员或主账号处理授权账户的备注,不自动充值 |
| POST | /user/GetUserTopUpMsg | 管理员查看全部;主账号查看本人及直属子账号备注 |
| POST | /user/UpdateTopUpQrcode | 管理员维护代理二维码 |
| POST | /user/GetTopUpQrcode?agentCode=... | 登录用户读取代理二维码 |
| POST | /user/ShopRenew | 保留原有 MediatR 事务续费用例 |
分页查询与流水查询通过 JSON 请求体提交;流水查询的 userName 与筛选字段放在同一对象中。UpdateUser、UpdateSubUser 使用 JSON 请求体,例如 `{"id":123,"nickName":"新昵称"}`。仅有单个业务参数的 GetUser、DelUser、GetTopUpQrcode 仍使用查询参数。
分页 Page 从 1 开始,PageSize 默认 50,最大 200。流水时间参数为 StartTime、EndTime(包含边界)。资料更新 null 表示保留原值,空字符串清空文本字段。账户名不可修改,以免破坏历史关联。SaLevel 仅管理员可设置为 0/1,子账号不能成为管理员。普通主账号不能授予自己没有的权限,也不能修改计费配置。子账号积分用户权限、OEM 和每单费用继承主账号,保留 2.2/2.5 元费用下限。
资金请求示例:
```json
{"userName":"target","requestId":"客户端生成的唯一请求号","amount":100,"operation":"充值","remark":"人工充值已核实"}
```
积分 Operation 支持“充值”“转移”;余额支持“充值”“消费”。Amount 始终为正数,最多两位小数。转移仅从当前登录账户转出,不支持负金额拉取对方积分。相同目标账户和资金类型下,重复 RequestId 返回已处理错误,不再次记账;调用失败重试必须保留请求号。不同请求号视作不同业务,调用方需确保业务唯一性。积分转入累计充值口径沿用旧系统。余额流水类型沿用旧系统充值 1、人工消费 0;商铺续费继续走专用接口。
## 运行与部署
1. 检查现有 MySQL `DefaultConnection` 配置和已有表。确认 `wxxd.user` 中存在可登录的 `sa_level=1` 管理员。
2. 审阅并执行 `Database/prepare-user-module.sql`。应用不自动建表或改表;资金表必须是 InnoDB,用户名必须有唯一索引。
3. 构建:`dotnet build Store.API/Store.Api.csproj`。
4. 验证:`dotnet run --project Tests/UserManagementSmoke/UserManagementSmoke.csproj`,以及原有 `dotnet run --project Tests/ShopRenewSmoke/ShopRenewSmoke.csproj`。
5. 启动:`dotnet run --project Store.API/Store.Api.csproj`。先调用宿主 `/api/v1.0/Auth/Login`,再携带 Bearer Token 调用用户接口。
认证兼容 WXUserManage 的旧明文密码和 Identity 带盐哈希,登录时仅验证,不修改密码;新建和修改密码直接保存哈希。旧项目按 SQL 明文比较密码的登录接口无法识别哈希密码,共用数据库时需同步切换相关调用方。新接口采用结构化 JSON,不兼容旧 inputData 拼接字符串及 time/code 签名。UserInfo 保留 Password 属性用于源码兼容,但不映射值、不向 JSON 序列化。
## 迁移边界与验证限制
旧文件中的 E57 第三方令牌、微信销售助手 Cookie、淘宝买手管理属于外部系统或独立业务,未加入用户账户模块。本次未修改旧项目或调用第三方服务。
集成验证使用临时 SQLite 库,覆盖认证、登录不修改密码、主子账号、越权、计费、资金写入、重复请求、超扣、强制流水失败的事务回滚、删除边界、备注和二维码更新。MySQL 的真实并发、行锁、历史表结构和旧客户端迁移仍需在测试库联调。可使用“代码审查”继续检查实现质量。
## 输入校验约定
User 模块的请求 DTO 只保留 `[Required]`,校验必填、null、空字符串和纯空白字符串。服务使用 `MAX.Framework.Validation.RequestValidator.Validate(request)` 统一执行这类非空校验,防止内部调用绕过 HTTP 模型验证。
```csharp
using MAX.Framework.Validation;
RequestValidator.Validate(request);
// 非空之外的规则由服务继续手动判断。
```
长度、数值范围、金额精度、操作类型、字段组合、日期顺序和二维码 Base64 格式在服务层手动检查。可复用检查集中于 `Services/UserInputChecks.cs`,资金金额与操作类型直接由 UserFundsService 判断;续费的非空输入由 ShopRenewRequest 声明,其余规则通过命令验证管道调用服务层检查后执行事务。
权限、账户归属、数据库记录是否存在、余额是否足够、重复请求等业务规则继续在服务或业务管道中判断。不合法的参数抛出 ValidationException,权限和业务错误沿用现有业务异常;现有过滤器将它们转换为 HTTP 200 和对应业务码。
公共 RequestValidator 位于 `Core/MAX.Framework/Validation/RequestValidator.cs`。它仍支持标准 DataAnnotations 及 IValidatableObject,其他模块可以直接复用;User 模块“DTO 仅非空”的约定不改变公共校验器能力。该工具不递归验证嵌套对象。
## 账户管理权限
统一使用 Services/UserAccessPolicy:管理员可管理任意账户,普通主账号可管理本人及直属子账号;普通子账号保留本人查询和本人改密能力,不能管理其他账户。UpdateUser 支持此统一范围,UpdateSubUser 同时要求目标确实是子账号。管理员创建子账号时可传 ParentUserName 指定任意主账号;普通主账号只能指定自己。
ChangePassword 可传 UserName 指定目标,省略表示本人。本人改密仍须正确 OldPassword;管理员或主账号重置管理范围内的其他账户时仅需 NewPassword。成功后注销目标账户的会话。
已确认的财务权限保持:直接充值积分/余额、余额扣款、计费配置和公共代理二维码维护仅管理员可用;普通主账号不能提升管理员级别。积分转移仅从当前登录账户转出,普通主账号的转入目标限直属子账号。充值备注的提交/处理不代表资金到账。
ShopRenew 可传 UserName 指定授权账户,以店铺所属账户积分扣款,流水记录实际 OperatorName。账户删除仍禁止删除当前登录账户,且要求无积分、无余额、无子账号和无店铺关联。
管理员角色账户即使存在父账号关系,也不能由普通主账号修改或重置密码,避免通过子账号关系获得管理员身份。