From aa82159d43c9aacd10c4db149758286b7a92d14c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=90=B4=E6=98=8A=E5=A4=A9?= <442384644@qq.com>
Date: Sat, 24 Oct 2026 11:15:36 +0800
Subject: [PATCH] =?UTF-8?q?fix(=E8=B5=84=E9=87=91=E6=B5=81=E6=B0=B4):=20?=
=?UTF-8?q?=E8=AF=BB=E5=8F=96=E4=BE=A7=E5=8F=AA=E5=B1=95=E7=A4=BA=E7=8E=B0?=
=?UTF-8?q?=E9=87=91=E6=B8=A0=E9=81=93=EF=BC=8C=E4=BA=A4=E6=98=93=E7=B1=BB?=
=?UTF-8?q?=E5=9E=8B=E4=B8=8B=E6=8B=89=E6=94=B9=E7=94=B1=E5=90=8E=E7=AB=AF?=
=?UTF-8?q?=E4=B8=8B=E5=8F=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- CapitalFlow 新增 searchCashOnlyAttr 搜索器:列表、导出、账单统计统一
whereNotIn('pay_type', ['', 'yue', 'admin']),历史遗留的站内结算流水不再展示,
与写入侧 PayServices::isCashChannel() 口径对齐。
- CapitalFlowServices::getList()/getFlowRecord() 注入 cash_only,trading_type
文案改为按值映射(不再依赖数组下标,历史下线类型回退「其他」)。
- 新增 type_options(全部/订单支付/订单退款/购买会员/线下收银)供前端下拉,
同时保留 status 全量文案兼容旧前端。
- 后台资金流水页:筛选改用 trading_type + typeOptions,支付方式直接取
pay_type_name,移除本地硬编码的支付方式表。
- 同步 .crmeb 售后文档的资金流水读取侧口径说明。
---
.../references/order-aftersale.md | 2 +-
.../model/system/statistics/CapitalFlow.php | 14 +++++++
.../statistic/CapitalFlowServices.php | 21 ++++++++--
.../src/pages/finance/capitalFlow/index.vue | 38 +++++--------------
4 files changed, 42 insertions(+), 33 deletions(-)
diff --git a/.crmeb/skills/crmeb-order-dev/references/order-aftersale.md b/.crmeb/skills/crmeb-order-dev/references/order-aftersale.md
index 359bc98fe..8af2c818b 100644
--- a/.crmeb/skills/crmeb-order-dev/references/order-aftersale.md
+++ b/.crmeb/skills/crmeb-order-dev/references/order-aftersale.md
@@ -72,7 +72,7 @@ AfterSaleServices::refund
- Handler 中的资金/资源副作用一律经 `RefundEffectContextInterface::once()` 登记去重,禁止直接裸写。
- 中台 `refund_status`(0 无 / 1 申请中 / 2 已退)由 Adapter `resolveRefundStatus` 推导,退款完成时 `syncStatus` 同步。
-**资金流水(`eb_capital_flow`)只记真实资金进出**:判定统一入口为 `PayServices::isCashChannel()`(第三方在线渠道 + 线下支付;余额 `yue`、后台代付、0 元单一律 false)。`CapitalFlowServices::setFlow()` 内部已做拦截,退款链路在调用前还要排除 `refund_type=balance` / `refund_method=balance` 的站内结转,即「余额支付订单退款」「原路退款但改退余额」都不产生现金流出,不记流水。
+**资金流水(`eb_capital_flow`)只记真实资金进出**:判定统一入口为 `PayServices::isCashChannel()`(第三方在线渠道 + 线下支付;余额 `yue`、后台代付、0 元单一律 false)。`CapitalFlowServices::setFlow()` 内部已做拦截,退款链路在调用前还要排除 `refund_type=balance` / `refund_method=balance` 的站内结转,即「余额支付订单退款」「原路退款但改退余额」都不产生现金流出,不记流水。读取侧口径一致:`CapitalFlow::searchCashOnlyAttr` 在列表、导出、账单统计中统一排除 `pay_type` 为 `yue` / `admin` / 空的历史流水,保证后台资金流水页面只出现现金渠道记录。
> **现状说明**:`OrderRefundCreateAfter / OrderRefundRefuse / OrderRefundCancelAfter` 的模块监听器已注册但服务层触发点待接线(见 [订单生命周期·事件地图](order-lifecycle.md#三订单事件地图)),相关后置逻辑目前挂在 `AfterSaleServices` 内部回调(`lifecycle->afterApply/afterAudit`)。
diff --git a/backend/app/model/system/statistics/CapitalFlow.php b/backend/app/model/system/statistics/CapitalFlow.php
index d8da50795..98c691dbc 100644
--- a/backend/app/model/system/statistics/CapitalFlow.php
+++ b/backend/app/model/system/statistics/CapitalFlow.php
@@ -59,4 +59,18 @@ class CapitalFlow extends BaseModel
{
if (!empty($value)) $query->whereIn('id', $value);
}
+
+ /**
+ * 仅现金渠道搜索器
+ * 资金流水只统计真实资金进出:余额支付(yue)、后台代付(admin)、0 元单(空) 属于站内结算,
+ * 即使历史数据中存在也不在资金流水、账单中展示。
+ * @param $query
+ * @param $value
+ */
+ public function searchCashOnlyAttr($query, $value)
+ {
+ if ($value) {
+ $query->whereNotIn('pay_type', ['', 'yue', 'admin']);
+ }
+ }
}
diff --git a/backend/app/services/statistic/CapitalFlowServices.php b/backend/app/services/statistic/CapitalFlowServices.php
index 047edb371..77cd5df1d 100644
--- a/backend/app/services/statistic/CapitalFlowServices.php
+++ b/backend/app/services/statistic/CapitalFlowServices.php
@@ -128,12 +128,25 @@ class CapitalFlowServices extends BaseService
{
$export = $where['export'] ?? 0;
unset($where['export']);
+ // 资金流水只展示现金渠道:余额支付、后台代付、0 元单等站内结算即使有历史数据也不展示
+ $where['cash_only'] = 1;
[$page, $limit] = $this->getPageValue();
- $status = ['全部', '支付订单', '订单退款', '充值订单', '充值退款', '抽奖红包', '佣金提现', '购买会员', '线下收银'];
+ // 全量类型文案(用于列表/导出展示,历史数据可能包含已下线的类型)
+ $typeLabels = [0 => '全部', 1 => '订单支付', 2 => '订单退款', 3 => '充值订单', 4 => '充值退款', 5 => '抽奖红包', 6 => '佣金提现', 7 => '购买会员', 8 => '线下收银'];
+ // 筛选下拉只保留资金流水页面实际使用的现金交易类型;value 与 trading_type 一一对应,不依赖数组下标
+ $type_options = [
+ ['label' => '全部', 'value' => 0],
+ ['label' => '订单支付', 'value' => 1],
+ ['label' => '订单退款', 'value' => 2],
+ ['label' => '购买会员', 'value' => 7],
+ ['label' => '线下收银', 'value' => 8],
+ ];
+ // 兼容旧前端:status 为下标即 trading_type 的全量类型列表;新前端改用 type_options
+ $status = ['全部', '订单支付', '订单退款', '充值订单', '充值退款', '抽奖红包', '佣金提现', '购买会员', '线下收银'];
$list = $this->dao->getList($where, $page, $limit);
foreach ($list as &$item) {
$item['add_time'] = date('Y-m-d H:i:s', $item['add_time']);
- $item['trading_type'] = $status[$item['trading_type']];
+ $item['trading_type'] = $typeLabels[$item['trading_type']] ?? '其他';
$item['pay_type_name'] = PayServices::getPayType()[$item['pay_type'] != 'routine' ? $item['pay_type'] : 'weixin'] ?? '其他方式';
}
$count = $this->dao->count($where);
@@ -143,7 +156,7 @@ class CapitalFlowServices extends BaseService
$fileName = '账单导出' . date('YmdHis') . rand(1000, 9999);
return compact('list', 'fileKey', 'header', 'fileName');
} else {
- return compact('list', 'count', 'status');
+ return compact('list', 'count', 'status', 'type_options');
}
}
@@ -170,6 +183,8 @@ class CapitalFlowServices extends BaseService
*/
public function getFlowRecord($where)
{
+ // 账单统计与流水列表口径保持一致,只统计现金渠道
+ $where['cash_only'] = 1;
[$page, $limit] = $this->getPageValue();
$data = $this->dao->getRecordList($where, $page, $limit);
$i = 1;
diff --git a/frontend/admin/src/pages/finance/capitalFlow/index.vue b/frontend/admin/src/pages/finance/capitalFlow/index.vue
index 44691974a..9cdfcf88a 100644
--- a/frontend/admin/src/pages/finance/capitalFlow/index.vue
+++ b/frontend/admin/src/pages/finance/capitalFlow/index.vue
@@ -23,8 +23,8 @@
>
-
-
+
+
@@ -73,9 +73,7 @@
-
- {{ item.title }}
-
+ {{ scope.row.pay_type_name || '--' }}
@@ -129,29 +127,8 @@ const modals = ref(false);
const total = ref(0);
const loading = ref(false);
const tabList = ref([]);
-const withdrawal = ref([]);
-const payment = [
- {
- title: '全部',
- value: '',
- },
- {
- title: '微信',
- value: 'weixin',
- },
- {
- title: '支付宝',
- value: 'alipay',
- },
- {
- title: '银行卡',
- value: 'bank',
- },
- {
- title: '线下支付',
- value: 'offline',
- },
-];
+// 交易类型筛选:后端返回 [{label, value}],value 直接对应 trading_type
+const typeOptions = ref([]);
const formValidate = ref({
trading_type: 0,
time: '',
@@ -214,7 +191,10 @@ function getList() {
let data = res.data;
tabList.value = data.list;
total.value = data.count;
- withdrawal.value = data.status;
+ typeOptions.value =
+ data.type_options && data.type_options.length
+ ? data.type_options
+ : (data.status || []).map((label, value) => ({ label, value }));
loading.value = false;
})
.catch((res) => {