소스 검색

feat(order): 添加阿里巴巴订单预下单功能

- 新增 AlibabaOrder 控制器处理 1688 订单检查逻辑
- 实现购物车验证和商品类型判断功能
- 添加地址信息获取和商品规格处理
- 集成 OrderService 预下单接口调用
- 新增路由配置支持 alibaba.checkOrder 接口
- 实现运费计算和订单预览功能
- 添加错误处理和数据校验机制
shichen 3 달 전
부모
커밋
34eaab9ed5
3개의 변경된 파일282개의 추가작업 그리고 1개의 파일을 삭제
  1. 59 0
      app/controller/api/store/order/AlibabaOrder.php
  2. 219 0
      app/services/ThirdParty/AlibabaAgent/OrderService.php
  3. 4 1
      route/api.php

+ 59 - 0
app/controller/api/store/order/AlibabaOrder.php

@@ -0,0 +1,59 @@
1
+<?php
2
+
3
+namespace app\controller\api\store\order;
4
+
5
+use app\common\repositories\store\order\StoreCartRepository;
6
+use app\services\ThirdParty\AlibabaAgent\OrderService;
7
+use think\facade\Db;
8
+
9
+class AlibabaOrder
10
+{
11
+    public function checkOrder(StoreCartRepository $cartRepository)
12
+    {
13
+        $cartId = request()->param('cart_id', []);
14
+        $addressId = request()->param('address_id');
15
+        $uid = 4895;
16
+
17
+        if (!($count = count(json_decode($cartId))) || $count != count($cartRepository->validIntersection(json_decode($cartId), $uid)))
18
+            return app('json')->fail('已失效或已下单');
19
+
20
+        $postagePrice = 0;
21
+
22
+        // 判断是否是阿里巴巴商品
23
+        $cartInfo = Db::name('store_cart')->whereIn('cart_id', json_decode($cartId))->select()->toArray();
24
+        $productInfo = Db::name('store_product')->whereIn('product_id', array_column($cartInfo, 'product_id'))->find();
25
+        if (!$productInfo['is_alibaba']) {
26
+            return app('json')->success(['postagePrice' => $postagePrice]);
27
+        }
28
+
29
+        // 获取地址
30
+        $addressInfo = Db::name('user_address')->where('address_id', $addressId)->find();
31
+        $addressParam = [
32
+            'fullName' => $addressInfo['real_name'],
33
+            'mobile' => $addressInfo['phone'],
34
+            'provinceText' => $addressInfo['province'],
35
+            'cityText' => $addressInfo['city'],
36
+            'areaText' => $addressInfo['district'],
37
+            'address' => $addressInfo['detail'],
38
+        ];
39
+
40
+
41
+        // 获取商品规格
42
+        $quantity = array_column($cartInfo, 'cart_num')[0];
43
+        $offerId = $productInfo['spu_id'];
44
+        $specId = Db::name('store_product_attr_value')->where('product_id', $productInfo['product_id'])->whereIn('unique', array_column($cartInfo, 'product_attr_unique'))->value('spec_id');
45
+        $cargoParamList = [
46
+            'offerId' => $offerId,
47
+            'specId' => $specId,
48
+            'quantity' => $quantity
49
+        ];
50
+
51
+        $orderService = new OrderService();
52
+
53
+        $res = $orderService->previewOrder(['cargoParamList' => $cargoParamList, 'addressParam' => $addressParam]);
54
+        if (!$res) {
55
+            return app('json')->fail('预下单失败');
56
+        }
57
+        return app('json')->success(['postagePrice' => $res['preview']['sumCarriage'] / 100]);
58
+    }
59
+}

+ 219 - 0
app/services/ThirdParty/AlibabaAgent/OrderService.php

@@ -0,0 +1,219 @@
1
+<?php
2
+/**
3
+ * 1688分销严选采购解决方案 - 订单服务类
4
+ *
5
+ * 提供预下单、下单、订单查询等功能
6
+ *
7
+ * @author: yourname
8
+ * @day: 2026/04/28
9
+ */
10
+
11
+namespace app\services\ThirdParty\AlibabaAgent;
12
+
13
+use think\facade\Db;
14
+use think\facade\Log;
15
+
16
+class OrderService extends AlibabaAgentBaseService
17
+{
18
+    /**
19
+     * API命名空间
20
+     */
21
+    const NAMESPACE = 'com.alibaba.trade';
22
+
23
+    /**
24
+     * 预下单
25
+     *
26
+     * API: com.alibaba.trade:alibaba.createOrder.preview-1
27
+     *
28
+     * 接口文档: https://open.1688.com/api/apidocdetail.htm?id=com.alibaba.trade%3Aalibaba.createOrder.preview-1
29
+     *
30
+     * 请求参数格式:
31
+     *   - flow: 流程标识(saleproxy=分销采购, general=普通下单)
32
+     *   - productList: 商品列表JSON数组,每个商品包含 offerId, quantity, specId
33
+     *   - addressParam: 地址参数JSON对象,包含 fullName, mobile, provinceText, cityText, areaText, address
34
+     *   - totalAmount: 总金额(单位:分,可选)
35
+     *   - message: 买家留言(可选)
36
+     *
37
+     * @param array $params 预下单参数
38
+     * @return array|false
39
+     */
40
+    public function previewOrder(array $params = [])
41
+    {
42
+        $businessParams = [];
43
+
44
+        // --- productList(商品列表,JSON字符串) ---
45
+        // 直接传入已格式化的数组,由方法转为JSON
46
+        if (!empty($params['cargoParamList'])) {
47
+            $businessParams['cargoParamList'] = is_array($params['cargoParamList'])
48
+                ? json_encode($params['cargoParamList'], JSON_UNESCAPED_UNICODE)
49
+                : $params['cargoParamList'];
50
+        }
51
+
52
+        // --- addressParam(地址参数,JSON字符串) ---
53
+        // 直接传入已格式化的数组,由方法转为JSON
54
+        if (!empty($params['addressParam'])) {
55
+            $businessParams['addressParam'] = is_array($params['addressParam'])
56
+                ? json_encode($params['addressParam'], JSON_UNESCAPED_UNICODE)
57
+                : $params['addressParam'];
58
+        }
59
+        // --- 调用API ---
60
+        $result = $this->executeParam2(
61
+            self::NAMESPACE,
62
+            'alibaba.createOrder.preview',
63
+            1,
64
+            $businessParams
65
+        );
66
+
67
+        if ($result === false) {
68
+            Log::channel('alibaba')->error('[预下单] API请求失败', $businessParams);
69
+            return false;
70
+        }
71
+
72
+        return $this->formatPreviewResult($result);
73
+    }
74
+
75
+    /**
76
+     * 格式化预下单返回结果
77
+     *
78
+     * 实际返回结构:
79
+     * {
80
+     *   "orderPreviewResuslt": [{
81
+     *     "tradeModeNameList": ["assureTrade"],
82
+     *     "status": true,
83
+     *     "sumPayment": 370,           // 总支付金额(单位:分)
84
+     *     "sumCarriage": 300,          // 总运费(单位:分)
85
+     *     "sumPaymentNoCarriage": 70,  // 商品总金额(不含运费,单位:分)
86
+     *     "flowFlag": "fenxiaonew",
87
+     *     "cargoList": [{              // 商品列表
88
+     *       "amount": 0.7,
89
+     *       "finalUnitPrice": 0.7,
90
+     *       "specId": "...",
91
+     *       "skuId": 6148579878582,
92
+     *       "offerId": 678279922176,
93
+     *       "openOfferId": "...",
94
+     *       "cargoPromotionList": []
95
+     *     }],
96
+     *     "tradeModelList": [{"name":"担保交易","tradeType":"assureTrade","opSupport":true}],
97
+     *     "payChannelInfos": [{"name":"alipay"}],
98
+     *     "orderGroup": "79d4f82d...",
99
+     *     "canUseOfficialSolution": false
100
+     *   }],
101
+     *   "success": true,
102
+     *   "unsupportedCrossBorderPayOfferList": []
103
+     * }
104
+     *
105
+     * @param array $result API原始返回数据
106
+     * @return array
107
+     */
108
+    protected function formatPreviewResult(array $result): array
109
+    {
110
+        $success = $result['success'] ?? false;
111
+        $previewList = $result['orderPreviewResuslt'] ?? [];
112
+
113
+        if (empty($previewList) || !is_array($previewList)) {
114
+            return [
115
+                'success' => $success,
116
+                'preview' => null,
117
+                'list'    => [],
118
+            ];
119
+        }
120
+
121
+        $formattedList = [];
122
+        foreach ($previewList as $item) {
123
+            $formattedList[] = [
124
+                'sumPayment'           => $item['sumPayment'] ?? 0,
125
+                'sumCarriage'          => $item['sumCarriage'] ?? 0,
126
+                'sumPaymentNoCarriage' => $item['sumPaymentNoCarriage'] ?? 0,
127
+                'flowFlag'             => $item['flowFlag'] ?? '',
128
+                // 'orderGroup'           => $item['orderGroup'] ?? '',
129
+                // 'status'               => $item['status'] ?? false,
130
+                // 'cargoList'            => $item['cargoList'] ?? [],
131
+                // 'tradeModeNameList'    => $item['tradeModeNameList'] ?? [],
132
+                // 'tradeModelList'       => $item['tradeModelList'] ?? [],
133
+                // 'payChannelInfos'      => $item['payChannelInfos'] ?? [],
134
+                // 'shopPromotionList'    => $item['shopPromotionList'] ?? [],
135
+                // 'tradeServiceList'     => $item['tradeServiceList'] ?? [],
136
+                // 'taoSampleSinglePromotion' => $item['taoSampleSinglePromotion'] ?? false,
137
+                // 'canUseOfficialSolution'   => $item['canUseOfficialSolution'] ?? false,
138
+            ];
139
+        }
140
+
141
+        return [
142
+            // 'success'    => $success,
143
+            'preview'    => $formattedList[0] ?? null,
144
+            // 'list'       => $formattedList,
145
+            // 'unsupportedCrossBorderPayOfferList' => $result['unsupportedCrossBorderPayOfferList'] ?? [],
146
+        ];
147
+    }
148
+
149
+    /**
150
+     * 构建商品列表参数(便捷方法)
151
+     *
152
+     * @param array $items 商品列表 [['offerId' => 123, 'quantity' => 2, 'skuId' => 456], ...]
153
+     * @return array
154
+     */
155
+    public function buildProductList(array $items): array
156
+    {
157
+        $productList = [];
158
+        foreach ($items as $item) {
159
+            $product = [
160
+                'offerId'  => (int)($item['offerId'] ?? $item['productId'] ?? 0),
161
+                'quantity' => (int)($item['quantity'] ?? 1),
162
+            ];
163
+            if (!empty($item['skuId'])) {
164
+                $product['skuId'] = (int)$item['skuId'];
165
+            }
166
+            if (!empty($item['price'])) {
167
+                $product['price'] = (int)$item['price']; // 单位:分
168
+            }
169
+            if (!empty($item['productUnit'])) {
170
+                $product['productUnit'] = $item['productUnit'];
171
+            }
172
+            $productList[] = $product;
173
+        }
174
+        return $productList;
175
+    }
176
+
177
+    /**
178
+     * 构建地址参数(便捷方法)
179
+     *
180
+     * @param int $addressId 地址ID
181
+     * @return array
182
+     */
183
+    public function buildAddressParam(int $addressId): array
184
+    {
185
+        return [
186
+            'addressId' => $addressId,
187
+        ];
188
+    }
189
+
190
+    /**
191
+     * 构建完整地址参数(使用系统地址数据格式)
192
+     *
193
+     * 适配系统 user_address 表字段:
194
+     *   address_id, real_name, phone, province, city, district, detail, post_code, code_list
195
+     *
196
+     * @param array $address 地址信息(系统user_address表数据)
197
+     * @return array
198
+     */
199
+    public function buildFullAddressParam(array $address): array
200
+    {
201
+        // 解析地区编码列表: "1,67449,35,493"
202
+        $codeList = $address['code_list'] ?? '';
203
+        $codes = explode(',', $codeList);
204
+        $districtCode = $codes[1] ?? $codes[0] ?? '';
205
+
206
+        return [
207
+            'fullName'       => $address['real_name'] ?? $address['fullName'] ?? '',
208
+            'mobile'         => $address['phone'] ?? $address['mobile'] ?? '',
209
+            'phone'          => $address['phone'] ?? $address['phone'] ?? '',
210
+            'postCode'       => $address['post_code'] ?? $address['postCode'] ?? '',
211
+            'cityText'       => $address['city'] ?? $address['cityText'] ?? '',
212
+            'provinceText'   => $address['province'] ?? $address['provinceText'] ?? '',
213
+            'areaText'       => $address['district'] ?? $address['areaText'] ?? '',
214
+            'townText'       => $address['village'] ?? $address['townText'] ?? '',
215
+            'address'        => $address['detail'] ?? $address['address'] ?? '',
216
+            'districtCode'   => $districtCode,
217
+        ];
218
+    }
219
+}

+ 4 - 1
route/api.php

@@ -139,7 +139,10 @@ Route::group('api/', function () {
139 139
             Route::get('UserVipInfo','/UserVipInfo');
140 140
         })->prefix('api.warehouse.VipUser');
141 141
 
142
-
142
+        // 1688
143
+        Route::group('alibaba',function (){
144
+            Route::post('checkOrder','/checkOrder');
145
+        })->prefix('api.store.order.AlibabaOrder');
143 146
 
144 147
 
145 148
         //代理订单