Files
StoreWeb/Modules/Store.Users

用户模块

参考旧版 D:/Works/KJD_WebService/KJD_WebService/BLL/WXXD/WXUserManage.cs,沿用用户、积分、余额、充值备注等旧表。接口采用 JSON DTO 和现有 Bearer Token 身份,不接受旧版拼接参数或客户端提供的操作者身份。

文件与职责

  • 文件:../Store.Users.Contracts/Dtos/*.cs、Dtos/ShopRenewRequest.cs:非空、长度、数值范围、日期顺序、金额精度和二维码格式。null 更新字段保留原值,空字符串可清空可选文本。
  • 文件:Services/UserAccessPolicy.cs:集中定义管理员、主账号、子账号的操作范围。
  • 文件:Services/UserManagementService.cs:账户增删改查、密码和流水查询。
  • 文件:Services/UserManagementService.TopUp.cs:充值备注和公共代理二维码。
  • 文件:Services/UserFundsService.cs:资金事务、按主键顺序锁定账户、权限校验、请求去重和流水写入。
  • 文件:Commands/ShopRenewCommand.cs、Behaviors/*.cs:续费输入验证 → 事务 → 当前身份、账户归属及积分检查 → 店铺归属 → 扣费及延长有效期。
  • 文件:Repositories/UserRepository.cs:子账号的有效 OEM、费用和积分用户权限继承。
  • 文件:Services/UserPassword.cs:兼容旧明文密码;新建和修改密码保存带盐哈希。

查询入口与后续缓存接入

公共用户查询已经通过 IUserQueryService → UserQueryService → IUserRepository.GetUser 集中读取,本次保留现有实现。登录凭据检查、资金操作和事务内的账户读取仍直接访问数据库,不为缓存准备迁移事务写入。

GetUser 返回主子账号配置合并后的结果,并被 Shops 等模块用于权限判断,不能直接给整个结果加缓存。后续只缓存允许延迟的资料时,应将展示查询与实时授权读取明确分开;若缓存继承配置,还需在主账号变更时失效关联子账号数据。密码、权限、余额与积分不得因接入展示缓存而改用旧状态。

店铺续费写入位于 Users,其到期记录由 Shops 的 Repository 直接查询数据库。当前不使用仓储缓存,不注册缓存提供程序或续费缓存失效管道;续费仍由原有事务管道统一提交或回滚。

权限规则

身份从登录上下文和数据库读取,管理员为 SaLevel == UserAdminLevel.SuperAdmin;普通主账号必须 GroupId == UserGroup.MainAccount 且无父账号。父账号缺失的子账号不会自动获得主账号权限。

操作 管理员 普通主账号 普通子账号
查询账户、资金流水 任意账户 本人和直属非管理员子账号 本人
创建主账号 允许 不允许 不允许
创建子账号 指定任意主账号 仅挂在本人名下 不允许
修改资料、删除账户 任意账户 本人和直属非管理员子账号 不允许
重置密码 任意账户,包括本人,无需原密码 子账号免原密码,本人需原密码 本人需原密码
积分充值、余额调整 任意账户 不允许 不允许
积分转移 向其他任意账户转出本人的积分 向直属非管理员子账号转出本人的积分 不允许
店铺续费、提交充值备注 任意账户 本人和直属非管理员子账号 本人
处理充值备注 任意账户 本人和直属非管理员子账号 不允许
管理员级别、计费配置、公共充值二维码 允许 不允许 不允许

管理员不受账户归属限制,仍须满足账户存在、余额充足等业务条件。删除要求无余额、无积分、无子账号、无店铺关联;本人满足条件也可删除,成功后注销会话。普通主账号不能向子账号授予自己没有的权限,不能通过管理具有管理员身份的子账号取得管理员权限。

校验与业务边界

DTO 用 [Required] 声明非空,使用数据注解和 IValidatableObject 声明输入规则。Service 入口统一调用 RequestValidator.Validate,续费通过 ValidationBehavior 调用 DTO 校验,保证内部调用也执行输入检查。Service 和后续管道只保留数据库状态、主子关系、权限、资金与店铺归属判断,不再重复校验必填字段。

账户登录名保持稳定,避免破坏店铺和历史流水关联。新建账户的主/子身份由接口决定,GroupId 必须与该身份一致。管理员创建子账号可指定 ParentUserName,普通主账号只能指定本人。查询列表先限定可见范围,再应用筛选与分页;不能用筛选条件扩大权限。

续费命令只接收 ShopRenewRequest,不再接收 OperatorName。管道从 ICurrentUser 读取实际操作者,目标账户省略时默认为本人。店铺必须属于目标账户,扣除目标账户积分,流水记录真实操作者。续费保留旧规则:每月最少 30 积分,在当前有效期和当前时间中较晚者的基础上延长。

资金请求的 Amount 为正数且最多两位小数;积分支持“充值”“转移”,余额支持“充值”“消费”。积分转入仍计入累计充值。资金和流水在同一事务提交;同一目标和资金类型下,重复 RequestId 拒绝再次记账。重试必须复用请求号。充值备注的处理不自动增加余额或积分。

接口与运行

所有用户接口要求 Bearer Token,采用 POST,路由为 v{version}/user/[action]。资料和资金操作使用 JSON 请求体;GetUser、DelUser、GetTopUpQrcode 使用查询参数。分页从 1 开始,默认每页 50 条,最大 200 条。

dotnet build Modules/Store.Users/Store.Users.csproj
dotnet build Store.API/Store.Api.csproj
dotnet run --project Store.API/Store.Api.csproj

连接现有 MySQL 前确认账户表用户名唯一、密码列可容纳 Identity 哈希、资金相关表支持事务。应用不自动创建或修改旧表。旧系统直接比较明文密码的登录接口不能识别新哈希,需随调用方迁移。

使用 dotnet test Tests/Store.Users.Tests/Store.Users.Tests.csproj 验证参数、权限、密码、资金和 HTTP 接口。SQLite 测试不能替代真实 MySQL 的并发与表结构联调。旧文件中的 E57 令牌、销售助手 Cookie、淘宝买手不属于本模块范围。可以继续使用“代码审查”检查实现质量。