local db refund id] * @return array */ public function formatRefundItemsFromRaw(array $raw_data, array| null $platform_refund_id_to_local_refund_id_map = null): array { $records = isset($raw_data[0]) ? $raw_data : [$raw_data]; $items = []; foreach ($records as $record) { $platform_refund_id = strval($record['refund_id']); $tz = $this->getStore()->getTimezoneString(); $raw = \json_encode($record); $items[] = [ 'company_id' => $this->getCompany()->id, 'platform_id' => $this->getPlatform()->id, 'store_id' => $this->getStore()->id, 'refund_id' => 0, 'platform_parent_refund_id' => '', 'platform_refund_id' => $platform_refund_id, 'refund_status_id' => $this->getRefundStatusId($record['status']), 'refund_type_id' => $this->getRefundTypeId($record), 'reason' => $record['reason'] ?? null, 'currency' => 'CNY', 'buyer_user_id' => $record['buyer_open_uid'] ?? null, 'platform_order_id' => strval($record['tid']), 'platform_sub_order_id' => strval($record['oid']), 'platform_product_id' => isset($record['num_iid']) ? strval($record['num_iid']) : null, 'quantity' => $record['num'] ?? 0, 'refund_amount' => (float) ($record['refund_fee'] ?? 0), 'order_created_date' => null, // @attention 暂时设置为 null, 后续会批量更新 'order_paid_date' => null, // @attention 暂时设置为 null, 后续会批量更新 'created_date' => Carbon::parse($record['created'], $tz)->format('Y-m-d H:i:sP'), 'updated_date' => isset($record['modified']) ? Carbon::parse($record['modified'], $tz)->format('Y-m-d H:i:sP') : null, 'completed_date' => isset($record['end_time']) ? Carbon::parse($record['end_time'], $tz)->format('Y-m-d H:i:sP') : null, 'raw' => $raw, 'ext' => null, 'created_at' => Carbon::now()->format('Y-m-d H:i:sP'), 'updated_at' => Carbon::now()->format('Y-m-d H:i:sP'), ]; } return $this->fillOrderDates($items); } /** * 根据退款子项的 store_id + platform_order_id 查询订单, * 将 order_created_date 和 order_paid_date 填入 $items 数组 * * 订单搜索范围:[退款 created_date - 3个月, 退款 created_date] * * @param array $items 退款子项数据数组 * @return array 填充了订单日期的退款子项数组 */ protected function fillOrderDates(array $items): array { $lookups = []; foreach ($items as $item) { $key = $item['store_id'] . ':' . $item['platform_order_id']; if (!isset($lookups[$key])) { $lookups[$key] = [ 'store_id' => $item['store_id'], 'platform_order_id' => $item['platform_order_id'], 'created_date' => $item['created_date'], ]; } } if (empty($lookups)) { return $items; } $orderDatesMap = $this->queryOrderDates($lookups); dump("Matched " . count($orderDatesMap) . " orders for " . count($lookups) . " refund item lookups"); foreach ($items as &$item) { $key = $item['store_id'] . ':' . $item['platform_order_id']; if (isset($orderDatesMap[$key])) { $item['order_created_date'] = $orderDatesMap[$key]['order_created_date']; $item['order_paid_date'] = $orderDatesMap[$key]['order_paid_date']; } } unset($item); return $items; } /** * 根据 store_id + platform_order_id 批量查询订单的创建时间和付款时间 * * 订单搜索范围:[退款 created_date - 3个月, 退款 created_date] * * @param array $lookups [key => ['store_id', 'platform_order_id', 'created_date']] * @return array [store_id:platform_order_id => ['order_created_date' => ..., 'order_paid_date' => ...]] */ protected function queryOrderDates(array $lookups): array { $orders = Order::query() ->where(function ($query) use ($lookups) { foreach ($lookups as $lookup) { $createdDate = Carbon::parse($lookup['created_date']); $query->orWhere(function ($q) use ($lookup, $createdDate) { $q->where('store_id', $lookup['store_id']) ->where('platform_order_id', $lookup['platform_order_id']) ->whereBetween('created_date', [ $createdDate->copy()->subMonths(3), $createdDate, ]); }); } }) ->get(['store_id', 'platform_order_id', 'created_date', 'paid_date']); $map = []; foreach ($orders as $order) { $key = $order->store_id . ':' . $order->platform_order_id; $map[$key] = [ 'order_created_date' => $order->created_date, 'order_paid_date' => $order->paid_date, ]; } return $map; } /** * 将 Tmall 退款状态映射为系统退款状态 ID * * @param string $platformStatus * @return int */ public function getRefundStatusId(string $platform_status): int { $map = $this->refundStatusMap(); return $map[$platform_status] ?? RefundStatus::APPLIED->value; } /** * Tmall 退款状态 → 系统退款状态映射 * * @return array */ private function refundStatusMap(): array { return [ TmallRefundStatus::WAIT_SELLER_AGREE->value => RefundStatus::APPLIED->value, TmallRefundStatus::WAIT_BUYER_RETURN_GOODS->value => RefundStatus::SELLER_ACCEPTED->value, TmallRefundStatus::WAIT_SELLER_CONFIRM_GOODS->value => RefundStatus::SELLER_ACCEPTED->value, TmallRefundStatus::SELLER_REFUSE_BUYER->value => RefundStatus::REFUSED->value, TmallRefundStatus::SUCCESS->value => RefundStatus::SUCCESS->value, TmallRefundStatus::CLOSED->value => RefundStatus::CLOSED->value, ]; } /** * 根据 Tmall 售后单数据判断退款类型 * * @param array $record 单条售后单原始数据 * @return int */ public function getRefundTypeId(array $record): int { $has_good_return = $record['has_good_return'] ?? false; $good_status = $record['good_status'] ?? ''; // 买家已退货或等待退货 → 退货退款 if ($has_good_return) { return RefundType::RETURN_AND_REFUND->value; } // 买家未收到货 → 仅退款 if ($good_status === 'BUYER_NOT_RECEIVED') { return RefundType::REFUND_ONLY->value; } // 买家已收货但无须退货 → 退款无须退货 if ($good_status === 'BUYER_RECEIVED') { return RefundType::REFUND_WITHOUT_RETURN->value; } return RefundType::REFUND_ONLY->value; } }