docs(02-01): 完成 PERM+FIN+POLL 9项修复计划 PERM-01/02 FIN-01/02/03 POLL-01/02/03/04
This commit is contained in:
945
docs/system-audit-report.md
Normal file
945
docs/system-audit-report.md
Normal file
@@ -0,0 +1,945 @@
|
||||
# CMP 卡管平台 — 系统业务审计报告
|
||||
|
||||
> 基于代码实现(非规范设计)的全链路审计,2026-03-20
|
||||
> 产品 + 技术双视角,Mermaid 流程图标注业务断点
|
||||
|
||||
---
|
||||
|
||||
## 〇、系统角色与代理层级
|
||||
|
||||
### 角色一览
|
||||
|
||||
| 角色 | 说明 | 登录方式 | 可用接口 | 状态 |
|
||||
|------|------|---------|----------|------|
|
||||
| 平台管理员 | 运营全局 | `/api/auth/login` | `/api/admin/*` | ✅ |
|
||||
| 一级代理 | 平台发展的代理商 | 同上(RBAC 区分) | 同上(数据隔离) | ✅ |
|
||||
| N 级子代理 | 代理发展的下级,最多 7 级 | 同上 | 同上 | ✅ |
|
||||
| 企业客户 | 被授权管理卡/设备的企业 | 同上(能登录) | **无可用接口** | 🔴 |
|
||||
| C 端客户 | 终端用户 | 微信/小程序 `/api/c/v1/auth/*` | `/api/c/v1/*` | ✅ |
|
||||
|
||||
### 代理层级模型
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
P[平台] --> A1[一级代理 A<br/>Shop level=1, parent_id=NULL]
|
||||
P --> A2[一级代理 B<br/>Shop level=1]
|
||||
A1 --> B1[二级代理 C<br/>level=2, parent_id=A]
|
||||
A1 --> B2[二级代理 D<br/>level=2, parent_id=A]
|
||||
B1 --> C1[三级代理 E<br/>level=3, parent_id=C]
|
||||
|
||||
style P fill:#e1f5fe
|
||||
style A1 fill:#fff9c4
|
||||
style A2 fill:#fff9c4
|
||||
style B1 fill:#ffe0b2
|
||||
style B2 fill:#ffe0b2
|
||||
style C1 fill:#ffccbc
|
||||
```
|
||||
|
||||
**技术实现**:`model/shop.go` — `ParentID *uint` + `Level int`(1-7),创建时自动算层级。
|
||||
**数据权限**:登录时预算 `SubordinateShopIDs`(自己+所有下级),GORM Callback 全局过滤。
|
||||
|
||||
---
|
||||
|
||||
## 一、平台搭建 → 代理经营 → C 端消费
|
||||
|
||||
> 这是系统的**主干流程**:平台建好基础数据,代理拿到资源去经营,C 端客户最终消费。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph 平台搭建["① 平台管理员搭建基础设施"]
|
||||
S1["创建运营商<br/>📡 中国移动/联通/电信<br/><code>POST /admin/carriers</code>"]
|
||||
S2["创建套餐系列<br/>📦 如'移动30G月租系列'<br/><code>POST /admin/package-series</code>"]
|
||||
S3["创建套餐<br/>💰 30G/月 ¥29.9<br/><code>POST /admin/packages</code>"]
|
||||
S4["导入 IoT 卡/设备<br/>📥 批量导入 ICCID/设备号<br/><code>POST /admin/iot-cards/import</code>"]
|
||||
S1 --> S2 --> S3
|
||||
S1 --> S4
|
||||
end
|
||||
|
||||
subgraph 代理发展["② 发展代理体系"]
|
||||
D1["创建一级代理<br/>🏪 开设店铺+管理员账号<br/><code>POST /admin/shops</code>"]
|
||||
D2["代理创建子代理<br/>🏪 parent_id=自己<br/>同一接口,RBAC控制"]
|
||||
D1 --> D2
|
||||
end
|
||||
|
||||
subgraph 代理授权["③ 给代理配货"]
|
||||
G1["分配资产给代理<br/>📦 IoT卡/设备 → 代理名下<br/><code>POST /admin/iot-cards/allocate</code><br/>status 1→2, 写入 shop_id"]
|
||||
G2["授权系列给代理<br/>🔑 代理可以卖哪些系列<br/><code>POST /admin/shop-series-grants</code><br/>配置:成本价、佣金金额"]
|
||||
G3["分配套餐<br/>📋 代理可卖哪些具体套餐<br/><code>POST /admin/shop-package-batch-allocations</code><br/>设置零售价、上架状态"]
|
||||
G2 --> G3
|
||||
end
|
||||
|
||||
subgraph 代理可向下级重复此过程["④ 代理给子代理配货(同样的流程)"]
|
||||
R1["代理也可以:<br/>• 分配资产给子代理<br/>• 授权系列给子代理<br/>• 设置子代理的成本价/佣金<br/><br/>⚠️ 子代理佣金 ≤ 自己佣金"]
|
||||
end
|
||||
|
||||
subgraph C端消费["⑤ C 端客户消费"]
|
||||
C1["扫码/输虚拟号验证资产<br/>📱 识别卡/设备<br/><code>POST /c/v1/auth/verify-asset</code><br/>asset_status 1→2(已销售)"]
|
||||
C2["微信授权登录<br/>👤 绑定客户身份<br/><code>POST /c/v1/auth/wechat-login</code>"]
|
||||
C3["查看可购套餐<br/>📋 根据资产所属代理展示<br/><code>GET /c/v1/asset/packages</code><br/>查 ShopPackageAllocation"]
|
||||
C4["下单支付<br/>💳 微信支付<br/><code>POST /c/v1/orders/create</code>"]
|
||||
C5["套餐生效<br/>✅ 立即激活或排队<br/>activateMainPackage()"]
|
||||
|
||||
C1 --> C2 --> C3 --> C4 --> C5
|
||||
end
|
||||
|
||||
平台搭建 --> 代理发展 --> 代理授权
|
||||
代理授权 --> 代理可向下级重复此过程
|
||||
代理授权 --> C端消费
|
||||
代理可向下级重复此过程 --> C端消费
|
||||
|
||||
classDef ok fill:#c8e6c9,stroke:#2e7d32
|
||||
classDef warn fill:#fff9c4,stroke:#f9a825
|
||||
classDef broken fill:#ffcdd2,stroke:#c62828
|
||||
```
|
||||
|
||||
### 这条链路的问题
|
||||
|
||||
| 步骤 | 问题 | 严重度 | 详情 |
|
||||
|------|------|--------|------|
|
||||
| ③ 创建套餐 | `enable_realname_activation` 默认 `true` | 🟡 | 不显式传 false 的套餐,全部变成"需实名后才激活"。`package/service.go:129` |
|
||||
| ⑤ 下单 | C 端实名校验值写错 | 🔴 **Bug** | 检查 `RealNameStatus != 1`,但已实名=2,已实名的卡反而不能买。`client_order/service.go:115` |
|
||||
| ④ 佣金链 | 某级缺授权则上级静默丢佣金 | 🟡 | 代理 B 没有系列授权 → A 的佣金直接不算,无告警。`commission_calculation/service.go:505-509` |
|
||||
|
||||
---
|
||||
|
||||
## 二、套餐激活的三条路径
|
||||
|
||||
> 客户买了套餐后,不一定能立刻生效。根据场景走不同路径。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
PAY["客户支付成功<br/>order/service.go"]
|
||||
|
||||
PAY --> CHECK_REALNAME{"套餐需要实名激活?<br/>EnableRealnameActivation"}
|
||||
|
||||
CHECK_REALNAME -- "false(不需要)" --> CHECK_ACTIVE{"已有生效中主套餐?"}
|
||||
CHECK_REALNAME -- "true(需要实名)" --> PENDING_REALNAME["🟡 进入待实名状态<br/>status=pending<br/>pending_realname_activation=true<br/><br/>等卡实名后自动激活"]
|
||||
|
||||
CHECK_ACTIVE -- "没有" --> ACTIVATE["✅ 立即激活<br/>status=active<br/>设置 activated_at / expires_at<br/>如果卡被停机,自动复机"]
|
||||
CHECK_ACTIVE -- "有" --> QUEUE["🟡 排队等候<br/>status=pending<br/>priority=max+1<br/><br/>前一个套餐到期后自动接力"]
|
||||
|
||||
PENDING_REALNAME --> REALNAME_CHECK["轮询检测卡实名状态<br/>polling:realname 任务<br/>Gateway 返回实名=true"]
|
||||
REALNAME_CHECK --> TRIGGER["触发首次实名激活<br/>triggerFirstRealnameActivation()"]
|
||||
TRIGGER --> RPUSH["RPush 到 Redis 队列<br/>package:first:activation"]
|
||||
RPUSH --> BROKEN["🔴 断链!<br/>scheduler 不消费此队列<br/>套餐永远不会被激活"]
|
||||
|
||||
QUEUE --> EXPIRE["当前套餐到期/用完"]
|
||||
EXPIRE --> NEXT["激活排队中的下一个<br/>ActivateQueuedPackage()<br/>package:queue:activation"]
|
||||
NEXT --> ACTIVATE2["✅ 激活<br/>级联失效旧加油包"]
|
||||
|
||||
style BROKEN fill:#ffcdd2,stroke:#c62828,stroke-width:3px
|
||||
style ACTIVATE fill:#c8e6c9,stroke:#2e7d32
|
||||
style ACTIVATE2 fill:#c8e6c9,stroke:#2e7d32
|
||||
style PENDING_REALNAME fill:#fff9c4,stroke:#f9a825
|
||||
style QUEUE fill:#fff9c4,stroke:#f9a825
|
||||
```
|
||||
|
||||
### 问题
|
||||
|
||||
| 路径 | 状态 | 详情 |
|
||||
|------|------|------|
|
||||
| 立即激活 | ✅ 正常 | 无活跃主套餐时直接激活 |
|
||||
| 排队接力 | ✅ 正常 | 前一个到期后自动激活下一个 |
|
||||
| 待实名激活 | 🔴 **断链** | RPush 到无人消费的队列,**囤货待实名的套餐永远不会自动激活** |
|
||||
|
||||
---
|
||||
|
||||
## 三、套餐使用生命周期
|
||||
|
||||
> 套餐激活后,经历流量消耗、重置、耗尽、到期等阶段。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
ACTIVE["✅ 生效中<br/>status=active"]
|
||||
|
||||
ACTIVE --> USE["日常使用消耗流量<br/>轮询 polling:carddata 检测用量<br/>Gateway 同步 current_month_usage_mb"]
|
||||
USE --> RESET_CHECK{"到重置周期?<br/>data_reset_cycle"}
|
||||
RESET_CHECK -- "是" --> RESET["流量重置<br/>package:data:reset 任务<br/>used_data_mb → 0"]
|
||||
RESET --> USE
|
||||
RESET_CHECK -- "否" --> EXHAUST_CHECK{"流量用完?"}
|
||||
EXHAUST_CHECK -- "否" --> EXPIRE_CHECK{"到期了?"}
|
||||
EXHAUST_CHECK -- "是" --> EXHAUSTED["⚡ 流量耗尽<br/>status=exhausted"]
|
||||
|
||||
EXPIRE_CHECK -- "否" --> USE
|
||||
EXPIRE_CHECK -- "是" --> EXPIRED["⏰ 到期<br/>status=expired"]
|
||||
|
||||
EXHAUSTED --> STOP["自动停机<br/>Gateway.StopCard()<br/>network_status=0<br/>stop_reason=traffic_exhausted"]
|
||||
EXHAUSTED --> NEXT_PKG
|
||||
|
||||
EXPIRED --> NEXT_PKG["激活排队套餐<br/>ActivateQueuedPackage()"]
|
||||
EXPIRED --> CASCADE["级联失效该主套餐的加油包"]
|
||||
|
||||
NEXT_PKG --> HAS_NEXT{"有排队套餐?"}
|
||||
HAS_NEXT -- "有" --> ACTIVATE_NEXT["✅ 激活下一个<br/>如被停机则自动复机"]
|
||||
HAS_NEXT -- "没有" --> NO_PACKAGE["❌ 无可用套餐<br/>保持停机状态<br/>等客户续购"]
|
||||
|
||||
NO_PACKAGE --> CUSTOMER_BUY["客户购买新套餐"]
|
||||
CUSTOMER_BUY --> ACTIVE
|
||||
|
||||
style ACTIVE fill:#c8e6c9
|
||||
style EXHAUSTED fill:#fff9c4
|
||||
style EXPIRED fill:#fff9c4
|
||||
style STOP fill:#ffcdd2
|
||||
style NO_PACKAGE fill:#ffcdd2
|
||||
style ACTIVATE_NEXT fill:#c8e6c9
|
||||
```
|
||||
|
||||
**技术链路验证**:✅ 这条完整链路都有对应的代码实现。停机→复机→套餐接力全部通了。
|
||||
|
||||
---
|
||||
|
||||
## 四、充值 → 佣金 → 提现
|
||||
|
||||
> 资金是系统的命脉。这里有三条独立的资金流。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph 充值流["C 端客户充值"]
|
||||
R1["客户发起充值<br/>💰 选择金额<br/><code>POST /c/v1/wallet/recharge</code>"]
|
||||
R2["微信支付"]
|
||||
R3["支付回调<br/><code>POST /callback/wechat</code><br/>recharge/service.go"]
|
||||
R4["✅ 资产钱包到账<br/>AssetWallet.balance += amount"]
|
||||
R5["更新累计充值<br/>accumulated_recharge_by_series"]
|
||||
R6{"满足一次性佣金条件?"}
|
||||
R7["✅ 佣金直接发放<br/>(同步,不走队列)<br/>→ 店铺 commission 钱包"]
|
||||
R8["触发自动购包检查<br/>task:auto_purchase_after_recharge"]
|
||||
|
||||
R1 --> R2 --> R3 --> R4 --> R5 --> R6
|
||||
R6 -- "是" --> R7
|
||||
R6 -- "否" --> R8
|
||||
R4 --> R8
|
||||
end
|
||||
|
||||
subgraph 订单佣金流["订单支付触发佣金"]
|
||||
O1["客户购买套餐"]
|
||||
O2["支付成功"]
|
||||
O3["异步佣金任务入队<br/>commission:calculate"]
|
||||
O4["链式佣金计算<br/>沿代理层级往上"]
|
||||
O5["✅ 各级代理 commission 钱包到账"]
|
||||
|
||||
O1 --> O2 --> O3 --> O4 --> O5
|
||||
end
|
||||
|
||||
subgraph 提现流["代理佣金提现"]
|
||||
W1["代理发起提现<br/><code>POST /admin/my/commission/withdrawals</code>"]
|
||||
W2["冻结 commission 钱包余额"]
|
||||
W3["平台审批<br/><code>POST /admin/commission-withdrawals/:id/approve</code>"]
|
||||
W4["扣除冻结余额<br/>status=已通过"]
|
||||
W5["🟡 缺失:确认到账<br/>模型有 status=4(已到账)<br/>但没有代码把它设为 4<br/>实际靠线下打款"]
|
||||
|
||||
W1 --> W2 --> W3 --> W4 --> W5
|
||||
end
|
||||
|
||||
subgraph 预充值流["平台给代理预充值"]
|
||||
P1["平台充值<br/><code>POST /admin/agent-recharges</code>"]
|
||||
P2["✅ 代理 main 钱包到账<br/>用于钱包支付订单"]
|
||||
P1 --> P2
|
||||
end
|
||||
|
||||
style W5 fill:#fff9c4,stroke:#f9a825,stroke-width:2px
|
||||
```
|
||||
|
||||
### 问题
|
||||
|
||||
| 环节 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 充值 → 钱包到账 | ✅ | 链路完整 |
|
||||
| 充值 → 一次性佣金 | ✅ | 同步发放,但不走异步队列(与订单佣金路径不一致) |
|
||||
| 订单 → 佣金计算 | ✅ | 异步链路完整 |
|
||||
| 佣金提现 → 审批 | ✅ | 冻结/审批/拒绝都有 |
|
||||
| 审批 → 到账确认 | 🟡 **缺失** | 没有"已到账"标记入口,提现审批后就结束了 |
|
||||
|
||||
---
|
||||
|
||||
## 五、停机 / 复机
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph 自动停机["自动停机(流量耗尽)"]
|
||||
AS1["轮询检测到无活跃套餐"]
|
||||
AS2["调用 Gateway 停机<br/>Gateway.StopCard()"]
|
||||
AS3["更新状态<br/>network_status=0<br/>stopped_at=now<br/>stop_reason=traffic_exhausted"]
|
||||
AS1 --> AS2 --> AS3
|
||||
end
|
||||
|
||||
subgraph 自动复机["自动复机(新套餐激活)"]
|
||||
AR1["新套餐激活触发回调"]
|
||||
AR2{"停机原因是流量耗尽?"}
|
||||
AR2 -- "是" --> AR3["调用 Gateway 复机<br/>Gateway.StartCard()"]
|
||||
AR2 -- "否(手动停机)" --> AR4["❌ 不自动复机<br/>手动停的需要手动开"]
|
||||
AR3 --> AR5["更新状态<br/>network_status=1<br/>resumed_at=now"]
|
||||
AR1 --> AR2
|
||||
end
|
||||
|
||||
subgraph 手动停复机["后台手动操作"]
|
||||
M1["管理员/代理手动停机<br/><code>POST /admin/assets/card/:iccid/stop</code>"]
|
||||
M2["管理员/代理手动复机<br/><code>POST /admin/assets/card/:iccid/start</code>"]
|
||||
M1 --> M1A["stop_reason=manual"]
|
||||
M2 --> M2A["stop_reason 清空"]
|
||||
end
|
||||
|
||||
style AS3 fill:#ffcdd2
|
||||
style AR5 fill:#c8e6c9
|
||||
style AR4 fill:#fff9c4
|
||||
```
|
||||
|
||||
**状态**:✅ 全部链路完整,包括 Gateway 实际调用、保护期一致性校验。
|
||||
|
||||
---
|
||||
|
||||
## 六、换货流程
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph 发起["① 后台发起换货"]
|
||||
E1["代理创建换货单<br/><code>POST /admin/exchanges</code><br/>选择旧资产<br/>状态:待填写信息"]
|
||||
end
|
||||
|
||||
subgraph 客户["② 客户填写信息"]
|
||||
E2["C 端提交收货地址<br/><code>POST /c/v1/exchange/:id/shipping-info</code><br/>状态:待发货"]
|
||||
end
|
||||
|
||||
subgraph 发货["③ 后台发货"]
|
||||
E3["绑定新资产+物流<br/><code>POST /admin/exchanges/:id/ship</code><br/>新资产必须 asset_status=1<br/>状态:已发货"]
|
||||
end
|
||||
|
||||
subgraph 完成["④ 完成换货"]
|
||||
E4["确认完成<br/><code>POST /admin/exchanges/:id/complete</code>"]
|
||||
E5{"需要迁移数据?"}
|
||||
E5 -- "是" --> E6["迁移:<br/>• 钱包余额<br/>• 套餐记录<br/>• 累计充值/首充<br/>• 客户绑定<br/>• 资源标签"]
|
||||
E6 --> E7["旧资产 asset_status → 3(已换货)"]
|
||||
E5 -- "否" --> E7B["仅标记完成"]
|
||||
end
|
||||
|
||||
发起 --> 客户 --> 发货 --> 完成
|
||||
|
||||
style E7 fill:#fff9c4
|
||||
```
|
||||
|
||||
**状态**:✅ 链路完整。旧资产正确标记,数据迁移覆盖钱包/套餐/标签等。
|
||||
|
||||
---
|
||||
|
||||
## 七、企业客户流程
|
||||
|
||||
> 🔴 **这是系统最大的业务断裂**
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph 平台操作["平台/代理 操作(✅ 已实现)"]
|
||||
E1["创建企业<br/><code>POST /admin/enterprises</code>"]
|
||||
E2["创建企业账号<br/>user_type=4"]
|
||||
E3["授权卡给企业<br/><code>POST /admin/enterprises/:id/allocate-cards</code>"]
|
||||
E4["授权设备给企业<br/><code>POST /admin/enterprises/:id/allocate-devices</code>"]
|
||||
E1 --> E2
|
||||
E1 --> E3
|
||||
E1 --> E4
|
||||
end
|
||||
|
||||
subgraph 企业使用["企业自己管理(🔴 完全缺失)"]
|
||||
F1["❌ 企业登录后无可用接口"]
|
||||
F2["❌ 看不到被授权的卡列表"]
|
||||
F3["❌ 看不到被授权的设备列表"]
|
||||
F4["❌ 无法对卡/设备停复机"]
|
||||
F5["❌ 无法查看套餐/流量"]
|
||||
F6["❌ 无法为卡/设备续购套餐"]
|
||||
end
|
||||
|
||||
平台操作 --"🔴 断裂"--> 企业使用
|
||||
|
||||
subgraph 已有代码未接入["Service 层已写好但没接路由"]
|
||||
G1["ListDevicesForEnterprise()"]
|
||||
G2["GetDeviceDetail()"]
|
||||
G3["SuspendCard() / ResumeCard()"]
|
||||
end
|
||||
|
||||
style 企业使用 fill:#ffcdd2,stroke:#c62828,stroke-width:3px
|
||||
style 已有代码未接入 fill:#fff9c4
|
||||
```
|
||||
|
||||
**缺失清单**:
|
||||
- ❌ 企业认证中间件(只有 AdminAuth 和 PersonalAuth)
|
||||
- ❌ `/api/enterprise/*` 或 `/api/h5/*` 路由组
|
||||
- ❌ 企业端 Handler 层
|
||||
- ❌ 企业端可用的卡/设备/套餐/流量接口
|
||||
|
||||
**规范中的设计**:
|
||||
- `openspec/specs/enterprise-device-authorization/spec.md` — 明确写了企业端 H5 设备列表、详情、停复机
|
||||
- `openspec/changes/archive/2026-01-29-add-enterprise-device-authorization/proposal.md` — "新增企业端设备管理 API(H5)"
|
||||
|
||||
---
|
||||
|
||||
## 八、资产状态系统
|
||||
|
||||
> 当前系统有**三套并行的状态字段**,容易混淆。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph status字段["status(分销链状态)"]
|
||||
S1["1 在库"] -- "AllocateCards()" --> S2["2 已分销"]
|
||||
S2 -. "❌ 无代码实现" .-> S3["3 已激活"]
|
||||
S3 -. "❌ 无代码实现" .-> S4["4 已停用"]
|
||||
end
|
||||
|
||||
subgraph asset_status字段["asset_status(业务生命周期)"]
|
||||
A1["1 在库"] -- "客户绑定资产" --> A2["2 已销售"]
|
||||
A2 -- "手动停用" --> A4["4 已停用"]
|
||||
A1 -- "手动停用" --> A4
|
||||
A2 -- "换货完成" --> A3["3 已换货"]
|
||||
end
|
||||
|
||||
subgraph network_status字段["network_status(网络状态)"]
|
||||
N1["1 开机"] -- "停机" --> N0["0 停机"]
|
||||
N0 -- "复机" --> N1
|
||||
end
|
||||
|
||||
style S3 fill:#e0e0e0,stroke:#9e9e9e,stroke-dasharray: 5 5
|
||||
style S4 fill:#e0e0e0,stroke:#9e9e9e,stroke-dasharray: 5 5
|
||||
```
|
||||
|
||||
**问题**:`status` 字段的 3(已激活)和 4(已停用)从未被任何代码使用。真正的业务状态走 `asset_status`,网络状态走 `network_status`。两个 status 字段语义重叠导致混淆。
|
||||
|
||||
---
|
||||
|
||||
## 九、实名状态不一致
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph 三处定义["同一个字段,三处定义不一致"]
|
||||
D1["Model 注释<br/>0=未实名 1=已实名"]
|
||||
D2["DTO 注释<br/>0=未实名 1=实名中 2=已实名"]
|
||||
D3["轮询代码实际写入<br/>true→2, false→0"]
|
||||
end
|
||||
|
||||
subgraph 影响["导致的问题"]
|
||||
I1["C 端下单校验<br/>RealNameStatus != 1<br/>只允许'实名中'通过?"]
|
||||
I2["已实名的卡 status=2<br/>2 != 1 为 true<br/>🔴 被拒单!"]
|
||||
end
|
||||
|
||||
D1 --> I1
|
||||
D2 --> I1
|
||||
D3 --> I2
|
||||
|
||||
style I2 fill:#ffcdd2,stroke:#c62828,stroke-width:3px
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十、代理佣金链式分配
|
||||
|
||||
> 当子代理的客户买了套餐,佣金怎么分?
|
||||
|
||||
```mermaid
|
||||
flowchart BT
|
||||
CUSTOMER["客户购买套餐<br/>💰 售价 ¥99"]
|
||||
|
||||
CUSTOMER --> C_AGENT["三级代理 C<br/>成本价 ¥60<br/>利润:¥99 - ¥60 = ¥39<br/>→ C 的 commission 钱包"]
|
||||
|
||||
C_AGENT --> B_AGENT["二级代理 B<br/>成本价 ¥40<br/>利润:¥60 - ¥40 = ¥20<br/>→ B 的 commission 钱包"]
|
||||
|
||||
B_AGENT --> A_AGENT["一级代理 A<br/>成本价 ¥25<br/>利润:¥40 - ¥25 = ¥15<br/>→ A 的 commission 钱包"]
|
||||
|
||||
A_AGENT --> PLATFORM["平台<br/>成本价 ¥25(A的成本价)<br/>不生成佣金记录<br/>平台收益在售价中"]
|
||||
|
||||
MISSING["⚠️ 如果 B 没有该系列授权<br/>→ B 和 A 都不会拿到佣金<br/>→ 无告警通知"]
|
||||
|
||||
style CUSTOMER fill:#e3f2fd
|
||||
style C_AGENT fill:#c8e6c9
|
||||
style B_AGENT fill:#c8e6c9
|
||||
style A_AGENT fill:#c8e6c9
|
||||
style PLATFORM fill:#e1f5fe
|
||||
style MISSING fill:#fff9c4,stroke:#f9a825
|
||||
```
|
||||
|
||||
**技术实现**:`commission_calculation/service.go` 的 `calculateChainOneTimeCommission()` 和差价佣金计算,沿 `Shop.ParentID` 循环向上,每级拿差额。
|
||||
|
||||
---
|
||||
|
||||
## 十一、异步任务完整性
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph 完整["✅ 链路完整"]
|
||||
T1["commission:calculate"]
|
||||
T2["polling:realname / carddata / package"]
|
||||
T3["polling:protect"]
|
||||
T4["package:data:reset"]
|
||||
T5["package:queue:activation"]
|
||||
T6["order:expire"]
|
||||
T7["auto_purchase_after_recharge"]
|
||||
T8["iot_card:import / device:import"]
|
||||
T9["commission:stats:* (3个)"]
|
||||
T10["alert:check / data:cleanup"]
|
||||
end
|
||||
|
||||
subgraph 断链["🔴 断链"]
|
||||
B1["package:first:activation<br/><br/>生产者:triggerFirstRealnameActivation()<br/>消费者:HandlePackageFirstActivation()<br/><br/>问题:RPush 到 Redis list<br/>但 scheduler 不消费这个队列"]
|
||||
end
|
||||
|
||||
subgraph 未确认["⚠️ 待确认"]
|
||||
U1["sim:status:sync<br/>有消费者,未确认生产者"]
|
||||
end
|
||||
|
||||
style B1 fill:#ffcdd2,stroke:#c62828,stroke-width:2px
|
||||
style U1 fill:#fff9c4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十二、系统全景互联图
|
||||
|
||||
> **这是整个系统的"大地图"**,展示所有业务域之间的数据流和触发关系。
|
||||
> 红色=断链/缺失,黄色=有风险,绿色=正常。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
%% ===== 平台搭建层 =====
|
||||
subgraph 平台搭建["🏗️ 平台搭建"]
|
||||
CARRIER["运营商"]
|
||||
SERIES["套餐系列"]
|
||||
PKG["套餐"]
|
||||
CARRIER --> SERIES --> PKG
|
||||
end
|
||||
|
||||
%% ===== 代理体系层 =====
|
||||
subgraph 代理体系["🏪 代理体系(多级)"]
|
||||
SHOP["创建店铺/子代理"]
|
||||
GRANT["授权系列给代理<br/>配置成本价/佣金"]
|
||||
ALLOC_PKG["分配套餐<br/>设置零售价/上架"]
|
||||
SHOP --> GRANT --> ALLOC_PKG
|
||||
end
|
||||
|
||||
%% ===== 资产层 =====
|
||||
subgraph 资产管理["📦 资产管理"]
|
||||
IMPORT["导入卡/设备"]
|
||||
ALLOC_ASSET["分配给代理<br/>status 1→2"]
|
||||
BIND["C端客户绑定<br/>asset_status 1→2"]
|
||||
IMPORT --> ALLOC_ASSET --> BIND
|
||||
end
|
||||
|
||||
%% ===== C端层 =====
|
||||
subgraph C端["📱 C 端客户"]
|
||||
LOGIN["微信登录"]
|
||||
SEE_PKG["查看可购套餐"]
|
||||
ORDER["下单购买"]
|
||||
PAY["微信支付"]
|
||||
USE["日常使用"]
|
||||
RECHARGE["充值"]
|
||||
LOGIN --> SEE_PKG --> ORDER --> PAY
|
||||
PAY --> USE
|
||||
USE --> RECHARGE
|
||||
end
|
||||
|
||||
%% ===== 套餐层 =====
|
||||
subgraph 套餐系统["📋 套餐生命周期"]
|
||||
ACTIVATE["激活"]
|
||||
QUEUE_WAIT["排队等候"]
|
||||
PENDING_REALNAME["待实名激活"]
|
||||
DATA_RESET["流量重置"]
|
||||
EXHAUST["流量耗尽"]
|
||||
EXPIRE["到期"]
|
||||
NEXT_ACTIVATE["激活下一个"]
|
||||
end
|
||||
|
||||
%% ===== 支付/财务层 =====
|
||||
subgraph 财务["💰 资金流"]
|
||||
WALLET_IN["资产钱包到账"]
|
||||
COMMISSION_CALC["佣金计算<br/>链式分账"]
|
||||
COMMISSION_WALLET["代理佣金钱包"]
|
||||
WITHDRAW["佣金提现"]
|
||||
AGENT_RECHARGE["平台给代理充值<br/>主钱包"]
|
||||
end
|
||||
|
||||
%% ===== 轮询层 =====
|
||||
subgraph 轮询["🔄 轮询系统"]
|
||||
POLL_REALNAME["实名检测"]
|
||||
POLL_CARDDATA["流量检测"]
|
||||
POLL_PACKAGE["套餐检测"]
|
||||
POLL_RESET["数据重置"]
|
||||
end
|
||||
|
||||
%% ===== 网关层 =====
|
||||
subgraph 网关["📡 Gateway"]
|
||||
GW_STOP["停机"]
|
||||
GW_START["复机"]
|
||||
GW_QUERY["查询实名/流量"]
|
||||
end
|
||||
|
||||
%% ===== 企业层 =====
|
||||
subgraph 企业["🏢 企业客户"]
|
||||
ENT_AUTH["授权卡/设备给企业"]
|
||||
ENT_USE["🔴 企业自己管理<br/>(完全缺失)"]
|
||||
end
|
||||
|
||||
%% ===== 换货层 =====
|
||||
subgraph 换货["🔄 换货"]
|
||||
EXCHANGE["发起→发货→完成"]
|
||||
MIGRATE["迁移钱包/套餐/标签"]
|
||||
end
|
||||
|
||||
%% ===== 自动任务 =====
|
||||
subgraph 自动任务["⚡ 自动任务"]
|
||||
AUTO_PURCHASE["🔴 自动购包<br/>(无人触发)"]
|
||||
ORDER_EXPIRE["订单超时取消"]
|
||||
ALERT["告警检查"]
|
||||
end
|
||||
|
||||
%% ========== 连线 ==========
|
||||
|
||||
%% 平台搭建 → 代理
|
||||
平台搭建 --> 代理体系
|
||||
平台搭建 --> 资产管理
|
||||
|
||||
%% 代理 → C端可见
|
||||
ALLOC_PKG --> SEE_PKG
|
||||
ALLOC_ASSET --> BIND
|
||||
|
||||
%% C端购买链路
|
||||
PAY -->|"支付成功"| ACTIVATE
|
||||
PAY -->|"有主套餐"| QUEUE_WAIT
|
||||
PAY -->|"⚠️ 需实名"| PENDING_REALNAME
|
||||
PAY -->|"异步"| COMMISSION_CALC
|
||||
|
||||
%% 充值链路
|
||||
RECHARGE -->|"回调"| WALLET_IN
|
||||
WALLET_IN -->|"满足条件"| COMMISSION_CALC
|
||||
RECHARGE -.->|"🔴 应触发但未接线"| AUTO_PURCHASE
|
||||
|
||||
%% 佣金链路
|
||||
COMMISSION_CALC -->|"各级差额"| COMMISSION_WALLET
|
||||
COMMISSION_WALLET --> WITHDRAW
|
||||
AGENT_RECHARGE -->|"主钱包"| ORDER
|
||||
|
||||
%% 套餐生命周期
|
||||
ACTIVATE --> USE
|
||||
USE --> DATA_RESET
|
||||
USE --> EXHAUST
|
||||
USE --> EXPIRE
|
||||
EXHAUST -->|"停机"| GW_STOP
|
||||
EXPIRE --> NEXT_ACTIVATE
|
||||
NEXT_ACTIVATE -->|"复机"| GW_START
|
||||
QUEUE_WAIT -->|"前一个到期"| NEXT_ACTIVATE
|
||||
|
||||
%% 实名激活断链
|
||||
POLL_REALNAME -->|"首次实名"| PENDING_REALNAME
|
||||
PENDING_REALNAME -.->|"🔴 断链"| ACTIVATE
|
||||
|
||||
%% 轮询 → 网关
|
||||
POLL_REALNAME --> GW_QUERY
|
||||
POLL_CARDDATA --> GW_QUERY
|
||||
POLL_CARDDATA -->|"扣减流量"| EXHAUST
|
||||
POLL_PACKAGE -->|"超额检测"| GW_STOP
|
||||
POLL_RESET --> DATA_RESET
|
||||
|
||||
%% 企业
|
||||
ALLOC_ASSET --> ENT_AUTH
|
||||
ENT_AUTH -.->|"🔴 断裂"| ENT_USE
|
||||
|
||||
%% 换货
|
||||
EXCHANGE --> MIGRATE
|
||||
MIGRATE -->|"旧资产 asset_status=3"| 资产管理
|
||||
|
||||
%% 自动任务
|
||||
ORDER -->|"超时"| ORDER_EXPIRE
|
||||
AUTO_PURCHASE -.->|"🔴 应连接"| ORDER
|
||||
|
||||
%% 样式
|
||||
style ENT_USE fill:#ffcdd2,stroke:#c62828,stroke-width:3px
|
||||
style PENDING_REALNAME fill:#ffcdd2,stroke:#c62828,stroke-width:2px
|
||||
style AUTO_PURCHASE fill:#ffcdd2,stroke:#c62828,stroke-width:2px
|
||||
style ACTIVATE fill:#c8e6c9
|
||||
style NEXT_ACTIVATE fill:#c8e6c9
|
||||
style COMMISSION_CALC fill:#c8e6c9
|
||||
style WALLET_IN fill:#c8e6c9
|
||||
```
|
||||
|
||||
### 图中的五条断链 / 缺失
|
||||
|
||||
| # | 断链位置 | 问题 | 影响 |
|
||||
|---|---------|------|------|
|
||||
| ① | 充值 → 自动购包 | `NewAutoPurchaseTask()` 无人调用 | 强充后不会自动购买套餐 |
|
||||
| ② | 实名检测 → 待实名套餐激活 | RPush 到无人消费的队列 | 囤货套餐永远不激活 |
|
||||
| ③ | 企业授权 → 企业管理 | 无企业端接口 | 企业用户无法使用系统 |
|
||||
| ④ | 自动购包 → 佣金 | 创建已支付订单但不触发佣金计算 | 自动购包的订单不产生佣金 |
|
||||
| ⑤ | C 端下单 → 实名校验 | `!= 1` 应为 `!= 2` | 已实名的卡被拒单 |
|
||||
|
||||
---
|
||||
|
||||
## 十三、自动购包完整链路
|
||||
|
||||
> 🔴 这条链路**有处理器但无人触发**
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph 预期流程["设计中的完整流程"]
|
||||
R1["C 端发起强充充值<br/><code>POST /c/v1/wallet/recharge</code><br/>关联套餐 LinkedPackageIDs"]
|
||||
R2["微信支付成功<br/>充值回调"]
|
||||
R3["资产钱包到账"]
|
||||
R4["🔴 应该触发自动购包<br/>Enqueue auto_purchase_after_recharge<br/><br/>但当前代码中<br/>NewAutoPurchaseTask() 无人调用"]
|
||||
R5["自动购包处理器<br/>auto_purchase.go:ProcessTask()"]
|
||||
R6["检查钱包余额"]
|
||||
R7["创建订单(已支付状态)"]
|
||||
R8["激活套餐"]
|
||||
R9["🟡 应触发佣金但未触发<br/>没有 enqueueCommissionCalculation"]
|
||||
|
||||
R1 --> R2 --> R3 --> R4
|
||||
R4 -.->|"未接线"| R5
|
||||
R5 --> R6 --> R7 --> R8
|
||||
R8 --> R9
|
||||
end
|
||||
|
||||
style R4 fill:#ffcdd2,stroke:#c62828,stroke-width:3px
|
||||
style R9 fill:#fff9c4,stroke:#f9a825,stroke-width:2px
|
||||
```
|
||||
|
||||
**技术详情**:
|
||||
- 生产者:`task/auto_purchase.go:202-214` — `NewAutoPurchaseTask()` 创建任务对象,但全项目无调用点
|
||||
- 消费者:`task/auto_purchase.go:76-199` — `ProcessTask()` 完整实现了购包逻辑
|
||||
- 数据前置:`client_order/service.go:370` — 充值记录的 `auto_purchase_status=pending` 已写入
|
||||
- 佣金缺失:`auto_purchase.go` 内部没有 `enqueueCommissionCalculation` 调用
|
||||
|
||||
---
|
||||
|
||||
## 十四、轮询系统互联图
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
SCHEDULER["轮询调度器<br/>每秒执行一次<br/>scheduler.go"]
|
||||
|
||||
SCHEDULER --> REALNAME["实名检测<br/>polling:realname"]
|
||||
SCHEDULER --> CARDDATA["流量检测<br/>polling:carddata"]
|
||||
SCHEDULER --> PACKAGE["套餐检测<br/>polling:package"]
|
||||
SCHEDULER --> RESET["数据重置<br/>每分钟检查"]
|
||||
SCHEDULER --> PKG_ACTIVATION["套餐过期检查<br/>→ 排队激活"]
|
||||
|
||||
SCHEDULER -.->|"🟡 未调度"| PROTECT["保护期一致性<br/>polling:protect"]
|
||||
|
||||
REALNAME --> R1["调 Gateway 查实名"]
|
||||
R1 --> R2["更新 real_name_status"]
|
||||
R2 --> R3{"首次实名?"}
|
||||
R3 -- "是" --> R4["🔴 触发待实名激活<br/>(断链:RPush 到无人消费队列)"]
|
||||
R3 -- "否" --> R5["重新入队"]
|
||||
|
||||
CARDDATA --> C1["调 Gateway 查流量"]
|
||||
C1 --> C2["更新卡月用量"]
|
||||
C2 --> C3["扣减套餐流量<br/>加油包优先"]
|
||||
C3 --> C4{"全部套餐耗尽?"}
|
||||
C4 -- "是" --> C5["调 Gateway 停机<br/>network_status=0"]
|
||||
C4 -- "否" --> C6["重新入队"]
|
||||
|
||||
PACKAGE --> P1["读套餐配置"]
|
||||
P1 --> P2{"超额?"}
|
||||
P2 -- "是" --> P3["调 Gateway 停机"]
|
||||
P2 -- "否" --> P4["重新入队"]
|
||||
|
||||
RESET --> RS1{"日/月/年重置时间到?"}
|
||||
RS1 -- "是" --> RS2["重置 data_usage_mb=0<br/>status→active<br/>计算 next_reset_at"]
|
||||
|
||||
PKG_ACTIVATION --> PA1["检查过期主套餐"]
|
||||
PA1 --> PA2["级联失效加油包"]
|
||||
PA2 --> PA3["激活排队主套餐"]
|
||||
PA3 --> PA4["触发自动复机回调"]
|
||||
|
||||
style R4 fill:#ffcdd2,stroke:#c62828,stroke-width:2px
|
||||
style PROTECT fill:#fff9c4,stroke:#f9a825,stroke-dasharray: 5 5
|
||||
style C5 fill:#ffcdd2
|
||||
```
|
||||
|
||||
**回调注入情况**:
|
||||
|
||||
| 回调接口 | 定义位置 | bootstrap 注入 | 状态 |
|
||||
|---------|---------|---------------|------|
|
||||
| `PollingCallback`(卡生命周期) | `iot_card/service.go` | `bootstrap/services.go` ✅ | ✅ 已接线 |
|
||||
| `StopResumeCallback`(停复机) | `package/usage_service.go` | ⚠️ 未确认 | 🟡 可能未注入 |
|
||||
| `ResumeCallback`(激活后复机) | `package/activation_service.go` | ⚠️ 未确认 | 🟡 可能未注入 |
|
||||
|
||||
---
|
||||
|
||||
## 十五、订单完整生命周期
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph 创建["创建订单"]
|
||||
CREATE_ADMIN["后台下单<br/><code>POST /admin/orders</code>"]
|
||||
CREATE_CLIENT["C端下单<br/><code>POST /c/v1/orders/create</code>"]
|
||||
CREATE_AUTO["自动购包创建<br/>🔴 未接线"]
|
||||
end
|
||||
|
||||
PENDING["待支付<br/>设置 expires_at<br/>开始超时倒计时"]
|
||||
|
||||
subgraph 支付["支付"]
|
||||
PAY_WECHAT["微信支付"]
|
||||
PAY_WALLET["钱包支付<br/>立即扣款"]
|
||||
PAY_CALLBACK["支付回调<br/><code>POST /callback/wechat</code>"]
|
||||
end
|
||||
|
||||
subgraph 支付后副作用["支付成功后的链式反应"]
|
||||
ACTIVATE_PKG["激活套餐<br/>(立即/排队/待实名)"]
|
||||
ENQUEUE_COMMISSION["入队佣金计算<br/>commission:calculate"]
|
||||
CLEAR_EXPIRE["清空 expires_at"]
|
||||
end
|
||||
|
||||
subgraph 超时["超时处理"]
|
||||
EXPIRE_CHECK["Asynq 每分钟扫描<br/>order_expire 任务"]
|
||||
CANCEL["取消订单<br/>payment_status=cancelled"]
|
||||
UNFREEZE["解冻钱包余额<br/>(如果冻结过)"]
|
||||
end
|
||||
|
||||
CREATE_ADMIN --> PENDING
|
||||
CREATE_CLIENT --> PENDING
|
||||
CREATE_AUTO -.-> PENDING
|
||||
|
||||
PENDING --> PAY_WECHAT --> PAY_CALLBACK
|
||||
PENDING --> PAY_WALLET
|
||||
|
||||
PAY_CALLBACK --> ACTIVATE_PKG
|
||||
PAY_CALLBACK --> ENQUEUE_COMMISSION
|
||||
PAY_CALLBACK --> CLEAR_EXPIRE
|
||||
|
||||
PAY_WALLET --> ACTIVATE_PKG
|
||||
PAY_WALLET --> ENQUEUE_COMMISSION
|
||||
|
||||
PENDING -->|"超过 expires_at"| EXPIRE_CHECK
|
||||
EXPIRE_CHECK --> CANCEL --> UNFREEZE
|
||||
|
||||
style CREATE_AUTO fill:#ffcdd2,stroke-dasharray: 5 5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十六、C 端客户完整旅程
|
||||
|
||||
> 从扫码到日常使用的端到端闭环
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
SCAN["📱 扫码/输入虚拟号<br/>verify-asset"]
|
||||
LOGIN["🔑 微信授权登录<br/>wechat-login / miniapp-login"]
|
||||
BIND_PHONE["📞 绑定手机号<br/>bind-phone"]
|
||||
PROFILE["👤 个人资料<br/>get/update profile"]
|
||||
|
||||
SEE_ASSET["📊 查看资产信息<br/>asset/info"]
|
||||
SEE_PKG["📋 查看可购套餐<br/>asset/packages"]
|
||||
SEE_HISTORY["📜 套餐历史<br/>asset/package-history"]
|
||||
REFRESH["🔄 刷新资产<br/>asset/refresh"]
|
||||
|
||||
BUY["🛒 购买套餐<br/>orders/create → 微信支付"]
|
||||
ORDER_LIST["📑 我的订单<br/>orders / orders/:id"]
|
||||
|
||||
WALLET["💰 钱包详情<br/>wallet/detail"]
|
||||
RECHARGE["💳 充值<br/>wallet/recharge"]
|
||||
RECHARGE_LIST["📃 充值记录<br/>wallet/recharges"]
|
||||
TRANSACTIONS["📃 流水<br/>wallet/transactions"]
|
||||
|
||||
REALNAME["🪪 实名认证<br/>realname/link"]
|
||||
|
||||
DEVICE_CARDS["📡 设备卡列表<br/>device/cards"]
|
||||
DEVICE_OPS["⚙️ 设备操作<br/>reboot / factory-reset / wifi / switch-card"]
|
||||
|
||||
EXCHANGE_PENDING["🔄 查看换货单<br/>exchange/pending"]
|
||||
EXCHANGE_SHIP["📦 提交收货信息<br/>exchange/:id/shipping-info"]
|
||||
|
||||
SCAN --> LOGIN --> BIND_PHONE --> PROFILE
|
||||
PROFILE --> SEE_ASSET
|
||||
SEE_ASSET --> SEE_PKG --> BUY
|
||||
SEE_ASSET --> SEE_HISTORY
|
||||
SEE_ASSET --> REFRESH
|
||||
|
||||
BUY --> ORDER_LIST
|
||||
SEE_ASSET --> WALLET --> RECHARGE
|
||||
WALLET --> RECHARGE_LIST
|
||||
WALLET --> TRANSACTIONS
|
||||
|
||||
SEE_ASSET --> REALNAME
|
||||
SEE_ASSET --> DEVICE_CARDS --> DEVICE_OPS
|
||||
|
||||
SEE_ASSET --> EXCHANGE_PENDING --> EXCHANGE_SHIP
|
||||
|
||||
style SCAN fill:#e3f2fd
|
||||
style BUY fill:#c8e6c9
|
||||
style RECHARGE fill:#c8e6c9
|
||||
```
|
||||
|
||||
**这条旅程的完整性**:✅ C 端所有接口都已实现(在 `/api/c/v1/*` 下),功能闭环。主要问题在后端处理逻辑(实名校验 bug、激活断链),不在接口层。
|
||||
|
||||
---
|
||||
|
||||
## 十六、TODO 留桩清单
|
||||
|
||||
> 代码中明确标记了 TODO 但未实现的功能。
|
||||
|
||||
| 位置 | TODO 内容 | 影响 |
|
||||
|------|----------|------|
|
||||
| `order/service.go:2356` | 实现富友支付发起逻辑 | 富友支付渠道不可用 |
|
||||
| `order/service.go:2362` | 实现富友小程序支付发起逻辑 | 同上 |
|
||||
| `order/service.go:2107,2163` | 从 payment_config_id 动态加载支付配置 | 多商户支付不可用 |
|
||||
| `recharge/service.go:271` | 按 payment_config_id 加载配置验签 | 充值验签留桩 |
|
||||
| `customer/service.go:53,66` | 通过 PersonalCustomerPhoneStore 管理手机号 | 客户手机号 CRUD 不完整 |
|
||||
| `polling/alert_service.go:395,405` | 集成邮件/短信服务 | 告警只写日志,不发通知 |
|
||||
|
||||
---
|
||||
|
||||
## 📋 完整问题清单
|
||||
|
||||
### 🔴 生产 Bug(业务跑不通)
|
||||
|
||||
| # | 问题 | 影响 | 位置 |
|
||||
|---|------|------|------|
|
||||
| **P0-1** | C 端实名校验值写错 | 已实名的卡无法购买需实名套餐 | `client_order/service.go:115` |
|
||||
| **P0-2** | 实名激活任务断链 | 囤货待实名的套餐永远不会自动激活 | `polling_handler.go:1093` |
|
||||
| **P0-3** | 自动购包任务无生产者 | `NewAutoPurchaseTask()` 全项目无人调用,强充后不会自动购包 | `task/auto_purchase.go:202` |
|
||||
| **P0-4** | 自动购包不触发佣金 | 创建了已支付订单但没有 `enqueueCommissionCalculation` | `task/auto_purchase.go` |
|
||||
|
||||
### 🟠 功能缺失(设计了没实现)
|
||||
|
||||
| # | 问题 | 影响 | 出处 |
|
||||
|---|------|------|------|
|
||||
| **P1-1** | 企业端接口完全缺失 | 企业用户无法管理自己的资产 | enterprise-device-authorization spec |
|
||||
| **P1-2** | 企业认证中间件缺失 | 企业无独立登录鉴权 | b-end-auth spec |
|
||||
| **P1-3** | 提现无"已到账"确认 | 审批后无法标记打款完成 | commission_withdrawal model |
|
||||
| **P1-4** | 富友支付未实现 | 该支付渠道不可用 | order/service.go:2356 |
|
||||
| **P1-5** | 多商户支付配置未实现 | 无法按商户动态加载支付 | order/service.go:2107 |
|
||||
| **P1-6** | `polling:protect` 未被调度 | 保护期一致性检查定义了但 scheduler 不消费 | polling/scheduler.go |
|
||||
|
||||
### 🟡 设计缺陷 / 技术债
|
||||
|
||||
| # | 问题 | 影响 | 位置 |
|
||||
|---|------|------|------|
|
||||
| **P2-1** | EnableRealnameActivation 默认 true | 所有套餐默认需实名 | package/service.go:129 |
|
||||
| **P2-2** | 实名状态值三处不一致 | 校验逻辑混乱 | model / DTO / polling |
|
||||
| **P2-3** | status 与 asset_status 冗余 | status 的 3/4 是死状态 | iot_card.go / device.go |
|
||||
| **P2-4** | 佣金链断裂无告警 | 中间级缺授权则上级静默丢佣金 | commission_calculation |
|
||||
| **P2-5** | 废弃字段仍在并行写入 | first_commission_paid / accumulated_recharge | recharge/service.go |
|
||||
| **P2-6** | 告警只写日志不发通知 | 邮件/短信未集成 | alert_service.go |
|
||||
| **P2-7** | StopResumeCallback / ResumeCallback 可能未注入 | 停复机和激活后复机的回调可能未在 bootstrap 接线 | activation_service / usage_service |
|
||||
|
||||
### 🔵 待确认
|
||||
|
||||
| # | 问题 | 说明 |
|
||||
|---|------|------|
|
||||
| **Q-1** | `sim:status:sync` 任务生产者 | 有消费者无生产者 |
|
||||
| **Q-2** | `package:queue:activation` 完整性 | 生产→消费链待验证 |
|
||||
| **Q-3** | 充值验签留桩 | recharge 验签由外层处理,未确认 |
|
||||
|
||||
---
|
||||
|
||||
## 🔧 修复优先级建议
|
||||
|
||||
### 立即修复(P0)— 业务跑不通
|
||||
1. **P0-1** `client_order/service.go:115` — `!= 1` 改为 `!= 2`
|
||||
2. **P0-2** `polling_handler.go:1093` — RPush 改为 `queueClient.Enqueue()`,或 scheduler 添加消费者
|
||||
3. **P0-3** 充值回调中补充 `NewAutoPurchaseTask` 的 Enqueue 调用
|
||||
4. **P0-4** `auto_purchase.go` 处理成功后补充 `enqueueCommissionCalculation`
|
||||
|
||||
### 尽快修复(P1)— 功能缺失
|
||||
5. 统一实名状态值定义
|
||||
6. 确认 `EnableRealnameActivation` 默认值是否符合业务预期
|
||||
7. 补充提现"已到账"标记接口
|
||||
8. 企业端接口(根据业务优先级排期)
|
||||
9. `polling:protect` 保护期检查接入调度
|
||||
|
||||
### 技术债清理(P2)
|
||||
10. 评估统一 `status` 和 `asset_status`
|
||||
11. 停止废弃字段的并行写入
|
||||
12. 佣金链断裂时添加告警日志/通知
|
||||
13. 确认 StopResumeCallback / ResumeCallback 注入情况
|
||||
704
docs/前端接口变更说明.md
704
docs/前端接口变更说明.md
@@ -1,6 +1,6 @@
|
||||
# 前端接口变更说明
|
||||
|
||||
> 最后更新:2026-03-19
|
||||
> 最后更新:2026-03-20
|
||||
|
||||
---
|
||||
|
||||
@@ -37,7 +37,9 @@
|
||||
| 接口 | 变更内容 |
|
||||
|------|----------|
|
||||
| `POST /api/admin/shop-package-batch-pricing/batch-update` | 仅支持批量调整成本价(移除 `pricing_target`) |
|
||||
| 所有返回 `PackageResponse` 的套餐列表接口 | 响应新增 `retail_price` 字段 |
|
||||
| 所有返回 `PackageResponse` 的套餐接口 | 响应新增 `virtual_ratio`、`one_time_commission_amount`、`tier_info`、`calendar_type`、`data_reset_cycle`、`enable_realname_activation` 等字段 |
|
||||
| `GET /api/admin/iot-cards/standalone` | 响应新增 `virtual_no`(虚拟号)字段 |
|
||||
| `GET /api/admin/devices` | `device_no` 重命名为 `virtual_no`,新增 `bound_card_count`、`first_commission_paid`、`accumulated_recharge` 字段 |
|
||||
| 运营商创建/编辑接口 | 新增 `realname_link_type` 和 `realname_link_template` 字段 |
|
||||
|
||||
### 新增(后台)
|
||||
@@ -54,6 +56,9 @@
|
||||
| `POST /api/admin/exchanges/:id/complete` | 确认完成 |
|
||||
| `POST /api/admin/exchanges/:id/cancel` | 取消换货 |
|
||||
| `POST /api/admin/exchanges/:id/renew` | 旧资产转新(generation+1) |
|
||||
| 微信支付配置管理(8 个接口) | `GET/POST/PUT/DELETE /api/admin/wechat-configs` 及 `active`、`activate`、`deactivate` 子路由 |
|
||||
| 代理预充值(4 个接口) | `/api/admin/agent-recharges` 创建/列表/详情/线下确认 |
|
||||
| 资产管理(11 个接口) | `/api/admin/assets/*` 资产解析、状态刷新、套餐、停复机、钱包概况/流水 |
|
||||
|
||||
### 新增(C 端)
|
||||
|
||||
@@ -124,13 +129,57 @@ POST /api/admin/shop-package-batch-pricing/batch-update
|
||||
|
||||
---
|
||||
|
||||
### 套餐列表响应新增 `retail_price` 字段
|
||||
### 套餐响应新增字段(`PackageResponse`)
|
||||
|
||||
所有返回 `PackageResponse` 的套餐列表接口,响应体新增字段:
|
||||
所有返回 `PackageResponse` 的套餐接口,响应体新增字段:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `retail_price` | int64 | 零售价(单位:分),代理商可见 |
|
||||
| `retail_price` | int64 | 零售价(分),代理商可见 |
|
||||
| `profit_margin` | int64 | 利润空间(分),仅代理用户可见 |
|
||||
| `current_commission_rate` | string | 当前返佣比例,仅代理用户可见 |
|
||||
| `one_time_commission_amount` | int64 | 一次性佣金金额(分),代理视角 |
|
||||
| `tier_info` | object | 梯度返佣信息(含 `current_rate` / `next_threshold` / `next_rate`),仅代理可见 |
|
||||
| `virtual_ratio` | float64 | 虚流量比例(`real_data_mb/virtual_data_mb`) |
|
||||
| `calendar_type` | string | 套餐周期类型:`natural_month` / `by_day` |
|
||||
| `duration_days` | int | 套餐天数(`calendar_type=by_day` 时有值) |
|
||||
| `data_reset_cycle` | string | 流量重置周期:`daily` / `monthly` / `yearly` / `none` |
|
||||
| `enable_realname_activation` | bool | 是否启用实名激活 |
|
||||
|
||||
---
|
||||
|
||||
### IoT 卡列表响应新增 `virtual_no` 字段
|
||||
|
||||
接口:
|
||||
|
||||
```
|
||||
GET /api/admin/iot-cards/standalone
|
||||
```
|
||||
|
||||
响应体新增字段:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `virtual_no` | string | 卡虚拟号(用于客服查找资产) |
|
||||
|
||||
---
|
||||
|
||||
### 设备列表字段调整
|
||||
|
||||
接口:
|
||||
|
||||
```
|
||||
GET /api/admin/devices
|
||||
```
|
||||
|
||||
字段变更如下:
|
||||
|
||||
| 变更类型 | 字段 | 类型 | 说明 |
|
||||
|----------|------|------|------|
|
||||
| 重命名 | `device_no` → `virtual_no` | string | 设备虚拟号/别名 |
|
||||
| 新增 | `bound_card_count` | int | 绑定卡数量 |
|
||||
| 新增 | `first_commission_paid` | bool | 一次性佣金是否已发放 |
|
||||
| 新增 | `accumulated_recharge` | int64 | 累计充值金额(分) |
|
||||
|
||||
---
|
||||
|
||||
@@ -360,6 +409,651 @@ flowchart TD
|
||||
|
||||
---
|
||||
|
||||
### 微信支付配置管理
|
||||
|
||||
> 权限:仅超级管理员和平台用户可访问。
|
||||
|
||||
#### W1 获取当前生效的支付配置
|
||||
|
||||
```
|
||||
GET /api/admin/wechat-configs/active
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
- **请求参数**:无
|
||||
|
||||
**响应体(`WechatConfigResponse`):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | uint | 配置ID |
|
||||
| `name` | string | 配置名称 |
|
||||
| `description` | string | 配置描述 |
|
||||
| `provider_type` | string | 支付渠道类型 (`wechat`:微信直连, `fuiou`:富友) |
|
||||
| `is_active` | bool | 是否激活 |
|
||||
| `oa_app_id` | string | 公众号AppID |
|
||||
| `oa_app_secret` | string | 公众号AppSecret(已脱敏) |
|
||||
| `oa_token` | string | 公众号Token(已脱敏) |
|
||||
| `oa_aes_key` | string | 公众号AES加密Key(已脱敏) |
|
||||
| `oa_oauth_redirect_url` | string | OAuth回调地址 |
|
||||
| `miniapp_app_id` | string | 小程序AppID |
|
||||
| `miniapp_app_secret` | string | 小程序AppSecret(已脱敏) |
|
||||
| `wx_mch_id` | string | 微信商户号 |
|
||||
| `wx_api_v3_key` | string | 微信APIv3密钥(已脱敏) |
|
||||
| `wx_api_v2_key` | string | 微信APIv2密钥(已脱敏) |
|
||||
| `wx_cert_content` | string | 微信支付证书内容(配置状态) |
|
||||
| `wx_key_content` | string | 微信支付密钥内容(配置状态) |
|
||||
| `wx_serial_no` | string | 微信证书序列号 |
|
||||
| `wx_notify_url` | string | 微信支付回调地址 |
|
||||
| `fy_ins_cd` | string | 富友机构号 |
|
||||
| `fy_mchnt_cd` | string | 富友商户号 |
|
||||
| `fy_term_id` | string | 富友终端号 |
|
||||
| `fy_private_key` | string | 富友私钥(配置状态) |
|
||||
| `fy_public_key` | string | 富友公钥(配置状态) |
|
||||
| `fy_api_url` | string | 富友API地址 |
|
||||
| `fy_notify_url` | string | 富友支付回调地址 |
|
||||
| `created_at` | string | 创建时间 |
|
||||
| `updated_at` | string | 更新时间 |
|
||||
|
||||
**敏感字段说明**:
|
||||
- 短密钥字段(如 `oa_app_secret`、`wx_api_v3_key`)返回 `xxxx***xxxx` 脱敏形式。
|
||||
- 长文本密钥/证书字段(如 `wx_cert_content`、`fy_private_key`)返回 `[已配置]` 或 `[未配置]`。
|
||||
|
||||
---
|
||||
|
||||
#### W2 支付配置列表
|
||||
|
||||
```
|
||||
GET /api/admin/wechat-configs
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
|
||||
**Query 参数:**
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `page` | int | 否 | 页码,最小 1 |
|
||||
| `page_size` | int | 否 | 每页数量,1~100 |
|
||||
| `provider_type` | string | 否 | 支付渠道类型 (`wechat`/`fuiou`) |
|
||||
| `is_active` | bool | 否 | 是否激活 (`true`:已激活, `false`:未激活) |
|
||||
|
||||
**响应体(`WechatConfigListResponse`):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `list` | array | 配置列表(每项字段同 `WechatConfigResponse`) |
|
||||
| `total` | int64 | 总数 |
|
||||
| `page` | int | 当前页 |
|
||||
| `page_size` | int | 每页数量 |
|
||||
|
||||
---
|
||||
|
||||
#### W3 创建支付配置
|
||||
|
||||
```
|
||||
POST /api/admin/wechat-configs
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
|
||||
**请求体(`CreateWechatConfigRequest`):**
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `name` | string | 是 | 配置名称 |
|
||||
| `description` | string | 否 | 配置描述 |
|
||||
| `provider_type` | string | 是 | 支付渠道类型 (`wechat`:微信直连, `fuiou`:富友) |
|
||||
| `oa_app_id` | string | 否 | 公众号AppID |
|
||||
| `oa_app_secret` | string | 否 | 公众号AppSecret |
|
||||
| `oa_token` | string | 否 | 公众号Token |
|
||||
| `oa_aes_key` | string | 否 | 公众号AES加密Key |
|
||||
| `oa_oauth_redirect_url` | string | 否 | OAuth回调地址 |
|
||||
| `miniapp_app_id` | string | 否 | 小程序AppID |
|
||||
| `miniapp_app_secret` | string | 否 | 小程序AppSecret |
|
||||
| `wx_mch_id` | string | 否 | 微信商户号 |
|
||||
| `wx_api_v3_key` | string | 否 | 微信APIv3密钥 |
|
||||
| `wx_api_v2_key` | string | 否 | 微信APIv2密钥 |
|
||||
| `wx_cert_content` | string | 否 | 微信支付证书内容(PEM格式) |
|
||||
| `wx_key_content` | string | 否 | 微信支付密钥内容(PEM格式) |
|
||||
| `wx_serial_no` | string | 否 | 微信证书序列号 |
|
||||
| `wx_notify_url` | string | 否 | 微信支付回调地址 |
|
||||
| `fy_ins_cd` | string | 否 | 富友机构号 |
|
||||
| `fy_mchnt_cd` | string | 否 | 富友商户号 |
|
||||
| `fy_term_id` | string | 否 | 富友终端号 |
|
||||
| `fy_private_key` | string | 否 | 富友私钥(PEM格式) |
|
||||
| `fy_public_key` | string | 否 | 富友公钥(PEM格式) |
|
||||
| `fy_api_url` | string | 否 | 富友API地址 |
|
||||
| `fy_notify_url` | string | 否 | 富友支付回调地址 |
|
||||
|
||||
**响应体**:`WechatConfigResponse`(字段同 W1,敏感字段脱敏返回)
|
||||
|
||||
---
|
||||
|
||||
#### W4 支付配置详情
|
||||
|
||||
```
|
||||
GET /api/admin/wechat-configs/:id
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
- **路径参数**:`id`(配置ID)
|
||||
- **响应体**:`WechatConfigResponse`(字段同 W1,敏感字段脱敏返回)
|
||||
|
||||
---
|
||||
|
||||
#### W5 更新支付配置
|
||||
|
||||
```
|
||||
PUT /api/admin/wechat-configs/:id
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
|
||||
**路径参数:** `id`(配置ID)
|
||||
|
||||
**请求体(`UpdateWechatConfigRequest`,全字段可选):**
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `name` | string | 否 | 配置名称 |
|
||||
| `description` | string | 否 | 配置描述 |
|
||||
| `provider_type` | string | 否 | 支付渠道类型 (`wechat`:微信直连, `fuiou`:富友) |
|
||||
| `oa_app_id` | string | 否 | 公众号AppID |
|
||||
| `oa_app_secret` | string | 否 | 公众号AppSecret |
|
||||
| `oa_token` | string | 否 | 公众号Token |
|
||||
| `oa_aes_key` | string | 否 | 公众号AES加密Key |
|
||||
| `oa_oauth_redirect_url` | string | 否 | OAuth回调地址 |
|
||||
| `miniapp_app_id` | string | 否 | 小程序AppID |
|
||||
| `miniapp_app_secret` | string | 否 | 小程序AppSecret |
|
||||
| `wx_mch_id` | string | 否 | 微信商户号 |
|
||||
| `wx_api_v3_key` | string | 否 | 微信APIv3密钥 |
|
||||
| `wx_api_v2_key` | string | 否 | 微信APIv2密钥 |
|
||||
| `wx_cert_content` | string | 否 | 微信支付证书内容(PEM格式) |
|
||||
| `wx_key_content` | string | 否 | 微信支付密钥内容(PEM格式) |
|
||||
| `wx_serial_no` | string | 否 | 微信证书序列号 |
|
||||
| `wx_notify_url` | string | 否 | 微信支付回调地址 |
|
||||
| `fy_ins_cd` | string | 否 | 富友机构号 |
|
||||
| `fy_mchnt_cd` | string | 否 | 富友商户号 |
|
||||
| `fy_term_id` | string | 否 | 富友终端号 |
|
||||
| `fy_private_key` | string | 否 | 富友私钥(PEM格式) |
|
||||
| `fy_public_key` | string | 否 | 富友公钥(PEM格式) |
|
||||
| `fy_api_url` | string | 否 | 富友API地址 |
|
||||
| `fy_notify_url` | string | 否 | 富友支付回调地址 |
|
||||
|
||||
**响应体**:`WechatConfigResponse`(字段同 W1,敏感字段脱敏返回)
|
||||
|
||||
---
|
||||
|
||||
#### W6 删除支付配置
|
||||
|
||||
```
|
||||
DELETE /api/admin/wechat-configs/:id
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
- **路径参数**:`id`(配置ID)
|
||||
- **请求体**:无
|
||||
- **响应体**:无(仅返回统一成功响应)
|
||||
|
||||
---
|
||||
|
||||
#### W7 激活支付配置
|
||||
|
||||
```
|
||||
POST /api/admin/wechat-configs/:id/activate
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
- **路径参数**:`id`(配置ID)
|
||||
- **请求体**:无
|
||||
- **响应体**:`WechatConfigResponse`(字段同 W1,敏感字段脱敏返回)
|
||||
|
||||
---
|
||||
|
||||
#### W8 停用支付配置
|
||||
|
||||
```
|
||||
POST /api/admin/wechat-configs/:id/deactivate
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
- **路径参数**:`id`(配置ID)
|
||||
- **请求体**:无
|
||||
- **响应体**:`WechatConfigResponse`(字段同 W1,敏感字段脱敏返回)
|
||||
|
||||
---
|
||||
|
||||
### 代理预充值
|
||||
|
||||
> 权限:企业账号无权访问。
|
||||
|
||||
#### R1 创建代理充值订单
|
||||
|
||||
```
|
||||
POST /api/admin/agent-recharges
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
|
||||
**请求体(`CreateAgentRechargeRequest`):**
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `shop_id` | uint | 是 | 目标店铺ID,代理只能填自己店铺 |
|
||||
| `amount` | int64 | 是 | 充值金额(分),范围100元~100万元 |
|
||||
| `payment_method` | string | 是 | 支付方式 (`wechat`:微信在线支付, `offline`:线下转账仅平台可用) |
|
||||
|
||||
**响应体(`AgentRechargeResponse`):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | uint | 充值记录ID |
|
||||
| `recharge_no` | string | 充值单号(ARCH前缀) |
|
||||
| `shop_id` | uint | 店铺ID |
|
||||
| `shop_name` | string | 店铺名称 |
|
||||
| `agent_wallet_id` | uint | 代理钱包ID |
|
||||
| `amount` | int64 | 充值金额(分) |
|
||||
| `payment_method` | string | 支付方式 (`wechat`:微信在线支付, `offline`:线下转账) |
|
||||
| `payment_channel` | string | 实际支付通道 (`wechat_direct`:微信直连, `fuyou`:富友, `offline`:线下转账) |
|
||||
| `payment_config_id` | uint | 关联支付配置ID,线下充值为 `null` |
|
||||
| `payment_transaction_id` | string | 第三方支付流水号 |
|
||||
| `status` | int | 状态 (1:待支付, 2:已完成, 3:已取消) |
|
||||
| `paid_at` | string | 支付时间 |
|
||||
| `completed_at` | string | 完成时间 |
|
||||
| `created_at` | string | 创建时间 |
|
||||
| `updated_at` | string | 更新时间 |
|
||||
|
||||
---
|
||||
|
||||
#### R2 代理充值订单列表
|
||||
|
||||
```
|
||||
GET /api/admin/agent-recharges
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
|
||||
**Query 参数(`AgentRechargeListRequest`):**
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `page` | int | 否 | 页码,默认1 |
|
||||
| `page_size` | int | 否 | 每页条数,默认20,最大100 |
|
||||
| `shop_id` | uint | 否 | 按店铺ID过滤 |
|
||||
| `status` | int | 否 | 按状态过滤 (1:待支付, 2:已完成, 3:已取消) |
|
||||
| `start_date` | string | 否 | 创建时间起始日期(YYYY-MM-DD) |
|
||||
| `end_date` | string | 否 | 创建时间截止日期(YYYY-MM-DD) |
|
||||
|
||||
**响应体(`AgentRechargeListResponse`):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `total` | int64 | 总记录数 |
|
||||
| `page` | int | 当前页码 |
|
||||
| `page_size` | int | 每页条数 |
|
||||
| `list` | array | 充值记录列表(每项字段同 `AgentRechargeResponse`) |
|
||||
|
||||
---
|
||||
|
||||
#### R3 代理充值订单详情
|
||||
|
||||
```
|
||||
GET /api/admin/agent-recharges/:id
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
- **路径参数**:`id`(充值记录ID)
|
||||
- **响应体**:`AgentRechargeResponse`(字段同 R1)
|
||||
|
||||
---
|
||||
|
||||
#### R4 确认线下充值
|
||||
|
||||
```
|
||||
POST /api/admin/agent-recharges/:id/offline-pay
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
|
||||
**路径参数:** `id`(充值记录ID)
|
||||
|
||||
**请求体(`AgentOfflinePayRequest`):**
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `operation_password` | string | 是 | 操作密码 |
|
||||
|
||||
**响应体**:`AgentRechargeResponse`(字段同 R1)
|
||||
|
||||
---
|
||||
|
||||
### 资产管理
|
||||
|
||||
#### Z1 通过标识符解析资产
|
||||
|
||||
```
|
||||
GET /api/admin/assets/resolve/:identifier
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
- **路径参数**:`identifier`(资产标识符,支持虚拟号/ICCID/IMEI/SN/MSISDN)
|
||||
|
||||
**响应体(`AssetResolveResponse`):**
|
||||
|
||||
**基础字段**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `asset_type` | string | 资产类型:`card` 或 `device` |
|
||||
| `asset_id` | uint | 资产数据库ID |
|
||||
| `virtual_no` | string | 虚拟号 |
|
||||
| `status` | int | 资产状态 |
|
||||
| `batch_no` | string | 批次号 |
|
||||
| `shop_id` | uint | 所属店铺ID |
|
||||
| `shop_name` | string | 所属店铺名称 |
|
||||
| `series_id` | uint | 套餐系列ID |
|
||||
| `series_name` | string | 套餐系列名称 |
|
||||
| `first_commission_paid` | bool | 一次性佣金是否已发放 |
|
||||
| `accumulated_recharge` | int64 | 累计充值金额(分) |
|
||||
| `activated_at` | string | 激活时间 |
|
||||
| `created_at` | string | 创建时间 |
|
||||
| `updated_at` | string | 更新时间 |
|
||||
|
||||
**状态聚合字段**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `real_name_status` | int | 实名状态:0未实名 1实名中 2已实名 |
|
||||
| `current_package` | string | 当前套餐名称(无套餐时为空) |
|
||||
| `package_total_mb` | int64 | 当前套餐总虚流量(MB),已按 `virtual_ratio` 换算 |
|
||||
| `package_used_mb` | float64 | 当前已用虚流量(MB),已按 `virtual_ratio` 换算 |
|
||||
| `package_remain_mb` | float64 | 当前套餐剩余虚流量(MB),已按 `virtual_ratio` 换算 |
|
||||
| `device_protect_status` | string | 设备保护期状态:`none`/`stop`/`start`(仅 `asset_type=device`) |
|
||||
|
||||
**绑定关系字段**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `iccid` | string | 卡ICCID(`asset_type=card`) |
|
||||
| `bound_device_id` | uint | 绑定设备ID(`asset_type=card`) |
|
||||
| `bound_device_no` | string | 绑定设备虚拟号(`asset_type=card`) |
|
||||
| `bound_device_name` | string | 绑定设备名称(`asset_type=card`) |
|
||||
| `bound_card_count` | int | 绑定卡数量(`asset_type=device`) |
|
||||
| `cards` | array | 绑定卡列表(`asset_type=device`,每项字段见下) |
|
||||
|
||||
**`cards` 子项(`BoundCardInfo`)**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `card_id` | uint | 卡ID |
|
||||
| `iccid` | string | ICCID |
|
||||
| `msisdn` | string | 手机号 |
|
||||
| `network_status` | int | 网络状态:0停机 1开机 |
|
||||
| `real_name_status` | int | 实名状态 |
|
||||
| `slot_position` | int | 插槽位置 |
|
||||
|
||||
**设备专属字段**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `device_name` | string | 设备名称 |
|
||||
| `imei` | string | 设备IMEI |
|
||||
| `sn` | string | 设备序列号 |
|
||||
| `device_model` | string | 设备型号 |
|
||||
| `device_type` | string | 设备类型 |
|
||||
| `max_sim_slots` | int | 最大插槽数 |
|
||||
| `manufacturer` | string | 制造商 |
|
||||
|
||||
**卡专属字段**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `carrier_id` | uint | 运营商ID |
|
||||
| `carrier_type` | string | 运营商类型 |
|
||||
| `carrier_name` | string | 运营商名称 |
|
||||
| `msisdn` | string | 手机号 |
|
||||
| `imsi` | string | IMSI |
|
||||
| `card_category` | string | 卡业务类型 |
|
||||
| `supplier` | string | 供应商 |
|
||||
| `activation_status` | int | 激活状态 |
|
||||
| `enable_polling` | bool | 是否参与轮询 |
|
||||
| `network_status` | int | 网络状态:0停机 1开机(仅 `asset_type=card`) |
|
||||
|
||||
---
|
||||
|
||||
#### Z2 资产实时状态
|
||||
|
||||
```
|
||||
GET /api/admin/assets/:asset_type/:id/realtime-status
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
|
||||
**路径参数:**
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `asset_type` | string | 是 | 资产类型:`card` 或 `device` |
|
||||
| `id` | uint | 是 | 资产ID |
|
||||
|
||||
**响应体(`AssetRealtimeStatusResponse`):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `asset_type` | string | 资产类型:`card` 或 `device` |
|
||||
| `asset_id` | uint | 资产ID |
|
||||
| `network_status` | int | 网络状态(`asset_type=card`):0停机 1开机 |
|
||||
| `real_name_status` | int | 实名状态(`asset_type=card`) |
|
||||
| `current_month_usage_mb` | float64 | 本月已用流量MB(`asset_type=card`) |
|
||||
| `last_sync_time` | string | 最后同步时间(`asset_type=card`) |
|
||||
| `device_protect_status` | string | 保护期状态(`asset_type=device`):`none`/`stop`/`start` |
|
||||
| `cards` | array | 绑定卡状态列表(`asset_type=device`,每项字段同 `BoundCardInfo`) |
|
||||
|
||||
---
|
||||
|
||||
#### Z3 刷新资产状态
|
||||
|
||||
```
|
||||
POST /api/admin/assets/:asset_type/:id/refresh
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
- **路径参数**:同 Z2
|
||||
- **请求体**:无
|
||||
- **响应体**:`AssetRealtimeStatusResponse`(字段同 Z2)
|
||||
|
||||
---
|
||||
|
||||
#### Z4 资产套餐列表
|
||||
|
||||
```
|
||||
GET /api/admin/assets/:asset_type/:id/packages
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
- **路径参数**:同 Z2
|
||||
|
||||
**响应体(数组,每项为 `AssetPackageResponse`):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `package_usage_id` | uint | 套餐使用记录ID |
|
||||
| `package_id` | uint | 套餐ID |
|
||||
| `package_name` | string | 套餐名称 |
|
||||
| `package_type` | string | 套餐类型:`formal`/`addon` |
|
||||
| `usage_type` | string | 使用类型:`single_card`/`device` |
|
||||
| `status` | int | 状态:0待生效 1生效中 2已用完 3已过期 4已失效 |
|
||||
| `status_name` | string | 状态名称 |
|
||||
| `data_limit_mb` | int64 | 套餐真流量总量(MB) |
|
||||
| `virtual_limit_mb` | int64 | 套餐虚流量总量(MB),按 `virtual_ratio` 换算 |
|
||||
| `data_usage_mb` | int64 | 已用真流量(MB) |
|
||||
| `virtual_used_mb` | float64 | 已用虚流量(MB),按 `virtual_ratio` 换算 |
|
||||
| `virtual_remain_mb` | float64 | 剩余虚流量(MB),按 `virtual_ratio` 换算 |
|
||||
| `virtual_ratio` | float64 | 虚流量比例(real/virtual) |
|
||||
| `activated_at` | string | 激活时间(待生效套餐为空) |
|
||||
| `expires_at` | string | 到期时间(待生效套餐为空) |
|
||||
| `master_usage_id` | uint | 主套餐ID(加油包时有值) |
|
||||
| `priority` | int | 优先级 |
|
||||
| `created_at` | string | 创建时间 |
|
||||
|
||||
---
|
||||
|
||||
#### Z5 当前生效套餐
|
||||
|
||||
```
|
||||
GET /api/admin/assets/:asset_type/:id/current-package
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
- **路径参数**:同 Z2
|
||||
- **响应体**:`AssetPackageResponse`(字段同 Z4)
|
||||
|
||||
---
|
||||
|
||||
#### Z6 设备停机
|
||||
|
||||
```
|
||||
POST /api/admin/assets/device/:device_id/stop
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
|
||||
**路径参数:**
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `device_id` | uint | 是 | 设备ID |
|
||||
|
||||
**响应体(`DeviceSuspendResponse`):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `success_count` | int | 成功停机卡数 |
|
||||
| `fail_count` | int | 失败卡数 |
|
||||
| `skip_count` | int | 跳过卡数(未实名或已停机) |
|
||||
| `failed_items` | array | 失败详情(`iccid` / `reason`) |
|
||||
|
||||
---
|
||||
|
||||
#### Z7 设备复机
|
||||
|
||||
```
|
||||
POST /api/admin/assets/device/:device_id/start
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
- **路径参数**:`device_id`(设备ID)
|
||||
- **请求体**:无
|
||||
- **响应体**:无(仅返回统一成功响应)
|
||||
|
||||
---
|
||||
|
||||
#### Z8 单卡停机
|
||||
|
||||
```
|
||||
POST /api/admin/assets/card/:iccid/stop
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
- **路径参数**:`iccid`(卡ICCID)
|
||||
- **请求体**:无
|
||||
- **响应体**:无(仅返回统一成功响应)
|
||||
|
||||
---
|
||||
|
||||
#### Z9 单卡复机
|
||||
|
||||
```
|
||||
POST /api/admin/assets/card/:iccid/start
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
- **路径参数**:`iccid`(卡ICCID)
|
||||
- **请求体**:无
|
||||
- **响应体**:无(仅返回统一成功响应)
|
||||
|
||||
---
|
||||
|
||||
#### Z10 资产钱包概况
|
||||
|
||||
```
|
||||
GET /api/admin/assets/:asset_type/:id/wallet
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
- **路径参数**:同 Z2
|
||||
|
||||
**响应体(`AssetWalletResponse`):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `wallet_id` | uint | 钱包数据库ID |
|
||||
| `resource_type` | string | 资源类型:`iot_card` 或 `device` |
|
||||
| `resource_id` | uint | 对应卡或设备的数据库ID |
|
||||
| `balance` | int64 | 总余额(分) |
|
||||
| `frozen_balance` | int64 | 冻结余额(分) |
|
||||
| `available_balance` | int64 | 可用余额 = `balance - frozen_balance`(分) |
|
||||
| `currency` | string | 币种,目前固定 CNY |
|
||||
| `status` | int | 钱包状态:1-正常 2-冻结 3-关闭 |
|
||||
| `status_text` | string | 状态文本 |
|
||||
| `created_at` | string | 创建时间(RFC3339) |
|
||||
| `updated_at` | string | 更新时间(RFC3339) |
|
||||
|
||||
---
|
||||
|
||||
#### Z11 资产钱包流水列表
|
||||
|
||||
```
|
||||
GET /api/admin/assets/:asset_type/:id/wallet/transactions
|
||||
```
|
||||
|
||||
- **认证**:需要后台 Bearer Token
|
||||
|
||||
**路径参数:**
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `asset_type` | string | 是 | 资产类型:`card` 或 `device` |
|
||||
| `id` | uint | 是 | 资产ID |
|
||||
|
||||
**Query 参数:**
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `page` | int | 否 | 页码,默认1 |
|
||||
| `page_size` | int | 否 | 每页数量,默认20,最大100 |
|
||||
| `transaction_type` | string | 否 | 交易类型过滤:`recharge`/`deduct`/`refund` |
|
||||
| `start_time` | string | 否 | 开始时间(RFC3339) |
|
||||
| `end_time` | string | 否 | 结束时间(RFC3339) |
|
||||
|
||||
**响应体(`AssetWalletTransactionListResponse`):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `list` | array | 流水列表(每项字段见下) |
|
||||
| `total` | int64 | 总记录数 |
|
||||
| `page` | int | 当前页码 |
|
||||
| `page_size` | int | 每页数量 |
|
||||
| `total_pages` | int | 总页数 |
|
||||
|
||||
**`list` 子项(`AssetWalletTransactionItem`):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | uint | 流水记录ID |
|
||||
| `transaction_type` | string | 交易类型:`recharge`/`deduct`/`refund` |
|
||||
| `transaction_type_text` | string | 交易类型文本:充值/扣款/退款 |
|
||||
| `amount` | int64 | 变动金额(分),充值为正数,扣款/退款为负数 |
|
||||
| `balance_before` | int64 | 变动前余额(分) |
|
||||
| `balance_after` | int64 | 变动后余额(分) |
|
||||
| `reference_type` | string | 关联业务类型:`recharge` 或 `order`(可空) |
|
||||
| `reference_no` | string | 关联业务编号:充值单号(CRCH…)或订单号(ORD…)(可空) |
|
||||
| `remark` | string | 备注(可空) |
|
||||
| `created_at` | string | 流水创建时间(RFC3339) |
|
||||
|
||||
---
|
||||
|
||||
## 五、新增的 C 端接口
|
||||
|
||||
所有接口位于 `/api/c/v1/` 下,**认证接口(`/auth/`)无需登录,其余全部需要 JWT 认证**(`Authorization: Bearer <token>`)。
|
||||
|
||||
191
docs/第三方文档/gateway设备详情同步接口.md
Normal file
191
docs/第三方文档/gateway设备详情同步接口.md
Normal file
@@ -0,0 +1,191 @@
|
||||
# 设备信息同步查询接口
|
||||
|
||||
## 基本信息
|
||||
|
||||
| 属性 | 值 |
|
||||
|------|-----|
|
||||
| 接口路径 | `POST /v1/iot/openApi/device/sync-info` |
|
||||
| 请求方式 | POST |
|
||||
| Content-Type | application/json |
|
||||
| 认证方式 | appId + sign 签名验证 |
|
||||
|
||||
## 接口说明
|
||||
|
||||
同步查询设备信息,直接返回设备完整数据。与异步接口 `/device/info` 的区别:
|
||||
|
||||
| | 异步 `/device/info` | 同步 `/device/sync-info` |
|
||||
|---|---|---|
|
||||
| 返回方式 | 立即返回 `requestId`,结果通过 `callbackUrl` 回调 | 直接返回设备信息 |
|
||||
| `callbackUrl` | 必传 | 不需要 |
|
||||
| 响应字段 | 部分字段(经 v1 映射) | 全量字段 |
|
||||
|
||||
## 请求参数
|
||||
|
||||
### 请求体
|
||||
|
||||
```json
|
||||
{
|
||||
"appId": "your_app_id",
|
||||
"sign": "签名字符串",
|
||||
"timestamp": "时间戳",
|
||||
"data": "加密后的业务数据"
|
||||
}
|
||||
```
|
||||
|
||||
### data 解密后结构
|
||||
|
||||
```json
|
||||
{
|
||||
"params": {
|
||||
"cardNo": "设备标识"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### cardNo 说明
|
||||
|
||||
| 长度 | 类型 | 示例 |
|
||||
|------|------|------|
|
||||
| > 15 位 | ICCID | `89860624590014720038` |
|
||||
| 15 位 | IMEI(设备号) | `868347081100410` |
|
||||
| 11 位 | SN(序列号) | `90001473913` |
|
||||
|
||||
## 响应参数
|
||||
|
||||
### 响应结构
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": { ... },
|
||||
"trace_id": "请求追踪ID"
|
||||
}
|
||||
```
|
||||
|
||||
### data 字段说明
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `device_id` | string | 设备ID(IMEI/SN) |
|
||||
| `device_name` | string | 设备名称 |
|
||||
| `imei` | string | IMEI号 |
|
||||
| `imsi` | string | IMSI用户标识码 |
|
||||
| `current_iccid` | string | 当前使用的ICCID |
|
||||
| `device_type` | string | 设备类型:`1`=诺行,`2`=玺龙,`3`=迎势达 |
|
||||
| `software_version` | string | 软件版本号 |
|
||||
| `mac_address` | string | MAC地址 |
|
||||
| `ip_address` | string | IP地址 |
|
||||
| `ssid` | string | WiFi热点名称 |
|
||||
| `wifi_password` | string | WiFi密码 |
|
||||
| `wifi_enabled` | bool | WiFi开关状态 |
|
||||
| `max_clients` | int | 最大连接客户端数 |
|
||||
| `limit_speed` | int | 限速速率(KB/s) |
|
||||
| `sync_interval` | int | 信息上报周期(秒) |
|
||||
| `switch_mode` | string | 切卡模式:`0`=自动,`1`=手动 |
|
||||
| `ul_stats` | string | 本次开机上传流量(字节) |
|
||||
| `dl_stats` | string | 本次开机下载流量(字节) |
|
||||
| `daily_usage` | string | 日使用流量(字节) |
|
||||
| `rsrp` | int | 参考信号接收功率(dBm) |
|
||||
| `rsrq` | int | 参考信号接收质量(dB) |
|
||||
| `sinr` | int | 信噪比(dB) |
|
||||
| `rssi` | string | 接收信号强度 |
|
||||
| `lan_ip` | string | 局域网网关IP地址 |
|
||||
| `wan_ip` | string | 基站分配IPv4地址 |
|
||||
| `run_time` | string | 设备本次开机运行时间(秒) |
|
||||
| `connect_time` | string | 设备本次联网时间(秒) |
|
||||
| `battery_level` | int | 电池电量百分比 |
|
||||
| `online_status` | int | 在线状态:`1`=在线,`2`=离线 |
|
||||
| `status` | int | 设备状态:`1`=正常,`0`=禁用 |
|
||||
| `last_update_time` | string | 设备信息最后更新时间(ISO 8601) |
|
||||
| `last_online_time` | string | 设备最后在线时间(ISO 8601) |
|
||||
| `created_at` | int | 创建时间(Unix时间戳) |
|
||||
| `updated_at` | int | 更新时间(Unix时间戳) |
|
||||
|
||||
> 所有字段均可能为 `null`,表示该字段暂无数据。
|
||||
|
||||
## 响应示例
|
||||
|
||||
### 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"device_id": "90001473913",
|
||||
"device_name": "868347081100410",
|
||||
"imei": "868347081100410",
|
||||
"imsi": null,
|
||||
"current_iccid": "89860865192590302917",
|
||||
"device_type": "3",
|
||||
"software_version": "TZ103W_CN1_YSDPTZ12_TH_P42U28_U_17_PRO_20260121",
|
||||
"mac_address": "00904D8486DE",
|
||||
"ip_address": null,
|
||||
"ssid": "ZJ-8486DE",
|
||||
"wifi_password": null,
|
||||
"wifi_enabled": true,
|
||||
"max_clients": null,
|
||||
"limit_speed": 0,
|
||||
"sync_interval": 600,
|
||||
"switch_mode": "0",
|
||||
"ul_stats": null,
|
||||
"dl_stats": null,
|
||||
"daily_usage": "150980",
|
||||
"rsrp": null,
|
||||
"rsrq": null,
|
||||
"sinr": null,
|
||||
"rssi": "-72",
|
||||
"lan_ip": null,
|
||||
"wan_ip": null,
|
||||
"run_time": "2099",
|
||||
"connect_time": null,
|
||||
"battery_level": null,
|
||||
"online_status": 2,
|
||||
"status": 1,
|
||||
"last_update_time": "2026-03-23T16:53:18+08:00",
|
||||
"last_online_time": "2026-03-23T16:53:18+08:00",
|
||||
"created_at": null,
|
||||
"updated_at": null
|
||||
},
|
||||
"trace_id": "trace_xxx"
|
||||
}
|
||||
```
|
||||
|
||||
### 错误响应
|
||||
|
||||
**cardNo 为空:**
|
||||
```json
|
||||
{
|
||||
"code": 401,
|
||||
"msg": "cardNo nil",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
**设备不存在:**
|
||||
```json
|
||||
{
|
||||
"code": 401,
|
||||
"msg": "获取设备信息错误,无效设备号",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
**服务调用失败:**
|
||||
```json
|
||||
{
|
||||
"code": 500,
|
||||
"msg": "服务调用失败",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
## 数据来源说明
|
||||
|
||||
接口数据按以下优先级获取:
|
||||
|
||||
1. **Redis 缓存**(设备最近一次上报的实时数据,TTL 10 分钟)
|
||||
2. **数据库**(缓存过期后 fallback,字段可能不完整)
|
||||
|
||||
设备在线时优先返回缓存中的实时上报数据;设备离线后缓存过期,返回数据库中最后一次同步的数据。部分字段(如 `wifi_password`、`battery_level` 等)在离线 fallback 场景下可能为 `null`。
|
||||
Reference in New Issue
Block a user