添加其他字段

This commit is contained in:
1173117610@qq.com 2026-06-26 16:34:34 +08:00
parent 511fffe26a
commit c3ed03c92f
3 changed files with 271 additions and 10 deletions

View File

@ -60,7 +60,7 @@ POST /api/offline-stats
对应工具:`get_offline_stats`
用途:订单状态分布、支付方式分布、订单类型分布、平均订单金额、平均实付金额、最大订单金额。
用途:订单状态分布、支付方式分布、订单类型分布、营销中台分布、退款状态分布、抵扣金额、手续费、`diamond` 字段代币值汇总、平均订单金额、平均实付金额、最大订单金额、活跃商户数、实付金额 Top 商户
### 3.3 商户排行榜
@ -204,7 +204,30 @@ AI 默认不要使用 `date`,除非调用平台只能传固定后台日期格
| `type` | string | 订单类型,对应后台 `type` |
| `btcId` | integer | 商户分类 ID对应后台 `btc_id` |
### 7.1 订单状态 `status` 取值
### 7.1 `diamond` 字段与 `btc_id` 的关系
后台订单里存在 `diamond` 字段。该字段不是固定只表示“钻石”,需要结合 `btc_id` 判断业务含义:
| btc_id | `diamond` 字段含义 |
| --- | --- |
| `1` | 钻石值 |
| `3` | 绿宝石值 |
| `4` | 算力值 |
`/api/offline-stats` 会返回:
| 字段 | 含义 |
| --- | --- |
| `totalTokenValue` | 所有订单 `diamond` 字段本身的合计 |
| `tokenValueSummary` | 按 `btc_id` 解释后的 `diamond` 字段汇总,例如钻石值、绿宝石值、算力值 |
注意:
- `diamond` 表示本单关联/附赠/记录的代币值。
- `diamond_ded` 表示消费券/钻石抵扣金额。
- 两者不是同一个字段,不要混淆。
### 7.2 订单状态 `status` 取值
| status | 含义 | 用户常见说法 |
| --- | --- | --- |

View File

@ -133,21 +133,52 @@ AI 必须严格遵守:
"data": {
"orderCount": 100,
"statusDistribution": {
"1": 47,
"2": 10
"1 已完成": 47,
"2 已取消": 10
},
"payTypeDistribution": {
"wechat": 50,
"balance": 20
"7 蜜传微信支付": 50,
"2 微信支付": 20
},
"typeDistribution": {
"offline": 100
"0 扫码订单": 70,
"5 视频团单": 30
},
"btcDistribution": {
"3 绿宝石中台(绿宝石)": 80,
"4 算力中台(算力)": 20
},
"refundDistribution": {
"0 未退款": 95,
"1 已退款": 5
},
"totalOrderAmount": 882.89,
"totalRealPay": 662.39,
"totalCoinDeduction": 100.00,
"totalBrokerDeduction": 10.00,
"totalDiamondDeduction": 20.00,
"totalTokenValue": 123.456789,
"tokenValueSummary": {
"diamond 钻石值": 10.000000,
"diamond 绿宝石值": 100.000000,
"diamond 算力值": 13.456789
},
"totalPreferentialAmount": 5.00,
"totalMerchantRatePrice": 2.50,
"totalDeductionAmount": 135.00,
"averageOrderAmount": 8.83,
"averageRealPay": 6.62,
"maxOrderAmount": 99.99,
"activeMerchantCount": 5,
"topMerchantsByRealPay": [
{
"merchantId": 110,
"merchantName": "示例商户",
"orderCount": 20,
"totalOrderAmount": 200.00,
"totalRealPay": 180.00
}
],
"_debug_query": {
"time": "2026/06/18-2026/06/24"
}
@ -163,11 +194,23 @@ AI 必须严格遵守:
| `statusDistribution` | 订单状态分布 |
| `payTypeDistribution` | 支付方式分布 |
| `typeDistribution` | 订单类型分布 |
| `btcDistribution` | 营销代币中台分布,如积分、钻石、绿宝石、算力 |
| `refundDistribution` | 退款状态分布 |
| `totalOrderAmount` | 订单总金额 |
| `totalRealPay` | 实付总金额 |
| `totalCoinDeduction` | 抵用券/积分抵扣金额合计,对应 `coin_ded` |
| `totalBrokerDeduction` | 佣金抵扣金额合计,对应 `broker_ded` |
| `totalDiamondDeduction` | 消费券/钻石抵扣金额合计,对应 `diamond_ded` |
| `totalTokenValue` | `diamond` 字段本身的总值,不是抵扣金额 |
| `tokenValueSummary` | 按 `btc_id` 解释后的 `diamond` 字段汇总 |
| `totalPreferentialAmount` | 平台让利抵扣券金额合计,对应 `pr_amount` |
| `totalMerchantRatePrice` | 商户承担通道手续费合计,对应 `merchant_rate_price` |
| `totalDeductionAmount` | 抵扣类金额合计,包含抵用券、佣金、消费券、平台让利抵扣券 |
| `averageOrderAmount` | 平均订单金额 |
| `averageRealPay` | 平均实付金额 |
| `maxOrderAmount` | 最大订单金额 |
| `activeMerchantCount` | 当前返回数据中的活跃商户数量 |
| `topMerchantsByRealPay` | 按实付金额排序的前 10 个商户 |
状态值参考:
@ -191,9 +234,18 @@ AI 必须严格遵守:
- 订单数:{orderCount} 单
- 订单总金额:{totalOrderAmount}
- 实付总金额:{totalRealPay}
- 抵用券/积分抵扣:{totalCoinDeduction}
- 佣金抵扣:{totalBrokerDeduction}
- 消费券/钻石抵扣:{totalDiamondDeduction}
- diamond 字段总值:{totalTokenValue}
- diamond 字段分中台汇总:{tokenValueSummary 中逐项列出}
- 平台让利抵扣券:{totalPreferentialAmount}
- 通道手续费:{totalMerchantRatePrice}
- 抵扣合计:{totalDeductionAmount}
- 平均订单金额:{averageOrderAmount}
- 平均实付金额:{averageRealPay}
- 最大订单金额:{maxOrderAmount}
- 活跃商户数:{activeMerchantCount}
状态分布:
- {statusDistribution 中逐项列出}
@ -204,9 +256,31 @@ AI 必须严格遵守:
订单类型分布:
- {typeDistribution 中逐项列出}
营销中台分布:
- {btcDistribution 中逐项列出}
退款状态分布:
- {refundDistribution 中逐项列出}
实付金额 Top 商户:
- {topMerchantsByRealPay 中逐项列出}
注意:以上结果基于接口本次返回的数据统计。
```
`diamond` 字段业务解释规则:
| btc_id | `diamond` 字段含义 |
| --- | --- |
| `1` | 钻石值 |
| `3` | 绿宝石值 |
| `4` | 算力值 |
注意:`diamond` 和 `diamond_ded` 不是同一个含义。
- `diamond`:本单关联/附赠/记录的代币值,需结合 `btc_id` 判断是钻石、绿宝石还是算力。
- `diamond_ded`:消费券/钻石抵扣金额,属于抵扣金额。
## 6. 商户排行榜 JSON 字段说明
接口:`POST /api/merchant-ranking`

View File

@ -32,37 +32,103 @@ final readonly class AnalyzeOfflineOrdersTool
$statusDistribution = [];
$payTypeDistribution = [];
$typeDistribution = [];
$btcDistribution = [];
$refundDistribution = [];
$merchantStats = [];
$totalOrderAmount = 0.0;
$totalRealPay = 0.0;
$totalCoinDeduction = 0.0;
$totalBrokerDeduction = 0.0;
$totalDiamondDeduction = 0.0;
$totalTokenValue = 0.0;
$tokenValueSummary = [];
$totalPreferentialAmount = 0.0;
$totalMerchantRatePrice = 0.0;
$maxOrderAmount = 0.0;
foreach ($orders as $order) {
$statusKey = (string) ($order['status'] ?? 'unknown');
$payTypeKey = (string) ($order['pay_type'] ?? 'unknown');
$typeKey = (string) ($order['type'] ?? 'unknown');
$statusKey = $this->statusLabel($order);
$payTypeKey = $this->payTypeLabel($order['pay_type'] ?? null);
$typeKey = $this->orderTypeLabel($order['type'] ?? null, $order['is_backend'] ?? null);
$btcKey = $this->btcLabel($order['btc_id'] ?? null, $order['btc_type'] ?? null);
$refundKey = $this->refundLabel($order['refund_status'] ?? null);
$orderAmount = (float) ($order['order_price'] ?? 0);
$realPay = (float) ($order['real_pay'] ?? 0);
$coinDeduction = (float) ($order['coin_ded'] ?? 0);
$brokerDeduction = (float) ($order['broker_ded'] ?? 0);
$diamondDeduction = (float) ($order['diamond_ded'] ?? 0);
$tokenValue = (float) ($order['diamond'] ?? 0);
$preferentialAmount = (float) ($order['pr_amount'] ?? 0);
$merchantRatePrice = (float) ($order['merchant_rate_price'] ?? 0);
$merchantId = (int) ($order['merchant_id'] ?? 0);
$tokenValueKey = $this->tokenValueLabel($order['btc_id'] ?? null);
$statusDistribution[$statusKey] = ($statusDistribution[$statusKey] ?? 0) + 1;
$payTypeDistribution[$payTypeKey] = ($payTypeDistribution[$payTypeKey] ?? 0) + 1;
$typeDistribution[$typeKey] = ($typeDistribution[$typeKey] ?? 0) + 1;
$btcDistribution[$btcKey] = ($btcDistribution[$btcKey] ?? 0) + 1;
$refundDistribution[$refundKey] = ($refundDistribution[$refundKey] ?? 0) + 1;
$totalOrderAmount += $orderAmount;
$totalRealPay += $realPay;
$totalCoinDeduction += $coinDeduction;
$totalBrokerDeduction += $brokerDeduction;
$totalDiamondDeduction += $diamondDeduction;
$totalTokenValue += $tokenValue;
$tokenValueSummary[$tokenValueKey] = round(($tokenValueSummary[$tokenValueKey] ?? 0) + $tokenValue, 6);
$totalPreferentialAmount += $preferentialAmount;
$totalMerchantRatePrice += $merchantRatePrice;
$maxOrderAmount = max($maxOrderAmount, $orderAmount);
if ($merchantId > 0) {
if (!isset($merchantStats[$merchantId])) {
$merchantStats[$merchantId] = [
'merchantId' => $merchantId,
'merchantName' => (string) ($order['merchant_name'] ?? ''),
'orderCount' => 0,
'totalOrderAmount' => 0.0,
'totalRealPay' => 0.0,
];
}
$merchantStats[$merchantId]['orderCount']++;
$merchantStats[$merchantId]['totalOrderAmount'] += $orderAmount;
$merchantStats[$merchantId]['totalRealPay'] += $realPay;
}
}
$orderCount = count($orders);
$topMerchants = array_values($merchantStats);
usort($topMerchants, static fn (array $left, array $right): int => $right['totalRealPay'] <=> $left['totalRealPay']);
$topMerchants = array_slice($topMerchants, 0, 10);
foreach ($topMerchants as $index => $merchant) {
$topMerchants[$index]['totalOrderAmount'] = round((float) $merchant['totalOrderAmount'], 2);
$topMerchants[$index]['totalRealPay'] = round((float) $merchant['totalRealPay'], 2);
}
return [
'orderCount' => $orderCount,
'statusDistribution' => $statusDistribution,
'payTypeDistribution' => $payTypeDistribution,
'typeDistribution' => $typeDistribution,
'btcDistribution' => $btcDistribution,
'refundDistribution' => $refundDistribution,
'totalOrderAmount' => round($totalOrderAmount, 2),
'totalRealPay' => round($totalRealPay, 2),
'totalCoinDeduction' => round($totalCoinDeduction, 2),
'totalBrokerDeduction' => round($totalBrokerDeduction, 2),
'totalDiamondDeduction' => round($totalDiamondDeduction, 2),
'totalTokenValue' => round($totalTokenValue, 6),
'tokenValueSummary' => $tokenValueSummary,
'totalPreferentialAmount' => round($totalPreferentialAmount, 2),
'totalMerchantRatePrice' => round($totalMerchantRatePrice, 2),
'totalDeductionAmount' => round($totalCoinDeduction + $totalBrokerDeduction + $totalDiamondDeduction + $totalPreferentialAmount, 2),
'averageOrderAmount' => $orderCount > 0 ? round($totalOrderAmount / $orderCount, 2) : 0,
'averageRealPay' => $orderCount > 0 ? round($totalRealPay / $orderCount, 2) : 0,
'maxOrderAmount' => round($maxOrderAmount, 2),
'activeMerchantCount' => count($merchantStats),
'topMerchantsByRealPay' => $topMerchants,
'_debug_query' => $query,
];
}
@ -75,4 +141,102 @@ final readonly class AnalyzeOfflineOrdersTool
];
}
/**
* @param array<string, mixed> $order
*/
private function statusLabel(array $order): string
{
$status = (int) ($order['status'] ?? -999);
$refundStatus = (int) ($order['refund_status'] ?? 0);
if ($status === 1 && $refundStatus === 1) {
return '1 已完成(已退款)';
}
if ($status === 1 && $refundStatus > 1) {
return '1 已完成(部分退款)';
}
return match ($status) {
0 => '0 待付款',
1 => '1 已完成',
2 => '2 已取消',
3 => '3 待使用/待核销',
4 => '4 已退款',
default => 'unknown 未知状态',
};
}
private function payTypeLabel(mixed $payType): string
{
return match ((int) $payType) {
0 => '0 中台微信',
1 => '1 中台支付宝',
2 => '2 微信支付',
3 => '3 公众号支付',
4 => '4 中台公众号',
7 => '7 蜜传微信支付',
default => 'unknown 未知支付渠道',
};
}
private function orderTypeLabel(mixed $type, mixed $isBackend): string
{
$orderType = (int) $type;
if ($orderType === 0 && (int) $isBackend === 1) {
return '0 后台扫码订单';
}
if ($orderType === 0) {
return '0 扫码订单';
}
return match ($orderType) {
1 => '1 点餐订单',
2 => '2 团购订单',
3 => '3 收银系统订单',
4 => '4 曹操出行订单',
5 => '5 视频团单',
default => 'unknown 未知订单类型',
};
}
private function btcLabel(mixed $btcId, mixed $btcType): string
{
$label = match ((int) $btcId) {
0 => '0 积分中台',
1 => '1 钻石中台',
3 => '3 绿宝石中台',
4 => '4 算力中台',
default => 'unknown 未知中台',
};
if ($btcType !== null && trim((string) $btcType) !== '') {
return $label . '' . $btcType . '';
}
return $label;
}
private function tokenValueLabel(mixed $btcId): string
{
return match ((int) $btcId) {
1 => 'diamond 钻石值',
3 => 'diamond 绿宝石值',
4 => 'diamond 算力值',
default => 'diamond 其他中台值',
};
}
private function refundLabel(mixed $refundStatus): string
{
return match ((int) $refundStatus) {
0 => '0 未退款',
1 => '1 已退款',
2 => '2 部分退款',
default => 'unknown 未知退款状态',
};
}
}