Files
junhong_cmp_fiber/docs/7月迭代/七月迭代实现与接口对接说明.md
break 73f5125d3d
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m26s
七月迭代短暂完结,还有很多后端的关键东西没有弄,这是一版赶时间做的东西
2026-07-25 17:06:58 +08:00

153 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 七月迭代实现与接口对接说明
> 面向:产品、前端、测试和联调人员
> 范围:`deliver-july-iteration-confirmed-scope` 及本期确认“后端已完成,只需前端联调”的需求
> 接口细节:以 [`docs/admin-openapi.yaml`](../admin-openapi.yaml) 为准,本文只说明关键调用和字段变化。
## 一、先看这几个关键结论
1. **设备没有限速接口**:限速只允许对 IoT 卡 ICCID 操作,固定档位 `-18`,不通过设备绑定卡间接限速。
2. **企微模板在企微后台创建**:系统只配置应用、默认发起人、账号 userid 绑定、`template_id` 和控件映射,不在本系统设计审批节点。
3. **代理发起审批走默认发起人**:企微单据使用配置的默认成员发起,但退款、充值等本地业务单仍记录真实代理提交人。
4. **下架套餐续费不新增专用接口**:历史订单和资产信息返回稳定套餐 ID前端仍调用现有 C 端创建订单接口生成一张新订单。
5. **实名流程由后端返回值决定**:前端使用 `effective_realname_policy`,不能按卡或设备自行推断;设备有任意一张有效绑定卡已实名即视为已实名。
6. **支付按钮由后端返回值决定**:前端使用 `allowed_payment_methods`,不要自行写死卡/设备的微信、支付宝或钱包规则。
7. **设备批量分配只复用导入任务外壳**:复用任务表、队列、进度和结果页;不会进入原 Excel 创建设备逻辑。
8. **系列套餐批量授权后端原本就支持多选**:前端把多选套餐组装为 `packages[]` 调现有接口即可,不需要新后端接口。
## 二、本期需求怎么实现
### 2.1 本期新增或修改后端的需求
| 需求 | 实现方式 | 关键口径检查 |
| --- | --- | --- |
| #189 换货后退款套餐未失效 | 退款处理不再只按旧资产查套餐,而是按原订单和换货迁移关系定位新资产上的对应套餐权益 | 只失效该退款订单产生的权益,不影响其他订单套餐;无前端改动 |
| #188 换货 C 端提醒 | 创建物流换货单时写可靠通知事件,继续走现有 C 端站内通知和未读弹窗 | 不做营销投放、ERP、自动创建其他单据 |
| #182/#44 提交人和审批展示 | 退款、代理充值、换货列表/详情批量解析提交人 ID 和名称;企微详情已具备将审批节点 userid 批量映射系统账号的内部能力 | 当前退款/充值业务 DTO 只稳定返回提交人和审批状态,尚未直接返回审批节点人员列表;如页面必须展示具体审批人,仍需把已有投影接入业务查询 |
| #181 订单渠道和资产标识 | C 端新订单保存正确 `purchase_role`;卡返回 ICCID设备优先 VirtualNo、为空时返回 IMEI | 不用 SN 冒充订单设备标识;历史空数据不伪造 |
| #41 店铺 C 端登录限制 | 店铺增加 `client_login_disabled`C 端验证资产后、签发短期令牌前检查所属店铺 | 只阻止新登录,不吊销已有 Token平台库存保持原行为 |
| #53 卡/设备实名筛选 | 卡按自身实名状态过滤;设备通过有效绑定卡 `EXISTS` 实时判断 | 设备任意一张有效绑定卡已实名即为已实名;未建投影或 Worker |
| #57 退款中禁止换货 | 在现有换货创建入口前检查资产未终结退款 | 拒绝文案为“该资产存在退款申请”;不依赖企微实时接口 |
| #62 三种实名顺序 | 保留 `none/before_order/after_order`,补齐卡和设备最多 500 条批量修改C 端返回生效策略 | 批量全成全败;设备和下卡冲突时以设备策略为准 |
| #97 主钱包低余额预警 | 消费主钱包扣款事实,余额首次从不少于 100 元跌破 100 元时通知店铺业务员 | 阈值以下不重复;恢复后再次跌破可再次提醒;无业务员不猜接收人 |
| #33 套餐临期提醒 | 每日计算 15/7/3 天节点,提供后台/代理临期列表及数量,并向个人客户发送站内通知 | 高亮为 815、47、03 天03 天优先;不发企微业务员提醒 |
| #34 员工线下代充值 | 原充值创建入口在 `offline` 场景创建通用审批实例和企微提交事件;通过后幂等入主钱包 | 在线扫码充值后置;真实提交人、金额和凭证明文业务快照保留 |
| #35 退款企微审批 | 原退款申请关联唯一通用审批实例,企微标准终态驱动现有退款处理 | 不做原路退款;重复回调/轮询不会重复退款或失效套餐 |
| #37 企业微信审批 | 完成应用连接、通讯录同步、账号绑定、默认发起人、模板映射、提交、回调、详情查询和轮询恢复 | 模板/审批人规则由企微维护;超时结果未知不盲目重提 |
| #36 批量订购套餐 | 单列 CSV整批选择一个套餐和支付方式走独立异步任务逐行复用现有订单和钱包规则 | 不选择代理,不在 CSV 中逐行指定套餐;部分失败不影响其他行 |
| #40 下架套餐老客户续费 | 普通可购列表排除下架套餐;当前使用者可用历史订单或当前资产返回的套餐 ID 调现有下单接口创建新订单 | 无专用续费接口;新客户和代理代购仍拒绝;历史订单不修改 |
| #42 六类导出 | 在现有导出任务框架新增 IoT 卡、套餐、钱包流水、代理充值、退款、换货 datasource | 按现有数据权限和字段来源导出,不建设新字段权限平台 |
| #47 卡固定档位限速 | 后台选择固定档位,按 ICCID 调用 Gateway并记录操作审计和 Integration Log | 仅 IoT 卡;设备无接口;超时返回结果未知并人工核对 |
| #48 按资产类型配置支付方式 | `system_config` 分别保存卡、设备允许的 `wallet/wechat/alipay` 集合C 端返回业务场景交集,订单端再次校验 | 至少保留一种;强充和普通充值剔除钱包;前端不可绕过 |
| #49 设备批量分配 | 扩展现有设备导入任务的 `operation_type`,新增分配代理和设置套餐系列两个 CSV 分支 | CSV 单列 VirtualNo/IMEI/SN复用现有权限、分配、系列绑定和幂等规则 |
### 2.2 后端此前已经完成,主要由前端正确调用或展示
| 需求 | 后端现状 | 前端要做什么 |
| --- | --- | --- |
| #45 换货新旧资产展示/搜索 | 换货列表已分别返回新旧资产,并支持两个独立搜索参数 | 分别提供“旧资产”和“新资产”搜索框,不要继续共用一个字段 |
| #46 预计套餐到期 | 资产详情、C 端资产信息和相关列表已返回预计最终到期字段 | 展示 `estimated_final_expires_at`;按 `is_expiring` 或剩余天数高亮,不要只显示当前套餐到期时间 |
| #55 套餐分配生效条件 | 套餐和分配接口已支持默认值、覆盖值和最终生效值 | 创建/编辑套餐传 `expiry_base`;分配时传 `expiry_base_override`,展示 `effective_expiry_base` |
| #60 店铺联系电话搜索 | 店铺列表已支持 11 位联系电话精确查询 | 将输入值作为 `contact_phone` 查询参数传给店铺列表接口 |
| #86 资产换货标识和跳转 | 资产解析接口已返回 `exchange_trace.previous_asset/next_asset``can_view` | 仅 `can_view=true` 且存在资产 ID 时允许跳转;该需求前端已对接可保持现状 |
| #38 代理信用额度 | 角色默认额度、店铺实际额度、资金概况和负可用余额均已有接口 | 使用分单位字段;更新时携带钱包 `version`;余额为负数时正常展示 |
| #94 状态同步和运营商回调 | 后端回调、定时触发和原轮询链路已装配 | 通常无前端新调用;状态页面继续读取现有资产状态字段 |
| #96 店铺业务员 | 店铺创建/更新、候选人、列表/详情和筛选都已支持业务员 | 创建/编辑店铺选择 `business_owner_account_id`;列表可按该 ID 筛选并展示名称 |
| #98 换货新资产继承旧店铺 | 换货完成时后端自动继承旧资产店铺归属 | 前端继续调用原换货完成接口,不新增分配步骤 |
| #43 系列套餐批量授权 | 创建授权和管理套餐接口均支持 `packages[]`,单次 1100 项 | 页面实现套餐多选,一次提交整个数组;删除项使用 `remove=true` |
> 注意:#44 的“提交人”已经可以直接对接;“审批人”目前不是退款、充值列表的稳定返回字段。后端已经能从企微详情快照解析并映射 userid但当前前端不能把 `processor_id` 当作完整企微审批人列表。
### 2.3 本期明确不由后端处理
- #84 H5 首页隐藏设备下 ICCID纯前端显示调整但本期范围确认标记为“不做”。
- #63 授权列表滚动条和字段顺序:纯前端页面调整,需求已关闭。
- #73 行业卡未实名复机:后端保持原有行业卡放行逻辑,不改接口、不改前端。
- #99 原路退款、#52 聚水潭、#51 跨品类换货、#39 分销码/佣金提现:本期不做。
- #168#90#75#64:已关闭,本期不重新修改。
## 三、需求与接口对接表
### 3.1 资产、店铺、订单和换货
| 需求 | 接口 | 参数或返回变化 / 前端调用说明 |
| --- | --- | --- |
| #41 登录限制 | `PUT /api/admin/shops/:id``GET /api/admin/shops``GET /api/admin/shops/:id` | 更新请求增加可选 `client_login_disabled`;列表/详情返回同名布尔值。C 端仍调 `POST /api/c/v1/auth/verify-asset`,受限时直接展示后端错误,不会返回资产令牌 |
| #53 实名筛选 | `GET /api/admin/iot-cards/standalone``GET /api/admin/devices` | 查询参数增加 `real_name_status=0|1`;响应已有 `real_name_status``real_name_status_name` |
| #62 实名顺序 | `PATCH /api/admin/assets/:identifier/realname-mode``POST /api/admin/iot-cards/batch-update-realname-policy``POST /api/admin/devices/batch-update-realname-policy` | 单条传 `realname_policy`;批量传 `asset_ids[] + realname_policy`,最多 500 条 |
| #62/#48 C 端初始化 | `GET /api/c/v1/asset/info?identifier=...` | 使用 `effective_realname_policy``realname_required``real_name_status``allowed_payment_methods`;前端不要自行覆盖 |
| #45 换货搜索 | `GET /api/admin/exchanges` | 使用 `old_asset_keyword``new_asset_keyword` 两个独立参数,可同时传并按 AND 组合 |
| #57/#98 换货 | `POST /api/admin/exchanges``POST /api/admin/exchanges/:id/complete` | 请求结构不变;存在退款时创建接口返回业务错误;完成后店铺归属由后端继承 |
| #188 换货提醒 | `GET /api/c/v1/notifications/unread-count``GET /api/c/v1/notifications``PUT /api/c/v1/notifications/:id/read` | 无新增通知接口;前端继续使用现有未读数、列表和已读接口弹窗展示 |
| #181 订单字段 | `GET /api/admin/orders``GET /api/admin/orders/:id``GET /api/c/v1/orders``GET /api/c/v1/orders/:id` | 返回正确 `purchase_role``asset_identifier`;设备标识为 VirtualNo 优先、IMEI 兜底 |
| #40 下架套餐续费 | `GET /api/c/v1/asset/info``GET /api/c/v1/orders``GET /api/c/v1/orders/:id``POST /api/c/v1/orders/create` | 资产信息返回 `current_package_id`,历史订单返回 `package_ids`;前端把选中的套餐 ID、资产 `identifier` 和当前允许的 `payment_method` 传给原创建订单接口 |
| #46/#86 资产展示 | `GET /api/admin/assets/resolve/:identifier``GET /api/c/v1/asset/info` | 返回预计最终到期字段;后台解析额外返回 `exchange_trace`,跳转前检查 `can_view` |
### 3.2 店铺业务员、信用和套餐授权
| 需求 | 接口 | 参数或返回变化 / 前端调用说明 |
| --- | --- | --- |
| #60 联系电话搜索 | `GET /api/admin/shops?contact_phone=11位号码` | 精确查询;可与店铺名称、编号等条件组合 |
| #96 店铺业务员 | `GET /api/admin/shops/business-owner-candidates``POST /api/admin/shops``PUT /api/admin/shops/:id``GET /api/admin/shops` | 创建/更新传 `business_owner_account_id`;列表可用同名参数筛选,响应展示账号 ID、名称和可用状态 |
| #38 信用额度 | `PUT /api/admin/roles/:id/default-credit``PUT /api/admin/shops/:id/credit-limit``GET /api/admin/shops/fund-summary` | 角色接口配置新建代理默认值;店铺接口传 `credit_enabled + credit_limit + version`;金额单位均为分 |
| #55 套餐默认生效条件 | `POST /api/admin/packages``PUT /api/admin/packages/:id``GET /api/admin/packages/:id` | 请求使用 `expiry_base=from_activation|from_purchase`;响应展示默认生效条件名称 |
| #55 分配覆盖 | `POST /api/admin/shop-package-batch-allocations``PATCH /api/admin/shop-package-allocations/:id/expiry-base` | 分配时使用 `expiry_base_override``null` 表示跟随套餐默认值 |
| #43 系列套餐多选 | `POST /api/admin/shop-series-grants``PUT /api/admin/shop-series-grants/:id/packages` | `packages` 是 1100 项数组,每项包含 `package_id``cost_price`,删除时传 `remove=true` |
### 3.3 审批、退款和员工线下代充值
| 需求 | 接口 | 参数或返回变化 / 前端调用说明 |
| --- | --- | --- |
| #182/#44 退款 | `GET /api/admin/refunds``GET /api/admin/refunds/:id` | 新增/补齐 `submitter_id``submitter_name``approval_provider``approval_status``approval_status_name` |
| #182/#44 充值 | `GET /api/admin/agent-recharges``GET /api/admin/agent-recharges/:id` | 同上;线下代充值的审批状态只读展示 |
| #44 换货 | `GET /api/admin/exchanges``GET /api/admin/exchanges/:id` | 新增/补齐 `submitter_id``submitter_name` |
| #34 员工线下代充 | `POST /api/admin/agent-recharges` | 仍用原入口;`payment_method=offline` 时传目标 `shop_id`、金额、15 个 `payment_voucher_key` 和备注,创建后等待企微审批 |
| #35 退款申请 | `POST /api/admin/refunds` | 仍用原入口;请求结构保持订单、实收金额、申请金额、凭证、原因等业务字段,创建后等待企微审批 |
| #34/#35 旧按钮 | `POST /api/admin/agent-recharges/:id/offline-pay``POST /api/admin/refunds/:id/approve|reject` | 仅兼容存量旧审批记录;企微记录前端不得显示这些操作按钮,最终按发布配置停用 |
| #37 企微应用 | `POST/GET /api/admin/wecom/applications``POST /api/admin/wecom/applications/:id/test` | 配置 corp_id、agent_id、Secret、回调 Token、EncodingAESKey管理端按明文填写 |
| #37 默认发起人和成员 | `POST /api/admin/wecom/applications/:id/members/sync``GET /api/admin/wecom/applications/:id/members``PUT /api/admin/wecom/applications/:id/default-creator` | 先同步成员,再从可见成员中选择默认 `userid` |
| #37 账号绑定 | `PUT /api/admin/accounts/:id/wecom-binding` | 管理员选择应用可见成员,绑定 `(corp_id,userid)`;不做扫码绑定 |
| #37 场景模板 | `PUT /api/admin/wecom/scenes/:business_type``GET /api/admin/wecom/scenes` | `business_type``refund_approval``offline_recharge_approval`;传 `application_id``template_id``control_mapping[]` |
| #37 回调 | `GET/POST /api/callback/wecom/approval/:application_id` | 由企微服务器调用,前端无需调用 |
### 3.4 通知、批量任务、导出和 Gateway
| 需求 | 接口 | 参数或返回变化 / 前端调用说明 |
| --- | --- | --- |
| #97 后台低余额通知 | `GET /api/admin/notifications/unread-count``GET /api/admin/notifications``PUT /api/admin/notifications/:id/read` | 复用现有后台通知接口;业务员账号正常展示低余额通知 |
| #33 临期列表 | `GET /api/admin/expiring-assets` | 支持资产类型、关键词、店铺、套餐、剩余天数和日期范围;响应含 `summary``expiry_level``is_priority` |
| #33 C 端提醒 | `GET /api/c/v1/notifications/unread-count``GET /api/c/v1/notifications` | 复用现有站内通知,前端在 15/7/3 天节点按未读通知弹窗 |
| #36 批量订购上传 | `POST /api/admin/storage/upload-url` | `purpose=batch_purchase`,上传 UTF-8 单列 CSV 后取得 `file_key` |
| #36 批量订购任务 | `POST /api/admin/asset-package-batch-orders``GET /api/admin/asset-package-batch-orders``GET /api/admin/asset-package-batch-orders/:id` | 创建传 `file_key + package_id + payment_method`,线下支付另传 `voucher_keys[]`;详情返回逐行结果 |
| #42 业务导出 | `POST /api/admin/export-tasks``GET /api/admin/export-tasks``GET /api/admin/export-tasks/:id` | `scene` 使用 `iot_card/package/agent_wallet_transaction/agent_recharge/refund/exchange`,筛选条件放 `query` |
| #47 卡限速 | `PUT /api/admin/iot-cards/:iccid/speed-tier` | 请求只传 `code=-1..8`;设备页面不要展示限速入口 |
| #48 后台支付配置 | `GET /api/admin/system-configs``PUT /api/admin/system-configs/:key` | Key 为 `c2b.payment.card_allowed_methods``c2b.payment.device_allowed_methods`;更新请求的 `value` 是 JSON 数组字符串 |
| #48 C 端支付 | `GET /api/c/v1/asset/info``GET /api/c/v1/wallet/recharge-check``POST /api/c/v1/orders/create``POST /api/c/v1/wallet/recharge` | 展示和提交都使用后端返回的 `allowed_payment_methods`;创建订单时 `payment_method` 必传,微信场景按接口要求传 `app_type` |
| #49 分配 CSV 上传 | `POST /api/admin/storage/upload-url` | `purpose=device_batch_allocation`,上传单列 CSV 后取得 `file_key` |
| #49 创建设备分配任务 | `POST /api/admin/devices/import/allocations` | 传 `file_key + operation_type + target_id``operation_type=assign_shop|assign_series` |
| #49 查询任务 | `GET /api/admin/devices/import/tasks``GET /api/admin/devices/import/tasks/:id` | 复用原设备导入任务页面,新增展示 `operation_type``operation_name``target_id``status_name` |
## 四、前端本期最容易漏掉的工作
- 换货列表拆成新、旧资产两个搜索参数。
- 资产预计到期展示使用“预计最终到期”,并按临期字段高亮。
- 套餐分配页面传递默认/覆盖生效条件。
- 店铺页面接入联系电话查询、业务员选择和 C 端登录限制开关。
- 系列套餐授权页面真正使用现有 `packages[]` 做多选提交。
- C 端实名流程只读取 `effective_realname_policy`
- C 端支付按钮只读取 `allowed_payment_methods`
- 历史订单续费继续调用原创建订单接口,不等待新续费接口。
- 企微退款和线下代充值只读展示审批状态,隐藏本地人工审批按钮。
- 卡页面提供固定档位限速;设备页面不得出现限速入口。
- 设备批量分配继续复用原设备导入任务列表/详情页面,但根据 `operation_type` 改标题和结果说明。
## 五、联调和验收边界
- 当前有两个需要在联调时明确的接口边界:
- #44:退款/充值列表已经返回提交人和审批状态,但没有直接返回企微审批节点人员列表;现有 userid 映射能力尚未接入这两个业务 DTO。
- #49:设备批量分配创建服务允许平台和代理账号,但复用的设备导入任务列表/详情 Handler 目前仍沿用“仅平台用户可查看”的旧限制。若本期只允许平台操作则前端应隐藏代理入口;若要求代理自行查看任务,需要再统一后端权限。
- OpenAPI 已生成,新增路径和 DTO 已检查;设备限速旧契约不存在。
- 本次没有执行数据库迁移、完整构建、自动化测试、LSP 或真实企微/Gateway 闭环。
- 企微、Gateway、Redis/Asynq、对象存储配置和回滚步骤见 [`七月迭代联调交付说明.md`](七月迭代联调交付说明.md)。
- 涉及金额的字段默认单位为分;前端展示时统一转换,提交时不要传浮点元金额。