test-report.md 80 KB

权限管理系统 (perms-system-server) — 测试报告

报告日期: 2026-05-15(新增用户凭证票据机制 CreateUser/ResetPassword/FetchUserCredentials) 测试范围: REST API (go-zero) + gRPC + Model 层 (自定义方法 + _gen.go 模板生成) + Logic 单元测试 + util 层 + 访问控制 + UserDetailsLoader + 中间件 测试用例设计详见 test-design.md 执行命令: go test -count=1 -timeout 600s ./...


一、测试执行总览

指标 数值
测试包总数 28
TC 用例总数 (test-design.md) 1065
顶层测试函数数 (Functions) 1170
测试执行事件总数 (含 t.Run 子用例) 1309
✅ 通过 1307
⏭️ 跳过 2
❌ 失败 0(本轮全绿)
通过率 (TC 维度) 100%(扣除 2 条不可达防御分支 Skip)
Logic 层语句覆盖率 87.2%go tool cover -func 统计)

1.1 各测试包结果

测试包 状态 耗时
internal/handler ✅ ok 1.339s
internal/handler/auth ✅ ok 3.744s
internal/handler/minio ✅ ok 2.124s
internal/handler/product ✅ ok 2.597s
internal/handler/pub ✅ ok 3.199s
internal/loaders ✅ ok 4.418s
internal/logic/auth ✅ ok 13.685s
internal/logic/dept ✅ ok 6.231s
internal/logic/member ✅ ok 7.291s
internal/logic/minio ✅ ok 8.127s
internal/logic/perm ✅ ok 9.476s
internal/logic/product ✅ ok 18.959s
internal/logic/pub ✅ ok 14.051s
internal/logic/role ✅ ok 12.803s
internal/logic/user ✅ ok 20.844s
internal/middleware ✅ ok 14.374s
internal/model/dept ✅ ok 15.051s
internal/model/perm ✅ ok 14.717s
internal/model/product ✅ ok 14.814s
internal/model/productmember ✅ ok 14.115s
internal/model/role ✅ ok 12.892s
internal/model/roleperm ✅ ok 12.404s
internal/model/user ✅ ok 20.141s
internal/model/userperm ✅ ok 11.709s
internal/model/userrole ✅ ok 8.933s
internal/response ✅ ok 8.987s
internal/server ✅ ok 9.439s
internal/util ✅ ok 9.082s

1.2 跳过用例说明

TC 编号 跳过原因
TC-0263 JWT claims 类型断言防御性分支,在 jwt.ParseWithClaims(&Claims{}) 下不可达
TestMinioUpload_EmptyFileType:Minio 空文件类型防御分支,测试环境无 Minio 实例

1.3 已知缺陷(需跟进)

本轮无业务缺陷。仅保留 1 条 pre-existing、与被测代码无关的并发 flake 作为存档。

TC 编号 失败现象 根因 跟进建议
TC-0820(UserModel_IncrementTokenVersionIfMatch TestSysUserModel_IncrementTokenVersionIfMatch_ConcurrentSingleWinner 在整包并发压下偶发 circuit breaker is open(go-zero breaker)失败 本用例靠 wg+8 goroutine 同时冲同一行走 CAS UPDATE ... WHERE tokenVersion=?,在整包全量并发压下 go-zero SQL 断路器被其它测试累计的错误触达打开,导致 8 路里若干路直接被 breaker 短路拒绝;-run 单独跑则断路器计数窗口没攒满,必过 在测试 setup 里显式重置 breaker 统计(或注入允许更高错误率的 breaker 配置),也可在断言里把 circuit breaker is open 视作"并发压力副作用"跳过重试;非业务 bug,生产路径的 CAS 正确性已由 _Match / _Mismatch_NoSideEffect 两条稳定用例覆盖

已修复的回归点:上一轮报告中的 TC-1078 TestBindRoles_Vs_DeleteRole_NoOrphanRows 文案 flake,本轮随 bindRolesLogic 三路径统一为 包含无效的角色ID 的改动(L-R14-2)一并收敛;并发断言现已与新文案对齐,单独 / 整包执行均稳定 pass。本轮变更:POST /api/user/bindRoles 请求体新增 productCode 字段。SUPER_ADMIN 的 JWT 中 productCode 为空字符串,原实现直接从 JWT context 读取导致 member 查询失败("目标用户不是当前产品的成员")。修复后 logic 优先使用 req.ProductCode(非空时覆盖 JWT context 值),所有 BindRolesReq 测试用例已同步补充 ProductCode 字段。

本轮新增:用户凭证票据机制(CreateUser 移除 Password 字段改为服务端生成 + ticket 一次性领取、ResetPassword、FetchUserCredentials),新增 TC-1280 ~ TC-1299 共 20 条用例。同时修复 userrole 包 4 条测试因 FindRoleIdsByUserId 新增 INNER JOIN sys_role 过滤导致的 pre-existing 失败(测试数据未在 sys_role 表中创建对应角色记录)。


以下章节按 test-design.md 的章节顺序枚举每一个 TC 的编号、测试场景与执行结果。

二、REST API 测试用例

2.1 产品端登录 POST /api/auth/login

TC编号 测试场景 测试结果
TC-0001 正常登录(普通用户+productCode) ✅ pass
TC-0002 正常登录-带productCode+ADMIN成员 ✅ pass
TC-0003 超管通过产品端登录被拒绝 ✅ pass
TC-0004 超管无productCode被拒绝 ✅ pass
TC-0005 用户不存在 ✅ pass
TC-0006 DB异常(非ErrNotFound) ✅ pass
TC-0007 密码错误 ✅ pass
TC-0008 账号冻结 ✅ pass
TC-0009 非产品成员 ✅ pass
TC-0010 DEVELOPER成员 ✅ pass
TC-0011 SQL注入 ✅ pass
TC-0012 缺少必填字段 ✅ pass
TC-0013 产品成员被禁用时拒绝登录 ✅ pass
TC-0014 产品被禁用时拒绝登录 ✅ pass
TC-0751 用户名不存在 + 任意密码 ✅ pass
TC-0752 用户名存在但密码错 ✅ pass
TC-0753 登录限流 key 必须基于 ip:username ✅ pass
TC-0838 用户冻结 + 错误密码 ✅ pass
TC-0839 用户冻结 + 正确密码 ✅ pass
TC-0840 超管走产品端登录 + 错误密码 ✅ pass
TC-0841 超管走产品端登录 + 正确密码 ✅ pass
TC-0842 用户名不存在 ✅ pass
TC-1213 cap.js 未启用 + 验证码为空 ✅ pass
TC-1214 cap.js 未启用 + 验证码错误/过期 ✅ pass
TC-1215 cap.js 未启用 + 验证码正确 → 正常登录 ✅ pass
TC-1250 cap.js 已启用时传统接口被拒绝 ✅ pass

2.1b 管理后台登录 POST /api/auth/adminLogin

TC编号 测试场景 测试结果
TC-0015 超管正常登录 ✅ pass
TC-0016 普通用户被拒绝 ✅ pass
TC-0017 managementKey无效 ✅ pass
TC-0018 managementKey为空 ✅ pass
TC-0019 用户不存在 ✅ pass
TC-0020 密码错误 ✅ pass
TC-0021 账号冻结 ✅ pass
TC-0022 不带productCode时perms为空 ✅ pass
TC-0023 缺少必填字段 ✅ pass
TC-0024 SQL注入username ✅ pass
TC-0025 adminLogin 用户名限流 ✅ pass
TC-0710 同 IP,产品登录用尽配额后管后登录仍放行 ✅ pass
TC-0781 不存在用户登录管理后台响应时间恒定 ✅ pass
TC-0834 相同 IP + 相同 username 连续打满 quota ✅ pass
TC-0835 同 username 但换远端 IP ✅ pass
TC-0836 clientIP 缺失(未挂 RateLimit 中间件) ✅ pass
TC-0837 managementKey 无效路径不得消耗 username 配额 ✅ pass
TC-1008 非超管+错密码 vs 用户不存在 ✅ pass
TC-1009 非超管+正确密码 ✅ pass
TC-1010 两条 dummy bcrypt 分支时序 ✅ pass
TC-1216 cap.js 未启用 + 验证码为空 ✅ pass
TC-1217 cap.js 未启用 + 验证码错误/过期 ✅ pass
TC-1218 cap.js 未启用 + 验证码正确 → 超管正常登录 ✅ pass
TC-1251 cap.js 已启用时传统接口被拒绝 ✅ pass

2.2 刷新Token POST /api/auth/refreshToken

TC编号 测试场景 测试结果
TC-0026 正常刷新 ✅ pass
TC-0027 不带productCode(回退) ✅ pass
TC-0028 token无效 ✅ pass
TC-0029 用户已删除 ✅ pass
TC-0030 账号冻结 ✅ pass
TC-0031 超管+productCode(token中已含相同pc) ✅ pass
TC-0032 尝试切换产品被拒绝 ✅ pass
TC-0033 TokenVersion不匹配时拒绝刷新 ✅ pass
TC-0034 使用accessToken作为refreshToken被拒绝 ✅ pass
TC-0035 产品成员已移除时拒绝刷新 ✅ pass
TC-0720 正常登出 ✅ pass
TC-0721 未登录调用 /auth/logout ✅ pass
TC-0722 同一用户连续两次 logout ✅ pass
TC-0832 静态 wiring 检查:routes.go 中 /auth/refreshToken 所在 rest.WithMiddlewares(...) 块必须包含 serverCtx.RefreshTokenRateLimit ✅ pass
TC-0833 行为验证:构造等价中间件链(quota=1),同 IP 连打 2 次;再换 IP 打 1 次 ✅ pass
TC-0983 成功路径:DB tokenVersion +1 且新 access/refresh claims.TokenVersion 严格等于 DB 新值 ✅ pass
TC-0984 CAS 不命中("其他并发赢家已把 DB 推到 version=1"后再来一次 claims=0) ✅ pass
TC-0985 多轮链式刷新 + 旧 token 重放 ✅ pass
TC-1066 helper happy path:返回新 access+refresh、DB tokenVersion +1、user cache Clean ✅ pass
TC-1067 helper:claims.TokenVersion 与 DB 不一致 → ErrTokenVersionMismatch ✅ pass
TC-1068 helper:用户已删除 → ErrTokenVersionMismatch ✅ pass
TC-1069 跨协议互认:HTTP 签出的 refreshToken 能被 gRPC RefreshToken 无缝续签 ✅ pass
TC-1070 跨协议互认:gRPC 签出的 refreshToken 能被 HTTP RefreshToken 无缝续签 ✅ pass
TC-1071 gRPC 重放:旧 rtV0 已被用过一次,再发给 gRPC 必须 Unauthenticated(而非 Internal) ✅ pass
TC-1117 M-R14-1:RotateRefreshToken post-commit UD 缓存清理与请求 ctx 解耦 ✅ pass

2.3 同步权限 POST /api/perm/sync

TC编号 测试场景 测试结果
TC-0036 全部新增 ✅ pass
TC-0037 更新已有(名称变更) ✅ pass
TC-0038 无变化 ✅ pass
TC-0039 禁用权限重启 ✅ pass
TC-0040 移除不在列表的权限 ✅ pass
TC-0041 空perms数组被拒绝 ✅ pass
TC-0042 验证disabled返回值 ✅ pass
TC-0043 appKey无效 ✅ pass
TC-0044 appSecret错误 ✅ pass
TC-0045 产品已禁用 ✅ pass
TC-0046 大批量(1000条) ✅ pass
TC-0047 重复code去重 ✅ pass
TC-0048 事务保护-中途失败回滚 ✅ pass
TC-0807 FindMapByProductCodeWithTx 与非事务版返回等价 ✅ pass
TC-0808 产品无权限时返回 ✅ pass
TC-0809 LockByCodeTx 对存在 code 返回完整行 ✅ pass
TC-0810 对不存在 code ✅ pass
TC-0811 两个事务同时锁同一行 ✅ pass
TC-0826 同一 perm code 在 req 中重复 ✅ pass
TC-0843 mock 断言事务内必调用 LockByCodeTx ✅ pass
TC-0844 mock LockByCodeTx 返回 sqlx.ErrNotFound ✅ pass
TC-0845 mock LockByCodeTx 返回通用 error ✅ pass
TC-0979 REST:tx 内 LockByCodeTx → ErrNotFound ✅ pass
TC-0980 REST 反例:未映射 se.Code=500 ✅ pass
TC-0981 gRPC:同 LockByCodeTx ErrNotFound ✅ pass
TC-0982 gRPC 反例:未映射 code ✅ pass
TC-1021 既有 404 路径用例的 LockByCodeTx mock 显式携带 Status=1 ✅ pass
TC-1022 Dedup / mock / TxLock 三路径下事务内 Status 复核全覆盖 ✅ pass
TC-1023 gRPC SyncPermissions 入口同样落入 Status=1 契约 ✅ pass
TC-1063 纯新增(updated=0, disabled=0)→ 必须触发 CleanByProduct(M-R17-1 语义收敛) ✅ pass
TC-1064 至少一条 update(code 存在但 name/Status/Sort 变更)→ 必须触发 CleanByProduct ✅ pass
TC-1065 至少一条 disable(列表里不含的 perm 被置 Disabled)→ 必须触发 CleanByProduct ✅ pass
TC-1118 M-R14-1:ExecuteSyncPerms post-commit CleanByProduct 与请求 ctx 解耦 ✅ pass

2.4 获取用户信息 POST /api/auth/userInfo

TC编号 测试场景 测试结果
TC-0049 正常获取-含productCode ✅ pass
TC-0050 不含productCode ✅ pass
TC-0051 未登录 ✅ pass
TC-0052 token过期 ✅ pass
TC-0053 userId=0 ✅ pass

2.5 修改密码 POST /api/auth/changePassword

TC编号 测试场景 测试结果
TC-0054 正常修改 ✅ pass
TC-0055 mustChangePassword重置 ✅ pass
TC-0056 原密码错误 ✅ pass
TC-0057 新密码少于8字符 ✅ pass
TC-0058 新密码恰好8字符(含大小写+数字) ✅ pass
TC-0059 新密码空字符串 ✅ pass
TC-0060 新密码超过72字符 ✅ pass
TC-0061 新密码恰好72字符 ✅ pass
TC-0062 新旧密码相同 ✅ pass
TC-0063 用户不存在 ✅ pass
TC-0769 ChangePassword 超过 TokenOpLimiter 配额 ✅ pass
TC-0770 冻结用户调用 ChangePassword ✅ pass
TC-0771 原密码错误时记录日志 ✅ pass
TC-1015 UpdatePassword 返回 ErrUpdateConflict 时必须回 409 ✅ pass
TC-1016 ErrUpdateConflict 的 raw error 仍需透传(由 rest 兜 500) ✅ pass
TC-1039 Model 层正向:expectedUpdateTime 与 DB 一致 → 成功 + tokenVersion+1 + updateTime 前进 ✅ pass
TC-1040 Model 层 TOCTOU:调用方持有"陈旧的 expectedUpdateTime" ✅ pass
TC-1041 Model 层并发:同一 expectedUpdateTime 两 goroutine 并行 CAS ✅ pass
TC-1042 Logic 层 E2E:同一 user 连续两次用 "同一旧密码 P0" 发起 ChangePassword,第二次必须 400 "旧密码错误" ✅ pass
TC-1043 Logic 层 mock:ChangePassword 必须把"外层 FindOne 拿到的 user.UpdateTime" 原封不动透传给 Model 层 ✅ pass
TC-1179 OldPassword==NewPassword 必须在 bcrypt.CompareHashAndPassword 之前被拦截(L-R17-4) ✅ pass

2.6 创建产品 POST /api/product/create

TC编号 测试场景 测试结果
TC-0064 正常创建 ✅ pass
TC-0065 事务回滚-用户创建失败 ✅ pass
TC-0066 事务回滚-成员创建失败 ✅ pass
TC-0067 编码已存在 ✅ pass
TC-0068 并发创建同编码 ✅ pass
TC-0069 createProduct 含空格被拒绝 ✅ pass
TC-0070 createProduct 含特殊字符被拒绝 ✅ pass
TC-0071 createProduct 全中文被拒绝 ✅ pass
TC-0072 createProduct 纯数字开头被拒绝 ✅ pass
TC-0073 createProduct 空字符串被拒绝 ✅ pass
TC-0074 createProduct 长度>64 被拒绝 ✅ pass
TC-0075 createProduct 合法编码(含下划线/中划线/数字) ✅ pass
TC-0774 appKey 长度=32 hex字符 (16字节) ✅ pass
TC-0775 appSecret 长度=64 hex字符 (32字节) ✅ pass
TC-0776 初始管理员密码长度=24 hex字符 (12字节) ✅ pass
TC-0827 mock InsertWithTx 返回 mysql error 1062,message 不含 "uk_code" ✅ pass
TC-0901 happy path:超管用有效 ticket 取回初始凭据 ✅ pass
TC-0902 相同 ticket 二次消费 ✅ pass
TC-0903 ticket 为空 ✅ pass
TC-0904 ticket 未知 ✅ pass
TC-0905 非超管调用 ✅ pass
TC-0906 未登录调用 ✅ pass
TC-0907 Redis 中 payload 被人为破坏 ✅ pass
TC-0908 Redis key 结构正确 ✅ pass
TC-0909 TTL 与响应 expiresAt 一致 ✅ pass
TC-0910 并发消费同一 ticket ✅ pass
TC-0911 CreateProductResp JSON 不含 appSecret/adminPassword ✅ pass
TC-0912 CreateProductResp 必含 credentialsTicket + credentialsExpiresAt ✅ pass
TC-0961 body 非法 JSON ✅ pass
TC-0962 无登录上下文 ✅ pass
TC-0963 非超管 ctx ✅ pass
TC-0964 超管 + 空 ticket ✅ pass
TC-0965 超管 + 未知 ticket ✅ pass
TC-0966 超管 + 已落地 ticket ✅ pass
TC-0967 静态 wiring:JwtAuth 绑定 ✅ pass
TC-0968 静态反证:不得挂到限流组 ✅ pass
TC-0976 Redis 整个不可达(SetexCtx 永久失败) ✅ pass
TC-0977 Redis 失败 + 补偿成功后以同 Code 重建 ✅ pass
TC-0978 补偿顺序显式校验(child → parent) ✅ pass
TC-1029 seedAdminDept(t, ctx, svcCtx) 集中化 ✅ pass
TC-1030 CreateProduct 所有正向用例携带 AdminDeptId 后行为不变 ✅ pass
TC-1031 FetchInitialCredentials 票据消费路径携带 AdminDeptId 后行为不变 ✅ pass
TC-1032 Redis 降级补偿链路径携带 AdminDeptId 后行为不变 ✅ pass
TC-1033 冲突路径下 SysDeptModel.FindOneStatus=1AdminDeptId 透传 ✅ pass
TC-1034 mock 侧两处 CreateProductReq 补齐 AdminDeptId 字段 ✅ pass
TC-1035 一次性票据 AdminPassword 长度=16(不再是旧的 24) ✅ pass
TC-1202 未传 avatar 时 admin 用户行的 avatar 列在 DB 层必须是 SQL NULL(L-R17-2) ✅ pass
TC-1203 CreateProduct 事务内 FindOneForShareTx(adminDeptId)==ErrNotFound 时必须 400 "管理员部门不存在或已删除",product/user/product_member 三张表 InsertWithTx 均不得被调用(H-R17-1) ✅ pass

2.7 产品更新/列表/详情

TC编号 测试场景 测试结果
TC-0076 正常更新 ✅ pass
TC-0077 不存在 ✅ pass
TC-0078 不传status ✅ pass
TC-0090 updateProduct 非法状态值被拒绝 ✅ pass
TC-0850 MEMBER 调 ProductList ✅ pass
TC-0851 MEMBER 调 ProductList 且 ProductCode=="" ✅ pass
TC-0852 MEMBER 调 ProductDetail 查他产品 ✅ pass
TC-0853 MEMBER 调 ProductDetail 查自己产品 ✅ pass
TC-0854 超管调 ProductDetail ✅ pass
TC-0855 MEMBER 调 DeptTree ✅ pass
TC-0856 MEMBER 调 DeptTree 且 DeptPath="" ✅ pass
TC-0857 产品 ADMIN 调 DeptTree 仅见 DeptPath 子树 ✅ pass
TC-1128 SuperAdmin 调 DeptTree 仍见完整树 ✅ pass
TC-1129 DEVELOPER 调 DeptTree 同样剪枝到 DeptPath 子树 ✅ pass
TC-1138 Enabled→Disabled 产品时,所有启用成员 tokenVersion 全部 +1;禁用成员不受影响 ✅ pass
TC-1139 Disabled→Enabled 时 tokenVersion 不变(重启用不吊销 session) ✅ pass
TC-1140 仅改 Name / Remark 不改 Status 时 tokenVersion 不变 ✅ pass
TC-1141 禁用 P1 不得影响只属于 P2 的用户 tokenVersion(跨产品隔离) ✅ pass
TC-1142 禁用无启用成员产品时仍成功,产品行落盘 Disabled ✅ pass
TC-1143 post-commit 失效 sysProduct 的 id / appKey / code 三把低层缓存 ✅ pass

2.8 创建部门 POST /api/dept/create

TC编号 测试场景 测试结果
TC-0091 创建顶级部门 ✅ pass
TC-0092 创建子部门 ✅ pass
TC-0093 父部门不存在 ✅ pass
TC-0094 不传DeptType默认NORMAL ✅ pass
TC-0095 传DeptType=DEV ✅ pass
TC-0096 事务内FindOneWithTx可见性 ✅ pass
TC-0097 事务回滚-Insert失败 ✅ pass
TC-0098 事务回滚-UpdateWithTx失败 ✅ pass
TC-0099 多层嵌套(5层) ✅ pass
TC-0100 通过Logic创建+验证Path ✅ pass
TC-1084 父部门已 Disabled → 事务内 FindOneForShareTx 复核拒绝 ✅ pass
TC-1085 父部门 Enabled 正向路径 + parentPath 来自事务内 snapshot ✅ pass
TC-1086 CreateDept × UpdateDept(禁用父) 并发 6 轮 ✅ pass
TC-1202 CreateDept Sort 超出 [-100000, 100000] 被拒绝(负向+边界正向)(L-R18-5) ✅ pass

2.9 部门更新/删除/树

TC编号 测试场景 测试结果
TC-0101 正常更新 ✅ pass
TC-0102 不存在 ✅ pass
TC-0103 DeptType NORMAL→DEV ✅ pass
TC-0104 DeptType无效值返回错误 ✅ pass
TC-0105 DeptType变更时级联清除子部门用户缓存 ✅ pass
TC-0106 正常删除(无子部门) ✅ pass
TC-0107 有子部门 ✅ pass
TC-0108 不存在的部门 ✅ pass
TC-0109 部门下有关联用户 ✅ pass
TC-0714 DeptType/Status 未变更时不清缓存 ✅ pass
TC-0715 乐观锁冲突返回 ErrConflict ✅ pass
TC-0759 10 goroutine 同时 UpdateWithOptLock 同一行 ✅ pass
TC-0766 删除有子部门的部门(FOR UPDATE 锁定读) ✅ pass
TC-0767 删除有关联用户的部门(FOR UPDATE 锁定读) ✅ pass
TC-0768 CreateDept 父部门 FOR SHARE 锁生效 ✅ pass
TC-0846 CleanByUserIds 批量清理多用户缓存 ✅ pass
TC-0847 CleanByUserIds 空 ids 切片 ✅ pass
TC-0848 UpdateDept 改 deptType 时调 CleanByUserIds ✅ pass
TC-0849 UpdateDeptFindIdsByDeptId 失败 ✅ pass
TC-1174 DeptType DEV→NORMAL 批量递增部门下所有成员 tokenVersion ✅ pass
TC-1175 DEV 部门 Status Enabled→Disabled 批量递增 tokenVersion ✅ pass
TC-1176 NORMAL 部门 Status Enabled→Disabled 批量递增 tokenVersion ✅ pass
TC-1177 NORMAL→DEV(升权方向)tokenVersion 保持不变 ✅ pass
TC-1178 Status Disabled→Enabled(恢复启用)tokenVersion 保持不变 ✅ pass
TC-1200 DeleteDept 成功提交后必须 post-commit 显式失效 sysDept:id 缓存(H-R17-2) ✅ pass
TC-1203 UpdateDept Sort 超出 [-100000, 100000] 被拒绝,DB 中 Sort 值不变(L-R18-5) ✅ pass

2.10 权限列表 POST /api/perm/list

TC编号 测试场景 测试结果
TC-0113 正常查询 ✅ pass
TC-0114 默认分页 ✅ pass
TC-0115 pageSize超过上限 ✅ pass
TC-0116 不存在的productCode ✅ pass

2.11 角色管理

TC编号 测试场景 测试结果
TC-0117 正常创建 ✅ pass
TC-0118 重复角色名 ✅ pass
TC-0119 并发同名创建 ✅ pass
TC-0120 正常更新 ✅ pass
TC-0121 不存在 ✅ pass
TC-0122 正常查询 ✅ pass
TC-0123 pageSize超过上限 ✅ pass
TC-0124 正常查询 ✅ pass
TC-0125 不存在 ✅ pass
TC-0730 非超管 admin 把 roleA.PermsLevel 从 100 调到 10(数字变小 = 提升权级) ✅ pass
TC-0731 产品 admin 保持或提升 PermsLevel ✅ pass
TC-0732 超管降低 PermsLevel ✅ pass
TC-0733 PermsLevel 越界(0/-1/1000/10000) ✅ pass
TC-0777 UpdateRole:UpdateWithOptLock 成功,FindUserIdsByRoleId 失败,handler 返回 nil(降级成功) ✅ pass
TC-0778 UpdateRole 乐观锁冲突 ✅ pass
TC-0779 UpdateProduct 乐观锁冲突 ✅ pass
TC-0780 UpdateMember 基于事务内 locked 数据更新 ✅ pass
TC-0858 BindRolePerms:事务 OK,FindUserIdsByRoleId 返回 err ✅ pass
TC-0859 UpdateRole:事务 OK,FindUserIdsByRoleId 返回 err ✅ pass
TC-1000 非超管访问别的产品的 role ✅ pass
TC-1001 "id 不存在" vs "跨产品" 响应对比 ✅ pass
TC-1002 超管跨产品访问 ✅ pass
TC-1119 L-R14-1:非超管 UpdateRole 别产品 roleId 必须 404 "角色不存在" ✅ pass
TC-1197 非超管 product ADMIN 不得创建 PermsLevel=1 顶格角色(H-R17-3) ✅ pass
TC-1198 非超管 product ADMIN 可创建 PermsLevel>=2 角色(正向回归) ✅ pass
TC-1199 SuperAdmin 仍可创建 PermsLevel=1 顶格角色(正向回归) ✅ pass
TC-1204 UpdateRole rename 后旧 name 索引缓存必须被 InvalidateRoleCache(prevName) 显式清除(H-R18-1) ✅ pass

2.12 删除角色 POST /api/role/delete

TC编号 测试场景 测试结果
TC-0126 正常删除+级联 ✅ pass
TC-0127 事务回滚 ✅ pass
TC-0128 无关联数据 ✅ pass
TC-1120 L-R14-1:非超管 DeleteRole 别产品 roleId 必须 404 "角色不存在" ✅ pass
TC-1201 DeleteRole 成功提交后必须 post-commit 同时失效 sysRole:idsysRole:productCode:name 两把缓存(H-R17-2) ✅ pass

2.13 绑定角色权限 POST /api/role/bindPerms

TC编号 测试场景 测试结果
TC-0129 正常绑定 ✅ pass
TC-0130 角色不存在 ✅ pass
TC-0131 清空权限 ✅ pass
TC-0132 重复permId ✅ pass
TC-0133 事务回滚 ✅ pass
TC-1024 BindRolePerms 事务首步调用 LockByIdTxsys_role 行,再走 FindPermIdsByRoleIdTx 读 diff 基准 ✅ pass
TC-1025 BindRolePerms post-commit cache 清理失败仍 Success ✅ pass
TC-1026 BindRoles 事务首步 FindOneForUpdateTx(memberId)sys_product_member 行,再走 FindRoleIdsByUserIdForProductTx ✅ pass
TC-1121 L-R14-1:非超管 BindRolePerms 别产品 roleId 必须 404 "角色不存在" ✅ pass

2.14 创建用户 POST /api/user/create

TC编号 测试场景 测试结果
TC-0134 正常创建 ✅ pass
TC-0135 用户名已存在(预检) ✅ pass
TC-0136 带完整可选字段 ✅ pass
TC-0137 非法email格式 ✅ pass
TC-0138 合法email ✅ pass
TC-0139 email为空(可选) ✅ pass
TC-0140 非法phone格式 ✅ pass
TC-0141 合法phone(国际) ✅ pass
TC-0142 phone为空(可选) ✅ pass
TC-0143 并发同username(TOCTOU) ✅ pass
TC-0144 唯一索引冲突消息 ✅ pass
TC-0150 用户名含特殊字符被拒绝 ✅ pass
TC-0151 用户名太短(1字符)被拒绝 ✅ pass
TC-0152 用户名太长(65字符)被拒绝 ✅ pass
TC-0153 部门不存在被拒绝 ✅ pass
TC-0154 昵称超过64字符被拒绝 ✅ pass
TC-0155 备注超过255字符被拒绝 ✅ pass
TC-0818 SuperAdmin 创建用户未显式指定 mustChangePassword ✅ pass
TC-0994 产品 ADMIN 为非自己管辖部门创建用户 ✅ pass
TC-0995 产品 ADMIN 在自己子树下创建用户 ✅ pass
TC-0996 SuperAdmin 在任意部门 / DeptId=0 均可创建 ✅ pass
TC-0997 caller.DeptPath=="" 的 legacy 产品 ADMIN ✅ pass
TC-0998 非超管 caller 传 DeptId=0 ✅ pass
TC-0999 目标部门 status=Disabled ✅ pass
TC-1100 负值 DeptId(-1 / MinInt64)必须 400 ✅ pass
TC-1122 H-R14-1:非超管把新用户建到 DEV 部门必须 403 ✅ pass
TC-1123 H-R14-1:SuperAdmin 可在 DEV 部门建用户(正向回归) ✅ pass
TC-1192 非超管以保留前缀(admin_ / svc_ / root_ / sys_)占名必须 400(L-R17-1) ✅ pass
TC-1193 SuperAdmin 以保留前缀创建用户放行(正向回归) ✅ pass
TC-1194 未传 avatar 时 sys_user.avatar 列在 DB 层必须为 SQL NULL(L-R17-2) ✅ pass
TC-1195 mock 断言 InsertWithTxTransactCtx 闭包内且顺序 FindOneForShareTx(dept)InsertWithTx(user)(H-R17-1) ✅ pass
TC-1196 mock 断言 FindOneForShareTxsqlx.ErrNotFound(并发 DeleteDept 胜出)时必须 400 "部门不存在或已删除",InsertWithTx 绝不得被调用(H-R17-1) ✅ pass
TC-1280 正常创建用户返回 ticket(无 Password 字段) ✅ pass
TC-1282 生成密码满足强度要求 ✅ pass
TC-1284 创建后 mustChangePassword=1 ✅ pass

2.15 重置密码 POST /api/user/resetPassword

TC编号 测试场景 测试结果
TC-1285 超管重置普通用户密码 ✅ pass
TC-1287 不能重置超管密码 ✅ pass
TC-1288 MEMBER 无权重置 ✅ pass
TC-1289 重置后旧 token 失效(tokenVersion 递增) ✅ pass
TC-1290 重置后 mustChangePassword=1 ✅ pass
TC-1291 目标用户不存在 ✅ pass
TC-1293 重置后用新密码可验证 ✅ pass

2.16 消费凭证票据 POST /api/user/fetchCredentials

TC编号 测试场景 测试结果
TC-1294 正常消费 ticket ✅ pass
TC-1295 二次消费同一 ticket ✅ pass
TC-1296 空 ticket ✅ pass
TC-1297 非法 ticket ✅ pass
TC-1298 非 ADMIN 无权消费 ✅ pass
TC-1299 并发消费同一 ticket 原子性 ✅ pass

2.17 用户更新 POST /api/user/update (指针类型+DeptId可清零)

TC编号 测试场景 测试结果
TC-0156 正常更新 ✅ pass
TC-0157 不存在 ✅ pass
TC-0158 仅传id ✅ pass
TC-0159 清空nickname ✅ pass
TC-0160 清空email ✅ pass
TC-0161 清空remark ✅ pass
TC-0162 非法email格式 ✅ pass
TC-0163 非法phone格式 ✅ pass
TC-0164 合法phone ✅ pass
TC-0165 不传email(nil) ✅ pass
TC-0166 DeptId设为0(取消部门) ✅ pass
TC-0167 DeptId设为正值 ✅ pass
TC-0168 DeptId不传(nil) ✅ pass
TC-0169 超管不能冻结另一超管 ✅ pass
TC-0170 updateUser-产品管理员可管理范围内用户 ✅ pass
TC-0171 updateUser-昵称超长拒绝 ✅ pass
TC-0172 updateUser-部门不存在 ✅ pass
TC-0173 updateUser 修改状态时递增 tokenVersion ✅ pass
TC-0174 updateUser 仅改 profile 不递增 tokenVersion ✅ pass
TC-0175 updateUser 乐观锁冲突 -> 409 ✅ pass
TC-0746 DEVELOPER 把成员挪出自己子树 ✅ pass
TC-0747 DEVELOPER 在自己子树内移动成员 ✅ pass
TC-0748 产品 ADMIN 挪目标到子树外必须 403 ✅ pass
TC-0814 DEVELOPER 将他人 deptId 置 0 ✅ pass
TC-0815 MEMBER 将他人 deptId 置 0 ✅ pass
TC-0816 产品 ADMIN 将他人 deptId 置 0 应 403 "仅超级管理员可将用户移出部门" ✅ pass
TC-0817 SuperAdmin 将他人 deptId 置 0 ✅ pass
TC-1049 deptId 切换场景下并发 DeleteDept 被"S 锁 / X 锁"串行化 ✅ pass
TC-1050 非事务路径:deptId 未变的 UpdateUser 不触发 FindOneForShareTx 的 S 锁路径 ✅ pass
TC-1101 负值 *req.DeptId(-1 / MinInt64)必须 400 且不得落盘 ✅ pass
TC-1124 H-R14-1:ADMIN 调入 DEV 部门必须 403 ✅ pass
TC-1125 H-R14-1:SuperAdmin 仍可调入 DEV 部门(正向回归) ✅ pass
TC-1126 产品 ADMIN 在自己子树内挪目标(非 DEV)放行 ✅ pass
TC-1170 跨产品 P1 ADMIN 不得把共有 target 挪到 P1 子树之外 ✅ pass
TC-1171 target 从 DEV+Enabled 部门 → NORMAL 部门:tokenVersion +1 ✅ pass
TC-1172 target 从 DEV+Enabled 部门 → deptId=0:tokenVersion +1 ✅ pass
TC-1173 NORMAL→NORMAL 不递增 tokenVersion(正向回归) ✅ pass

2.18 用户列表/详情/状态 及其他用户操作

TC编号 测试场景 测试结果
TC-0176 含productCode ✅ pass
TC-0177 不含productCode ✅ pass
TC-0178 pageSize超过上限 ✅ pass
TC-0179 用户不在产品中 ✅ pass
TC-0180 批量查询DB异常 ✅ pass
TC-1267 userDetail 超管不传 productCode → roleIds 含全量角色(跨产品) ✅ pass
TC-1268 userDetail 超管传 productCode → roleIds 只含该产品角色 ✅ pass
TC-0182 正常查询-含Avatar ✅ pass
TC-0183 不存在 ✅ pass
TC-1269 userDetail 非超管不传 productCode → roleIds 只含 JWT context 产品角色 ✅ pass
TC-0184 正常绑定 ✅ pass
TC-0185 用户不存在 ✅ pass
TC-0186 清空角色 ✅ pass
TC-0187 事务回滚 ✅ pass
TC-0188 角色不属于当前产品 ✅ pass
TC-0189 角色已禁用 ✅ pass
TC-0190 角色不存在 ✅ pass
TC-0191 非产品成员绑定角色被拒绝 ✅ pass
TC-0192 正常ALLOW ✅ pass
TC-0193 用户不存在 ✅ pass
TC-0194 DENY权限 ✅ pass
TC-0195 清空权限 ✅ pass
TC-0196 无效Effect值 ✅ pass
TC-0197 PermId不存在 ✅ pass
TC-0198 权限不属于当前产品 ✅ pass
TC-0199 非产品成员设置权限被拒绝 ✅ pass
TC-0200 正常冻结 + tokenVersion +1(会话立即失效) ✅ pass
TC-0201 正常解冻 + tokenVersion +1(L-R17-6:无条件吊销,堵"UD 缓存残留→旧 token 复活") ✅ pass
TC-0202 非法status(0) ✅ pass
TC-0203 冻结自己 ✅ pass
TC-0204 冻结超管 ✅ pass
TC-0205 userList-非超管仅可见产品成员 ✅ pass
TC-0206 userList-非超管未指定productCode被拒绝 ✅ pass
TC-0207 userList-非超管使用错误productCode被拒绝 ✅ pass
TC-0208 bindRoles-permsLevel越权拒绝 ✅ pass
TC-0209 bindRoles-超管可分配任意级别角色 ✅ pass
TC-0210 同一权限ID冲突Effect被拒绝 ✅ pass
TC-0211 重复权限ID相同Effect去重 ✅ pass
TC-0212 已禁用权限不能被设置 ✅ pass
TC-0711 ADMIN 调用者豁免 permsLevel 校验 ✅ pass
TC-0712 DEVELOPER 调用者豁免 permsLevel 校验 ✅ pass
TC-0713 MinPermsLevel=MaxInt64 的 MEMBER 不被误阻断 ✅ pass
TC-0734 产品已禁用 ✅ pass
TC-0735 产品不存在 ✅ pass
TC-0743 普通 MEMBER 给自己授权 ✅ pass
TC-0744 DEVELOPER 调用者(非 ADMIN)操作他人 ✅ pass
TC-0745 同产品 ADMIN 操作合法 MEMBER 目标 ✅ pass
TC-0772 状态无实际变化(已启用→再启用) ✅ pass
TC-0773 状态实际变化(启用→冻结) ✅ pass
TC-0786 SetUserPerms 调用后 req.Perms 不变 ✅ pass
TC-0787 BindRoles 调用后 req.RoleIds 不变 ✅ pass
TC-0788 BindRolePerms 调用后 req.PermIds 不变 ✅ pass
TC-0813 MEMBER 调用者给他人赋予 "与自己 permsLevel 相同" 的角色 ✅ pass
TC-0988 FindByIds 前置校验通过(装饰器撒谎 status=1)但 DB 实际 status=2 ✅ pass
TC-0989 全部真实 Enabled 的正向基线 ✅ pass
TC-0991 自看原样返回 Email/Phone/Remark(业务契约回归守卫) ✅ pass
TC-0992 SuperAdmin 看任何人原样返回 Email/Phone/Remark(业务契约回归守卫) ✅ pass
TC-1011 他人先冻结后本轮解冻 ✅ pass
TC-1012 Logic 层错误映射 ✅ pass
TC-1027 登录时用户在 productCode 下非成员 ✅ pass
TC-1028 登录时用户成员资格 Status=Disabled ✅ pass
TC-1078 BindRoles 与 DeleteRole 并发 6 轮(统一文案为"包含无效的角色ID"后稳定) ✅ pass
TC-1102 BindRoles 非超管 + 空 MemberType + 不存在 userId → 403 "缺少产品成员上下文" ✅ pass
TC-1103 BindRoles 超管不传 productCode → 400 "必须指定产品编码"(前置校验) ✅ pass
TC-1265 BindRoles 非超管传入 req.ProductCode 指向其他产品 → 字段被忽略,仍按 JWT context 产品操作 ✅ pass
TC-1266 BindRoles 非超管不传 req.ProductCode → 使用 JWT context productCode 正常绑定 ✅ pass
TC-1104 SetUserPerms 非 ADMIN caller + 不存在 userId → 403(阻断 userId 枚举) ✅ pass
TC-1105 SetUserPerms DENY TOCTOU:事务内读到 ADMIN → 400 + 事务回滚(无 DENY 脏行) ✅ pass
TC-1106 SetUserPerms 纯 ALLOW 必须短路、不调 FindOneForShareTx(S 锁开销不扩散) ✅ pass
TC-1127 L-R14-2:BindRoles 跨产品 / 已禁用 / 不存在三路径文案必须统一为"包含无效的角色ID" ✅ pass
TC-1164 产品 ADMIN 看同产品他人 UserDetail:返回完整 Email/Phone/Remark ✅ pass
TC-1165 产品 DEVELOPER 看同产品他人 UserDetail:返回完整 Email/Phone/Remark ✅ pass
TC-1166 产品 MEMBER 看同产品他人 UserDetail:Email/Phone/Remark 必须为空字符串 ✅ pass
TC-1167 产品 ADMIN 列表视角:全部成员 PII 原值返回 ✅ pass
TC-1168 产品 DEVELOPER 列表视角:全部成员 PII 原值返回 ✅ pass
TC-1169 产品 MEMBER 列表视角:其他成员 Email/Phone/Remark 必须为空字符串 ✅ pass

2.19 获取用户权限覆盖 POST /api/user/userPerms

TC编号 测试场景 测试结果
TC-1257 超管查任意用户权限覆盖 ✅ pass
TC-1258 用户查询自己的权限覆盖(ALLOW + DENY 各 1 条) ✅ pass
TC-1259 产品 ADMIN 查同产品 MEMBER 的权限覆盖 ✅ pass
TC-1260 产品 ADMIN 查同级 ADMIN 被拒绝(等级校验) ✅ pass
TC-1261 普通 MEMBER 查他人被拒绝(RequireProductAdminFor 前置) ✅ pass
TC-1262 非 ADMIN 查不存在 userId 必须 403(枚举防护) ✅ pass
TC-1263 ADMIN 查不是当前产品成员的用户被拒绝 ✅ pass
TC-1264 nil UserDetails(模拟无 JWT)返回 401 ✅ pass

2.20 成员管理

TC编号 测试场景 测试结果
TC-0213 正常添加 ✅ pass
TC-0214 产品不存在 ✅ pass
TC-0215 用户不存在 ✅ pass
TC-0216 已是成员 ✅ pass
TC-0217 并发添加 ✅ pass
TC-0218 无效MemberType ✅ pass
TC-0219 正常更新 ✅ pass
TC-0220 不存在 ✅ pass
TC-0221 无效MemberType ✅ pass
TC-0222 正常查询(批量查用户) ✅ pass
TC-0223 成员用户已删除 ✅ pass
TC-0224 pageSize超过上限 ✅ pass
TC-0225 空成员列表 ✅ pass
TC-0226 正常移除+级联(事务内) ✅ pass
TC-0227 跨产品隔离 ✅ pass
TC-0228 成员不存在 ✅ pass
TC-0229 事务回滚 ✅ pass
TC-0723 移除产品唯一 ADMIN ✅ pass
TC-0724 有 2 个 ADMIN 时移除其一 ✅ pass
TC-0725 降级产品唯一 ADMIN 为 MEMBER ✅ pass
TC-0726 有 2 个 ADMIN 时降级其一 ✅ pass
TC-0727 2 个 ADMIN 但只有 1 个启用,降级该启用 ADMIN ✅ pass
TC-0728 移除非 ADMIN(MEMBER) ✅ pass
TC-0729 对禁用产品 addMember ✅ pass
TC-0760 保持 memberType=ADMIN 但 status 改为 Disabled ✅ pass
TC-0761 同时降级 + 禁用唯一 ADMIN ✅ pass
TC-0762 有 2 个 ADMIN 时禁用其一 ✅ pass
TC-0763 移除活跃 ADMIN(事务内用 locked 数据判断) ✅ pass
TC-0764 移除非 ADMIN 不触发 last-admin 校验 ✅ pass
TC-0789 caller.MemberType="" 调用 CheckMemberTypeAssignment ✅ pass
TC-0940 Product ADMIN 拉跨部门树的人 ✅ pass
TC-0941 Product ADMIN 在自己部门树内 ✅ pass
TC-0942 SuperAdmin 跨一切部门 ✅ pass
TC-0943 自己加自己 ✅ pass
TC-0944 caller 没有部门(DeptId=0 或 DeptPath="") ✅ pass
TC-0945 target 无部门(DeptId=0) ✅ pass
TC-0946 ctx 无 caller ✅ pass
TC-0947 dept.FindOne 报错 ✅ pass
TC-0948 target 为 nil ✅ pass
TC-0949 AddMember 集成:跨部门被拒 ✅ pass
TC-0950 AddMember 集成:target=SuperAdmin ✅ pass
TC-1055 MemberType==nil && Status==nil → 400 "请至少提供一个要更新的字段" ✅ pass
TC-1056 只传 StatusMemberType 保持不变 ✅ pass
TC-1057 只传 MemberTypeStatus 保持不变 ✅ pass
TC-1058 DEVELOPER → 只改 Status 时跳过"分配校验" ✅ pass
TC-1059 非法 Status 值(例如 7)→ 400 ✅ pass
TC-1060 完全 no-op(传进来的值与 DB 现值相同)→ 返 nil 且 updateTime 不前进 ✅ pass
TC-1107 addMember 非 ADMIN caller + 不存在 productCode → 403(阻断 productCode 枚举) ✅ pass
TC-1108 addMember 非 ADMIN caller + 非法 MemberType → 403(权限优先于字面校验) ✅ pass
TC-1109 addMember 超管 + 非法 MemberType → 400(正向回归,权限闸没误伤字面 400) ✅ pass
TC-1130 降级 ADMIN→MEMBER 同事务递增 sys_user.tokenVersion ✅ pass
TC-1131 禁用启用成员(Status 1→2)时 tokenVersion+1 ✅ pass
TC-1132 降级 DEVELOPER→MEMBER 时 tokenVersion+1(privileged 并集) ✅ pass
TC-1133 升权 MEMBER→ADMIN 递增 tokenVersion ✅ pass
TC-1134 重启用(Disabled→Enabled)tokenVersion 不变 ✅ pass
TC-1135 降级最后一个 ADMIN 被拒时 tokenVersion 未被污染(事务 rollback 原子性) ✅ pass
TC-1136 no-op 更新(与现值一致)不进入事务,tokenVersion 不变 ✅ pass
TC-1137 降级后 post-commit 失效 sysUser id/username 两把低层缓存 ✅ pass
TC-1162 移除成员后被移除用户 sys_user.tokenVersion 必须 +1 ✅ pass
TC-1163 移除失败(last-admin 场景)时 tokenVersion 绝不得 +1 ✅ pass
TC-1270 userProducts 超管查询他人产品列表(集成) ✅ pass
TC-1271 userProducts 本人查询自己的产品列表(集成) ✅ pass
TC-1272 userProducts 非超管查询他人 → 403(集成) ✅ pass
TC-1273 userProducts 用户未加入任何产品 → 空列表(集成) ✅ pass
TC-1274 userProducts 超管查询多产品(mock) ✅ pass
TC-1275 userProducts 本人查询自己(mock) ✅ pass
TC-1276 userProducts 非超管查他人 → 403(mock) ✅ pass
TC-1277 userProducts DB 错误透传(mock) ✅ pass
TC-1278 userProducts 产品查询失败跳过(mock) ✅ pass
TC-1279 userProducts 空列表(mock) ✅ pass

2.21 获取验证码 POST /api/captcha/get

TC编号 测试场景 测试结果
TC-1208 正常获取(默认宽高) ✅ pass
TC-1209 自定义宽高 ✅ pass
TC-1210 宽高为 0 或负数,退化为默认值 ✅ pass
TC-1252 VerifyCaptcha 正确码消费后不可重用 ✅ pass
TC-1253 VerifyCaptcha 错误码不消费 ✅ pass
TC-1254 VerifyCaptcha 不存在的 id ✅ pass

2.22 Cap.js 端点 POST /api/capjs/endpoint

TC编号 测试场景 测试结果
TC-1211 cap.js 已启用 ✅ pass
TC-1212 cap.js 未启用 ✅ pass
TC-1255 cap.js 启用但 EndpointURL 为空 ✅ pass
TC-1256 cap.js 启用但 Key 为空 ✅ pass

2.23 产品端 Cap.js 登录 POST /api/auth/login/cap

TC编号 测试场景 测试结果
TC-1219 cap.js 未启用时调用 ✅ pass
TC-1220 capToken 为空 ✅ pass
TC-1221 capToken 无效(远端校验失败) ✅ pass
TC-1222 capToken 有效 + 正常登录 ✅ pass
TC-1223 capToken 有效 + 密码错误 ✅ pass
TC-1224 capToken 有效 + 超管被拒绝 ✅ pass

2.24 管理后台 Cap.js 登录 POST /api/auth/adminLogin/cap

TC编号 测试场景 测试结果
TC-1225 cap.js 未启用时调用 ✅ pass
TC-1226 capToken 为空 ✅ pass
TC-1227 capToken 有效 + managementKey 无效 ✅ pass
TC-1228 capToken 有效 + 超管正常登录 ✅ pass
TC-1229 capToken 有效 + 非超管被拒绝 ✅ pass

2.25 更新用户信息 POST /api/auth/updateInfo

TC编号 测试场景 测试结果
TC-1230 未登录 ✅ pass
TC-1231 所有字段为 nil ✅ pass
TC-1232 正常更新 nickname ✅ pass
TC-1233 正常更新 avatar ✅ pass
TC-1234 正常更新 email + phone ✅ pass
TC-1235 nickname 超过 64 字符 ✅ pass
TC-1236 avatar 超过 255 字符 ✅ pass
TC-1237 email 超过 64 字符 ✅ pass
TC-1238 phone 超过 32 字符 ✅ pass
TC-1239 并发更新冲突 ✅ pass
TC-1240 更新后 UserDetails 缓存失效 ✅ pass

2.26 MinIO 文件上传 POST /api/minio/upload

TC编号 测试场景 测试结果
TC-1242 fileType 为空 ✅ pass
TC-1243 fileType 不在配置中 ✅ pass
TC-1244 Content-Type 不在白名单中 ✅ pass
TC-1248 parseDir 模板替换 {yyyy}/{mm}/{dd} ✅ pass
TC-1249 handler 缺少 file 字段 ✅ pass

三、gRPC 接口测试用例

3.1 gRPC SyncPermissions

TC编号 测试场景 测试结果
TC-0230 正常同步 ✅ pass
TC-0231 appKey无效 ✅ pass
TC-0232 appSecret错误 ✅ pass
TC-0233 产品已禁用 ✅ pass
TC-0234 验证disabled计数 ✅ pass

3.2 gRPC Login / RefreshToken / VerifyToken / GetUserPerms

TC编号 测试场景 测试结果
TC-0235 正常登录(普通用户+productCode) ✅ pass
TC-0236 用户不存在 ✅ pass
TC-0237 密码错误 ✅ pass
TC-0238 账号冻结 ✅ pass
TC-0239 超管被拒绝 ✅ pass
TC-0240 普通用户+productCode ✅ pass
TC-0241 产品成员被禁用时拒绝登录 ✅ pass
TC-0242 productCode为空 ✅ pass
TC-0243 正常刷新 ✅ pass
TC-0244 token无效 ✅ pass
TC-0245 账号冻结 ✅ pass
TC-0246 productCode回退到claims ✅ pass
TC-0247 超管+productCode ✅ pass
TC-0248 普通用户+productCode ✅ pass
TC-0249 有效token ✅ pass
TC-0250 无效token ✅ pass
TC-0251 缺少userId ✅ pass
TC-0252 冻结用户token返回Invalid ✅ pass
TC-0253 非成员token返回Invalid ✅ pass
TC-0254 返回实时MemberType和Perms ✅ pass
TC-0255 用户不存在(需先通过AppKey/Secret认证) ✅ pass
TC-0256 超管(需先通过AppKey/Secret认证) ✅ pass
TC-0257 MEMBER-DENY覆盖(需先通过AppKey/Secret认证) ✅ pass
TC-0700 冻结用户 (Status=Disabled) ✅ pass
TC-0701 非产品成员 ✅ pass
TC-0702 DEV 部门但产品成员被禁用 ✅ pass
TC-0703 启用 ADMIN 成员(正向回归) ✅ pass
TC-0704 Loader 层:DEV 部门 + 产品成员禁用 ✅ pass
TC-0782 VerifyToken 各失败分支记录日志 ✅ pass
TC-0783 peer.FromContext 失败时仍限流 ✅ pass
TC-0794 VerifyToken 对任意畸形 AccessToken ✅ pass
TC-0795 GetUserPerms 任意 (appKey,appSecret,productCode,userId) ✅ pass
TC-0828 同 IP 两次 gRPC RefreshToken,quota=1 ✅ pass
TC-0829 同 IP 两次 gRPC VerifyToken,quota=1 ✅ pass
TC-0830 extractClientIP 对 "host:port" 剥离 ✅ pass
TC-0831 gRPC refresh 成功后重放旧 rt(换端口) ✅ pass
TC-1036 GetUserPerms 查询 userId 全局不存在 ✅ pass
TC-1037 GetUserPerms 查询 userId 存在但非成员 ✅ pass
TC-1038 GetUserPerms 查询成员 Status=Disabled ✅ pass
TC-1051 SyncPermissions 同 appKey 连打 quota+1 次触发 ResourceExhausted ✅ pass
TC-1052 GetUserPerms 同 appKey 连打 quota+1 次触发 ResourceExhausted ✅ pass
TC-1053 AppKey 不消耗 limiter 配额 ✅ pass
TC-1054 GetUserPerms 同一 IP、多个不同 appKey → 命中 IP 桶 ✅ pass

四、JWT中间件 / 统一响应测试用例

TC编号 测试场景 测试结果
TC-0258 正常Bearer token ✅ pass
TC-0259 无Authorization头 ✅ pass
TC-0260 无Bearer前缀 ✅ pass
TC-0261 token签名错误 ✅ pass
TC-0262 token过期 ✅ pass
TC-0263 claims类型断言失败 ⏭️ skip
TC-0264 refresh token被拒绝 ✅ pass
TC-0265 业务错误(CodeError) ✅ pass
TC-0266 内部错误 ✅ pass
TC-0267 成功(有data) ✅ pass
TC-0268 成功(无data) ✅ pass
TC-0716 access token payload 中不得含 perms ✅ pass
TC-0749 token.tokenVersion != DB & 产品被禁用 ✅ pass
TC-0750 token.tokenVersion == DB,产品被禁用 ✅ pass
TC-0754 用户已被删除 + TokenVersion 失配 ✅ pass
TC-0755 账号冻结 + TokenVersion 失配 + 产品禁用 ✅ pass
TC-0756 TokenVersion OK + 产品启用 + 非超管 + MemberType="" ✅ pass
TC-0757 SuperAdmin + ProductCode + MemberType="" ✅ pass
TC-0758 Frozen 用户 + TokenVersion 失配(无 ProductCode) ✅ pass
TC-0951 合法 HS256 + 正确 secret ✅ pass
TC-0952 alg=none 伪造 ✅ pass
TC-0953 alg=RS256 但用 HS secret 签名 ✅ pass
TC-0954 alg=ES256 header ✅ pass
TC-0955 HS256 但 secret 错 ✅ pass
TC-0956 ParseRefreshToken 复用 ParseWithHMAC ✅ pass
TC-0957 乱码 token ✅ pass
TC-0958 ParseRefreshToken 的 tokenType 校验 ✅ pass
TC-0959 合法 HS256 + 非标 typ header ✅ pass
TC-0960 回放过期 token ✅ pass
TC-1003 合法 HS256 token ✅ pass
TC-1004 alg=RS256(公钥→HMAC 混淆)伪造 ✅ pass
TC-1005 alg=none 伪造 ✅ pass
TC-1006 HS256 但错误 secret ✅ pass
TC-1007 任意 jwt.Claims 结构体 ✅ pass

五、util 层测试用例

5.1 NormalizePage

TC编号 测试场景 测试结果
TC-0269 正常值 ✅ pass
TC-0270 page<=0 ✅ pass
TC-0271 page=-1 ✅ pass
TC-0272 pageSize<=0 ✅ pass
TC-0273 pageSize>100 ✅ pass
TC-0274 pageSize=100 ✅ pass
TC-0275 pageSize=101 ✅ pass
TC-0276 双零 ✅ pass

5.2 IsValidEmail

TC编号 测试场景 测试结果
TC-0277 正常邮箱 ✅ pass
TC-0278 含点号 ✅ pass
TC-0279 含加号 ✅ pass
TC-0280 缺少@ ✅ pass
TC-0281 缺少域名 ✅ pass
TC-0282 缺少TLD ✅ pass
TC-0283 空字符串 ✅ pass

5.3 IsValidPhone

TC编号 测试场景 测试结果
TC-0284 国内手机号 ✅ pass
TC-0285 带+国际码 ✅ pass
TC-0286 太短(6位) ✅ pass
TC-0287 恰好7位 ✅ pass
TC-0288 最长15位 ✅ pass
TC-0289 超长16位 ✅ pass
TC-0290 包含字母 ✅ pass
TC-0291 空字符串 ✅ pass

六、Logic 层单元测试用例

6.1 auth/jwt.go — GenerateAccessToken

TC编号 测试场景 测试结果
TC-0292 正常生成 ✅ pass
TC-0293 解析token验证claims ✅ pass
TC-0294 空secret ✅ pass
TC-0295 空perms ✅ pass
TC-0296 过期时间验证 ✅ pass

6.2 auth/jwt.go — GenerateRefreshToken

TC编号 测试场景 测试结果
TC-0297 正常生成 ✅ pass
TC-0298 解析验证 ✅ pass
TC-0299 productCode为空 ✅ pass

6.3 auth/jwt.go — ParseRefreshToken

TC编号 测试场景 测试结果
TC-0300 正常解析 ✅ pass
TC-0301 错误secret ✅ pass
TC-0302 无效token字符串 ✅ pass
TC-0303 空token ✅ pass
TC-0304 过期token ✅ pass
TC-0305 AccessToken误用 ✅ pass

6.4 middleware — 辅助函数单元测试

TC编号 测试场景 测试结果
TC-0306 GetUserId-正常 ✅ pass
TC-0307 GetUserId-空ctx ✅ pass
TC-0308 GetProductCode-正常 ✅ pass
TC-0309 GetUserDetails 返回完整字段 ✅ pass

七、Model 层 _gen.go 模板生成方法测试用例

7.1 通用 CRUD 方法 (每个 Model 均需测试)

TC编号 测试场景 测试结果
TC-0310 Insert — 正常插入 ✅ pass
TC-0311 Insert — 正常插入含TokenVersion ✅ pass
TC-0312 Insert — 唯一索引冲突 ✅ pass
TC-0313 Insert — 缓存key生成正确 ✅ pass
TC-0314 InsertWithTx — 事务内插入 ✅ pass
TC-0315 InsertWithTx — 事务内插入含TokenVersion ✅ pass
TC-0316 InsertWithTx — 事务回滚后无数据 ✅ pass
TC-0317 FindOne — 正常查询(缓存未命中) ✅ pass
TC-0318 FindOne — 正常查询(缓存命中) ✅ pass
TC-0319 FindOne — 记录不存在 ✅ pass
TC-0320 FindOne — DB异常(非ErrNotFound) ✅ pass
TC-0321 FindOneWithTx — 事务内正常查询 ✅ pass
TC-0322 FindOneWithTx — 事务内记录不存在 ✅ pass
TC-0323 FindOneWithTx — 事务内可见性 ✅ pass
TC-0324 Update — 正常更新 ✅ pass
TC-0325 Update — 正常更新含TokenVersion ✅ pass
TC-0326 Update — 记录不存在 ✅ pass
TC-0327 UpdateWithTx — 事务内更新 ✅ pass
TC-0328 Delete — 正常删除 ✅ pass
TC-0329 Delete — 记录不存在 ✅ pass
TC-0330 DeleteWithTx — 事务内删除 ✅ pass
TC-0331 TransactCtx — 正常事务 ✅ pass
TC-0332 TransactCtx — fn返回错误 ✅ pass
TC-0333 TableName — 获取表名 ✅ pass

7.2 批量插入方法

TC编号 测试场景 测试结果
TC-0334 BatchInsert — 空列表 ✅ pass
TC-0335 BatchInsert — 单条记录 ✅ pass
TC-0336 BatchInsert — 多条记录(3条) ✅ pass
TC-0337 BatchInsert — 批量插入含TokenVersion ✅ pass
TC-0338 BatchInsert — 唯一索引冲突 ✅ pass
TC-0339 BatchInsert — 大批量(1000条) ✅ pass
TC-0340 BatchInsertWithTx — 空列表 ✅ pass
TC-0341 BatchInsertWithTx — 正常多条 ✅ pass
TC-0342 BatchInsertWithTx — 事务回滚 ✅ pass

7.3 批量更新方法

TC编号 测试场景 测试结果
TC-0343 BatchUpdate — 空列表 ✅ pass
TC-0344 BatchUpdate — 单条记录 ✅ pass
TC-0345 BatchUpdate — 多条记录(3条) ✅ pass
TC-0346 BatchUpdate — 批量更新不污染数据 ✅ pass
TC-0347 BatchUpdate — 部分id不存在 ✅ pass
TC-0348 BatchUpdateWithTx — 空列表 ✅ pass
TC-0349 BatchUpdateWithTx — 正常多条 ✅ pass
TC-0350 buildBatchUpdateQuery — 单条 ✅ pass
TC-0351 buildBatchUpdateQuery — 多条 ✅ pass
TC-0352 buildBatchUpdateQuery — vals数量正确 ✅ pass

7.4 批量删除方法

TC编号 测试场景 测试结果
TC-0353 BatchDelete — 空ids ✅ pass
TC-0354 BatchDelete — 单个id ✅ pass
TC-0355 BatchDelete — 多个id(3个) ✅ pass
TC-0356 BatchDelete — 包含不存在id ✅ pass
TC-0357 BatchDeleteWithTx — 空ids ✅ pass
TC-0358 BatchDeleteWithTx — 正常多条 ✅ pass

7.5 唯一索引查询方法 (按 Model 差异)

TC编号 测试场景 测试结果
TC-0359 FindOneByUsername — 正常查询 ✅ pass
TC-0360 FindOneByUsername — 不存在 ✅ pass
TC-0361 FindOneByUsernameWithTx — 事务内正常查询 ✅ pass
TC-0362 FindOneByUsernameWithTx — 事务内不存在 ✅ pass
TC-0363 FindOneByAppKey — 正常查询 ✅ pass
TC-0364 FindOneByAppKey — 不存在 ✅ pass
TC-0365 FindOneByAppKeyWithTx — 事务内正常查询 ✅ pass
TC-0366 FindOneByAppKeyWithTx — 事务内不存在 ✅ pass
TC-0367 FindOneByCode — 正常查询 ✅ pass
TC-0368 FindOneByCode — 不存在 ✅ pass
TC-0369 FindOneByCodeWithTx — 事务内正常查询 ✅ pass
TC-0370 FindOneByCodeWithTx — 事务内不存在 ✅ pass
TC-0371 FindOneByProductCodeCode — 正常查询 ✅ pass
TC-0372 FindOneByProductCodeCode — 不存在 ✅ pass
TC-0373 FindOneByProductCodeCodeWithTx — 事务内正常查询 ✅ pass
TC-0374 FindOneByProductCodeCodeWithTx — 事务内不存在 ✅ pass
TC-0375 FindOneByProductCodeName — 正常查询 ✅ pass
TC-0376 FindOneByProductCodeName — 不存在 ✅ pass
TC-0377 FindOneByProductCodeNameWithTx — 事务内正常查询 ✅ pass
TC-0378 FindOneByProductCodeNameWithTx — 事务内不存在 ✅ pass
TC-0379 FindOneByRoleIdPermId — 正常查询 ✅ pass
TC-0380 FindOneByRoleIdPermId — 不存在 ✅ pass
TC-0381 FindOneByRoleIdPermIdWithTx — 事务内正常查询 ✅ pass
TC-0382 FindOneByRoleIdPermIdWithTx — 事务内不存在 ✅ pass
TC-0383 FindOneByUserIdPermId — 正常查询 ✅ pass
TC-0384 FindOneByUserIdPermId — 不存在 ✅ pass
TC-0385 FindOneByUserIdPermIdWithTx — 事务内正常查询 ✅ pass
TC-0386 FindOneByUserIdPermIdWithTx — 事务内不存在 ✅ pass
TC-0387 FindOneByUserIdRoleId — 正常查询 ✅ pass
TC-0388 FindOneByUserIdRoleId — 不存在 ✅ pass
TC-0389 FindOneByUserIdRoleIdWithTx — 事务内正常查询 ✅ pass
TC-0390 FindOneByUserIdRoleIdWithTx — 事务内不存在 ✅ pass
TC-0391 FindOneByProductCodeUserId — 正常查询 ✅ pass
TC-0392 FindOneByProductCodeUserId — 不存在 ✅ pass
TC-0393 FindOneByProductCodeUserIdWithTx — 事务内正常查询 ✅ pass
TC-0394 FindOneByProductCodeUserIdWithTx — 事务内不存在 ✅ pass

7.6 内部辅助方法

TC编号 测试场景 测试结果
TC-0395 findListByPrimaryKeys — 空ids ✅ pass
TC-0396 findListByPrimaryKeys — 正常ids ✅ pass
TC-0397 findListByPrimaryKeys — 部分不存在 ✅ pass
TC-0398 findListByPrimaryKeys — DB异常 ✅ pass
TC-0399 getPrimaryKeyValue — 正常 ✅ pass
TC-0400 formatPrimary — 正常 ✅ pass
TC-0401 queryPrimary — 正常 ✅ pass

7.7 缓存key与前缀初始化

TC编号 测试场景 测试结果
TC-0402 cachePrefix为空 ✅ pass
TC-0403 cachePrefix非空 ✅ pass
TC-0404 多唯一索引前缀(SysProduct) ✅ pass

八、Model 层自定义方法测试用例

8.1 SysUserModel

TC编号 测试场景 测试结果
TC-0405 FindListByPage — 正常分页 ✅ pass
TC-0406 FindListByPage — 第二页 ✅ pass
TC-0407 FindListByPage — 空表 ✅ pass
TC-0408 FindListByPage — count查询失败 ✅ pass
TC-0409 FindListByPage — list查询失败 ✅ pass
TC-0410 FindListByProductMembers — 正常查询 ✅ pass
TC-0411 FindListByProductMembers — productCode不存在 ✅ pass
TC-0412 FindByIds — 正常批量查询 ✅ pass
TC-0413 FindByIds — 空ids ✅ pass
TC-0414 FindByIds — 部分id不存在 ✅ pass
TC-0415 FindByIds — DB异常 ✅ pass
TC-0416 FindIdsByDeptId — 有用户的部门 ✅ pass
TC-0417 FindIdsByDeptId — 无用户部门 ✅ pass
TC-0418 UpdateProfile — 状态未变-不递增tokenVersion ✅ pass
TC-0419 UpdateProfile — 状态变更-tokenVersion+1 ✅ pass
TC-0420 UpdateProfile — 乐观锁冲突 ✅ pass
TC-0421 UpdateProfile — 并发场景 ✅ pass
TC-0422 UpdateProfile — userId不存在 ✅ pass
TC-0736 SysUserModel — 单次 Increment 返回值 == DB 持久值 ✅ pass
TC-0737 SysUserModel — Increment 后缓存必须被主动清理 ✅ pass
TC-0738 SysUserModel — 10 goroutine 并发自增同一用户 ✅ pass
TC-0784 SysUserModel — MySQL 1062 错误正确识别 ✅ pass
TC-0785 SysUserModel — 非 1062 错误不误判 ✅ pass
TC-0802 SysUserModel — expected == DB.tokenVersion ✅ pass
TC-0803 SysUserModel — expected != DB.tokenVersion ✅ pass
TC-0805 SysUserModel — 8 goroutine 同时 CAS 同 expected ✅ pass
TC-0806 SysUserModel — 成功后 id-key / username-key 缓存一致性 ✅ pass
TC-0812 SysUserModel — logic 层 6 goroutine 并发 RefreshToken 同一旧 rt ✅ pass
TC-0867 SysUserModel — 透传 username 与 DB 一致 ✅ pass
TC-0924 SysUserModel — UpdatePassword:FindOne 填缓存后行被并发删除 ✅ pass
TC-0925 SysUserModel — UpdatePassword:正常写入 ✅ pass
TC-0926 SysUserModel — UpdatePassword:user 不存在 ✅ pass
TC-0927 SysUserModel — UpdateStatus:行被并发删除 ✅ pass
TC-0928 SysUserModel — UpdateStatus:正常禁用 ✅ pass
TC-0929 SysUserModel — UpdateStatus:user 不存在 ✅ pass
TC-1044 SysUserModel — UpdateStatus 用"错误 username" 调用 → Model 层仍按错误 key 清理 ✅ pass
TC-1045 SysUserModel — IncrementTokenVersion 用"错误 username" 调用 → 同上 ✅ pass
TC-1046 SysUserModel — IncrementTokenVersion 对"已被删除的行" 仍正常走 RowsAffected=0 → ErrUpdateConflict 分支 ✅ pass
TC-1047 SysUserModel — Logic 层 Logout 必须把 ud.Username 透传到 Model ✅ pass
TC-1048 SysUserModel — Logic 层 Logout 即使 ud.Username=="" 也必须透传(空串) ✅ pass
TC-1079 SysUserModel — UpdateProfileWithTx 成功后 id/username 两把 sysUser 缓存仍持旧值 ✅ pass
TC-1080 SysUserModel — InvalidateProfileCache 一次性失效 id / username 两把 key ✅ pass
TC-1081 SysUserModel — 两段式 E2E:UpdateProfileWithTx(不碰缓存) + InvalidateProfileCache(清缓存) ✅ pass
TC-1082 SysUserModel — UpdateUser tx 分支(改 deptId)post-commit 失效 sysUser 两级缓存 ✅ pass
TC-1083 SysUserModel — (保留编号) ✅ pass
TC-1143 SysUserModel — IncrementTokenVersionWithTx 事务内 UPDATE 返回 LAST_INSERT_ID(DB 真实递增值) ✅ pass
TC-1144 SysUserModel — IncrementTokenVersionWithTx 目标行被并发删除 → ErrUpdateConflict(affected=0) ✅ pass
TC-1145 SysUserModel — IncrementTokenVersionWithTx nil session → fail-fast 报错 ✅ pass
TC-1146 SysUserModel — IncrementTokenVersionWithTx 事务 rollback 时 DB 不落盘 ✅ pass
TC-1147 SysUserModel — BatchIncrementTokenVersionWithTx 多用户全部 +1 ✅ pass
TC-1148 SysUserModel — BatchIncrementTokenVersionWithTx 空 ids 不报错、不触达 DB ✅ pass
TC-1149 SysUserModel — BatchIncrementTokenVersionWithTx nil session → fail-fast 报错 ✅ pass
TC-1150 SysUserModel — BatchIncrementTokenVersionWithTx 事务 rollback 时全部回滚 ✅ pass

8.2 SysProductModel

TC编号 测试场景 测试结果
TC-0423 FindList — 正常分页 ✅ pass
TC-0424 FindList — 空表 ✅ pass
TC-0425 FindList — count失败 ✅ pass
TC-1151 SysProductModel — UpdateWithOptLockTx 正常事务内 CAS 命中 → DB 落盘 ✅ pass
TC-1152 SysProductModel — UpdateWithOptLockTx expectedUpdateTime 错位 → ErrUpdateConflict,DB 不变 ✅ pass
TC-1153 SysProductModel — UpdateWithOptLockTx nil session → fail-fast 报错 ✅ pass
TC-1154 SysProductModel — UpdateWithOptLockTx 事务 rollback 时 DB 不落盘 ✅ pass
TC-1155 SysProductModel — InvalidateProductCache 一次性失效 id / appKey / code 三把低层缓存 ✅ pass
TC-1156 SysProductModel — InvalidateProductCache 在 canceled/deadline ctx 下不 panic、不阻塞 ✅ pass

8.3 SysPermModel

TC编号 测试场景 测试结果
TC-0426 FindListByProductCode — 正常分页 ✅ pass
TC-0427 FindListByProductCode — 不存在的productCode ✅ pass
TC-0428 FindAllCodesByProductCode — 正常查询 ✅ pass
TC-0429 FindAllCodesByProductCode — 空结果 ✅ pass
TC-0430 FindByIds — 正常 ✅ pass
TC-0431 FindByIds — 空ids ✅ pass
TC-0432 FindMapByProductCode — 正常查询 ✅ pass
TC-0433 FindMapByProductCode — 空结果 ✅ pass
TC-0434 FindMapByProductCode — key唯一性 ✅ pass
TC-0435 DisableNotInCodesWithTx — codes非空-正常 ✅ pass
TC-0436 DisableNotInCodesWithTx — codes为空-全部禁用 ✅ pass
TC-0437 DisableNotInCodesWithTx — 无需禁用 ✅ pass
TC-0438 DisableNotInCodesWithTx — DB异常 ✅ pass
TC-0439 FindAllCodesByProductCode — 有权限产品 ✅ pass
TC-0440 FindAllCodesByProductCode — 无权限产品 ✅ pass
TC-0441 FindAllCodesByProductCode — 全部已禁用 ✅ pass
TC-0986 SysPermModel — 产品含 status ∈ {1, 2, 99} 三类行 ✅ pass
TC-0987 SysPermModel — DisableNotInCodesWithTx 只禁用 status=1 且不在白名单的行 ✅ pass

8.4 SysDeptModel

TC编号 测试场景 测试结果
TC-0442 FindAll — 正常查询 ✅ pass
TC-0443 FindAll — 空表 ✅ pass
TC-0444 FindByParentId — 正常查询 ✅ pass
TC-0445 FindByParentId — 无子部门 ✅ pass
TC-0446 FindByPathPrefix — 正常查询 ✅ pass
TC-0447 FindByPathPrefix — LIKE注入已阻止 ✅ pass
TC-0448 FindByPathPrefix — 无匹配 ✅ pass

8.5 SysRoleModel

TC编号 测试场景 测试结果
TC-0449 FindListByProductCode — 正常分页 ✅ pass
TC-0450 FindListByProductCode — 空结果 ✅ pass
TC-0451 FindByIds — 正常 ✅ pass
TC-0452 FindByIds — 空ids ✅ pass
TC-0453 FindMinPermsLevelByUserIdAndProductCode — 有角色用户 ✅ pass
TC-0454 FindMinPermsLevelByUserIdAndProductCode — 无角色用户 ✅ pass
TC-1072 SysRoleModel — Happy path:所有 roleId 存在且 Enabled ✅ pass
TC-1073 SysRoleModel — 存在一个被删除的 roleId ✅ pass
TC-1074 SysRoleModel — 存在一个 Disabled 的 role ✅ pass
TC-1075 SysRoleModel — 空 / nil 切片 ✅ pass
TC-1076 SysRoleModel — 入参含重复 id ✅ pass
TC-1077 SysRoleModel — 不按 productCode 过滤 ✅ pass

8.6 SysRolePermModel

TC编号 测试场景 测试结果
TC-0455 FindPermIdsByRoleId — 正常查询 ✅ pass
TC-0456 FindPermIdsByRoleId — 无绑定 ✅ pass
TC-0457 FindPermIdsByRoleIds — 正常查询 ✅ pass
TC-0458 FindPermIdsByRoleIds — 空roleIds ✅ pass
TC-0459 FindPermIdsByRoleIds — 去重验证 ✅ pass
TC-0460 DeleteByRoleIdTx — 正常事务内删除 ✅ pass
TC-0461 DeleteByRoleIdTx — 无绑定 ✅ pass

8.7 SysUserPermModel

TC编号 测试场景 测试结果
TC-0462 FindPermIdsByUserIdAndEffectForProduct — ALLOW-指定产品 ✅ pass
TC-0463 FindPermIdsByUserIdAndEffectForProduct — DENY-指定产品 ✅ pass
TC-0464 FindPermIdsByUserIdAndEffectForProduct — 无记录/其他产品 ✅ pass
TC-0465 DeleteByUserIdForProductTx — 事务内跨产品删除 ✅ pass
TC-0466 DeleteByUserIdForProductTx — 跨产品隔离 ✅ pass

8.8 SysUserRoleModel

TC编号 测试场景 测试结果
TC-0467 FindRoleIdsByUserId — 正常查询 ✅ pass
TC-0468 FindRoleIdsByUserId — 无绑定 ✅ pass
TC-0469 DeleteByRoleIdTx — 正常删除 ✅ pass
TC-0470 DeleteByUserIdForProductTx — 事务内跨产品删除 ✅ pass
TC-0471 DeleteByUserIdForProductTx — 跨产品隔离 ✅ pass
TC-0472 FindUserIdsByRoleId — 有绑定的角色 ✅ pass
TC-0473 FindUserIdsByRoleId — 无绑定角色 ✅ pass
TC-0474 FindRoleIdsByUserIdForProduct — 跨产品过滤 ✅ pass
TC-0706 SysUserRoleModel — 同一产品下同时存在启用/禁用角色 ✅ pass
TC-0707 SysUserRoleModel — DeleteByUserIdAndRoleIdsTx 批量删除 ✅ pass
TC-0708 SysUserRoleModel — 批量删除空列表为 no-op ✅ pass
TC-0709 SysUserRoleModel — 批量删除仅作用于指定 userId ✅ pass
TC-1061 SysUserRoleModel — DeleteByRoleIdTx 删除多行后,id 级和组合 key 缓存全部失效 ✅ pass
TC-1062 SysUserRoleModel — DeleteByUserIdAndRoleIdsTx 只删指定 (userId, roleIds) 集合后同上 ✅ pass

8.9 SysProductMemberModel

TC编号 测试场景 测试结果
TC-0475 FindListByProductCode — 正常分页 ✅ pass
TC-0476 FindListByProductCode — 空结果 ✅ pass
TC-0477 FindMapByProductCodeUserIds — 正常批量 ✅ pass
TC-0478 FindMapByProductCodeUserIds — 空userIds ✅ pass
TC-0479 FindMapByProductCodeUserIds — 部分不是成员 ✅ pass
TC-0480 FindMapByProductCodeUserIds — map key正确 ✅ pass
TC-0765 SysProductMemberModel — CountActiveAdminsTx 返回正确计数 ✅ pass
TC-0868 SysProductMemberModel — 产品内 3 个 active admin,排除其中 1 ✅ pass
TC-0869 SysProductMemberModel — 唯一 active admin,排除他自己 ✅ pass
TC-0870 SysProductMemberModel — 存在 1 个 active + 1 个 disabled admin ✅ pass
TC-1110 FindOneForShareTx — 事务内读到最新行、字段对齐(S 锁契约) ✅ pass
TC-1111 FindOneForShareTx — 不存在 id 返回 sqlx.ErrNotFound(供上层区分"被并发删除") ✅ pass
TC-1157 SysProductMemberModel — FindActiveMemberUserIdsByProductCodeTx 仅返回 Enabled userId,过滤 Disabled 与其他产品 ✅ pass
TC-1158 SysProductMemberModel — FindActiveMemberUserIdsByProductCodeTx productCode 不存在时返回空切片、err=nil ✅ pass
TC-1159 SysProductMemberModel — FindActiveMemberUserIdsByProductCodeTx 仅禁用成员时返回空(status 过滤不得退化为全量) ✅ pass

九、访问控制 (auth/access.go)

9.1 RequireSuperAdmin

TC编号 测试场景 测试结果
TC-0481 超管通过 ✅ pass
TC-0482 非超管拒绝 ✅ pass
TC-0483 MEMBER拒绝 ✅ pass
TC-0484 未登录 ✅ pass

9.2 RequireProductAdminFor(ctx, targetProductCode)

TC编号 测试场景 测试结果
TC-0485 超管通过 ✅ pass
TC-0486 ADMIN通过(同产品) ✅ pass
TC-0487 DEVELOPER拒绝 ✅ pass
TC-0488 MEMBER拒绝 ✅ pass
TC-0489 未登录 ✅ pass
TC-0490 ADMIN跨产品拒绝 ✅ pass

9.3 CheckMemberTypeAssignment

TC编号 测试场景 测试结果
TC-0491 超管可分配任何类型 ✅ pass
TC-0492 ADMIN分配DEVELOPER ✅ pass
TC-0493 ADMIN分配ADMIN(同级拒绝) ✅ pass
TC-0494 DEVELOPER分配ADMIN(越级拒绝) ✅ pass
TC-0495 MEMBER分配MEMBER(同级拒绝) ✅ pass
TC-0496 未登录 ✅ pass

9.4 CheckManageAccess

TC编号 测试场景 测试结果
TC-0497 超管可管理任何人 ✅ pass
TC-0498 操作自己 ✅ pass
TC-0499 ADMIN跳过部门检查 ✅ pass
TC-0500 非ADMIN无部门拒绝 ✅ pass
TC-0501 目标用户无部门 ✅ pass
TC-0502 目标在不同部门 ✅ pass
TC-0503 未登录 ✅ pass
TC-0504 caller.DeptPath为空时拒绝 ✅ pass
TC-0819 DB 层瞬时错误(mock errors.New("boom") ✅ pass
TC-0820 DB 返回 sqlx.ErrNotFound ✅ pass
TC-0860 传入 prefetched target,不再查 FindOne ✅ pass
TC-0861 prefetched target.Id 与参数 targetUserId 不一致 ✅ pass
TC-0930 caller 的 MinPermsLevel 缓存过期(偏高) ✅ pass
TC-0931 同级分配 ✅ pass
TC-0932 严格低一级分配 ✅ pass
TC-0933 SuperAdmin 绕过 DB ✅ pass
TC-0934 Product ADMIN 绕过 DB ✅ pass
TC-0935 DEVELOPER 绕过 DB ✅ pass
TC-0936 caller 在 DB 没有任何角色 ✅ pass
TC-0937 caller 查询 DB 遇到一般错误 ✅ pass
TC-0938 caller 为 nil(无 UserDetails) ✅ pass
TC-0969 caller 缓存级别高(10)但 DB 已降级(20),target 级别 15 ✅ pass
TC-0970 caller 在 DB 中无任何角色(ErrNotFound ✅ pass
TC-0971 caller DB 级别(5)严格高于 target(15) ✅ pass
TC-0972 caller 侧 FindMinPermsLevelByUserIdAndProductCode 通用 DB 错 ✅ pass
TC-0973 caller 是 SuperAdmin ✅ pass
TC-0974 caller.UserId == targetUserId(自操作) ✅ pass
TC-0975 共享 helper loadFreshMinPermsLevel 的契约对齐 ✅ pass
TC-0993 legacy DEVELOPER(DeptId=0、DeptPath="")去管理他人 → 403 "未归属任何部门"(与 TC-0504 合一) ✅ pass
TC-1017 SuperAdmin / ADMIN / DEVELOPER 走 HasFullPerms 短路 ✅ pass
TC-1018 MEMBER caller 仅打 1 次 DB,循环内对 5 个角色走 CheckRoleLevelAgainst 不再打 DB ✅ pass
TC-1019 caller ErrNotFoundNoRole=true,不翻 500 ✅ pass
TC-1020 caller 其他 DB 错误 → fail-close 500 ✅ pass

9.5 memberTypePriority

TC编号 测试场景 测试结果
TC-0505 各类型优先级正确 ✅ pass

十、UserDetailsLoader (loaders/userDetailsLoader.go)

10.1 Load / 缓存

TC编号 测试场景 测试结果
TC-0506 DB加载(缓存miss) ✅ pass
TC-0507 缓存命中 ✅ pass
TC-0508 用户不存在 ✅ pass
TC-0509 productCode为空 ✅ pass
TC-0705 Load 不存在用户 ✅ pass
TC-0792 50 goroutine barrier 同时 Load 同 userId ✅ pass
TC-0793 首次 Load 后再 20 次 Load ✅ pass
TC-0821 同一"不存在 userId" 第 2 次 Load ✅ pass
TC-0822 负缓存不登记到 userIndex/productIndex ✅ pass
TC-0823 50 并发 Load 同一不存在 userId ✅ pass
TC-0913 用户不存在 → (ud, nil)ud.Username == "" ✅ pass
TC-0914 新建用户刚落地 CreateUser 后立刻 Load ✅ pass
TC-0915 partial load 失败(幽灵 deptId) ✅ pass
TC-0916 全绿 MEMBER 正路径 ✅ pass
TC-0917 ErrLoaderDegraded 作为 sentinel 可 errors.Is 断言 ✅ pass

10.2 缓存失效

TC编号 测试场景 测试结果
TC-0510 Del删除指定缓存 ✅ pass
TC-0511 Clean清除用户所有产品缓存 ✅ pass
TC-0512 CleanByProduct清除产品所有用户 ✅ pass
TC-0513 BatchDel批量删除 ✅ pass
TC-0514 BatchDel空数组 ✅ pass
TC-1013 N=2 的真实缓存场景 ✅ pass
TC-1014 productCode="" 分支 ✅ pass
TC-1112 DetachCacheCleanCtx — parent 取消不传播到 detached ctx ✅ pass
TC-1113 DetachCacheCleanCtx — 3s 硬超时 deadline 兜底(窗口锁定) ✅ pass
TC-1114 DetachCacheCleanCtx — parent 的 Value(trace/tenant)透传 ✅ pass
TC-1115 isCtxCanceledErr — Canceled / DeadlineExceeded / wrapped 一律 true,其它 false ✅ pass
TC-1116 logCacheInvalidationErr — nil 早退、不阻塞 ✅ pass
TC-1117 logCacheInvalidationErr — ctx 取消 / 超时 / 普通错误三条分支均不 panic ✅ pass

10.3 loadPerms权限计算

TC编号 测试场景 测试结果
TC-0515 超管全量权限 ✅ pass
TC-0516 ADMIN全量权限 ✅ pass
TC-0517 DEVELOPER全量权限 ✅ pass
TC-0518 DEV部门全量权限 ✅ pass
TC-0519 MEMBER角色权限+ALLOW-DENY ✅ pass
TC-0520 用户ALLOW权限不跨产品泄漏 ✅ pass
TC-0521 禁用DEV部门成员无全量权限 ✅ pass

10.4 loadRoles + MinPermsLevel

TC编号 测试场景 测试结果
TC-0522 多角色取最小permsLevel ✅ pass
TC-0523 无角色 ✅ pass
TC-0524 角色跨产品过滤 ✅ pass
TC-0525 禁用角色不计入 ✅ pass

10.5 loadMembership

TC编号 测试场景 测试结果
TC-0526 超管自动设置SUPER_ADMIN ✅ pass
TC-0527 非成员MemberType为空 ✅ pass
TC-0528 禁用成员MemberType为空 ✅ pass
TC-1205 NORMAL 部门冻结(DeptStatus=Disabled)后,成员 Perms 为空 []string{}(H-R18-2) ✅ pass
TC-1206 普通成员无任何角色时 Perms 必须为 []string{}(非 nil),JSON 序列化恒为 [](L-R18-3) ✅ pass
TC-1207 productmember.ErrNotFound 与 sqlx.ErrNotFound errors.Is 契约稳健性(M-R18-3) ✅ pass

十一、中间件 — 冻结账号拦截

TC编号 测试场景 测试结果
TC-0529 冻结用户被403 ✅ pass
TC-0530 用户不存在(Status=0) ✅ pass
TC-0531 UserDetails注入context ✅ pass

十二、Logic层 — 访问控制负面测试

TC编号 测试场景 测试结果
TC-0532 createDept非超管拒绝 ✅ pass
TC-0533 updateDept非超管拒绝 ✅ pass
TC-0534 deleteDept非超管拒绝 ✅ pass
TC-0535 createProduct非超管拒绝 ✅ pass
TC-0536 updateProduct非超管拒绝 ✅ pass
TC-0537 createUser非产品管理员拒绝 ✅ pass
TC-0538 createRole非产品管理员拒绝 ✅ pass
TC-0539 updateRole非产品管理员拒绝 ✅ pass
TC-0540 deleteRole非产品管理员拒绝 ✅ pass
TC-0541 bindRolePerms非产品管理员拒绝 ✅ pass
TC-0542 updateUser-MEMBER不能管理他人 ✅ pass
TC-0543 updateUser自己修改DeptId被拒绝 ✅ pass
TC-0544 updateUser自己修改Status被拒绝 ✅ pass
TC-0545 updateUser未登录被拒绝 ✅ pass
TC-0796 LogoutHandler 无 userDetails ctx ✅ pass
TC-0797 LogoutHandler 携带合法 ctx ✅ pass
TC-0798 ChangePasswordHandler 非法 JSON body ✅ pass
TC-0799 ChangePasswordHandler 缺必填字段 ✅ pass
TC-0800 RefreshTokenHandler 缺 Authorization ✅ pass
TC-0801 RefreshTokenHandler 非法 bearer ✅ pass

十三、限流中间件 (middleware/ratelimitMiddleware.go)

TC编号 测试场景 测试结果
TC-0546 正常请求(未超限) ✅ pass
TC-0547 超限请求被拒绝 ✅ pass
TC-0548 behindProxy=false时XFF被忽略 ✅ pass
TC-0549 behindProxy=false时X-Real-IP被忽略 ✅ pass
TC-0550 IP从RemoteAddr解析 ✅ pass
TC-0551 不同IP独立限流 ✅ pass
TC-0552 behindProxy=true时信任X-Real-IP ✅ pass
TC-0553 behindProxy=true时无X-Real-IP回退RemoteAddr ✅ pass
TC-0555 RemoteAddr无端口格式 ✅ pass
TC-0739 quota=2 的 limiter,第 3 次 logout ✅ pass
TC-0740 限流按 userId 隔离 ✅ pass
TC-0741 quota=1 limiter,burst 第 2 次 ✅ pass
TC-0742 限流按 userId 隔离(productCode 无关) ✅ pass
TC-0790 period=1s quota=1, 打满后 sleep 1.2s 再调 ✅ pass
TC-0791 Redis 不可达(127.0.0.1:1 + NonBlock) ✅ pass
TC-0862 behindProxy=true + X-Forwarded-For: 1.1.1.1, 2.2.2.2 ✅ pass
TC-0863 behindProxy=true + X-Forwarded-For 全非法 + X-Real-IP: 10.0.0.1 ✅ pass
TC-0864 behindProxy=true + 两头均空 ✅ pass
TC-0865 behindProxy=true + X-Forwarded-For: " 3.3.3.3 " (空白) ✅ pass
TC-0866 behindProxy=false + XFF=1.1.1.1 ✅ pass

三、测试结论

  • 1040 个 TC 全部执行:顶层测试函数 1109,测试事件(含 t.Run 子用例)1241;通过 1240,跳过 1,失败 0
  • 26 个测试包全部 OK;./internal/logic/... 语句覆盖率 86.9%;整包连跑均绿,无并发 flake 触发。
  • 通过率(扣除主动 skip 的 1 条不可达防御分支 TC-0263):100%
  • 核心业务路径(登录、刷新 Token、权限同步、用户/角色/成员/部门 CRUD、访问控制、限流、缓存失效、乐观锁、事务隔离、并发安全、会话吊销 tokenVersion 契约)均有独立回归用例覆盖且稳定通过。