6.5 KiB
Context
代理扫码注册在审批前已保存完整注册记录,并关联一个业务类型为 agent_distribution_approval 的通用审批实例。终态消费者在通过时原子创建店铺、代理账号和钱包,在驳回时只更新注册记录。现有对外入口只有无需认证的创建接口,后台没有查询或恢复入口。
注册记录的 parent_shop_id 是提交时分销码所属直接上级店铺事实,可直接支撑代理直属范围;审批前不存在下级店铺,因此不能复用店铺列表或基于已建店层级查询。通用审批与企业微信上下文已经保存提交状态、外部审批单号、失败摘要和恢复时间,可用于只读投影与原实例恢复。
Goals / Non-Goals
Goals:
- 为平台与直接上级代理提供注册申请列表和详情,区分业务状态、审批状态和提交状态。
- 以注册时冻结的
parent_shop_id固化代理直属可见范围。 - 提供提交失败或结果未知时的原审批恢复,不改变注册终审和建店事务。
- 在允许业务处理所需完整联系资料的同时,排除认证凭证和内部集成细节。
Non-Goals:
- 不把待审批注册记录投影为虚拟店铺,不加入现有店铺列表。
- 不提供本地人工通过、驳回、修改注册资料或删除申请。
- 不允许代理查看更深层下级申请,不使用
SubordinateShopIDs扩大范围。 - 不新增数据库迁移、导出、批量审批或第三方依赖。
Decisions
使用独立注册 Query,不复用店铺或通用审批列表
在 distributionwithdrawal 查询边界增加注册列表与详情。主查询以 tb_agent_distribution_registration 为业务事实,审批实例和企业微信上下文只用于状态投影。
备选把申请混入店铺列表;放弃,因为审批前不存在店铺,虚拟店铺会污染 ID、状态和权限语义。备选只查询通用审批列表;放弃,因为通用快照不是完整注册查询模型,也不适合业务筛选。
代理范围精确匹配直接上级店铺
超级管理员和平台用户不加店铺过滤。代理账号必须有有效当前店铺,并施加 parent_shop_id = 当前店铺 ID;范围为空时列表返回空,详情和恢复返回统一不可见错误。企业账号和其他类型拒绝。
不使用当前店铺的下级集合:业务已确认代理只看使用自己分销码提交的直接申请,更深层申请属于其下级代理的运营范围。注册记录保持提交时的直接上级快照,即使之后层级变化也不重算。
查询分别投影三层状态
响应分别返回:
- 注册业务状态:待审批、已通过、已驳回;
- 通用审批状态:提交中、审批中及各终态;
- 企业微信提交状态:待提交、发送中、已提交、失败、结果未知。
可恢复标记由服务端基于三层事实和上级店铺启用状态计算,前端不得自行推断。列表按页收集上级店铺和审批实例 ID,批量读取店铺名称、审批实例及企微上下文,避免 N+1。
筛选时间复用严格 RFC3339 闭区间解析规则;申请时间使用 start_time/end_time,决定时间使用 decided_start_time/decided_end_time。手机号精确匹配,店铺名称、编号、用户名和联系人使用受长度限制的子串匹配。
完整联系资料仅作为授权业务投影
根据业务决定,平台账号和有权直接上级代理均返回完整手机号、完整省市区和详细地址,便于处理注册申请。DTO 明确禁止包含密码哈希;验证码没有持久化在注册记录,也不得通过其它联查返回。失败摘要只使用安全文本,不返回企微响应原文、Outbox 事件键、租约或内部重试字段。
审计只记录注册 ID、店铺编号、上级店铺、状态和脱敏手机号,不复制完整手机号或地址。列表/详情访问沿用统一访问审计,不为每次读取新增业务动作。
恢复用例编排通用审批可靠事实
新增 POST /api/admin/agent-distribution-registrations/{id}/recover-approval。Application 用例在事务内锁定注册记录,校验状态仍待审批、实例非空、实例业务类型和业务 ID 一致,并复核权限及上级店铺仍启用。
恢复顺序:
- 已有企微
sp_no:请求同步原审批详情。 - 提交结果未知:触发原实例确认流程,禁止直接重提。
- 明确失败且确认未受理:恢复
approval:{instance_id}:submission稳定事件。 - 事件等待重试、处理中或审批进行中:幂等返回当前状态。
- 注册或审批终态:状态冲突。
HTTP 不同步调用企微创建审批接口,外部查询和提交继续由 Worker 执行。恢复不修改注册资料或密码哈希,不创建任何业务实体,也不新增审批实例。
停用上级只读不恢复
列表与详情读取历史注册记录时不要求上级店铺启用,保证运营可追溯。恢复必须要求上级店铺仍存在且启用,因为审批通过终态同样要求启用上级;在无落地条件时恢复只会制造无法完成的审批。
恢复动作写审计,记录操作者及其店铺、实例、前置状态、所选动作和安全结果;不复制个人敏感原文。
Risks / Trade-offs
- [代理可见完整手机号与地址扩大个人信息暴露] → 严格限制为直接上级店铺,企业账号拒绝,响应与审计白名单排除认证凭证和不必要复制。
- [平台用户当前无实际店铺范围] → 按已确认规则平台全量,不引入未定义的平台范围推导。
- [上级店铺后续迁移导致历史归属与当前层级不同] → 以注册时
parent_shop_id为稳定运营事实,不重算。 - [恢复与正常 Worker 重试竞争] → 实例锁和稳定事件条件更新保证单次状态变化,活动事件只返回当前状态。
- [关键词筛选拖慢列表] → 限制输入长度并仅在注册业务列上查询;数据规模扩大后再评估专用索引,不预建检索系统。
Migration Plan
- 不执行数据库迁移;发布注册 Query、恢复用例、后台路由及审批状态投影。
- 在维护者指定测试环境创建直属、非直属、待审批、已通过、已驳回及提交异常申请,验证数据范围和筛选。
- 验证明确失败、结果未知、已有
sp_no、并发恢复和上级停用拒绝,不产生第二个实例或任何半套业务实体。 - 回滚时移除后台入口并回退二进制;注册记录、审批实例和审计事实保持不变。