七月迭代短暂完结,还有很多后端的关键东西没有弄,这是一版赶时间做的东西
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m26s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m26s
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-07-24
|
||||
@@ -0,0 +1,163 @@
|
||||
## Context
|
||||
|
||||
本 Change 以 `docs/7月迭代/七月迭代需求范围确认表.md` 为业务范围,以 `docs/7月迭代/物联网卡管系统-需求.csv` 为原始来源,以 `docs/7月迭代/企业微信审批官方接口调研.md` 为企微能力依据。旧 Change 已完成部分公共能力,但其剩余任务存在漏项、错误口径和过度 DDD 迁移,不能继续作为实施顺序。
|
||||
|
||||
当前仓库已经具备可复用的通用审批实例、标准决策同步、公共 Outbox/Relay、异步任务、对象存储、站内通知、`system_config`、钱包流水和 Integration Log。本设计只补业务接线和确实缺失的企微 Adapter,不重建这些基础设施。
|
||||
|
||||
涉及方包括后端、前端、测试、部署人员,以及企业微信和 Gateway 配置负责人。当前仓库没有前端源码,因此本 Change 交付后端接口、OpenAPI 契约和联调门禁。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- 按已确认口径覆盖所有“本期做”需求,并核验所有“已完成,只联调/验收”需求。
|
||||
- 让简单字段、筛选、配置和 Bug 保持简单,能够独立开发、独立验证、独立上线。
|
||||
- 让退款和员工线下代充值真正通过企微模板审批闭环,同时保留金额、幂等和终态处理的现有安全边界。
|
||||
- 冻结前后端需要的字段、接口、状态和错误语义,避免实现阶段再次扩张范围。
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- 不继续执行旧 Change 中未被本 Change重新列出的任务。
|
||||
- 不做代理在线扫码充值、原路退款、聚水潭、跨品类换货、分销码/佣金提现、自动限速、通用营销/ERP、本地审批流程设计器、未来钉钉适配或全局 Audit Event 专项。
|
||||
- 不迁移未触碰的旧模块,不因为新增字段或筛选建立聚合、Repository 接口、事件消费者或历史投影。
|
||||
- 不强制吊销已登录 C 端 Token;店铺限制只阻止新的登录流程。
|
||||
- 本次执行不编写或补齐测试代码,不运行测试、构建、LSP、迁移、OpenAPI 生成或真实外部环境验收;实现收口到生产代码可联调状态。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 新 Change 是后续实施的唯一契约
|
||||
|
||||
本 Change 校验通过后,`complete-july-iteration-test-release` 只作为历史完成证据归档,不再从其中领取任务。已完成代码不会回滚或重复实现;本 Change 的 tasks 会先做一次证据核验,再只实现真实缺口。
|
||||
|
||||
**理由**:旧 Change 的 task 勾选、proposal/spec 和仓库状态已经漂移,继续补丁式修订容易再次执行错误任务。
|
||||
|
||||
**否决方案**:在旧 Change 上继续增删 191 个任务。该方案无法可靠区分已完成公共能力、错误口径和本期真实需求。
|
||||
|
||||
### 2. 按纵向切片选择最小架构通道
|
||||
|
||||
| 需求切片 | 主通道 | 完整业务边界 | 明确不迁移范围 |
|
||||
| --- | --- | --- | --- |
|
||||
| #189、#181、#57 | 旧 `Handler → Service → Store → Model` | 对现有换货、退款、订单路径做局部修复和拦截 | 不迁移换货、退款或订单模块 |
|
||||
| #182、#44、#53 | 既有 Query 或旧 Store 查询 | 列表/详情字段、实名筛选、批量账号名称解析 | 不建聚合、历史投影或事件消费者 |
|
||||
| #41、#62、#48 | 简单写 + 既有读取 | 店铺开关、实名策略、系统配置的更新和查询 | 不建领域模型,不重构认证/支付模块 |
|
||||
| #188、#97、#33 | Application/既有通知 Adapter | 产生明确业务事件后向既有站内通知接线 | 不建营销平台或新通知中心 |
|
||||
| #36、#42、#49 | 既有异步导入/导出 Infrastructure + 业务 Service | 文件校验、异步处理、结果文件和业务写入 | 不再建第二套任务、对象存储或进度平台 |
|
||||
| #47 | 旧 IoT 卡 Service + Gateway Adapter | 固定档位校验、卡 ICCID 权限解析、一次 Gateway 调用 | 不提供设备限速,不通过设备绑定关系间接限速,不自动计算运营商策略,不迁移资产模块 |
|
||||
| #34、#35 | 复杂写 Application/Domain | 申请快照、审批终态、充值入账或退款终结 | 不把企微 DTO/状态码写入资金领域 |
|
||||
| #37 | Infrastructure Adapter + Application Port | 通讯录、模板、提交、回调、轮询和状态翻译 | 不建设本地审批流引擎或继续抽象未来渠道 |
|
||||
| #40 | 旧 Service/Query | 下架套餐续费资格与可购列表过滤 | 不迁移套餐或订单全模块 |
|
||||
|
||||
依赖通过构造函数结构体字段注入。旧 Service 保持现有注入方式;企微 Adapter 实现现有 `internal/application/approval` Port,由 bootstrap 同时注入 API、回调和 Worker。新增 Handler 必须同步两个文档生成器。
|
||||
|
||||
### 3. API 只表达当前业务,不暴露通用平台
|
||||
|
||||
所有接口继续使用 Fiber、Validator、`pkg/errors` 和 `{code,msg,data,timestamp}` 统一响应;既有项目字段名为 `msg`,不引入另一套 `message` 字段。列表默认 20、最大 100,平台/代理数据权限沿用现有中间件与 GORM Callback。
|
||||
|
||||
关键契约如下:
|
||||
|
||||
| 场景 | 接口决定 |
|
||||
| --- | --- |
|
||||
| 店铺 C 端登录限制 | 店铺模型和管理接口增加 `client_login_disabled`,默认 `false`;复用店铺更新接口或增加单一语义 PATCH。C 端 `verify-asset` 在签发 asset token 前按资产 `shop_id` 拦截 |
|
||||
| 企微账号绑定 | 账号列表/详情返回 `wecom_corp_id`、`wecom_userid`、`wecom_name`;管理员更新账号时选择通讯录成员,不提供个人扫码接口 |
|
||||
| 企微通讯录 | 提供分页成员选择接口,支持姓名或 userid 搜索;只展示应用可见成员,不依赖手机号/邮箱 |
|
||||
| 企微场景配置 | 管理员配置 `business_type → template_id` 及业务字段到控件 ID/类型/选项 key 的映射;模板必须先在企微后台创建 |
|
||||
| 实名顺序 | 保持已约定的单资产 PATCH、卡批量 POST、设备批量 POST;枚举仅为 `none/before_order/after_order`,批量上限 500、全成全败 |
|
||||
| 列表人员字段 | 退款、充值、换货返回提交人 ID/名称;审批人仅从已同步企微详情中的 userid 批量映射,映射不到返回空,不实时调用企微 |
|
||||
| 批量订购 | CSV 只有资产标识列;请求额外选择一个套餐和一个支付方式,不选择代理;整批使用相同参数 |
|
||||
| 导出 | 复用现有导出任务入口,通过六个 datasource code 区分;每类只输出本业务已有且已确认的字段 |
|
||||
| 支付方式 | `system_config` 分别保存卡和设备允许的支付方式;两类资产默认均启用 `wallet/wechat/alipay`,三项可独立取消但至少保留一项;C 端返回当前购包场景有效集合,创建订单时后端再次校验 |
|
||||
| 设备批量分配 | 复用现有导入任务 API 和状态模型,CSV 单列设备标识;业务参数选择“代理”或“套餐系列”及目标 ID |
|
||||
|
||||
### 4. 店铺登录限制在资产令牌前拦截
|
||||
|
||||
`client_auth.Service.VerifyAsset` 解析出卡或设备后取得资产 `shop_id`,再读取店铺开关。无店铺/平台库存保持当前行为;开关关闭时返回统一禁止错误,不签发短期资产令牌,因此不会进入微信登录、客户创建或资产绑定。
|
||||
|
||||
不在每个 C 端业务接口重复查询店铺开关,也不吊销已有 Token。这严格对应“不能登录”而不是“立即踢下线”,并避免在本期引入 Token 版本和全局会话撤销。
|
||||
|
||||
### 5. 企微只做 Adapter,审批流程仍由企微模板拥有
|
||||
|
||||
系统按应用保存加密 Secret、回调 Token、EncodingAESKey、`corp_id/agent_id` 和一个从当前可见成员中选择的默认审批发起人;`access_token` 按应用缓存并预留提前刷新。管理员从应用可见通讯录选择成员,账号绑定键为 `(corp_id, userid)`,姓名和部门仅为展示快照。内部员工已绑定且仍可见时优先以本人发起,代理等非企微账号始终以应用默认成员发起;本地申请仍保留真实业务提交人,不用默认成员冒充业务操作者。
|
||||
|
||||
场景配置保存已知 `template_id`。保存或发布时调用 `oa/gettemplatedetail` 校验控件 ID、类型、必填项和选择项 key。系统不创建模板、不保存审批节点、不计算部门领导或财务人员;`oa/applyevent` 使用 `use_template_approver=1`,审批人由企微后台模板决定。
|
||||
|
||||
提交过程:
|
||||
|
||||
1. 业务用例校验内部提交人的企微绑定或应用默认发起人仍可见、场景已启用且模板结构有效。
|
||||
2. 退款或线下充值在同一 GORM 事务保存业务申请、通用审批实例和提交 Outbox。
|
||||
3. Adapter 消费提交事件,上传必要附件并调用 `oa/applyevent`,保存返回的 `sp_no` 到通用实例 `external_ref`。
|
||||
4. 外部调用记录 Integration Log;结果未知时不盲目创建第二张审批单,转人工/查询恢复。
|
||||
5. 回调端点在 5 秒内完成验签解密、必要幂等记录和快速响应,再异步调用 `getapprovaldetail`。
|
||||
6. Adapter 将企微状态翻译为现有标准决策;现有决策 Outbox 分别驱动退款终结或线下代充入账。
|
||||
7. 定时任务扫描未终态实例,并用批量拉取审批单号与详情查询补偿回调遗漏。
|
||||
|
||||
企业微信审批详情的审批节点 userid 可以用于页面展示,但只做读取投影;不能映射系统账号时返回空名称,不影响业务终态。
|
||||
|
||||
### 6. 通知只复用现有站内能力
|
||||
|
||||
- 创建物流换货单成功后,向关联个人客户写一次站内通知;C 端按既有未读弹窗机制展示。
|
||||
- 店铺主钱包余额从不低于 100 元变为低于 100 元时,向 `business_owner_account_id` 对应后台账号发送一次通知;余额恢复到阈值以上后允许下一次下降再次提醒。
|
||||
- 每日扫描预计套餐到期日在 15、7、3 天节点的资产,生成后台/代理临期列表所需标识,并向关联个人客户发送节点通知。相同资产、节点、到期日只通知一次。
|
||||
|
||||
通知属于可靠副作用时使用现有 Outbox 和幂等键;不新增短信、企微业务员提醒或自定义营销内容。
|
||||
|
||||
### 7. 批量与导出复用已完成基础设施
|
||||
|
||||
批量订购和设备分配只新增业务解析器/执行器,继续使用现有对象存储、任务五态、Asynq 重试、失败明细和下载能力。文件级校验失败不写业务数据;涉及扣款/订购时按现有订单幂等键和钱包流水保证不重复扣款。
|
||||
|
||||
六类导出分别实现 datasource,查询直接投影 DTO,不串联多个业务迁移。不存在的字段不得临时创造含义;先从既有数据组合,确认确实缺失后再增加最小字段和迁移。
|
||||
|
||||
### 8. 数据、事务、常量与缓存
|
||||
|
||||
- 新表/字段通过 PostgreSQL 增量迁移添加,不建外键,不写 GORM 关联标签。
|
||||
- 简单列表字段使用批量 `WHERE id IN (...)` 或 JOIN 投影,禁止逐行查询账号。
|
||||
- 批量实名策略在单事务内校验全部资产后统一更新;任何冲突则全部回滚。
|
||||
- 退款和充值金额继续使用现有 Domain Ledger/钱包流水与乐观锁;审批 Adapter 不直接修改余额或退款金额。
|
||||
- 所有新枚举、任务类型、场景编码、导出 datasource code 和 Redis key 在 `pkg/constants` 定义并写中文注释。
|
||||
- 企微 token 使用 Redis 缓存和短锁;Redis 不可用时允许受控回源,但不得把 token 返回前端。
|
||||
|
||||
### 9. Audit Event、Domain Ledger、Integration Log 与 Outbox 决策
|
||||
|
||||
| 切片 | Audit Event | Domain Ledger | Integration Log | Outbox |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 字段、筛选、Bug、只读导出 | N/A:无敏感状态变更;实现时登记覆盖基线理由 | N/A 或沿用既有订单/套餐事实 | N/A | N/A |
|
||||
| 店铺登录开关、企微账号绑定、模板场景、支付方式配置 | 使用现有统一审计接缝记录操作者和前后值;不建设新的审计中心 | N/A | 模板校验调用记录 | 缓存失效仅沿用现有配置机制 |
|
||||
| 退款、线下代充 | 审批申请人和配置操作进入既有审计/申请快照;企微自动终态不伪造人工操作者 | 沿用退款、订单、钱包流水 | 所有企微请求、响应摘要、回调和结果未知 | 申请提交、标准终态和资金后续动作使用现有 Outbox |
|
||||
| 通知 | N/A:通知记录本身是投递事实 | N/A | N/A | 必须,按业务键和接收人防重 |
|
||||
| Gateway 限速 | 记录后台操作者、资产和档位 | N/A | 必须记录请求、响应及结果未知 | 无后续可靠副作用时 N/A |
|
||||
| 批量订购/分配 | 沿用任务提交审计;资金动作沿用既有记录 | 订购扣款沿用订单/钱包流水 | 涉及外部支付或 Gateway 时沿用 | 仅业务已有可靠副作用继续使用 |
|
||||
|
||||
实现前必须增量更新 `.scratch/tech-global-audit/审计覆盖基线.md`,不得因本 Change 排除全局 Audit Event 专项而跳过上述已有审计接缝。
|
||||
|
||||
### 10. 续费、待支付订单与强充支付方式边界
|
||||
|
||||
- 历史已支付订单和资产当前套餐只提供稳定的资产、套餐引用。用户点击续费时继续调用现有 C 端创建订单接口并生成一张新订单,不新增续费接口,也不在旧订单上修改支付方式。
|
||||
- 续费新订单创建前按当前 `system_config`、当前套餐价格/流量和当前强充状态重新选择支付方式;订单创建成功后,和普通订单一样固化所选方式。既有待支付订单再次支付时只能执行其已保存方式,不允许切换。
|
||||
- 卡和设备分别维护非空支付方式集合,合法选项均为 `wallet`、`wechat`、`alipay`,三项可独立启停;首次初始化两类资产均全选。配置非法或读取失败时失败关闭,不推断或放开支付渠道。
|
||||
- C 端购包接口返回“配置集合与当前业务场景的交集”。当前资产仍命中既有强充规则时必须剔除 `wallet`;普通钱包充值场景固定剔除 `wallet`。后端在创建订单、创建充值单和支付准备时执行相同校验,不能信任前端展示结果。
|
||||
- 强充继续沿用现有流程:第三方支付充值成功后先入资产钱包,再通过既有异步自动购包任务扣钱包并购买所选套餐。支付方式配置不得把强充改成提示后由用户手工充值。
|
||||
- 强充资格必须复用既有一次性佣金状态语义:`first_recharge` 与 `accumulated_recharge` 是同一强充/自动购包流程的不同触发配置;该系列一次性佣金已经触发后不再强充。不得在 C 端订单服务复制一套遗漏已触发状态的判断。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **[企微应用看不到提交人]** → 保存绑定前只允许选择应用可见成员,提交前再次校验;权限变化时明确失败,不创建业务申请和审批实例。
|
||||
- **[管理员修改企微模板导致控件映射失效]** → 保存/发布场景时拉取模板详情并保存指纹,提交前校验关键控件;失效只阻断新提交,存量审批继续同步。
|
||||
- **[回调丢失或重复]** → 回调快速响应、按 `sp_no` 幂等同步,保留未终态轮询和时间窗补偿。
|
||||
- **[企微提交超时但实际成功]** → 标记结果未知并先查详情/批量单号,不直接重试创建,避免重复审批。
|
||||
- **[简单需求再次被扩张]** → tasks 明确每个切片的不迁移范围;实现阶段不得改变架构通道,确需改变必须先更新提案。
|
||||
- **[大批量影响接口响应]** → 实名批量限制 500 且同步事务;CSV 和导出走异步任务,列表/人员映射使用批量查询。
|
||||
- **[店铺开关不能立即踢掉在线用户]** → 这是本期有意取舍;接口和文档明确只阻止新登录。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 先完成仓库证据核验,确认公共 Outbox、审批核心、通知、导入/导出和 `system_config` 可复用,禁止重复建表。
|
||||
2. 按纵向切片先交付无外部依赖的 Bug、字段、筛选、开关、实名、支付配置和本地通知。
|
||||
3. 增量迁移企微应用/场景/账号绑定及确实缺失的业务字段,迁移脚本保持向前兼容,不删除旧字段。
|
||||
4. 部署企微 Adapter、回调和轮询 Worker,配置测试应用、模板和账号绑定,完成真实退款/线下代充闭环后再关闭旧人工审批入口。
|
||||
5. 部署批量订购、导出、限速和设备批量分配,完成前后端联调与任务失败恢复验证。
|
||||
6. 完成生产代码、必要迁移文件、接口契约、配置说明和实施证据,以 `gofmt`、只读检查与 `git diff --check` 做静态收口并交付联调;自动化测试、构建、迁移执行和真实外部环境验收由后续流程承担。
|
||||
|
||||
回滚时,简单字段和兼容接口可回退应用版本;新增列/表暂不删除。产生企微审批、退款、充值、钱包流水、订单、通知、Outbox 或 Integration Log 后不得清表或恢复旧 Writer,只能暂停新入口并前向修复。旧人工审批入口仅在新链路验证完成后停用,因此切换前仍可回退。
|
||||
|
||||
## Open Questions
|
||||
|
||||
无阻塞性业务问题。实施和部署阶段仅需提供测试环境企微应用 Secret、可见范围、模板 ID、可信 IP、回调 Token/EncodingAESKey,以及 Gateway 测试配置;这些是环境输入,不改变需求范围。
|
||||
@@ -0,0 +1,53 @@
|
||||
## Why
|
||||
|
||||
`complete-july-iteration-test-release` 将七月迭代中的字段、筛选和局部缺陷扩大成模块迁移、通用平台和长依赖链,且遗漏或误解了多条 CSV 需求,已经不能作为可靠实施契约。feature-2026-july-confirmed-scope 依据已填写的需求范围确认表重新建立一个可直接上线的最小交付 Change:优先复用现有代码,完成用户可见需求,不借本期工作重构未触碰模块。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 新 Change 完整替代 `complete-july-iteration-test-release` 的后续规划;旧 Change 在本 Change 校验通过前保留完成证据,之后归档为被替代,不继续执行其未完成任务。
|
||||
- 实施遵循“存量能力优先”:优先修改或复用现有接口、字段、Service、Query、任务和基础设施;只有现有能力确实无法承载已确认需求时,才允许做边界最小、可说明必要性的新增。
|
||||
- 本 Change 当前交付不编写或补齐测试代码,不运行单元、集成、验收或业务流程测试,也不执行 `go test`、`go build`、LSP 诊断、迁移和 OpenAPI 生成;只完成生产代码、必要迁移文件和契约文档至可联调状态,可使用 `gofmt`、只读检查与 `git diff --check` 做静态收口。
|
||||
- 将 #189、#181、#57 等缺陷和 #53、#44、#182 等字段/筛选需求收敛为旧 Service、Store 和现有 Query 上的局部修改,不进行 DDD 迁移。
|
||||
- 增加店铺级 C 端登录限制开关;仅阻止该店铺资产发起新登录,不建立代理 API 权限体系,也不强制吊销已登录 Token。
|
||||
- 补齐三种实名顺序及卡/设备批量配置,保持既有 `realname_policy` 模型和前后端接口约定。
|
||||
- 复用已有站内通知,完成换货单弹窗、固定 100 元钱包余额提醒、套餐 15/7/3 天临期列表和 C 端提醒,不建设通用营销平台。
|
||||
- 复用已完成的渠道无关审批核心,交付最小企业微信 Adapter:管理员从通讯录为系统账号绑定 `(corp_id, userid)`,后台创建模板,本系统配置业务场景/模板/控件映射,完成发起、回调、详情查询和轮询补偿;不做扫码绑定、本地流程设计器或未来渠道抽象扩建。
|
||||
- **BREAKING**:退款和员工线下代充值的人工审批结果改由企业微信终态驱动;新链路可用后停用原系统内人工通过/驳回入口。代理在线扫码充值后置。
|
||||
- 交付单列 CSV 批量订购、六类业务导出、固定档位限速、按资产类型配置支付方式、CSV 批量分配设备等已确认功能,均复用现有导入、导出、Gateway、`system_config` 和任务基础设施。
|
||||
- 冻结已经完成的 #45、#46、#55、#60、#86、#38、#94、#96、#98、#43,仅做接口联调或代码证据核验;冻结行业卡现有复机行为,不按旧提案改写。
|
||||
- 明确排除原路退款、聚水潭、跨品类换货、分销佣金提现、代理在线扫码充值、通用营销/ERP、自动限速、本地审批流引擎、全局 Audit Event 专项,以及已关闭且不处理的需求。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `shop-client-login-control`: 店铺级 C 端新登录开关及资产登录拦截。
|
||||
- `wecom-approval-integration`: 企微应用、通讯录账号映射、模板场景绑定、审批发起、回调和轮询补偿。
|
||||
- `package-expiration-reminder`: 套餐 15/7/3 天临期列表、站内通知和 C 端弹窗提醒。
|
||||
- `asset-package-batch-order`: 单列 CSV、整批统一套餐和支付方式的批量订购。
|
||||
- `business-data-export`: lot 卡、钱包流水、套餐、退款、换货和代理充值六类独立导出。
|
||||
- `asset-speed-tier-management`: 后台为有权限 IoT 卡手动选择固定限速档位并调用 Gateway;不提供设备限速。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `card-replacement`: 换货前拦截活跃退款,并修复换货套餐在原订单退款后未失效的问题。
|
||||
- `exchange-client-notification`: 创建物流换货单后复用站内通知在 C 端弹窗。
|
||||
- `order-management`: 修复 C 端订单渠道和订单资产标识返回,并支持从历史订单使用稳定资产/套餐引用发起新的下架套餐续费订单。
|
||||
- `refund-api`: 返回正确设备资产标识、提交人,并将退款审批结果切换为企微驱动。
|
||||
- `agent-recharge`: 返回提交人,仅保留员工线下代充值并接入企微审批,代理在线扫码充值后置。
|
||||
- `exchange-admin-management`: 换货列表和详情返回提交人。
|
||||
- `asset-realname-policy`: 支持三种实名顺序、单资产修改、卡/设备批量修改和 C 端生效策略字段。
|
||||
- `asset-queries`: 卡按自身实名状态筛选;设备任意一张有效绑定卡已实名即视为设备已实名。
|
||||
- `agent-wallet`: 主钱包低于固定 100 元时向店铺业务员发送一次站内预警。
|
||||
- `package-management`: 下架套餐从普通可购列表排除,但允许当前使用者通过续费入口购买。
|
||||
- `payment-dynamic-config`: 通过 `system_config` 分别配置卡和设备允许的支付方式;两类资产初始化均全选钱包、微信和支付宝,三项可独立取消,C 端再按强充场景过滤钱包并由订单端复核。
|
||||
- `device`: 复用现有导入任务模式,通过单列 CSV 批量分配设备所属代理或套餐系列。
|
||||
|
||||
## Impact
|
||||
|
||||
- **后端范围**:局部触及换货、订单、退款、充值、资产、店铺、套餐、钱包、设备导入、导出、通知和 Gateway;只有审批终态与资金处理保留现有 Application/Domain 边界,简单字段、筛选和 Bug 沿用 `Handler → Service → Store → Model` 或既有 Query。
|
||||
- **API/前端**:新增店铺登录开关、企微账号选择/绑定、模板场景配置、实名批量配置、批量订购、导出、限速和设备批量分配接口;修改列表/详情字段、C 端初始化与支付方式返回。新增 Handler 必须同步 `cmd/api/docs.go`、`cmd/gendocs/main.go` 和 OpenAPI 文档。
|
||||
- **数据与基础设施**:使用 PostgreSQL、Redis/Asynq、现有 Outbox、Integration Log、对象存储、站内通知和 `system_config`;不新增外键或 GORM 关联标签,不引入新依赖。
|
||||
- **外部系统**:企业微信自建应用与 Gateway。企微上线需应用 Secret、审批权限、通讯录可见范围、可信 IP、回调 Token/EncodingAESKey 和模板 ID。
|
||||
- **性能**:列表保持分页并批量解析提交人/审批人,禁止 N+1;批量实名上限 500 且事务全成全败;CSV/导出沿用异步任务,外部接口设置超时、幂等和补偿。
|
||||
- **交付边界**:本 Change 以生产代码、迁移文件、接口契约、联调配置和实施证据齐备为完成标准;自动化测试、构建、LSP、迁移执行、OpenAPI 生成及真实企微/Gateway 环境验收均不在本次执行范围,后续联调或发布流程另行承担。
|
||||
@@ -0,0 +1,27 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 充值查询返回提交人
|
||||
代理充值列表和详情 SHALL 返回提交人账号 ID 和名称;账号名称 MUST 通过批量查询获得,代理仍只能查看自身店铺范围内记录。
|
||||
|
||||
#### Scenario: 查询线下代充值记录
|
||||
- **WHEN** 有权限用户查询员工提交的线下代充值
|
||||
- **THEN** 响应包含提交人 ID、提交人名称和当前审批状态
|
||||
|
||||
### Requirement: 员工线下代充值使用企微审批
|
||||
平台员工创建线下代充值申请时 MUST 固化目标店铺、金额、凭证和真实提交人,并关联唯一通用审批实例;approved 首次终态 SHALL 调用现有钱包入账用例,其他终态不得入账。代理在线扫码充值不属于本 Change。
|
||||
|
||||
#### Scenario: 企微审批通过线下代充值
|
||||
- **WHEN** 有效线下代充值申请首次取得 approved 标准决策
|
||||
- **THEN** 系统以唯一业务号幂等增加目标店铺主钱包余额并写入钱包流水
|
||||
|
||||
#### Scenario: 企微审批拒绝线下代充值
|
||||
- **WHEN** 申请取得 rejected、cancelled 或 deleted 标准决策
|
||||
- **THEN** 系统终结申请且不增加钱包余额
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: 线下充值确认
|
||||
**Reason**: 操作密码人工确认无法满足已确认的企微模板审批流程,并可能绕过部门领导和财务审批。
|
||||
|
||||
**Migration**: 企微审批闭环可用后停用 `POST /api/admin/agent-recharges/:id/offline-pay`;存量记录按切换清单处理,新申请只接受企微终态。
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 主钱包低余额通知业务员
|
||||
店铺主钱包可用余额低于固定 100 元时,系统 SHALL 向店铺 `business_owner_account_id` 对应业务员发送站内通知;不提供按店铺自定义阈值,不向无业务员的店铺猜测接收人。
|
||||
|
||||
#### Scenario: 余额首次降至阈值以下
|
||||
- **WHEN** 主钱包余额从不少于 100 元变为少于 100 元且店铺已配置业务员
|
||||
- **THEN** 业务员收到一条低余额站内通知
|
||||
|
||||
#### Scenario: 阈值以下重复余额变化
|
||||
- **WHEN** 钱包已处于 100 元以下又发生多次扣减
|
||||
- **THEN** 系统不重复发送同一低余额周期通知;余额恢复后再次跌破可重新通知
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 单列 CSV 创建批量订购任务
|
||||
内部员工 SHALL 上传仅包含资产标识的 CSV,并在请求中为整批选择一个套餐和一个支付方式;系统不得要求 CSV 包含代理、套餐系列或支付账户,也不得按行选择不同套餐。
|
||||
|
||||
#### Scenario: 创建合法批量订购任务
|
||||
- **WHEN** 员工上传单列资产 CSV 并选择有效套餐与支付方式
|
||||
- **THEN** 系统创建异步任务并通过统一响应返回任务 ID 和初始状态
|
||||
|
||||
### Requirement: 批量订购复用现有订单和扣款规则
|
||||
任务执行器 MUST 对每个资产执行现有购买资格、支付方式、价格和幂等校验,生成成功/失败明细;重复消费不得重复创建订单或扣款。
|
||||
|
||||
#### Scenario: 部分资产校验失败
|
||||
- **WHEN** 一批资产中部分资产不可购买所选套餐
|
||||
- **THEN** 系统保留每行失败原因并继续处理可独立成功的其他资产,任务汇总真实成功和失败数量
|
||||
|
||||
#### Scenario: 任务被重复投递
|
||||
- **WHEN** Asynq 重复执行同一批量订购任务
|
||||
- **THEN** 已成功行不重复下单或扣款
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 卡和设备列表支持实名状态筛选
|
||||
卡列表 SHALL 按卡自身有效实名状态筛选;设备列表 SHALL 在任意一张当前有效绑定卡已实名时将设备视为已实名,否则视为未实名。查询 MUST 使用现有表联查或子查询,不得为该筛选新增历史投影、Worker 或事件消费者。
|
||||
|
||||
#### Scenario: 设备有一张实名绑定卡
|
||||
- **WHEN** 设备绑定多张有效卡且其中任意一张已实名
|
||||
- **THEN** 设备出现在“已实名”筛选结果中且不出现在“未实名”结果中
|
||||
|
||||
#### Scenario: 设备没有实名绑定卡
|
||||
- **WHEN** 设备不存在任何已实名的有效绑定卡
|
||||
- **THEN** 设备出现在“未实名”筛选结果中
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 实名顺序只使用三种策略
|
||||
资产实名策略 MUST 为 `none`、`before_order` 或 `after_order`;设备下卡存在策略冲突时,C 端有效流程 MUST 以设备策略为准,前端不得自行覆盖。
|
||||
|
||||
#### Scenario: 设备策略覆盖下卡策略
|
||||
- **WHEN** 设备与其绑定卡配置了不同实名策略
|
||||
- **THEN** C 端返回并执行设备的 effective_realname_policy
|
||||
|
||||
### Requirement: 支持单资产和批量修改实名策略
|
||||
系统 SHALL 提供已约定的单资产 PATCH、卡批量 POST 和设备批量 POST;批量请求最多 500 个 `asset_ids`,MUST 先校验全部资产和权限后在单事务中全成全败。
|
||||
|
||||
#### Scenario: 批量修改包含无权限资产
|
||||
- **WHEN** 请求中的任一资产不存在、无权限或策略冲突
|
||||
- **THEN** 系统返回明确错误且所有资产策略均保持不变
|
||||
|
||||
### Requirement: C 端初始化返回生效策略
|
||||
C 端资产初始化响应 SHALL 包含 `effective_realname_policy`、`realname_required` 和 `realname_status`,并使用 `{code,msg,data,timestamp}` 统一响应。
|
||||
|
||||
#### Scenario: 无需实名资产初始化
|
||||
- **WHEN** 资产有效策略为 `none`
|
||||
- **THEN** 响应返回 `effective_realname_policy=none` 且允许直接进入购买流程
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 后台手动设置卡固定限速档位
|
||||
有权限的后台账号 SHALL 为 IoT 卡选择系统定义的固定限速档位,系统 MUST 校验卡权限和档位常量后调用现有 Gateway;Gateway 限速对象只能是卡 ICCID,系统不得提供设备限速或通过设备绑定关系间接限速,也不得根据流量、套餐或运营商规则自动计算档位。
|
||||
|
||||
#### Scenario: 成功设置限速档位
|
||||
- **WHEN** 管理员为有权限 IoT 卡选择有效固定档位
|
||||
- **THEN** 系统以该卡 ICCID 调用 Gateway 并返回统一响应,记录操作者、卡、档位和外部交互结果
|
||||
|
||||
#### Scenario: 设备不得调用限速
|
||||
- **WHEN** 用户尝试通过设备或设备绑定关系设置限速
|
||||
- **THEN** 系统不提供该接口且不得向 Gateway 发送设备 IMEI、设备 ID 或设备当前绑定卡
|
||||
|
||||
#### Scenario: Gateway 结果未知
|
||||
- **WHEN** Gateway 请求超时且结果不可确定
|
||||
- **THEN** 系统返回可识别的结果未知错误并保留 Integration Log,不盲目声称设置成功
|
||||
@@ -0,0 +1,16 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 六类业务数据可独立导出
|
||||
系统 SHALL 复用现有导出任务框架,分别提供 lot 卡、钱包流水、套餐、退款、换货和代理充值 datasource;每类导出按当前业务权限和筛选条件读取已有字段,不建设统一字段权限平台。
|
||||
|
||||
#### Scenario: 创建退款导出任务
|
||||
- **WHEN** 有权限用户以退款列表筛选条件创建退款导出
|
||||
- **THEN** 系统异步生成仅包含其可见退款数据的文件,并通过任务接口提供进度和下载结果
|
||||
|
||||
### Requirement: 导出字段必须有稳定来源
|
||||
每个 datasource MUST 明确列名、数据来源和格式;现有表或可批量关联数据无法提供的字段不得伪造,必须先补充经确认的最小数据字段后才能导出。
|
||||
|
||||
#### Scenario: 字段没有数据来源
|
||||
- **WHEN** 业务要求字段在现有数据中不存在且无法可靠推导
|
||||
- **THEN** 该 datasource 不得用空含义或错误值冒充字段,任务保持未交付直到字段契约确认
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 未终结退款阻止换货
|
||||
现有换货创建用例 MUST 在写入换货单前查询该资产关联订单的未终结退款;存在时返回“该资产存在退款申请”,且不得创建换货单或修改资产。
|
||||
|
||||
#### Scenario: 资产存在活跃退款
|
||||
- **WHEN** 管理员为存在未终结退款申请的卡或设备创建换货单
|
||||
- **THEN** 系统拒绝操作并返回业务错误“该资产存在退款申请”
|
||||
|
||||
### Requirement: 原订单退款必须失效迁移后的套餐权益
|
||||
当套餐权益已随换货迁移到新资产后,原购买订单退款终态处理 MUST 沿换货迁移关系定位新资产上的对应套餐使用记录并按现有退款规则失效,不得因仍按旧资产路径查询而遗漏。
|
||||
|
||||
#### Scenario: 换货后原订单退款通过
|
||||
- **WHEN** 旧资产套餐已迁移到新资产且原购买订单退款通过
|
||||
- **THEN** 新资产上由该订单产生的套餐权益被正确失效,其他订单权益不受影响
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: CSV 批量分配设备代理或套餐系列
|
||||
系统 SHALL 复用现有设备导入任务模式,接收仅含设备标识的 CSV,并由请求参数选择本批操作类型和一个目标代理或套餐系列;不得为该需求新建独立任务平台。
|
||||
|
||||
#### Scenario: 批量分配目标代理
|
||||
- **WHEN** 管理员上传合法设备 CSV 并选择一个有权限目标代理
|
||||
- **THEN** 异步任务按现有分配规则处理设备并输出成功、失败明细
|
||||
|
||||
#### Scenario: 批量设置套餐系列
|
||||
- **WHEN** 管理员上传合法设备 CSV 并选择一个有效套餐系列
|
||||
- **THEN** 异步任务按现有设备系列绑定规则处理并输出结果
|
||||
|
||||
#### Scenario: 重复消费任务
|
||||
- **WHEN** 同一设备分配任务被重复投递
|
||||
- **THEN** 系统保持目标关系一致且不产生重复分配副作用
|
||||
@@ -0,0 +1,9 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 换货查询返回提交人
|
||||
换货列表和详情 SHALL 返回创建该换货单的提交人账号 ID 和名称;名称 MUST 批量解析并遵守当前平台/代理店铺权限。
|
||||
|
||||
#### Scenario: 查询换货列表
|
||||
- **WHEN** 有权限用户查询换货列表
|
||||
- **THEN** 每条可见记录包含提交人 ID 和提交人名称
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 创建物流换货单后发送 C 端通知
|
||||
物流换货单创建成功后,系统 SHALL 复用现有个人客户站内通知能力向该资产关联客户发送一次换货提醒,C 端 SHALL 按既有未读通知机制弹窗;通知不包含通用营销配置、ERP 发货或自动创建其他单据。
|
||||
|
||||
#### Scenario: 成功创建物流换货单
|
||||
- **WHEN** 后台成功创建需要客户处理的物流换货单
|
||||
- **THEN** 关联客户收到一条可在 C 端弹窗展示的站内通知
|
||||
|
||||
#### Scenario: 重复处理创建事件
|
||||
- **WHEN** 同一换货单通知事件被重复消费
|
||||
- **THEN** 同一客户不会收到重复通知
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: C 端订单保存并返回正确渠道
|
||||
C 端创建的卡或设备订单 MUST 使用既有 `purchase_role` 保存正确订单渠道;订单列表和详情 SHALL 返回渠道以及对应资产标识,卡使用 ICCID,设备优先使用 VirtualNo、为空时使用 IMEI,不得使用 SN 或误用卡字段导致标识为空。
|
||||
|
||||
#### Scenario: 查询 C 端设备订单
|
||||
- **WHEN** 管理员查询由 C 端创建的设备订单列表或详情
|
||||
- **THEN** 响应返回 `purchase_role` 以及按 VirtualNo 优先、IMEI 兜底生成的设备资产标识
|
||||
|
||||
### Requirement: 历史订单可发起新的下架套餐续费订单
|
||||
历史订单 SHALL 提供续费所需的稳定套餐和资产引用;当前使用该下架套餐的资产可以从历史订单发起一张新订单,并在创建前按当前配置选择支付方式。系统不得修改原历史订单,也不得为续费新增另一套下单接口。
|
||||
|
||||
#### Scenario: 从历史订单续费下架套餐
|
||||
- **WHEN** 客户从自身历史订单对仍在使用的下架套餐发起续费
|
||||
- **THEN** 系统使用现有创建订单接口按套餐当前价格、流量和支付配置创建新订单,并允许客户重新选择当前有效支付方式
|
||||
|
||||
#### Scenario: 支付既有待支付订单
|
||||
- **WHEN** 客户对已经创建的普通或续费待支付订单再次发起支付
|
||||
- **THEN** 系统只执行该订单创建时保存的支付方式,不允许在原订单上切换渠道
|
||||
@@ -0,0 +1,16 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 生成套餐临期节点
|
||||
系统 SHALL 每日识别预计套餐到期时间距离当前 15、7、3 天的资产;后台和代理端 SHALL 提供分页临期列表及数量,临期项可高亮并优先展示,查询必须遵守店铺层级权限。
|
||||
|
||||
#### Scenario: 资产进入七天节点
|
||||
- **WHEN** 某资产预计套餐到期时间距当前日期为 7 天
|
||||
- **THEN** 系统在有权限的临期列表和数量中包含该资产
|
||||
|
||||
### Requirement: C 端按节点接收站内提醒
|
||||
系统 SHALL 复用现有站内通知,在每个资产、到期日和 15/7/3 天节点最多向关联个人客户发送一次续费提醒;C 端继续使用既有未读通知弹窗,不新增营销内容配置。
|
||||
|
||||
#### Scenario: 定时任务重复执行
|
||||
- **WHEN** 同一天同一节点的扫描任务重复执行
|
||||
- **THEN** 同一接收人只产生一条对应资产的临期通知
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 下架套餐只允许符合资格的续费
|
||||
普通 C 端可购套餐列表 MUST 排除下架套餐;当前资产在当前世代仍有该套餐的生效中使用记录时,客户可通过资产当前套餐或历史订单提供的稳定引用调用现有创建订单接口续费,代理代购和新客户普通购买仍 MUST 被拒绝。续费始终读取套餐当前价格、流量和其他配置,不沿用历史权益快照。
|
||||
|
||||
#### Scenario: 当前客户续费下架套餐
|
||||
- **WHEN** 客户资产正在使用某下架套餐并通过资产或历史订单引用提交现有创建订单接口
|
||||
- **THEN** 系统按套餐当前配置允许创建一张新订单
|
||||
|
||||
#### Scenario: 新客户购买下架套餐
|
||||
- **WHEN** 非当前使用者通过普通购买入口请求下架套餐
|
||||
- **THEN** 系统返回套餐不可购买错误
|
||||
|
||||
#### Scenario: 套餐重新上架后购买
|
||||
- **WHEN** 原下架套餐更新价格或流量后重新上架
|
||||
- **THEN** 套餐重新出现在普通可购列表,普通购买和续费均使用更新后的当前配置
|
||||
@@ -0,0 +1,46 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 按资产类型配置允许支付方式
|
||||
`system_config` SHALL 分别定义卡和设备允许的支付方式集合。两个配置首次初始化均 MUST 包含 `wallet`、`wechat`、`alipay`;管理员可独立取消或重新勾选任一方式,但每个集合至少保留一种合法方式。C 端 SHALL 返回配置集合与当前业务场景限制的交集,订单创建和支付准备 MUST 在后端再次校验;配置为空、包含未知值、重复值或无法读取时失败关闭,不得放开全部方式。
|
||||
|
||||
#### Scenario: 卡配置不允许微信支付
|
||||
- **WHEN** 卡允许支付方式不包含微信且客户端尝试创建微信支付订单
|
||||
- **THEN** 系统拒绝订单或支付准备请求并返回支付方式不可用错误
|
||||
|
||||
#### Scenario: 前端读取设备允许方式
|
||||
- **WHEN** C 端初始化设备购买流程
|
||||
- **THEN** 响应只返回设备配置允许的支付方式列表
|
||||
|
||||
#### Scenario: 管理员独立关闭钱包
|
||||
- **WHEN** 管理员在卡或设备支付配置中取消勾选钱包且仍保留至少一种其他方式
|
||||
- **THEN** 系统保存配置,后续 C 端购包响应不再返回钱包且后端拒绝钱包支付请求
|
||||
|
||||
#### Scenario: 管理员取消全部方式
|
||||
- **WHEN** 管理员尝试取消某类资产的全部支付方式
|
||||
- **THEN** 系统拒绝更新并保留原有效配置
|
||||
|
||||
### Requirement: 强充场景过滤钱包并保持自动购包
|
||||
系统判定当前资产仍命中既有强充规则时,C 端购包可选方式 MUST 在资产配置集合基础上剔除 `wallet`;后端创建订单时 MUST 执行相同校验。选择有效微信或支付宝后 SHALL 继续沿用充值入账和异步自动购包流程,不得降级为提示用户手工充值。
|
||||
|
||||
#### Scenario: 首次强充选择支付方式
|
||||
- **WHEN** 当前资产尚未触发对应系列的一次性佣金且本次购包命中强充规则
|
||||
- **THEN** 前端响应不包含钱包,微信或支付宝支付成功后系统自动完成充值入账和后续购包
|
||||
|
||||
#### Scenario: 绕过前端提交钱包强充
|
||||
- **WHEN** 客户绕过前端在强充购包请求中提交 `wallet`
|
||||
- **THEN** 后端拒绝请求且不创建套餐订单、充值单或支付单
|
||||
|
||||
#### Scenario: 一次性佣金已经触发
|
||||
- **WHEN** 当前资产对应系列的一次性佣金已经触发
|
||||
- **THEN** 系统不再按该规则强充,购包可选方式重新按当前资产配置集合返回
|
||||
|
||||
### Requirement: 新订单选择方式并由待支付订单固化
|
||||
普通购买和从历史订单发起的续费 SHALL 在创建新订单前选择当前有效支付方式;新订单创建成功后 MUST 固化该方式,后续支付不得在原订单上切换。续费不是修改历史订单,历史订单和既有待支付订单均不得被覆盖支付方式。
|
||||
|
||||
#### Scenario: 从历史订单发起续费
|
||||
- **WHEN** 客户从历史已支付订单发起续费
|
||||
- **THEN** 客户可从当前有效集合重新选择方式并创建一张新订单
|
||||
|
||||
#### Scenario: 待支付订单尝试切换方式
|
||||
- **WHEN** 客户支付既有待支付订单时提交与订单保存值不同的方式
|
||||
- **THEN** 系统拒绝切换并保持订单原支付方式
|
||||
@@ -0,0 +1,32 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 退款查询返回资产和提交人
|
||||
退款列表和详情 SHALL 返回提交人账号 ID、提交人名称及正确资产标识;设备退款必须返回设备标识而不是错误或空的卡标识。人员名称 MUST 批量查询,数据权限保持现有店铺层级规则。
|
||||
|
||||
#### Scenario: 查询设备退款
|
||||
- **WHEN** 有权限用户查询设备订单产生的退款
|
||||
- **THEN** 响应返回设备资产标识、提交人 ID 和提交人名称
|
||||
|
||||
### Requirement: 退款终态由企微审批驱动
|
||||
退款申请 SHALL 关联唯一通用审批实例;企微标准决策为 approved 时执行现有退款终结,rejected/cancelled/deleted 时按对应终态结束,不得由列表可见权限替代审批权限。
|
||||
|
||||
#### Scenario: 企微审批通过退款
|
||||
- **WHEN** 退款关联企微审批首次同步为 approved
|
||||
- **THEN** 系统幂等执行退款通过后的订单、套餐、资产和佣金处理
|
||||
|
||||
#### Scenario: 重复同步审批终态
|
||||
- **WHEN** 同一 approved 决策被回调和轮询重复同步
|
||||
- **THEN** 退款、订单和资金副作用只执行一次
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: 审批通过接口
|
||||
**Reason**: 审批决定改由企业微信模板流程产生,系统内人工通过会绕过既定审批链。
|
||||
|
||||
**Migration**: 企微测试闭环可用后停用 `POST /api/admin/refunds/:id/approve`;存量退款在切换清单中按原 provider 完成,不伪造企微审批。
|
||||
|
||||
### Requirement: 审批拒绝接口
|
||||
**Reason**: 审批拒绝改由企业微信回调或轮询同步的标准决策驱动。
|
||||
|
||||
**Migration**: 企微测试闭环可用后停用 `POST /api/admin/refunds/:id/reject`,前端改为只读展示审批状态。
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 店铺可配置 C 端新登录限制
|
||||
系统 SHALL 为每个店铺保存 `client_login_disabled`,默认值 MUST 为 `false`;平台管理员可通过店铺管理接口修改并读取该字段,响应 MUST 使用 `{code,msg,data,timestamp}`,代理不得修改其他店铺配置。
|
||||
|
||||
#### Scenario: 管理员开启店铺登录限制
|
||||
- **WHEN** 平台管理员将某店铺 `client_login_disabled` 更新为 `true`
|
||||
- **THEN** 系统保存配置并在店铺详情和列表中返回最新值
|
||||
|
||||
### Requirement: 受限店铺资产不得发起新登录
|
||||
C 端资产校验在签发短期资产令牌前 MUST 检查卡或设备所属店铺;受限店铺资产 MUST 返回统一禁止错误,未归属店铺的资产保持原行为,已有登录 Token 不因本开关被主动吊销。
|
||||
|
||||
#### Scenario: 受限店铺资产验证
|
||||
- **WHEN** 用户使用 `client_login_disabled=true` 店铺名下的卡或设备调用资产验证接口
|
||||
- **THEN** 系统返回禁止登录且不签发 asset token
|
||||
|
||||
#### Scenario: 已登录用户保持会话
|
||||
- **WHEN** 店铺开启限制前用户已经取得有效 C 端 Token
|
||||
- **THEN** 系统不因该配置主动吊销 Token,本需求只阻止后续新登录
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 管理员绑定系统账号与企微成员
|
||||
系统 SHALL 从已配置自建应用的可见通讯录分页查询成员,并允许管理员将系统账号绑定到 `(corp_id, userid)`;列表和详情 SHALL 返回 userid、姓名及绑定状态,手机号和邮箱不得作为绑定前置条件,不得提供用户扫码自助绑定流程。
|
||||
|
||||
#### Scenario: 从通讯录选择成员绑定账号
|
||||
- **WHEN** 管理员在账号列表为系统账号选择一个应用可见的企微成员
|
||||
- **THEN** 系统保存 corp_id、userid 和展示快照,后续审批可使用该 userid 作为申请人
|
||||
|
||||
#### Scenario: 绑定不可见成员
|
||||
- **WHEN** 管理员提交不在当前企微应用可见范围内的 userid
|
||||
- **THEN** 系统返回参数或权限错误且不保存绑定
|
||||
|
||||
### Requirement: 业务场景绑定企微后台模板
|
||||
系统 SHALL 允许管理员配置业务类型、企微 `template_id` 和业务字段到控件 ID/类型/选项 key 的映射;模板必须已在企微后台创建,系统 MUST 通过模板详情接口校验映射,不得在本系统创建审批节点或审批人规则。
|
||||
|
||||
#### Scenario: 发布有效场景配置
|
||||
- **WHEN** 管理员提交的模板存在且所有必填业务字段均能映射到有效控件
|
||||
- **THEN** 系统启用该场景并保存模板结构校验结果
|
||||
|
||||
#### Scenario: 模板控件已经变化
|
||||
- **WHEN** 模板详情与已配置的控件 ID、类型或选项 key 不一致
|
||||
- **THEN** 系统阻止新审批提交并返回明确配置错误,不影响存量审批同步
|
||||
|
||||
### Requirement: 使用绑定成员发起模板审批
|
||||
退款和员工线下代充值 SHALL 通过现有 Approval Port 创建通用审批实例和提交 Outbox;企微 Adapter MUST 对已绑定的内部员工使用本人 userid,对代理等非企微账号使用应用已配置且当前可见的默认发起人 userid,并使用 template_id、`use_template_approver=1` 和映射后的控件值调用 `oa/applyevent`。成功后保存 `sp_no`,业务 Domain/Application 不得解析企微 DTO 或状态码;通用实例中的真实业务提交人不得被默认企微发起人覆盖。
|
||||
|
||||
#### Scenario: 成功发起审批
|
||||
- **WHEN** 提交人已绑定企微成员、场景有效且业务申请校验通过
|
||||
- **THEN** 系统原子保存业务单、通用审批实例和提交事件,并最终把企微 sp_no 关联到该实例
|
||||
|
||||
#### Scenario: 发起前置不完整
|
||||
- **WHEN** 内部提交人未绑定且应用未配置可用默认发起人、代理提交时默认发起人不可见、模板无效或 Adapter 未配置
|
||||
- **THEN** 系统在写入业务单、审批实例和 Outbox 前失败关闭
|
||||
|
||||
#### Scenario: 代理发起退款审批
|
||||
- **WHEN** 代理账号提交退款且应用已配置当前可见的默认发起人
|
||||
- **THEN** 企微审批以默认成员作为 creator_userid 发起,但本地业务申请和通用审批实例仍记录代理为真实提交人
|
||||
|
||||
#### Scenario: 提交结果未知
|
||||
- **WHEN** 调用企微超时且无法判断审批是否创建成功
|
||||
- **THEN** 系统标记结果未知并进入查询恢复,不得直接创建第二张审批单
|
||||
|
||||
### Requirement: 回调和轮询共同同步审批结果
|
||||
系统 MUST 验证并解密 `sys_approval_change` 回调、快速响应,再按 sp_no 获取审批详情并幂等翻译为标准决策;系统 MUST 周期性查询未终态审批并通过批量单号/详情接口补偿回调遗漏。
|
||||
|
||||
#### Scenario: 重复审批回调
|
||||
- **WHEN** 同一审批状态变化被企微重复推送
|
||||
- **THEN** 系统只产生一次对应标准终态和一次业务终态处理
|
||||
|
||||
#### Scenario: 回调丢失
|
||||
- **WHEN** 企微审批已终态但系统未收到回调
|
||||
- **THEN** 轮询任务通过审批详情发现终态并驱动相同的标准决策同步用例
|
||||
|
||||
### Requirement: 审批人员只做可选读取映射
|
||||
系统 SHALL 从已同步审批详情的流程节点取得审批人 userid,并批量映射系统账号;能映射时返回审批人 ID/名称,不能映射时返回空值,不得为展示字段实时逐条调用企微或建立本地审批人规则。
|
||||
|
||||
#### Scenario: 审批人已绑定系统账号
|
||||
- **WHEN** 列表关联的企微审批详情包含已绑定 userid
|
||||
- **THEN** 列表返回对应系统账号 ID 和名称
|
||||
@@ -0,0 +1,57 @@
|
||||
## 0. 需求证据与实施基线
|
||||
|
||||
- [x] 0.1 建立本 Change 的需求证据矩阵,逐项核对 #45、#46、#55、#60、#86、#38、#94、#96、#98、#43 已有接口和迁移,记录只需联调的证据;同时确认 #73 当前行业卡复机行为不改。【主:只读核验|边界:代码与契约证据|不迁移:任何业务模块】
|
||||
|
||||
## 1. 局部缺陷、字段与筛选
|
||||
|
||||
- [x] 1.1 修复 #189:沿现有换货迁移关系让原订单退款准确失效新资产上的对应套餐权益,不影响其他订单权益。【主:旧 Service|边界:单个退款后套餐失效路径|不迁移:换货、退款、套餐模块】
|
||||
- [x] 1.2 修复 #181:C 端订单使用既有 `purchase_role` 保存正确渠道,订单/退款列表与详情中卡返回 ICCID,设备按 VirtualNo 优先、IMEI 兜底返回资产标识。【主:旧 Service/Query|边界:字段来源与 DTO|不迁移:订单或退款模块】
|
||||
- [x] 1.3 完成 #182/#44 的退款、充值、换货提交人 ID/名称返回,使用批量账号查询避免 N+1。【主:Query/旧 Store|边界:人员字段投影|不迁移:审批模型或历史回填】
|
||||
- [x] 1.4 完成 #53 卡/设备实名筛选:卡按自身状态,设备以任意有效绑定卡已实名为已实名。【主:Query/旧 Store|边界:实时 EXISTS 查询|不新建:投影、Worker、事件消费者】
|
||||
- [x] 1.5 完成 #57:在现有换货创建入口以当前未终结退款状态拦截并返回“该资产存在退款申请”。【主:旧 Service|边界:创建前单次校验|不依赖:企微 Adapter】
|
||||
- [x] 1.6 为 1.1~1.5 更新 DTO description、错误码、中文注释和相关 API 契约;未新增 Handler,不改两个文档生成器装配文件。【主:API 契约|不扩展:其他字段】
|
||||
|
||||
## 2. 店铺、实名、套餐与支付配置
|
||||
|
||||
- [x] 2.1 交付 #41 店铺 C 端登录限制纵向切片:增量迁移 `client_login_disabled`,复用店铺管理更新/查询,在 `VerifyAsset` 签发令牌前拦截;默认允许登录且不吊销已有 Token。【主:简单写 + 旧 Service|不迁移:认证和代理权限体系】
|
||||
- [x] 2.2 交付 #62 实名策略纵向切片:保持三种枚举和单资产 PATCH,补齐卡/设备批量接口(最多 500、事务全成全败)及 C 端 `effective_realname_policy` 等字段。【主:简单写/Query|不迁移:资产模块】
|
||||
- [x] 2.3 交付 #40 下架套餐续费:普通可购列表排除下架套餐,当前使用者通过资产/历史订单稳定引用调用现有下单接口创建新订单并按当前套餐配置购买,代理代购/新客户仍拒绝;既有待支付订单保持原支付方式。【主:旧 Service/Query|不新增:续费接口|不迁移:套餐、订单模块】
|
||||
- [x] 2.4 交付 #48 支付方式配置:在 `system_config` 为卡/设备分别初始化全选 `wallet/wechat/alipay` 且支持三项独立启停、至少保留一项;C 端返回配置与业务场景交集,强充和普通充值剔除钱包,订单/支付准备后端强校验,强充继续自动购包,并修复 C 端强充资格遗漏已触发状态的问题;增量更新审计覆盖基线。【主:简单写 + Query|辅:既有 system_config/强充自动购包|仅现有能力无法承载时做最小新增】
|
||||
|
||||
## 3. 站内通知接线
|
||||
|
||||
- [x] 3.1 交付 #188:创建物流换货单成功后通过现有 Outbox/站内通知向关联个人客户发送一次 C 端弹窗提醒。【主:Application + 既有通知 Adapter|不建设:营销投放、地址表单、ERP】
|
||||
- [x] 3.2 交付 #97:主钱包余额首次跌破固定 100 元时通知店铺业务员,阈值以下不重复、恢复后可再次触发。【主:Application + Outbox|辅:既有钱包事实|不增加:可配置阈值】
|
||||
- [x] 3.3 交付 #33:每日识别 15/7/3 天节点,提供后台/代理分页临期列表、数量/高亮/优先标识,并向 C 端发送节点站内通知。【主:Application/Query + 既有通知|不建设:企微业务员提醒或营销平台】
|
||||
|
||||
## 4. 企业微信最小审批 Adapter
|
||||
|
||||
- [x] 4.1 交付企微应用连接纵向切片:安全配置 corp_id/agent_id/Secret/回调凭据,按应用缓存 access_token、短锁防并发回源,并记录外部调用 Integration Log。【主:Infrastructure Adapter|不迁移:通用审批核心】
|
||||
- [x] 4.2 交付通讯录与账号绑定纵向切片:分页拉取/同步应用可见成员,账号列表/详情返回绑定字段,管理员从候选成员绑定 `(corp_id,userid)`;不做扫码和手机号自动匹配。【主:简单写 + Infrastructure/Query|不建设:组织模型】
|
||||
- [x] 4.3 交付业务场景与模板纵向切片:保存业务类型、template_id、控件/选项映射,调用模板详情校验并阻断失效配置;更新审计覆盖基线。【主:简单写 + Infrastructure Adapter|不建设:模板创建、审批节点、审批人规则】
|
||||
- [x] 4.4 实现 WeCom Adapter 对现有 Approval Port 的准备与提交:内部员工使用有效绑定 userid,代理等非企微账号使用应用默认发起人,按后台模板流程组装控件、必要时上传附件、调用 applyevent、保存 sp_no,并正确处理提交失败/结果未知;真实业务提交人保持不变。【主:Infrastructure Adapter + Outbox 消费|不修改:资金领域规则】
|
||||
- [x] 4.5 交付加密回调纵向切片:GET 验证、POST 验签解密、receiveid 校验、事件幂等、快速响应并异步拉取详情,将企微状态翻译为现有标准决策;新增 Handler 时同步两个文档生成器。【主:Infrastructure Adapter + Application|不直接执行:退款/充值终态】
|
||||
- [x] 4.6 交付主动恢复与读取投影:批量单号时间窗补偿、未终态详情轮询、结果未知恢复、审批节点 userid 批量映射系统账号。【主:Infrastructure Worker + Query|不实时逐行调用企微】
|
||||
|
||||
## 5. 退款与员工线下代充接入企微
|
||||
|
||||
- [x] 5.1 交付 #34 员工线下代充值纵向切片:业务申请、真实提交人、金额/凭证快照、通用审批实例和提交 Outbox 同事务创建;approved 通过既有钱包用例幂等入账,其他终态不入账。【主:复杂写 Application/Domain|辅:Approval Port|不实现:代理在线扫码充值】
|
||||
- [x] 5.2 交付 #35 退款企微审批纵向切片:现有退款申请关联唯一通用实例,标准终态驱动既有退款处理,确保换货迁移套餐、订单、资产和佣金副作用幂等。【主:复杂写 Application/Domain|辅:Approval Port|不实现:原路退款】
|
||||
- [x] 5.3 完成退款 approve/reject 和线下充值 offline-pay 旧入口的可控停用代码及存量 provider 切换清单;默认保留旧入口,待后续真实联调验收通过后再由发布配置停用,前端接口改为只读审批状态。【主:API 切换/迁移|不删除:历史业务事实】
|
||||
|
||||
## 6. 批量、导出与 Gateway
|
||||
|
||||
- [x] 6.1 交付 #36 单列 CSV 批量订购纵向切片:复用现有上传/任务五态/对象存储,整批选择一个套餐和支付方式,不选择代理;逐行复用订单资格、价格、支付和幂等规则。【主:Infrastructure + 旧订单 Service|不新建:批量任务平台】
|
||||
- [x] 6.2 交付 #42 lot 卡与套餐两个导出 datasource,按现有筛选、权限和字段来源输出。【主:Query/既有导出 Infrastructure|不建设:统一字段权限平台】
|
||||
- [x] 6.3 交付 #42 钱包流水与代理充值两个导出 datasource,金额使用既有事实和稳定格式,代理数据权限正确。【主:Query/既有导出 Infrastructure|不修改:钱包流水】
|
||||
- [x] 6.4 交付 #42 退款与换货两个导出 datasource,复用本 Change 已补齐的资产和提交人字段。【主:Query/既有导出 Infrastructure|不依赖:企微实时详情】
|
||||
- [x] 6.5 交付 #47 IoT 卡固定档位限速纵向切片:定义中文注释常量、后台权限和卡 ICCID 解析,调用 Gateway 并记录操作者与 Integration Log;移除错误的设备限速入口,不通过设备绑定关系间接限速。【主:旧 Service + Gateway Adapter|不实现:设备限速或自动限速算法】
|
||||
- [x] 6.6 交付 #49 CSV 批量分配设备纵向切片:复用现有导入任务,单列设备标识,整批选择目标代理或套餐系列,沿用现有分配/系列绑定权限和幂等规则。【主:Infrastructure + 旧设备 Service|不新建:导入平台】
|
||||
|
||||
## 7. 文档与联调交付
|
||||
|
||||
- [x] 7.1 补齐所有新增/修改 API 的 DTO description、枚举名称字段、中文错误码、路由注释和统一响应示例;新增 Handler 同步 `cmd/api/docs.go`、`cmd/gendocs/main.go`,但本次不执行 OpenAPI 生成。【主:API 契约】
|
||||
- [x] 7.2 增量维护 `.scratch/tech-global-audit/审计覆盖基线.md`,逐项登记 Audit Event、Domain Ledger、Integration Log、Outbox 或 N/A 理由。【主:治理门禁】
|
||||
- [x] 7.3 对全部变更生产代码执行 `gofmt`、只读一致性检查和 `git diff --check`,记录未执行测试、构建、LSP、迁移和 OpenAPI 生成的验证缺口。【主:静态收口】
|
||||
- [x] 7.4 准备企微、Gateway、Redis/Asynq、对象存储和前端联调所需配置、接口说明、已知限制及回滚步骤;本次不执行真实环境闭环。【主:联调交付】
|
||||
- [x] 7.5 更新实施证据和任务状态,保留 `complete-july-iteration-test-release` 的历史完成证据;严格校验、真实联调验收和归档由后续流程处理。【主:OpenSpec 收口】
|
||||
@@ -0,0 +1,77 @@
|
||||
# 七月迭代已完成范围实施证据
|
||||
|
||||
> 核验日期:2026-07-25
|
||||
> 范围:OpenSpec `deliver-july-iteration-confirmed-scope` 任务 0.1~7.5。首个矩阵保留 0.1 对历史已完成范围的只读证据,后续章节记录本 Change 的生产代码、迁移、契约和静态收口证据。
|
||||
|
||||
## 需求证据矩阵
|
||||
|
||||
| 需求 | 已有实现与接口证据 | 迁移证据 | 自动化验证证据 | 结论与联调边界 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| #45 换货新旧资产展示与独立搜索 | `internal/service/exchange/asset_snapshot.go`、`internal/query/exchange/list.go`、`internal/handler/admin/exchange.go`;`GET /api/admin/exchanges` 支持 `old_asset_keyword` 和 `new_asset_keyword` | N/A:不新增表或字段 | `internal/service/exchange/asset_snapshot_test.go`、`internal/query/exchange/list_test.go`、`internal/handler/admin/exchange_test.go` | 后端快照与搜索已交付;前端页面与浏览器人工验收属联调。 |
|
||||
| #46 资产预计最终到期 | `internal/query/packageexpiry/query.go`;后台解析、C 端资产信息和资产列表均返回预计到期投影 | `migrations/000164_add_package_expiry_query_indexes.*.sql` | `internal/query/packageexpiry/query_test.go`、`internal/routes/asset_exchange_trace_integration_test.go` | Query、HTTP 字段与索引已交付;页面展示为联调范围。 |
|
||||
| #55 套餐分配生效条件 | `internal/domain/package/terms.go`、`internal/service/package/`;分配、系列授权和单条覆盖写入购买快照 | `migrations/000162_add_expiry_base_override_to_shop_package_allocation.*.sql`、`000163_add_package_usage_terms_snapshots.*.sql` | `internal/domain/package/terms_test.go`、`internal/store/postgres/package_terms_snapshot_integration_test.go`、`internal/service/package/activation_terms_integration_test.go`、`internal/handler/admin/shop_package_expiry_base_integration_test.go` | 快照写入、历史回退与异常拒绝已交付;前端选项联调待同窗验收。 |
|
||||
| #60 店铺联系电话搜索 | `internal/service/shop/service.go`、`internal/query/shop/business_owner.go`;`GET /api/admin/shops?contact_phone=` 精确匹配 | `migrations/000160_add_shop_contact_phone_index.*.sql` | `internal/routes/shop_integration_test.go` 覆盖参数、分页、权限、精确匹配、店铺范围和索引 | 后端与索引已交付;筛选页面及四类账号人工验收为联调范围。 |
|
||||
| #86 资产前代/后代换货标识 | `internal/query/asset/exchange_trace.go`、`internal/routes/asset.go`;资产解析返回 `exchange_trace` | `migrations/000161_add_exchange_trace_indexes.*.sql` | `internal/query/asset/exchange_trace_test.go`、`internal/routes/asset_exchange_trace_integration_test.go` | 单节点前/后代投影与可见性已交付;前端跳转和历史异常抽样为联调/发布验收。 |
|
||||
| #38 代理主钱包信用额度 | `internal/domain/wallet/`、`internal/application/wallet/`、`internal/application/role/default_credit.go`、`internal/query/shop/fund_summary.go`;角色默认额度、店铺调额、资金概况和开放余额接口已装配 | `migrations/000171`~`000177` | 本次目标包编译通过;资金迁移异常查询、并发与真实 PostgreSQL/Redis/Asynq 验收在原总 Change 中明确延期 | 代码与接口已交付;真实资金并发和迁移数据门禁属测试环境验收。 |
|
||||
| #94 状态同步与运营商回调 | `internal/application/cardobservation/`、`internal/infrastructure/cardobservation/`、`internal/handler/callback/{ctcc,cmcc,cucc}_realname.go`;公共 `ApplyCardObservation`、观测序列和四类回调已装配 | `migrations/000178`~`000181` | 本次目标包编译通过;原总结明确真实运营商样例、Gateway、19/20 位命中和迁移重放延期 | 后端代码与测试环境装配已完成;不宣称生产验收完成,只剩真实外部环境联调门禁。 |
|
||||
| #96 店铺业务员归属 | `internal/query/shop/business_owner.go`、`internal/infrastructure/shop/recipient_resolver.go`;店铺创建/更新、列表/详情/候选人与通知接收人解析已装配 | `migrations/000169_add_shop_business_owner.*.sql` | 本次目标包编译通过;原总结明确 HTTP/迁移与真实数据库验收延期 | 后端纵向切片已交付;真实数据库和前端人工验收为联调范围。 |
|
||||
| #98 换货新资产继承旧店铺 | `internal/service/exchange/service.go` 在完成换货时拒绝其他店铺资产,并让平台库存新资产继承旧资产 `shop_id`;`internal/service/exchange/migration.go` 沿用新归属写租户标签 | N/A:复用现有资产与换货表 | `go test ./internal/service/exchange` 通过;当前没有以 UR#98 命名的专项自动化用例 | 写侧实现已存在;平台库存、他店资产拒绝、完成换货和历史可见性需按 INT-02 联调验收。 |
|
||||
| #43 系列套餐批量授权 | `internal/service/shop_series_grant/service.go`、`internal/routes/shop_series_grant.go`;复用创建、详情、更新和套餐管理接口,1~100 项在单事务内校验和写入 | N/A:复用现有授权模型 | `internal/handler/admin/shop_package_expiry_base_integration_test.go` 只覆盖系列授权写入的生效条件;`internal/service/shop_series_grant` 当前无专项测试 | 后端批量命令已交付;权限、价格、`remove`、原子性和真实并发验收仍是明确的发布验收缺口。 |
|
||||
| #73 行业卡复机行为冻结 | `internal/service/iot_card/stop_resume_service.go` 明确保持 `card_category=industry` 无需实名即可进入复机判定;本 Change 不改写该规则 | N/A | `internal/service/iot_card/stop_resume_service_test.go` 当前只覆盖风险 Gateway 扩展判定,没有直接锁定行业卡复机的特征测试 | 代码行为已确认且本任务不修改;直接回归测试为已记录的后续风险。 |
|
||||
|
||||
## 2026-07-25 目标验证记录
|
||||
|
||||
- 执行:`go test ./internal/service/exchange ./internal/query/exchange ./internal/query/asset ./internal/query/packageexpiry ./internal/service/package ./internal/routes ./internal/handler/admin ./internal/domain/wallet ./internal/application/wallet ./internal/query/shop ./internal/infrastructure/shop ./internal/service/iot_card ./internal/application/cardobservation ./internal/handler/callback`
|
||||
- 结果:全部退出码为 0;有测试的包全部 PASS,无测试文件的包完成编译检查。
|
||||
- 诊断:对上表核心 Go 文件执行 `gopls check`,退出码为 0,无诊断输出。首次沙箱内执行因 Go 缓存目录不可写而无法加载 workspace,改为允许缓存写入后重跑成功。
|
||||
|
||||
## 未扩展的范围
|
||||
|
||||
- 本任务未修改换货、套餐、钱包、卡状态、店铺或授权业务代码。
|
||||
- #45、#46、#55、#60、#86 具备完整自动化证据,可归为只剩联调/人工验收。
|
||||
- #38、#43、#94、#96 有后端实现与迁移,但专项自动化或真实环境验证明确延期;#98 有实现但无归属继承专项测试。这些不得误写为“自动化/生产验收已完成”。
|
||||
|
||||
## 2026-07-25 局部缺陷、字段与筛选实现记录
|
||||
|
||||
> 用户明确要求本 Change 不编写或补齐测试代码、不运行测试,只完成生产代码至可联调状态;实现优先修改和复用现有能力,确实无法承载时才做最小新增。以下记录生产代码证据,不宣称自动化验证通过。
|
||||
|
||||
| 任务 | 生产代码证据 | 当前结论 | 未执行验证 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1.1 #189 | `internal/service/package/activation_service.go` 按退款订单与套餐使用记录定位权益,不再用旧资产 ID 缩小查询范围 | 换货迁移后仍可失效原订单对应权益,其他订单权益不受影响 | 退款/换货目标测试、LSP 诊断 |
|
||||
| 1.2 #181 | `internal/service/client_order/service.go` 写入既有 `purchase_role=self_purchase`;卡快照写 ICCID,设备按 VirtualNo→IMEI;`internal/service/order/service.go` 同步按 ID 下单设备快照顺序 | 未新增渠道字段,不读取兼容或回填历史空值,只保证新订单正确 | 订单/退款 HTTP 与 DB 测试、LSP 诊断 |
|
||||
| 1.3 #182/#44 | 退款与换货以 `creator`、充值以 `user_id` 投影统一的 `submitter_id/submitter_name`;`GetDisplayAccountsByIDs` 批量读取账号展示名并支持软删除历史账号 | 列表无 N+1;保留既有 `creator` 字段 | 三类列表/详情权限与响应测试、LSP 诊断 |
|
||||
| 1.4 #53 | 卡列表直接过滤 `tb_iot_card.real_name_status`;设备列表用有效绑定关系与卡实名状态的实时 `EXISTS/NOT EXISTS` | 无投影、Worker、事件消费者 | 分页、权限、多卡场景测试、LSP 诊断 |
|
||||
| 1.5 #57 | `RefundStore.HasUnfinishedByAsset` 拦截待审批、已退回、已通过但资产处理未完成的退款;换货创建前失败关闭;新增错误码 1207 | 已拒绝或 `asset_reset=true` 的已完成退款放行,错误消息为“该资产存在退款申请” | 换货创建 HTTP/DB 测试、LSP 诊断 |
|
||||
| 1.6 API 契约 | 更新订单/退款资产标识说明、实名筛选 DTO、提交人 DTO 和中文错误码;未新增 Handler | 相关 DTO 已进入生成后的 OpenAPI,无需新增 Handler 装配 | 契约测试、LSP 诊断 |
|
||||
|
||||
## 2026-07-25 完整交付矩阵
|
||||
|
||||
| 任务 | 主要代码、迁移与文档证据 | 当前结论 | 后续验证边界 |
|
||||
| --- | --- | --- | --- |
|
||||
| 2.1 店铺登录限制 | `migrations/000182_*`、`internal/application/shop/update.go`、`internal/service/client_auth/service.go`、店铺 DTO | 店铺配置默认允许登录;开启后仅阻止新资产登录,不吊销已有 Token;测试库结构已落库 | 四类账号权限和 C 端登录联调 |
|
||||
| 2.2 实名策略 | `internal/service/iot_card/realname_policy_batch.go`、`internal/service/device/realname_policy_batch.go`、C 端资产 DTO | 三种策略、单资产与最多 500 条批量全成全败、设备策略优先生效均已接线 | HTTP/DB 事务和设备多卡场景联调 |
|
||||
| 2.3 下架套餐续费 | `internal/service/purchase_validation/service.go`、`internal/service/client_order/service.go`、订单 DTO | 普通列表排除下架套餐;当前使用者可用现有下单入口创建新续费订单,历史订单不修改 | 当前使用者、代理代购、新客户与待支付订单联调 |
|
||||
| 2.4 支付方式配置 | `internal/bootstrap/payment_method_config.go`、`internal/service/paymentmethod/`、系统配置和 C 端订单/充值接线 | 卡、设备分别维护非空 `wallet/wechat/alipay` 集合;强充过滤钱包且后端复核 | 配置更新、缓存失败关闭、三种支付渠道真实联调 |
|
||||
| 3.1~3.3 站内通知 | `internal/application/exchange/`、`internal/infrastructure/exchange/`、`internal/infrastructure/wallet/debit_consumer.go`、`internal/application/packageexpiry/`、`migrations/000183_*` | 换货提醒、主钱包首次跌破 100 元提醒、套餐 15/7/3 天提醒均复用现有通知和可靠事件 | Scheduler、重复消费、接收人和 C 端弹窗联调 |
|
||||
| 4.1~4.6 企微 Adapter | `migrations/000184_*`~`000189_*`、`internal/application/wecom/`、`internal/infrastructure/wecom/`、企微 Handler/Route/Worker、`docs/wecom-application-connection/功能总结.md` | 应用连接、可见成员、账号绑定、模板映射、默认发起人、提交、加密回调、详情同步和主动恢复已装配 | 可信 IP、真实模板、回调公网、附件、结果未知和轮询闭环 |
|
||||
| 5.1 员工线下代充值 | `migrations/000190_*`、`internal/application/agentrecharge/`、充值 Service/DTO | 真实提交人、明文业务快照、通用审批实例和提交 Outbox 同事务创建;approved 幂等入主钱包 | 真实企微终态、钱包流水和重复决策联调 |
|
||||
| 5.2 退款企微审批 | `migrations/000191_*`、`internal/application/refundapproval/`、`internal/service/refund/approval_decision.go` | 唯一通用实例和企微标准终态驱动既有退款处理,钱包、佣金、套餐和换货迁移副作用保持幂等 | 全退款/部分退款、换货后退款和重复终态联调 |
|
||||
| 5.3 旧入口切换 | `pkg/config/config.go`、`docs/wecom-application-connection/存量审批切换清单.md` | 旧入口默认开启;新企微记录禁止旧入口绕过,关闭后前端只读审批状态 | 存量 provider 清单和生产切换审批 |
|
||||
| 6.1 批量订购 | `migrations/000192_*`、`internal/service/asset_package_batch_order/`、Handler/Route/Worker、`docs/asset-package-batch-order/功能总结.md` | 单列 CSV、统一套餐和支付方式、逐行复用订单/钱包幂等规则 | 对象存储、Worker、部分成功、钱包扣款和线下凭证联调 |
|
||||
| 6.2~6.4 六类导出 | `internal/exporter/{iot_card,package,agent_wallet_transaction,agent_recharge,refund,exchange}_scene.go`、`docs/business-data-export/功能总结.md` | 六个 datasource 已注册,复用现有导出任务、筛选和数据权限 | 大数据量、文件下载、金额格式和各账号权限联调 |
|
||||
| 6.5 卡固定档位限速 | `internal/service/iot_card/speed_tier.go`、Gateway flow-card、卡 Handler/Route、`docs/iot-card-speed-tier/功能总结.md` | 仅卡 ICCID 支持 `-1..8` 固定档位;无设备入口;超时记 unknown 且不盲目重试 | Gateway 全档位、明确失败、超时和人工核对 |
|
||||
| 6.6 设备 CSV 批量分配 | `migrations/000193_*`、`internal/task/device_batch_allocation.go`、设备导入任务扩展、`docs/device-batch-allocation/功能总结.md` | 只复用导入任务外壳;`assign_shop/assign_series` 独立分支复用现有权限和业务方法 | CSV、对象存储、Worker 中断恢复和重复消费联调 |
|
||||
| 7.1 API 契约 | DTO description/enum/status_name、路由注释、两个文档生成器和 `docs/admin-openapi.yaml` | 生成器执行成功;新接口和 DTO 存在,旧设备限速契约不存在 | 前端契约联调 |
|
||||
| 7.2 审计基线 | `.scratch/tech-global-audit/审计覆盖基线.md` | 0.1~7.5 均有 Audit Event、Domain Ledger、Integration Log、Outbox 决定或 N/A 理由 | 后续生产治理评审 |
|
||||
| 7.3 静态收口 | 本文“静态收口记录” | `gofmt` 和 `git diff --check` 通过;禁用模式未发现新增违规;测试库迁移已验证至 `000194` | 测试、完整构建和 LSP 未执行 |
|
||||
| 7.4 联调交付 | `docs/7月迭代/七月迭代联调交付说明.md`、`docs/environment-variables.md` | 配置、接口、限制和回滚步骤齐备 | 真实环境闭环由后续流程执行 |
|
||||
|
||||
## 2026-07-25 静态收口记录
|
||||
|
||||
- 对当前工作树 169 个新增或修改的非测试 Go 文件执行 `gofmt -w`,随后 `gofmt -d` 无差异。
|
||||
- 执行 `git diff --check`,退出码为 0。
|
||||
- 对新增行检查 `fmt.Errorf`、裸 `go func` 和 `database/sql`:仅 Worker 定时任务注册的启动期错误使用 `fmt.Errorf`;没有新增裸 goroutine,也没有新增 `database/sql` 依赖。模型中的 `database/sql/driver` 是既有 `driver.Valuer` 实现,不是直接数据库访问。
|
||||
- 检查生产路由、Handler、Service、Gateway、常量及生成后的 OpenAPI:仅保留 `PUT /api/admin/iot-cards/{iccid}/speed-tier` 卡 ICCID 限速;不存在设备限速路由、DTO 或通过设备绑定卡间接限速的入口。
|
||||
- 根据用户后续明确要求,已执行 `env GOCACHE=/private/tmp/junhong-go-build-cache go run cmd/gendocs/main.go`,生成成功;已确认卡限速与设备批量分配路径和 DTO 存在,旧设备限速契约不存在。因此“未执行 OpenAPI 生成”不再是验证缺口。
|
||||
- 根据用户后续明确要求,已校准测试库迁移元数据并执行 `000187`~`000194`;MCP 复核确认目标表列数、表注释、字段注释、增量字段、索引和约束均无缺口,最终迁移版本为干净的 `194`。
|
||||
- 本 Change 仍未执行单元/集成/验收测试、`go test`、完整 `go build`、LSP 诊断和真实企业微信/Gateway 联调。OpenAPI 生成器依赖树编译成功不能替代完整构建结论。
|
||||
Reference in New Issue
Block a user