AGENTS.md
11c407d
采购员的接口权限存储在 casbin_rule 表,由 ensurePurchaserRoleAndPolicies() 依据代码中的 access.PurchaserAPIs() 全量重建(先删除 ptype='p' AND v0=purchaser 再重建)。
casbin_rule
ensurePurchaserRoleAndPolicies()
access.PurchaserAPIs()
ptype='p' AND v0=purchaser
该函数仅被两条迁移调用:
server/cmd/migrate/migration/version-local/1786701700000_purchaser_role.go
server/cmd/migrate/migration/version-local/1787885400000_goauto_menus.go:35
而 #148 新增的三个接口位于更晚的迁移批次(1787983400000_purchase_spec_match_work_item.go)。迁移按版本号一次性执行、执行后记录 version 不再重跑,因此上述两条早已完成的迁移不会再被触发去重建策略。
1787983400000_purchase_spec_match_work_item.go
结果:server/app/goauto/access/purchaser.go 中已登记的三条
server/app/goauto/access/purchaser.go
{"查看采购规格匹配", "/api/admin/v1/purchase-tasks/:taskId/matching", "GET", true}, {"重新尝试采购规格匹配", "/api/admin/v1/purchase-tasks/:taskId/matching/requeue", "POST", true}, {"人工选择采购规格", "/api/admin/v1/purchase-tasks/:taskId/matching/manual", "POST", true},
从未写入 casbin_rule,采购员调用即被拒绝。管理员账号不受影响(go-admin 的 admin 角色绕过 casbin),因此该缺陷只在采购员账号下暴露。
access/purchaser.go 新增条目不会触发任何编译错误或测试失败,也没有任何机制提醒实施者补一条迁移。后续待做工单(#135 备货采购、#137 权限菜单、#143 版本发布等)均会新增接口,同一问题必然复发,且症状具有迷惑性——代码里权限写着 true,运行时却报无权限。
access/purchaser.go
true
AdminAPIs
Purchaser
sys_menu
PurchaserAPIs()
sys_api
v0 = purchaser
Purchaser: false
/matching
/matching/requeue
/matching/manual
go test ./app/goauto/access/... ./cmd/migrate/...
Architecture-and-Code-Map
Deployment-and-Operations
sync
sync --check
待实施。
已按 2026-08-29 用户授权实施 #156 的权限启动对账,并完成本机 Admin/API 重启及权限写入验证。
ReconcilePurchaserPermissions
ptype=p, v0=purchaser
FirstOrCreate
go test ./app/goauto/access/... ./cmd/migrate/... ./cmd/api/...
go test ./...
.\scripts\verify.ps1 -Component server
python dev_scripts/harness.py check --strict
git diff --check
首次重启前:
首次启动对账后:
GET /api/admin/v1/purchase-tasks/:taskId/matching
POST /api/admin/v1/purchase-tasks/:taskId/matching/requeue
POST /api/admin/v1/purchase-tasks/:taskId/matching/manual
随后第二次重启验证幂等:
d0222d4337992bedcf2b973bc3bb9d43922c0ea3
harness.py sync
7c18159 fix(#156): reconcile purchaser permissions at startup
origin/main
浏览器当前登录的是管理员会话(可见“系统管理/开发工具”等管理员菜单),不是采购员会话,因此没有用管理员权限冒充采购员接口验收。为避免改变真实采购任务状态,也未点击“AI 重试/人工保存”。
待采购员账号实际登录后,验证采购任务详情的匹配读取、AI 重试和人工保存不再返回 403;管理员专属接口仍不可访问。工单保持打开、待用户验收。
用户于 2026-08-29 明确确认本工单通过验收。验收结论已记录,现关闭工单。没有新的长期事实变化,本次不重复同步 Wiki。
No dependencies set.
The note is not visible to the blocked user.
所属与来源
AGENTS.md「Gitea 交互与工单最小读取」记录回退原因。根因(提交
11c407d复核)采购员的接口权限存储在
casbin_rule表,由ensurePurchaserRoleAndPolicies()依据代码中的access.PurchaserAPIs()全量重建(先删除ptype='p' AND v0=purchaser再重建)。该函数仅被两条迁移调用:
server/cmd/migrate/migration/version-local/1786701700000_purchaser_role.goserver/cmd/migrate/migration/version-local/1787885400000_goauto_menus.go:35而 #148 新增的三个接口位于更晚的迁移批次(
1787983400000_purchase_spec_match_work_item.go)。迁移按版本号一次性执行、执行后记录 version 不再重跑,因此上述两条早已完成的迁移不会再被触发去重建策略。结果:
server/app/goauto/access/purchaser.go中已登记的三条从未写入
casbin_rule,采购员调用即被拒绝。管理员账号不受影响(go-admin 的 admin 角色绕过 casbin),因此该缺陷只在采购员账号下暴露。这是结构性问题,不是一次性疏漏
access/purchaser.go新增条目不会触发任何编译错误或测试失败,也没有任何机制提醒实施者补一条迁移。后续待做工单(#135 备货采购、#137 权限菜单、#143 版本发布等)均会新增接口,同一问题必然复发,且症状具有迷惑性——代码里权限写着true,运行时却报无权限。目标
AdminAPIs为唯一事实源,仍执行全量重建以保证减权生效。非目标
AdminAPIs中任何一条的Purchaser取值。sys_menu与角色菜单绑定的既有语义(#137 定义的「只增不删」不在本工单范围)。实施方案
一、权限同步改为服务启动时对账
ensurePurchaserRoleAndPolicies()的调用从「版本化迁移」改为「每次服务启动时执行一次」,与代码中的AdminAPIs/PurchaserAPIs()对账。sys_api中缺失的条目补齐;casbin_rule策略全量重建,保证代码中减权后旧策略不会残留;v0 = purchaser的策略,不得触碰其他角色与用户自建策略。二、防复发测试
AdminAPIs中每一条在对账执行后都能在casbin_rule(采购员部分按PurchaserAPIs())与sys_api中找到对应记录。casbin_rule中不再存在(验证减权生效)。三、当前缺失条目
安全边界
access/purchaser.go,不下放到界面配置。Purchaser: false的接口仍对采购员关闭。验收标准
/matching、/matching/requeue、/matching/manual三个接口,人工干预与 AI 重试均可保存成功。casbin_rule中采购员策略与PurchaserAPIs()完全一致(多余的被清除、缺失的被补齐)。sys_api中包含AdminAPIs的全部条目。Purchaser: false的接口对采购员仍然拒绝。验证方式
go test ./app/goauto/access/... ./cmd/migrate/...依赖、并行与风险
v0 = purchaser限定范围,并配针对性测试。文档影响
Architecture-and-Code-Map:权限矩阵的同步时机由版本化迁移改为服务启动对账。Deployment-and-Operations(若存在):说明新增接口权限无需额外迁移,重启即生效。sync与一轮sync --check,把页面与 revision 写回本工单。状态
待实施。
实施完成,待验收
已按 2026-08-29 用户授权实施 #156 的权限启动对账,并完成本机 Admin/API 重启及权限写入验证。
实现
ReconcilePurchaserPermissions,API 启动、路由注册和监听端口之前执行。AdminAPIs权限矩阵幂等补齐全部sys_api;ptype=p, v0=purchaser的 Casbin 策略;FirstOrCreate的新增记录填入 path/action,已在本次启动对账中改用结构化条件,并用测试锁定。自动化验证
go test ./app/goauto/access/... ./cmd/migrate/... ./cmd/api/...:通过。go test ./...(server):通过。.\scripts\verify.ps1 -Component server:通过。python dev_scripts/harness.py check --strict:通过。git diff --check:通过。本机 MySQL / Admin API 实证
首次重启前:
首次启动对账后:
GET /api/admin/v1/purchase-tasks/:taskId/matchingPOST /api/admin/v1/purchase-tasks/:taskId/matching/requeuePOST /api/admin/v1/purchase-tasks/:taskId/matching/manualsys_api各 1 条随后第二次重启验证幂等:
文档与提交
Architecture-and-Code-Map已在线更新并回读,revision:d0222d4337992bedcf2b973bc3bb9d43922c0ea3harness.py sync和一次sync --checkDeployment-and-Operations页面当前不存在,按工单约定未创建虚假运维页7c18159 fix(#156): reconcile purchaser permissions at startuporigin/main尚需人工验收
浏览器当前登录的是管理员会话(可见“系统管理/开发工具”等管理员菜单),不是采购员会话,因此没有用管理员权限冒充采购员接口验收。为避免改变真实采购任务状态,也未点击“AI 重试/人工保存”。
待采购员账号实际登录后,验证采购任务详情的匹配读取、AI 重试和人工保存不再返回 403;管理员专属接口仍不可访问。工单保持打开、待用户验收。
用户于 2026-08-29 明确确认本工单通过验收。验收结论已记录,现关闭工单。没有新的长期事实变化,本次不重复同步 Wiki。