七月迭代短暂完结,还有很多后端的关键东西没有弄,这是一版赶时间做的东西
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m26s

This commit is contained in:
2026-07-25 17:06:58 +08:00
parent ad9f613dd6
commit 73f5125d3d
249 changed files with 17137 additions and 877 deletions

View File

@@ -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`;存量记录按切换清单处理,新申请只接受企微终态。

View File

@@ -0,0 +1,13 @@
## ADDED Requirements
### Requirement: 主钱包低余额通知业务员
店铺主钱包可用余额低于固定 100 元时,系统 SHALL 向店铺 `business_owner_account_id` 对应业务员发送站内通知;不提供按店铺自定义阈值,不向无业务员的店铺猜测接收人。
#### Scenario: 余额首次降至阈值以下
- **WHEN** 主钱包余额从不少于 100 元变为少于 100 元且店铺已配置业务员
- **THEN** 业务员收到一条低余额站内通知
#### Scenario: 阈值以下重复余额变化
- **WHEN** 钱包已处于 100 元以下又发生多次扣减
- **THEN** 系统不重复发送同一低余额周期通知;余额恢复后再次跌破可重新通知

View File

@@ -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** 已成功行不重复下单或扣款

View File

@@ -0,0 +1,13 @@
## ADDED Requirements
### Requirement: 卡和设备列表支持实名状态筛选
卡列表 SHALL 按卡自身有效实名状态筛选;设备列表 SHALL 在任意一张当前有效绑定卡已实名时将设备视为已实名,否则视为未实名。查询 MUST 使用现有表联查或子查询不得为该筛选新增历史投影、Worker 或事件消费者。
#### Scenario: 设备有一张实名绑定卡
- **WHEN** 设备绑定多张有效卡且其中任意一张已实名
- **THEN** 设备出现在“已实名”筛选结果中且不出现在“未实名”结果中
#### Scenario: 设备没有实名绑定卡
- **WHEN** 设备不存在任何已实名的有效绑定卡
- **THEN** 设备出现在“未实名”筛选结果中

View File

@@ -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` 且允许直接进入购买流程

View File

@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 后台手动设置卡固定限速档位
有权限的后台账号 SHALL 为 IoT 卡选择系统定义的固定限速档位,系统 MUST 校验卡权限和档位常量后调用现有 GatewayGateway 限速对象只能是卡 ICCID系统不得提供设备限速或通过设备绑定关系间接限速也不得根据流量、套餐或运营商规则自动计算档位。
#### Scenario: 成功设置限速档位
- **WHEN** 管理员为有权限 IoT 卡选择有效固定档位
- **THEN** 系统以该卡 ICCID 调用 Gateway 并返回统一响应,记录操作者、卡、档位和外部交互结果
#### Scenario: 设备不得调用限速
- **WHEN** 用户尝试通过设备或设备绑定关系设置限速
- **THEN** 系统不提供该接口且不得向 Gateway 发送设备 IMEI、设备 ID 或设备当前绑定卡
#### Scenario: Gateway 结果未知
- **WHEN** Gateway 请求超时且结果不可确定
- **THEN** 系统返回可识别的结果未知错误并保留 Integration Log不盲目声称设置成功

View File

@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 六类业务数据可独立导出
系统 SHALL 复用现有导出任务框架,分别提供 lot 卡、钱包流水、套餐、退款、换货和代理充值 datasource每类导出按当前业务权限和筛选条件读取已有字段不建设统一字段权限平台。
#### Scenario: 创建退款导出任务
- **WHEN** 有权限用户以退款列表筛选条件创建退款导出
- **THEN** 系统异步生成仅包含其可见退款数据的文件,并通过任务接口提供进度和下载结果
### Requirement: 导出字段必须有稳定来源
每个 datasource MUST 明确列名、数据来源和格式;现有表或可批量关联数据无法提供的字段不得伪造,必须先补充经确认的最小数据字段后才能导出。
#### Scenario: 字段没有数据来源
- **WHEN** 业务要求字段在现有数据中不存在且无法可靠推导
- **THEN** 该 datasource 不得用空含义或错误值冒充字段,任务保持未交付直到字段契约确认

View File

@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 未终结退款阻止换货
现有换货创建用例 MUST 在写入换货单前查询该资产关联订单的未终结退款;存在时返回“该资产存在退款申请”,且不得创建换货单或修改资产。
#### Scenario: 资产存在活跃退款
- **WHEN** 管理员为存在未终结退款申请的卡或设备创建换货单
- **THEN** 系统拒绝操作并返回业务错误“该资产存在退款申请”
### Requirement: 原订单退款必须失效迁移后的套餐权益
当套餐权益已随换货迁移到新资产后,原购买订单退款终态处理 MUST 沿换货迁移关系定位新资产上的对应套餐使用记录并按现有退款规则失效,不得因仍按旧资产路径查询而遗漏。
#### Scenario: 换货后原订单退款通过
- **WHEN** 旧资产套餐已迁移到新资产且原购买订单退款通过
- **THEN** 新资产上由该订单产生的套餐权益被正确失效,其他订单权益不受影响

View File

@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: CSV 批量分配设备代理或套餐系列
系统 SHALL 复用现有设备导入任务模式,接收仅含设备标识的 CSV并由请求参数选择本批操作类型和一个目标代理或套餐系列不得为该需求新建独立任务平台。
#### Scenario: 批量分配目标代理
- **WHEN** 管理员上传合法设备 CSV 并选择一个有权限目标代理
- **THEN** 异步任务按现有分配规则处理设备并输出成功、失败明细
#### Scenario: 批量设置套餐系列
- **WHEN** 管理员上传合法设备 CSV 并选择一个有效套餐系列
- **THEN** 异步任务按现有设备系列绑定规则处理并输出结果
#### Scenario: 重复消费任务
- **WHEN** 同一设备分配任务被重复投递
- **THEN** 系统保持目标关系一致且不产生重复分配副作用

View File

@@ -0,0 +1,9 @@
## ADDED Requirements
### Requirement: 换货查询返回提交人
换货列表和详情 SHALL 返回创建该换货单的提交人账号 ID 和名称;名称 MUST 批量解析并遵守当前平台/代理店铺权限。
#### Scenario: 查询换货列表
- **WHEN** 有权限用户查询换货列表
- **THEN** 每条可见记录包含提交人 ID 和提交人名称

View File

@@ -0,0 +1,13 @@
## ADDED Requirements
### Requirement: 创建物流换货单后发送 C 端通知
物流换货单创建成功后,系统 SHALL 复用现有个人客户站内通知能力向该资产关联客户发送一次换货提醒C 端 SHALL 按既有未读通知机制弹窗通知不包含通用营销配置、ERP 发货或自动创建其他单据。
#### Scenario: 成功创建物流换货单
- **WHEN** 后台成功创建需要客户处理的物流换货单
- **THEN** 关联客户收到一条可在 C 端弹窗展示的站内通知
#### Scenario: 重复处理创建事件
- **WHEN** 同一换货单通知事件被重复消费
- **THEN** 同一客户不会收到重复通知

View File

@@ -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** 系统只执行该订单创建时保存的支付方式,不允许在原订单上切换渠道

View File

@@ -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** 同一接收人只产生一条对应资产的临期通知

View File

@@ -0,0 +1,16 @@
## ADDED Requirements
### Requirement: 下架套餐只允许符合资格的续费
普通 C 端可购套餐列表 MUST 排除下架套餐;当前资产在当前世代仍有该套餐的生效中使用记录时,客户可通过资产当前套餐或历史订单提供的稳定引用调用现有创建订单接口续费,代理代购和新客户普通购买仍 MUST 被拒绝。续费始终读取套餐当前价格、流量和其他配置,不沿用历史权益快照。
#### Scenario: 当前客户续费下架套餐
- **WHEN** 客户资产正在使用某下架套餐并通过资产或历史订单引用提交现有创建订单接口
- **THEN** 系统按套餐当前配置允许创建一张新订单
#### Scenario: 新客户购买下架套餐
- **WHEN** 非当前使用者通过普通购买入口请求下架套餐
- **THEN** 系统返回套餐不可购买错误
#### Scenario: 套餐重新上架后购买
- **WHEN** 原下架套餐更新价格或流量后重新上架
- **THEN** 套餐重新出现在普通可购列表,普通购买和续费均使用更新后的当前配置

View File

@@ -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** 系统拒绝切换并保持订单原支付方式

View File

@@ -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`,前端改为只读展示审批状态。

View File

@@ -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本需求只阻止后续新登录

View File

@@ -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 和名称