# 用户模块迁移与运行 本模块参考 `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。账户删除仍禁止删除当前登录账户,且要求无积分、无余额、无子账号和无店铺关联。 管理员角色账户即使存在父账号关系,也不能由普通主账号修改或重置密码,避免通过子账号关系获得管理员身份。