yueli
bcbee8a194
feat(outbound): 申请人撤回自己的申请单 + 我的申请单端点
背景
----
出库审批页是管理视角(需 outbound_approval 权限),普通申请人提交后
**没有任何入口看回自己的单据**,更谈不上撤回。
服务层:抽出共用释放逻辑
------------------------
新增 OutboundApprovalService.withdraw_request(),与既有的 close_request()
(管理路径)形成两条独立入口:
close_request —— 管理路径,需 outbound_approval 等权限,仅 status==1
withdraw_request —— 申请人路径,仅校验「单据归属」,status 0 或 1 均可
两者各自完成权限与状态校验后,调用**同一个** _release_and_close()。
释放逻辑只有一份实现,不会因修改其中一处而漏掉另一处。
为什么单独开一条路径,而不是在 close_request 里加 if 分支:
权限模型不同(管理角色 vs 单据归属)。混在一个函数里,后续修改容易
互相影响 —— 这正是需要避免的访问控制风险。
API
---
POST /outbound/request/<id>/withdraw 申请人撤回(仅 @jwt_required)
· 归属断言:非本人且非特权 → 403「无权撤回他人的申请单」
· 状态守卫:仅 0/1 可撤回;执行成功后 status 会被置 3,故该判断
本身即执行守卫,已执行或已撤回的单都进不来
GET /outbound/my-requests 我的申请(仅 @jwt_required)
· applicant_id 硬编码为当前登录用户,不接受任何入参覆盖
两者都**不做模块权限校验** —— 普通申请人无需持有 outbound_approval
(那是管理权限)。与「给审批端点加 if 降级放行」是两条路:后者把管理
逻辑与用户逻辑混在一个端点里,一旦 is_privileged_viewer() 判定出错
即越权;本端点从设计上就没有「看别人」的分支。
安全实测
--------
普通员工查我的申请(此前 403) → 200
B 查列表看不到 A 的单 → 39 单中无 A 的单
B 撤回 A 的单 → 403,且库存未被释放
A 撤回自己的单 → 200,库存 17→20 完全释放
主管代撤他人工单 → 200(特权路径)
待审批(status=0) 撤回 → 200,库存释放
重复撤回 → 400「当前状态不可撤回」
2026-09-10 15:36:59 +08:00
..
2026-09-10 15:36:59 +08:00
2026-09-10 14:16:27 +08:00
2026-09-10 10:14:31 +08:00
2026-09-10 15:36:59 +08:00
2026-09-10 14:16:27 +08:00
2026-09-10 14:16:27 +08:00
2026-09-10 14:16:27 +08:00