diff --git a/backend/app/dao/order/OrderCenterDao.php b/backend/app/dao/order/OrderCenterDao.php index 4d6ed8372..d767cc088 100644 --- a/backend/app/dao/order/OrderCenterDao.php +++ b/backend/app/dao/order/OrderCenterDao.php @@ -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(); } /** diff --git a/backend/app/services/statistic/TradeStatisticServices.php b/backend/app/services/statistic/TradeStatisticServices.php index 59cb1170a..82990ba78 100644 --- a/backend/app/services/statistic/TradeStatisticServices.php +++ b/backend/app/services/statistic/TradeStatisticServices.php @@ -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); }