$this->getCompany()->id, 'platform_id' => $this->getPlatform()->id, 'store_id' => $this->getStore()->id, 'order_status_id' => $this->getOrderStatusId($record['status']), 'platform_order_id' => $record['tid'], 'payment_method_id' => $this->getPaymentMethodId(), 'presale' => false, 'total_amount' => $record['total_fee'], 'total_paid' => $record['payment'], 'total_discount' => $record['discount_fee'], // 实际收到的金额,如果平台有商家补贴,此金额需要重新计算 - 部分退款订单也影响此字段的值 'total_received' => $record['received_payment'], 'freight_fee' => 0, 'tax_fee' => 0, 'discount_fee' => 0, 'commission_fee' => 0, 'coupon_amount' => 0, 'voucher_amount' => 0, 'order_type_id' => OrderType::Normor_Order->value, 'created_date' => $record['created'], 'updated_date' => $record['modified'] ?? null, 'paid_date' => $record['pay_time'] ?? null, 'shipping_date' => $record['consign_time'] ?? null, 'zipcode' => $record['receiver_zip'] ?? '', 'city' => $record['receiver_city'] ?? '', 'province' => $record['receiver_state'] ?? '', 'country' => 'CN', 'raw' => $raw, 'ext' => null, 'buyer_user_id' => $record['buyer_open_uid'], 'hash' => \md5($raw), 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), ]; } }); } /** * 获取实体的唯一键字段(用于 upsert 的 uniqueBy 参数) * * 定义订单的唯一约束字段组合 * * @return array 唯一键字段名数组 */ public function getUniqueBy(): array { // 天猫订单的唯一性由店铺 + 平台订单号确定(实际上由平台订单号就已经足够) // @attention create_date 为数据库强制要求,必需携带 return ['store_id', 'platform_order_id', 'created_date']; } /** * 获取可更新的字段列表(用于 upsert 的 update 参数) * * 明确定义哪些字段在更新时可以被修改 * 排除:主键、唯一键、创建时间、关联 ID 等不应变更的字段 * * @return array 可更新字段名数组 */ public function getUpdateFields(): array { // 方案1:手动指定可更新字段(推荐,最明确) // 根据实际业务需求添加其他可更新字段 return [ 'company_id', 'platform_id', 'store_id', 'order_status_id', 'platform_order_id', 'payment_method_id', 'presale', 'total_amount', 'total_paid', 'total_discount', 'total_received', 'freight_fee', 'tax_fee', 'discount_fee', 'commission_fee', 'coupon_amount', 'voucher_amount', 'order_type_id', 'created_date', 'updated_date', 'paid_date', 'shipping_date', 'zipcode', 'city', 'province', 'country', 'raw', 'ext', 'buyer_user_id', 'hash', 'updated_at', // 更新时刷新,created_at 不包含以保留原始创建时间 ]; // 方案2:动态计算(如果字段较多且经常变动) // $entity = $this->entityMatch($this->getData()); // $excludeFields = array_merge( // ['id', 'created_at', 'created_date', 'company_id', 'platform_id'], // $this->getUniqueBy() // ); // return $this->getTableColumnsExcept($entity, $excludeFields); } /** * 根据淘宝订单状态确定对应的 Tools 订单状态 * * @param string $platform_order_status 淘宝平台订单状态 * @return int Tools 订单状态 ID */ public function getOrderStatusId(string $platform_order_status): int { $status_map = $this->orderStatusMap(); return $status_map[$platform_order_status] ?? OrderStatus::PAYMENT_COMPLETE->value; } /** * 淘宝订单状态到 Tools 订单状态的映射 * * @return array */ private function orderStatusMap(): array { return [ // Payment pending (1) 'TRADE_NO_CREATE_PAY' => OrderStatus::PAYMENT_PENDING->value, // 没有创建支付宝交易 'WAIT_BUYER_PAY' => OrderStatus::PAYMENT_PENDING->value, // 等待买家付款 'PAY_PENDING' => OrderStatus::PAYMENT_PENDING->value, // 国际信用卡支付付款确认中 'WAIT_PRE_AUTH_CONFIRM' => OrderStatus::PAYMENT_PENDING->value, // 0元购合约中 // Payment fail (2) 'TRADE_CLOSED_BY_TAOBAO' => OrderStatus::PAYMENT_FAIL->value, // 付款以前,卖家或买家主动关闭交易 // Payment complete (3) 'SELLER_CONSIGNED_PART' => OrderStatus::PAYMENT_COMPLETE->value, // 卖家部分发货 'WAIT_SELLER_SEND_GOODS' => OrderStatus::PAYMENT_COMPLETE->value, // 等待卖家发货,即:买家已付款 // Awaiting shipment (4) 'PAID_FORBID_CONSIGN' => OrderStatus::AWAITING_SHIPMENT->value, // 拼团中订单或者发货强管控的订单,已付款但禁止发货 // Shipped (5) 'WAIT_BUYER_CONFIRM_GOODS' => OrderStatus::SHIPPED->value, // 等待买家确认收货,即:卖家已发货 'TRADE_BUYER_SIGNED' => OrderStatus::SHIPPED->value, // 买家已签收,货到付款专用 // Finished (8) 'TRADE_FINISHED' => OrderStatus::FINISHED->value, // 交易成功 // Cancel before shipping (9) 'TRADE_CLOSED' => OrderStatus::CANCEL_BEFORE_SHIPPING->value, // 付款以后用户退款成功,交易自动关闭 ]; } public function getPaymentMethodId() : int { // @attention 暂时固定返回支付方式为支付宝, 其他平台需要定义付款方式映射 return PaymentMethod::ALIPAY_CN->value; } /** * * orderItems 的输出结果为 \App\Model\OrderItem::fill() 可以直接使用的数据格式 * * @param array $raw_data * @param array $platform_orders_id_to_local_db_order_id_map * @return array */ public function formatOrderItemsFromRaw(array $raw_data, array $platform_orders_id_to_local_db_order_id_map): array { // 由于 $raw_data 是批量数据,包含多条订单结果,因此订单子项需要从集合中提取 // Tmall 平台响应体的数据格式为: collection->payload->orders // $platform_orders_id_to_local_db_order_id_map 内部元素数据格式为 [platform_order_id => local db order id] $items = []; $product_keys = []; // 索引 => 查询键 foreach ($raw_data as $raw_orders) { $parent_order_created_date = Carbon::parse($raw_orders['created']); foreach ($raw_orders['orders']['order'] as $raw_order_item) { // 数据库中父订单 id $db_order_id = $platform_orders_id_to_local_db_order_id_map[$raw_orders['tid']]; $platform_order_id = strval($raw_orders['tid']); // 记录当前索引 $index = count($items); // 先添加 item,product_id 初始为 0 $items[] = $this->tmallOrderItemMap($raw_order_item, $platform_order_id, $db_order_id, $parent_order_created_date, 0); // 记录该索引对应的产品查询键 (Tmall 使用 num_iid 作为 item_id,sku_id 作为 model_id) $model_id = !empty($raw_order_item['sku_id']) ? (string)$raw_order_item['sku_id'] : null; $product_keys[$index] = $this->buildProductMapKey((string)$raw_order_item['num_iid'], $model_id); } } // 批量查询产品 并使用匹配到的产品 覆写 $items product_id 字段的值 if (!empty($product_keys)) { $products_id_map = $this->batchQueryProducts($product_keys); foreach ($product_keys as $index => $key) { if (isset($products_id_map[$key])) { $items[$index]['product_id'] = $products_id_map[$key]; } } } return $items; } /** * 构建产品映射键 * * @param string $item_id 平台商品 ID * @param string|null $model_id 平台 SKU ID * @return string */ private function buildProductMapKey(string $item_id, ?string $model_id): string { return $item_id . ':' . ($model_id ?? 'null'); } /** * 批量查询产品 ID * * 使用 PostgreSQL VALUES + JOIN 语法优化,避免生成冗长的 OR 条件链 * * @param array $product_keys 索引 => 查询键 的映射 * @return array 查询键 => 产品 ID 的映射 */ private function batchQueryProducts(array $product_keys): array { $store_id = $this->getStore()->id; $unique_keys = array_unique(array_values($product_keys)); if (empty($unique_keys)) { return []; } // 构建 VALUES 子句和绑定参数 $values = []; $bindings = []; foreach ($unique_keys as $key) { [$item_id, $model_id] = explode(':', $key, 2); $values[] = '(?::text, ?::text)'; $bindings[] = $item_id; $bindings[] = $model_id === 'null' ? null : $model_id; } $valuesClause = implode(', ', $values); $bindings[] = $store_id; // PostgreSQL 原生 SQL:VALUES 作为驱动表(小表在左侧) // store_id 条件移入 JOIN ON 子句,让索引过滤更早介入 $sql = " SELECT p.id, p.platform_item_id, p.platform_model_id FROM (VALUES {$valuesClause}) AS v(item_id, model_id) INNER JOIN products p ON p.store_id = ? AND p.platform_item_id = v.item_id AND (p.platform_model_id = v.model_id OR (v.model_id IS NULL AND p.platform_model_id IS NULL)) "; $products = \Hyperf\DbConnection\Db::select($sql, $bindings); $map = []; foreach ($products as $product) { $key = $this->buildProductMapKey($product->platform_item_id, $product->platform_model_id); $map[$key] = $product->id; } return $map; } /** * Tmall 订单子项映射转换 * @return void */ private function tmallOrderItemMap(array $item, string $platform_order_id, int $parent_order_id, Carbon $parent_order_created_date): array { // $item = [ // "adjust_fee" => "0.00", // "buyer_rate" => false, // "cid" => 50026872, // "consign_due_time" => "2_5", // "discount_fee" => "231.98", // "divide_order_fee" => "504.00", // "is_bybt_order" => false, // "is_daixiao" => false, // "is_idle" => "0", // "is_jzfx" => false, // "is_oversold" => false, // "nr_outer_iid" => "768990037900", // "num" => 1, // "num_iid" => 637901668632, // "oid" => 4932549972242808538, // "oid_str" => "4932549972242808538", // "order_attr" => "{"esDate":"2025-12-14","pushTime":"2025-12-09 11:30:00"}", // "order_from" => "WAP,WAP", // "outer_iid" => "768990037900", // "outer_sku_id" => "768990037900", // "part_mjz_discount" => "15.00", // "payment" => "504.00", // "pic_path" => "https://img.alicdn.com/bao/uploaded/i4/2413132940/O1CN01duCukP1XaZLX2jVQy_!!2413132940.jpg", // "price" => "693.00", // "refund_status" => "NO_REFUND", // "s_tariff_fee" => "0.00", // "seller_rate" => false, // "seller_type" => "B", // "sku_id" => "6123547511321", // "sku_properties_name" => "颜色分类:高倍Omega鱼油软胶囊 180粒/瓶", // "snapshot_url" => "za:4932549972242808538_1", // "status" => "WAIT_SELLER_SEND_GOODS", // "store_code" => "STORE_11388627", // "sub_order_tax_fee" => "57.98", // "sub_order_tax_promotion_fee" => "0.00", // "sub_order_tax_rate" => "0", // "tax_coupon_discount" => "57.98", // "tax_free" => true, // "title" => "【优惠价】NordicNaturals挪威小鱼dha深海鱼油Omega3成人rTG高纯度epa胶囊", // "total_fee" => "461.02", // ] return [ 'company_id' => $this->getCompany()->id, 'platform_id' => $this->getPlatform()->id, 'store_id' => $this->getStore()->id, 'order_id' => $parent_order_id, 'platform_order_id' => strval($platform_order_id), 'sub_order_id' => $item['oid'], // @attention sku 的处理需要仅以规范和约束,确保 sku 准确 'sub_order_type_id' => null, // @attention 值为 0 表示未找到产品 id 'product_id' => 0, // 值为 0 表示未找到产品 id, 之后会被批量查询覆盖 'platform_product_id' =>$item['num_iid'], // @attention @TODO 需要对 运营侧的产品维护做进一步规范 'product_sku' => $item['outer_sku_id'] ?? null, // @attention @TODO 需要对 运营侧的产品维护做进一步规范 'product_barcode' => null, 'unit_price' => $item['price'], 'quantity' => $item['num'], 'discount' => $item['discount_fee'], 'total' => $item['divide_order_fee'], // @attention 扩展字段,用来存储其他信息 'ext' => null, 'created_date' => $parent_order_created_date, ]; } }