fix(统计): 修正交易统计退款与环比数据

- 商品退款按 refund_status=已退款、refund_time 统计,兼容全额退款 pay_status=3
- 商品支付/余额支付按支付口径统计,退款在支出中单独列示
- 修复余额支付环比使用两个百分比再计算、无基期环比出现百万级数值的问题
This commit is contained in:
吴昊天 2026-10-22 13:18:53 +08:00
parent dba17dd449
commit b2fbcf257a
2 changed files with 31 additions and 19 deletions

View File

@ -202,13 +202,14 @@ class OrderCenterDao extends BaseDao
* 根据条件获取字段求和
* @param array $where 查询条件
* @param string $field 求和字段
* @param string $timeField 时间过滤字段(支付统计用 pay_time,退款统计用 refund_time)
* @return float 返回求和结果
*/
public function getWhereSumField(array $where, string $field)
public function getWhereSumField(array $where, string $field, string $timeField = 'pay_time')
{
return $this->buildWhereQuery($where)
->when(isset($where['timeKey']), function ($query) use ($where) {
$query->whereBetweenTime('pay_time', $where['timeKey']['start_time'], $where['timeKey']['end_time']);
->when(isset($where['timeKey']), function ($query) use ($where, $timeField) {
$query->whereBetweenTime($timeField, $where['timeKey']['start_time'], $where['timeKey']['end_time']);
})
->sum($field);
}
@ -218,13 +219,14 @@ class OrderCenterDao extends BaseDao
* @param array $where 查询条件
* @param string $field 求和字段
* @param string $group 分组字段
* @param string $timeField 时间过滤字段(支付统计用 pay_time,退款统计用 refund_time)
* @return mixed 返回分组结果
*/
public function getGroupField(array $where, string $field, string $group)
public function getGroupField(array $where, string $field, string $group, string $timeField = 'pay_time')
{
return $this->buildWhereQuery($where)
->when(isset($where['timeKey']), function ($query) use ($where, $field, $group) {
$query->whereBetweenTime('pay_time', $where['timeKey']['start_time'], $where['timeKey']['end_time']);
->when(isset($where['timeKey']), function ($query) use ($where, $field, $group, $timeField) {
$query->whereBetweenTime($timeField, $where['timeKey']['start_time'], $where['timeKey']['end_time']);
$timeUinx = "%H";
if ($where['timeKey']['days'] == 1) {
$timeUinx = "%H";
@ -240,7 +242,7 @@ class OrderCenterDao extends BaseDao
$query->field("sum($field) as number,FROM_UNIXTIME($group, '$timeUinx') as time");
$query->group("FROM_UNIXTIME($group, '$timeUinx')");
})
->order('pay_time ASC')->select()->toArray();
->order($timeField . ' ASC')->select()->toArray();
}
/**

View File

@ -332,17 +332,16 @@ class TradeStatisticServices extends BaseService
$outYeOrderMoney = $this->getOrderTotalMoney(['pay_type' => "yue", 'time' => $where['time']], 'sum');
$lastOutYeOrderMoney = $this->getOrderTotalMoney(['pay_type' => "yue", 'time' => $dateWhere['time']], 'sum', "", $isNum);
$outYeOrderCurve = $this->getOrderTotalMoney(['pay_type' => "yue", 'time' => $where['time']], 'group', 'pay_time');
$outYeOrderChain = $this->countRate($outYeOrderMoney, $lastOutYeOrderMoney);
//余额购买会员
$outYeMemberMoney = $this->getMemberTotalMoney(['pay_type' => "yue", 'time' => $where['time']], 'sum');
$lastOutYeMemberMoney = $this->getMemberTotalMoney(['pay_type' => "yue", 'time' => $dateWhere['time']], 'sum', "", $isNum);
$outYeMemberCurve = $this->getMemberTotalMoney(['pay_type' => "yue", 'time' => $where['time']], 'group', "pay_time");
$outYeMemberChain = $this->countRate($outYeMemberMoney, $lastOutYeMemberMoney);
//余额支付
$outYeMoney = bcadd($outYeOrderMoney, $outYeMemberMoney, 2);
$lastOutYeMoney = bcadd($lastOutYeOrderMoney, $lastOutYeMemberMoney, 2);
$outYeCurve = $this->totalArrData([$outYeOrderCurve, $outYeMemberCurve]);
$outYeChain = $this->countRate($outYeOrderChain, $outYeMemberChain);
// 环比必须用余额支付总额计算,不能拿“订单余额环比”和“会员余额环比”再算一次
$outYeChain = $this->countRate($outYeMoney, $lastOutYeMoney);
$topData[6] = [
'title' => '余额支付金额',
'desc' => '用户下单时使用余额实际支付的金额',
@ -374,7 +373,7 @@ class TradeStatisticServices extends BaseService
//商品退款金额(退款统计已归中台:refund_status=已退款 累计 refund_price)
$outOrderRefund = $this->getOrderRefundTotalMoney(['time' => $where['time']], 'sum');
$lastOutOrderRefund = $this->getOrderRefundTotalMoney(['time' => $dateWhere['time']], 'sum', "", $isNum);
$outOrderRefundCurve = $this->getOrderRefundTotalMoney(['time' => $where['time']], 'group', 'pay_time');
$outOrderRefundCurve = $this->getOrderRefundTotalMoney(['time' => $where['time']], 'group', 'refund_time');
$orderRefundChain = $this->countRate($outOrderRefund, $lastOutOrderRefund);
$topData[8] = [
'title' => '商品退款金额',
@ -565,22 +564,23 @@ class TradeStatisticServices extends BaseService
*/
public function getOrderRefundTotalMoney($where, string $selectType, string $group = '', bool $isNum = false)
{
/** 退款金额统计:从订单中台按 refund_status=已退款 累计 refund_price(旧售后单表已删除,不再参与统计) */
/** 退款金额统计:退款成功订单按 refund_time 归属区间,累计 refund_price(部分/全额退款均已成功) */
/** @var OrderCenterDao $storeOrderService */
$storeOrderService = app()->make(OrderCenterDao::class);
$whereOrderMoner['refund_status'] = OrderCenter::REFUND_SUCCESS;
$whereOrderMoner['pay_status'] = 1;
// 全额退款订单 pay_status=3,不能再限定 pay_status=1,否则退款金额恒为 0
$whereOrderMoner['is_del'] = 0;
$whereOrderMoner['timeKey'] = $this->TimeConvert($where['time'], $isNum);
switch ($selectType) {
case "sum":
$totalMoney = $storeOrderService->getWhereSumField($whereOrderMoner, 'refund_price');
$totalMoney = $storeOrderService->getWhereSumField($whereOrderMoner, 'refund_price', 'refund_time');
break;
case "group":
$totalMoney = $storeOrderService->getGroupField($whereOrderMoner, 'refund_price', $group);
$totalMoney = $storeOrderService->getGroupField($whereOrderMoner, 'refund_price', 'refund_time', 'refund_time');
break;
default:
$totalMoney = $storeOrderService->getWhereSumField($whereOrderMoner, 'refund_price');
$totalMoney = $storeOrderService->getWhereSumField($whereOrderMoner, 'refund_price', 'refund_time');
}
if ($group) {
@ -603,9 +603,18 @@ class TradeStatisticServices extends BaseService
/** 普通商品订单支付金额 */
/** @var OrderCenterDao $storeOrderService */
$storeOrderService = app()->make(OrderCenterDao::class);
// 支付口径:已支付/部分退款/全额退款都计入营业额,退款金额在“支出”中单独统计;
// 旧逻辑限定 pay_status=1 且 refund_status=0,会漏掉新退款链路下 pay_status=2/3 的订单。
$orderSumField = isset($where['refund_status']) ? "refund_price" : "pay_price";
$whereOrderMoner['refund_status'] = isset($where['refund_status']) ? $where['refund_status'] : 0;
$whereOrderMoner['pay_status'] = 1;
$whereOrderMoner['is_del'] = 0;
$whereOrderMoner[] = ['pay_status', 'in', [
OrderCenter::PAY_PAID,
OrderCenter::PAY_PARTIAL_REFUND,
OrderCenter::PAY_FULL_REFUND,
]];
if (isset($where['refund_status'])) {
$whereOrderMoner['refund_status'] = $where['refund_status'];
}
// 支付方式条件(余额支付营收等场景,与 getMemberTotalMoney 对齐;旧版遗留:传了 pay_type 却未应用)
if (isset($where['pay_type'])) {
$whereOrderMoner['pay_type'] = $where['pay_type'];
@ -839,7 +848,8 @@ class TradeStatisticServices extends BaseService
public function countRate($nowValue, $lastValue)
{
if ($lastValue == 0 && $nowValue == 0) return 0;
if ($lastValue == 0) return round(bcmul(bcdiv($nowValue, 1, 4), 100, 2), 2);
// 无上期基数时无法计算环比,返回 0,避免 now*100 产生百万级虚假增长率
if ($lastValue == 0) return 0;
if ($nowValue == 0) return -100;
return bcmul(bcdiv((bcsub($nowValue, $lastValue, 2)), $lastValue, 2), 100, 2);
}