get($shipping_id); if (!$record) return []; $cached = $this->get_express_cache($shipping_id); $timeline = []; $timeline[] = [ 'time' => (string) ($record['created_at'] ?? ''), 'label' => sprintf(__('商家已发货:%s %s', 'aksrt'), $record['carrier_label'] ?? '', $record['tracking_no'] ?? ''), 'type' => 'shipped', ]; if (!empty($cached['traces'])) { foreach ($cached['traces'] as $trace) { $timeline[] = [ 'time' => (string) ($trace['time'] ?? ''), 'label' => (string) ($trace['context'] ?? ''), 'type' => 'trace', ]; } } if (!empty($cached['signed'])) { $timeline[] = [ 'time' => (string) ($record['delivered_at'] ?? ($cached['query_time'] ?? '')), 'label' => __('快递已签收', 'aksrt'), 'type' => 'signed', ]; } usort($timeline, static fn($a, $b) => strcmp($a['time'] ?? '', $b['time'] ?? '')); return $timeline; } /* ============================================================ * Shipping Fee Resolution (§6.6 运费模板) * * 3 types: free / fixed / amount (满额包邮) * Inheritance: product → category → theme defaults * ============================================================ */ public function resolve_shipping_fee(int $product_id, float $order_amount = 0, array $address = []): array { $config = $this->get_product_shipping_config($product_id); $fee_type = (string) ($config['fee_type'] ?? 'fixed'); $fee_amount = (float) ($config['fee_amount'] ?? 0); $free_threshold = (float) ($config['free_threshold'] ?? 0); if ($fee_type === 'free') { return ['fee' => 0.0, 'type' => 'free', 'label' => __('包邮', 'aksrt')]; } if ($fee_type === 'amount' && $free_threshold > 0 && $order_amount >= $free_threshold) { return ['fee' => 0.0, 'type' => 'free_threshold', 'label' => sprintf(__('满%.0f包邮', 'aksrt'), $free_threshold)]; } $region_fee = $this->resolve_region_fee($config, $address); if ($region_fee !== null) { return ['fee' => $region_fee, 'type' => 'region', 'label' => __('地区运费', 'aksrt')]; } return ['fee' => $fee_amount, 'type' => 'fixed', 'label' => sprintf(__('运费 ¥%.2f', 'aksrt'), $fee_amount)]; } private function get_product_shipping_config(int $product_id): array { $meta = get_post_meta($product_id, '_aksrt_shipping_config', true); if (is_array($meta) && !empty($meta['fee_type'])) { return $meta; } if (class_exists('AKSRT_Shop_Term_Meta')) { $cat_config = AKSRT_Shop_Term_Meta::resolve_inheritance($product_id, 'shipping_fee'); if (is_array($cat_config) && !empty($cat_config['fee_type'])) { return $cat_config; } } if (class_exists('AKSRT_Settings')) { $shop = AKSRT_Settings::get_shop_settings(); return [ 'fee_type' => sanitize_key((string) ($shop['defaults']['shipping_fee_type'] ?? 'fixed')), 'fee_amount' => (float) ($shop['defaults']['shipping_fee_amount'] ?? 12), 'free_threshold' => (float) ($shop['defaults']['shipping_free_threshold'] ?? 0), 'region_fees' => (array) ($shop['defaults']['region_fees'] ?? []), ]; } return ['fee_type' => 'fixed', 'fee_amount' => 12, 'free_threshold' => 0, 'region_fees' => []]; } private function resolve_region_fee(array $config, array $address): ?float { $region_fees = (array) ($config['region_fees'] ?? []); if (empty($region_fees) || empty($address['province'])) { return null; } $province = (string) $address['province']; foreach ($region_fees as $region) { $provinces = (array) ($region['provinces'] ?? []); if (in_array($province, $provinces, true)) { return (float) ($region['fee'] ?? 0); } } return null; } public static function get_china_regions(): array { $file = get_template_directory() . '/assets/data/china-regions.json'; if (!file_exists($file)) { return self::get_builtin_provinces(); } $data = json_decode((string) file_get_contents($file), true); return is_array($data) ? $data : self::get_builtin_provinces(); } private static function get_builtin_provinces(): array { return [ ['code' => '11', 'name' => '北京市'], ['code' => '12', 'name' => '天津市'], ['code' => '13', 'name' => '河北省'], ['code' => '14', 'name' => '山西省'], ['code' => '15', 'name' => '内蒙古自治区'], ['code' => '21', 'name' => '辽宁省'], ['code' => '22', 'name' => '吉林省'], ['code' => '23', 'name' => '黑龙江省'], ['code' => '31', 'name' => '上海市'], ['code' => '32', 'name' => '江苏省'], ['code' => '33', 'name' => '浙江省'], ['code' => '34', 'name' => '安徽省'], ['code' => '35', 'name' => '福建省'], ['code' => '36', 'name' => '江西省'], ['code' => '37', 'name' => '山东省'], ['code' => '41', 'name' => '河南省'], ['code' => '42', 'name' => '湖北省'], ['code' => '43', 'name' => '湖南省'], ['code' => '44', 'name' => '广东省'], ['code' => '45', 'name' => '广西壮族自治区'], ['code' => '46', 'name' => '海南省'], ['code' => '50', 'name' => '重庆市'], ['code' => '51', 'name' => '四川省'], ['code' => '52', 'name' => '贵州省'], ['code' => '53', 'name' => '云南省'], ['code' => '54', 'name' => '西藏自治区'], ['code' => '61', 'name' => '陕西省'], ['code' => '62', 'name' => '甘肃省'], ['code' => '63', 'name' => '青海省'], ['code' => '64', 'name' => '宁夏回族自治区'], ['code' => '65', 'name' => '新疆维吾尔自治区'], ['code' => '71', 'name' => '台湾省'], ['code' => '81', 'name' => '香港特别行政区'], ['code' => '82', 'name' => '澳门特别行政区'], ]; } }