136 lines
4.1 KiB
Markdown
136 lines
4.1 KiB
Markdown
## 实施计划:GMV 支付方式拆分展示
|
||
|
||
### 需求分析
|
||
|
||
当前渠道统计只展示一个"累计实付金额"(GMV 总数),用户无法看到这笔钱的构成。需要拆分为:
|
||
- **现金支付** (`actual_amount`) — 用户通过微信支付的真金白银
|
||
- **优惠券抵扣** (`discount_amount`) — 优惠券抵扣部分
|
||
- **积分抵扣** (`points_amount`) — 积分抵扣部分(当前数据为0,但字段已预留)
|
||
|
||
验证:`total_amount = actual_amount + discount_amount + points_amount` 在所有订单上完全成立(0条不等式)。
|
||
|
||
### 数据现状(dev 环境)
|
||
|
||
| 支付方式 | 订单数 | 金额(元) | 占比 |
|
||
|---------|--------|---------|------|
|
||
| GMV 总额 | 3,595 | 124,526.80 | 100% |
|
||
| 现金 | 3,595 | 90,067.85 | 72.3% |
|
||
| 优惠券 | 1,896 | 34,458.95 | 27.7% |
|
||
| 积分 | 0 | 0.00 | 0% |
|
||
|
||
### 技术方案
|
||
|
||
orders 表已有完整的拆分字段,**无需新建表或字段**,只需在查询和展示层增加维度。
|
||
|
||
### 实施步骤
|
||
|
||
#### Step 1: 后端 — 扩展数据结构
|
||
|
||
文件:`internal/service/channel/channel.go`
|
||
|
||
1.1 `StatsOverview` 结构体新增字段:
|
||
```go
|
||
CashCents int64 `json:"cash_cents"` // 现金支付(分)
|
||
CouponCents int64 `json:"coupon_cents"` // 优惠券抵扣(分)
|
||
PointsCents int64 `json:"points_cents"` // 积分抵扣(分)
|
||
```
|
||
|
||
1.2 `StatsDailyItem` 结构体新增字段:
|
||
```go
|
||
CashCents int64 `json:"cash_cents"`
|
||
CouponCents int64 `json:"coupon_cents"`
|
||
PointsCents int64 `json:"points_cents"`
|
||
```
|
||
|
||
#### Step 2: 后端 — 修改 GMV 查询方法
|
||
|
||
文件:`internal/service/channel/channel.go`
|
||
|
||
2.1 `calcGMVByTotalAmount` 改为同时返回 actual_amount / discount_amount / points_amount 的分组统计:
|
||
|
||
```go
|
||
type GMVBreakdown struct {
|
||
Total int64
|
||
Cash int64
|
||
Coupon int64
|
||
Points int64
|
||
}
|
||
|
||
func calcGMVByTotalAmount(...) (GMVBreakdown, map[string]GMVBreakdown)
|
||
```
|
||
|
||
查询 SELECT 增加:`orders.actual_amount, orders.discount_amount, orders.points_amount`
|
||
|
||
2.2 `GetStats` 方法中将拆分数据写入 Overview 和 Daily:
|
||
```go
|
||
out.Overview.CashCents = breakdown.Cash
|
||
out.Overview.CouponCents = breakdown.Coupon
|
||
out.Overview.PointsCents = breakdown.Points
|
||
```
|
||
|
||
#### Step 3: 后端 — List 接口也返回拆分数据(可选)
|
||
|
||
文件:`internal/service/channel/channel.go`
|
||
|
||
`ChannelWithStat` 结构体和 `List` 方法中的 GMV 查询也增加拆分统计,用于渠道列表页 tooltip 展示。
|
||
|
||
#### Step 4: 前端 — API 类型更新
|
||
|
||
文件:`web/admin/src/api/channels.ts`
|
||
|
||
```ts
|
||
interface StatsOverview {
|
||
// ... existing fields
|
||
cash_cents?: number
|
||
coupon_cents?: number
|
||
points_cents?: number
|
||
}
|
||
|
||
interface StatsDailyItem {
|
||
// ... existing fields
|
||
cash_cents?: number
|
||
coupon_cents?: number
|
||
points_cents?: number
|
||
}
|
||
```
|
||
|
||
#### Step 5: 前端 — Stats 概览卡片展示
|
||
|
||
文件:`web/admin/src/views/operations/channels/index.vue`
|
||
|
||
在"累计实付金额"卡片下方或旁边展示支付构成:
|
||
- 显示 3 个子指标:现金 / 优惠券 / 积分
|
||
- 各自显示金额和占比百分比
|
||
- 积分为 0 时可隐藏或灰显
|
||
|
||
#### Step 6: 前端 — 每日趋势图支持
|
||
|
||
文件:`web/admin/src/views/operations/channels/index.vue`
|
||
|
||
在 revenue tab 的折线图中,可以选择查看:
|
||
- GMV 总额(默认)
|
||
- 现金 / 优惠券 / 积分 分层堆叠
|
||
|
||
#### Step 7: 测试
|
||
|
||
文件:`internal/service/channel/channel_stats_test.go`
|
||
|
||
更新测试用例,验证 GMV 拆分字段的正确性。
|
||
|
||
### 关键文件
|
||
|
||
| 文件 | 操作 | 说明 |
|
||
|------|------|------|
|
||
| `internal/service/channel/channel.go` | 修改 | 数据结构 + 查询逻辑 |
|
||
| `internal/service/channel/channel_stats_test.go` | 修改 | 测试覆盖拆分字段 |
|
||
| `web/admin/src/api/channels.ts` | 修改 | API 类型定义 |
|
||
| `web/admin/src/views/operations/channels/index.vue` | 修改 | 概览卡片 + 图表展示 |
|
||
|
||
### 风险与缓解
|
||
|
||
| 风险 | 缓解措施 |
|
||
|------|----------|
|
||
| 旧版前端未适配新字段 | 新字段均为可选,不影响旧版展示 |
|
||
| 积分字段当前全为0 | 字段预留,后续开启积分抵扣时自动生效 |
|
||
| 查询性能 | 无额外 JOIN,只增加 3 个 SUM 列,影响可忽略 |
|