Jelajahi Sumber

up - 调试参数与流程

liugc 9 bulan lalu
induk
melakukan
4aaa0dc486
2 mengubah file dengan 379 tambahan dan 416 penghapusan
  1. 52 70
      app/api/controller/DouYinController.php
  2. 327 346
      app/api/service/DouYinService.php

+ 52 - 70
app/api/controller/DouYinController.php

@@ -35,18 +35,7 @@ class DouYinController extends BaseApiController
 {
 
     public array $notNeedLogin = ['testNotify','getClientToken','register','account','getAllGoods','getGoodsDetail','payNotify'];
-    public function testNotify()
-    {
-        // https://weixiudev.kyjlkj.com/api/dou_yin/testNotify
-        $params = $this->request->post();
-        Log::info('dy_testNotify:'.json_encode($params));
-    }
 
-    public function getClientToken()
-    {
-        $access_token = DouYinService::getClientToken($this->request->get()['isRefresh']??false);
-        return $this->success('',['access_token'=>$access_token]);
-    }
     /**
      * 手机号注册
      * @author liugc <466014217@qq.com>
@@ -93,7 +82,6 @@ class DouYinController extends BaseApiController
         }
     }
 
-
     /**
      * @notes 退出登录
      * @return \think\response\Json
@@ -110,6 +98,7 @@ class DouYinController extends BaseApiController
     }
 
 
+    // ******************************** 列表详情 _商品 _订单 _退款
     /**
      * 所有商品
      * @return \think\response\Json
@@ -122,8 +111,6 @@ class DouYinController extends BaseApiController
         $params['platform_value'] = 6;
         return $this->dataLists((new GoodsLists())->setParams($params));
     }
-
-
     /**
      * 商品详情
      * @return \think\response\Json
@@ -137,28 +124,40 @@ class DouYinController extends BaseApiController
         $result = GoodsLogic::detail($params['goods_category_id'],'category',$this->userId,$params);
         return $this->data($result);
     }
-
-
     /**
-     * 下单
+     * 订单列表
      * @return \think\response\Json
      * @author liugc <466014217@qq.com>
      * @date 2025/5/22 14:35
      */
-    /*public function submitOrder()
+    public function getOrderLists()
     {
-        try {
-            $params = $this->request->post();
-            $params['user_id'] = $this->userId;
-            $params['user_info'] = $this->userInfo;
-            $order_number = DouYinService::submitOrder($params);
-            $requestOrderData = DouYinService::getByteAuthorization($order_number);
-            $requestOrderData['order_number'] = $order_number;
-            return $this->success('',$requestOrderData);
-        } catch (\Exception $e) {
-            return $this->fail($e->getMessage());
-        }
-    }*/
+        $params = $this->request->get();
+        $params['user_id'] = $this->userId;
+        return $this->dataLists((new DouyinOrderLists())->setParams($params));
+    }
+    public function getOrderDetail()
+    {
+        $params = $this->request->get();
+        $params['user_id'] = $this->userId;
+        $result = DouYinService::getOrderDetail($params);
+        return $this->data($result);
+    }
+    /**
+     * 退款订单列表
+     * @return \think\response\Json
+     * @author liugc <466014217@qq.com>
+     * @date 2025/5/22 14:35
+     */
+    public function getRefundLists()
+    {
+        $params = $this->request->get();
+        $params['user_id'] = $this->userId;
+        return $this->dataLists((new DouyinRefundOrderLists())->setParams($params));
+    }
+
+
+    // ******************************** 订单业务
     /**
      * 拉起支付所需参数
      * @return \think\response\Json
@@ -171,8 +170,7 @@ class DouYinController extends BaseApiController
             $params = $this->request->post();
             $params['user_id'] = $this->userId;
             $params['user_info'] = $this->userInfo;
-            $requestOrderData = DouYinService::getByteAuthorization($params['order_number']);
-            $requestOrderData['order_number'] = $params['order_number'];
+            $requestOrderData = DouYinService::getPluginCreateOrderData($params['goods_id']??'',$params['quantity']??1,$params['douyin_order_id']??'');
             return $this->success('',$requestOrderData);
         } catch (\Exception $e) {
             return $this->fail($e->getMessage());
@@ -193,7 +191,6 @@ class DouYinController extends BaseApiController
             return json(["err_no"=>1001,"err_tips"=>$e->getMessage()], 200);
         }
     }
-
     /**
      * 支付回调
      * @return \think\response\Json
@@ -212,22 +209,21 @@ class DouYinController extends BaseApiController
             return json(["err_no"=>1001,"err_tips"=>$e->getMessage()], 200);
         }
     }
-    /**
-     * 取消订单
-     * @return \think\response\Json
-     * @author liugc <466014217@qq.com>
-     * @date 2025/5/22 14:35
-     */
-    public function cancelOrder()
+    public function payTailNotify()
     {
         try {
-            $params = $this->request->post(); // order_number
-            DouYinService::cancelOrder($params);
-            return $this->success();
+            $params = $this->request->post();
+            $msg = is_array($params['msg'])?$params['msg']:json_decode($params['msg'],true);
+            if(DouYinService::payTailNotify($msg)){
+                return json(["err_no"=>0,"err_tips"=>"success"], 200);
+            }
         } catch (\Exception $e) {
-            return $this->fail($e->getMessage());
+            return json(["err_no"=>1001,"err_tips"=>$e->getMessage()], 200);
         }
     }
+
+
+    // ******************************** 订单预约/改约
     /**
      * 预约
      * @return \think\response\Json
@@ -244,7 +240,6 @@ class DouYinController extends BaseApiController
             return $this->fail($e->getMessage());
         }
     }
-
     /**
      * 修改预约
      * @return \think\response\Json
@@ -261,25 +256,24 @@ class DouYinController extends BaseApiController
             return $this->fail($e->getMessage());
         }
     }
+
+
+    // ******************************** 订单 取消 退款
     /**
-     * 订单列表
+     * 取消订单
      * @return \think\response\Json
      * @author liugc <466014217@qq.com>
      * @date 2025/5/22 14:35
      */
-    public function getOrderLists()
-    {
-        $params = $this->request->get();
-        $params['user_id'] = $this->userId;
-        return $this->dataLists((new DouyinOrderLists())->setParams($params));
-    }
-
-    public function getOrderDetail()
+    public function cancelOrder()
     {
-        $params = $this->request->get();
-        $params['user_id'] = $this->userId;
-        $result = DouYinService::getOrderDetail($params);
-        return $this->data($result);
+        try {
+            $params = $this->request->post(); // order_number
+            DouYinService::cancelOrder($params);
+            return $this->success();
+        } catch (\Exception $e) {
+            return $this->fail($e->getMessage());
+        }
     }
     /**
      * 退款
@@ -298,18 +292,6 @@ class DouYinController extends BaseApiController
             return $this->fail($e->getMessage());
         }
     }
-    /**
-     * 退款订单列表
-     * @return \think\response\Json
-     * @author liugc <466014217@qq.com>
-     * @date 2025/5/22 14:35
-     */
-    public function getRefundLists()
-    {
-        $params = $this->request->get();
-        $params['user_id'] = $this->userId;
-        return $this->dataLists((new DouyinRefundOrderLists())->setParams($params));
-    }
     /**
      * 退款回调
      * @author liugc <466014217@qq.com>

+ 327 - 346
app/api/service/DouYinService.php

@@ -25,6 +25,7 @@ class DouYinService
 {
     protected static int $terminal = \app\common\enum\user\UserTerminalEnum::DOUYIN;
     protected static int $external_platform_id = 6;
+    protected CONST EXTERNAL_PLATFORM_ID = 6;
 
     // ********************************* 注册登录
     public static function register(array $params)
@@ -82,7 +83,6 @@ class DouYinService
     }
 
     // **************************** 商品管理 goods_category_id goods_id external_platform_id
-
     public static function addProduct($params)
     {
         $send_url = env('internal_api.api_url_host').'platf/dou_yin/addGoods';
@@ -95,170 +95,186 @@ class DouYinService
         return $res?:[];
     }
 
-    public static function addSku($params)
+    // ******************************** 订单业务
+    public static function getOrderDetail($params)
     {
-        $sku_url = config('douyin.host').'goodlife/v1/goods/sku/batch_save/';
-        $res = http_request($sku_url,self::getSkuParams($params),['Content-Type' => 'application/json;charset=utf-8','access-token' => self::getClientToken()]);
-        Log::info('addSku:'.json_encode($res));
-        // 根据返回后更新本地库对应关系 $res['data']['sku_id_list']
-        if(isset($res['data']['sku_id_list']) && !empty($res['data']['sku_id_list'])){
-            foreach ($res['data']['sku_id_list'] as $item) {
-                ExternalPlatformGoods::where('goods_id', $item['out_sku_id'])->where('external_platform_id', $params['external_platform_id'])->update([
-                    'external_goods_sn' => $item['sku_id']
-                ]);
-            }
+        //抖音订单信息/商品信息/预约信息(地址、时间、履约状态与信息)
+        // $params['order_number']  user_id
+        $order = DouyinOrder::with(['goods','serviceWork','douyinRefundOrder'])->where('order_number', $params['order_number'])->where('user_id', $params['user_id'])->findOrEmpty();
+        if($order->isEmpty()){
+            return [];
+        }
+        $orderInfo = $order->toArray();
+        empty($orderInfo['goods']) && $orderInfo['goods'] = [];
+        empty($orderInfo['serviceWork']) && $orderInfo['serviceWork'] = [];
+        empty($orderInfo['douyinRefundOrder']) && $orderInfo['douyinRefundOrder'] = [];
+
+        $work_status = $orderInfo['serviceWork']['work_status']??0;
+        $performance = [];
+        // tmp
+        switch ($work_status){
+            case 0:
+                $performance[] = ['status' => '待派单','title' => '待派单','time' => date('Y-m-d H:i:s',time())];
+                break;
+            case 1:
+            case 2:
+            case 3:
+                $performance[] = ['status' => '待派单','title' => '待派单','time' => date('Y-m-d H:i:s',time())];
+                $performance[] = ['status' => '已派单','title' => '已派单','time' => date('Y-m-d H:i:s',time())];
+                break;
+            case 4:
+            case 5:
+            case 6:
+                $performance[] = ['status' => '待派单','title' => '待派单','time' => date('Y-m-d H:i:s',time())];
+                $performance[] = ['status' => '已派单','title' => '已派单','time' => date('Y-m-d H:i:s',time())];
+                $performance[] = ['status' => '服务中','title' => '服务中','time' => date('Y-m-d H:i:s',time())];
+                break;
+            case 7:
+            case 8:
+                $performance[] = ['status' => '待派单','title' => '待派单','time' => date('Y-m-d H:i:s',time())];
+                $performance[] = ['status' => '已派单','title' => '已派单','time' => date('Y-m-d H:i:s',time())];
+                $performance[] = ['status' => '服务中','title' => '服务中','time' => date('Y-m-d H:i:s',time())];
+                $performance[] = ['status' => '已完结','title' => '已完结','time' => date('Y-m-d H:i:s',time())];
+                break;
         }
-        return [];
+        $orderInfo['performance'] = $performance;
+
+        return $orderInfo;
     }
 
-    public static function getProductParams($params)
+    // 预下单接口 - 前端  首次/尾款
+    public static function getPluginCreateOrderData($goods_id, $quantity, $douyinOrderId)
     {
-        $goods = Goods::where('id',$params['goods_id']??0)->findOrEmpty();
-        if($goods->isEmpty()){
-            return [];
-        }
-        $goods = $goods->toArray();
-        return [
-            "account_id" => '7511543640776017961',
-            "product" => [
-                "out_id" => $params['goods_id']??0,
-                "product_name" => $goods['goods_name']??'无',
-                "product_type" => 22,
-                "category_id" => 6004003,
-                "category_full_name" =>  "其他维修服务",
-                "biz_line" =>  5,
-                "account_name" =>  "亿蜂快修·武汉市",
-                //{\"params\":\"{\"spuId\":\"xxxxx\",\"skuId\":\"xxxxxx\"}\",\"path\":\"pages/any/path\",\"app_id\":\"xxxxx\"}
-                "out_url" => json_encode(['app_id'=>config('douyin.appId'),'path'=>'pages/detail','params'=>json_encode(['goods_id'=>$params['goods_id']??0])]),
-                "pois" =>[
+        try {
+            $goods = Goods::where('id',$goods_id)->findOrEmpty();
+            if($goods->isEmpty()){
+                throw new \Exception('商品不存在!');
+            }
+            $goods = $goods->toArray();
+            $platformGoods = ExternalPlatformGoods::where('goods_id', $goods_id)->where('external_platform_id', self::EXTERNAL_PLATFORM_ID)->findOrEmpty();
+            if($platformGoods->isEmpty()){
+                throw new \Exception('外部商品不存在!');
+            }
+            $platformGoods = $platformGoods->toArray();
+            $quantity = $quantity?:1;
+            $data = [
+                /*"goodsList" => [
+                    "quantity" => $quantity,
+                    "price" => $platformGoods['service_fee'] * 100,
+                    "goodsName" => $goods['goods_name'],
+                    "goodsPhoto" => $goods['goods_image']??'',
+                    "goodsId" => '',
+                    "goodsType" => 1
+                ],*/
+                "skuList" => [
                     [
-                        "poi_id" => "7511543640776017961"
+                        "quantity" => $quantity,
+                        "skuId" => (string)$platformGoods['external_goods_sn'],
+                        "skuType" => 2,
+                        "price" => $platformGoods['service_fee'] * 100,
                     ]
                 ],
-                "product_ext" => [
-                    "auto_online" => true,
-                    "display_price"=> ["low_price" => 30,"high_price" => 300],
-                    "test_extra"=> ["test_flag" => true,"uids" => ["15482888"]]
+                /*"bookInfo" => [
+                    "itemBookInfoList"=>[
+                        [
+                            "poiId" => '7511543640776017961',
+                            "shopName" => '亿蜂快修·武汉市',
+                            "outShopId" => self::EXTERNAL_PLATFORM_ID,
+                            "skuId" => (string)$platformGoods['external_goods_sn'],
+                            "bookStartTime" => $bookStartTime?:'',
+                            "bookEndTime" => $bookEndTime?:'',
+                        ]
+                    ]
+                ],*/
+                "payment" => [
+                    "totalAmount" => $quantity * $platformGoods['service_fee'] * 100,
                 ],
-                "attr_key_value_map" => self::getAttrKeyValueMapParams('product',$goods),
-                "sold_end_time" => time() + 180 * 86400,
-                "sold_start_time" => time()
-            ]
-        ];
-    }
-    public static function getSkuParams($params)
-    {
-        $goods = Goods::where('id',$params['goods_id']??0)->findOrEmpty();
-        if ($goods->isEmpty()) {
-            return [];
-        }
-        $goods = $goods->toArray();
-        // 查询商品信息
-        return [
-            "account_id" => '7511543640776017961',
-            "product_out_id" => $params['goods_category_id']??0,
-            "sku" => [
-                [
-                    "out_sku_id"=> $goods['id'],
-                    "sku_name"=> $goods['name'],
-                    "origin_amount"=> $goods['service_total'] * 100,
-                    "actual_amount"=> $goods['service_fee'] * 100,
-                    "stock"=> [
-                        "limit_type"=> 2,
-                        "stock_qty"=> 9999
-                    ],
-                    "status"=> 1,
-                    "attr_key_value_map"=> self::getAttrKeyValueMapParams('sku',$goods)
-                ]
-            ]
-        ];
-    }
-
-
-    public static function getAttrKeyValueMapParams($type,$params)
-    {
-        $res = [];
-        if($type === 'product'){
-            $res = [
-                "appointment"=>json_encode([
-                            "need_appointment"=>true,
-                            "ahead_time_type"=>2,
-                            "ahead_hour_num"=> 5,
-                            "external_link"=>"pages/detail",
-                            "order_appointment_time_url"=>"pages/order"
-                ]),
-                "auto_renew"=>true,
-                "can_no_use_date" => json_encode(["enable"=>false]),
-                "Description" => json_encode([]),
-                "image_list" => json_encode([["url"=>$params['goods_image']??'']]),
-                "limit_use_rule" => json_encode(["is_limit_use"=>true, "use_num_per_consume"=>1]),
-                "Notification" => json_encode([["title"=>$params['goods_name']??'',"content"=>$params['goods_name']??'']]),
-                "RefundPolicy"=> "2",
-                "refund_need_merchant_confirm"=> true,
-                "show_channel"=> 1,
-                "superimposed_discounts"=> false,
-                "trade_url"=> json_encode(['app_id'=>config('douyin.appId'),'path'=>'pages/detail','params'=>json_encode(['goods_id'=>$params['id']??0])]),
-                "use_date"=> json_encode(["use_date_type"=>2, "day_duration"=>15]),
-                "use_time"=> json_encode(["use_time_type"=>1]),
-                //"user_num_limit"=> ,
-                "code_source_type"=> 3,
-                "settle_type"=> 1,
-                "use_type"=> 1,
-                "limit_rule"=> json_encode(["is_limit"=>false]),
-                "out_id"=> $params['id']??0
-            ];
-        }
-        if($type === 'sku'){
-            $res = [
-                "code_source_type"=> 3,
-                "limit_rule"=> json_encode(["is_limit"=>false]),
-                "settle_type"=> 1
+                //"callbackUrl" => $callbackUrl?:'',
+                "callbackData" => [
+                    "outShopId" => self::EXTERNAL_PLATFORM_ID,
+                    "skuId" => (string)$platformGoods['external_goods_sn'],
+                    "quantity" => $quantity,
+                    "douyinOrderId" => $douyinOrderId?:0,
+                ],
+                /*"tradeOption" => json_encode([
+                    "life_trade_flag" => 1,
+                    "order_relation_info" => [
+                        "related_order_id" => 0, // 加价时上个订单号
+                        "relation_type" => 'multi_buy_as_one'
+                    ]
+                ])*/
             ];
+
+            if($douyinOrderId){ // 说明是来自首单订单即要创建尾款
+                $data['callbackUrl'] = '';
+                $data['tradeOption'] = [
+                    "life_trade_flag" => 1,
+                    "order_relation_info" => [
+                        "related_order_id" => '', // 加价时上个订单号
+                        "relation_type" => 'multi_buy_as_one'
+                    ]
+                ];
+            }
+            /*else{ // 创建首单订单
+                $data['callbackUrl'] = '';
+            }*/
+            return $data;
+        } catch (\Exception $e) {
+            throw new \Exception($e->getMessage());
         }
-        return $res;
     }
 
-
-    // ******************************** 订单
-
+    // ++++++++++++++++++++ 首次/尾款 扩展点
     /**
-     * 扩展点回调提交订单
+     * 预下单扩展点
      * @param $params
      * @throws \Exception
      * @author liugc <466014217@qq.com>
      * @date 2025/6/4 14:03
      */
-    public static function submitOrderNotify($params)
+    public static function submitOrderNotify($params = [])
     {
         try {
+            $params['external_platform_id'] = self::EXTERNAL_PLATFORM_ID;
             // order_id goods  total_amount discount cp_extra create_order_time phone_num contact_name open_id
-            if(empty($params['user_info']['mobile'])){
+            if(empty($params['phone_num'])){
                 throw new \Exception('手机号不能为空!');
             }
-            // goods_id  user_info.mobile  user_id quantity
-            $order_number = self::submitOrder([
-                'goods_id'=>$params['goods'][0]['goods_id'],
-                'user_id'=>$params['user_id'],
-                'user_info'=>$params['user_info'],
-                'quantity'=>$params['goods'][0]['quantity']
-            ]);
+            $user_id = $params['user_id'];
+
+            if($params['cp_extra']['douyinOrderId']>0){ // 说明是尾款单
+                // 创建尾款单
+                $order_number = self::tailOrder([
+                    'douyinOrderId'=>$params['cp_extra']['douyinOrderId'],
+                    'user_id'=>$user_id??0,
+                    'total_amount'=>$params['total_amount'],
+                    'discount_amount'=>$params['discount_amount'],
+                ]);
+                //$payNotifyUrl = 'payTailNotify';
+            }else{
+                // 创建首单 goods_id  user_info.mobile  user_id quantity
+                $order_number = self::submitOrder([
+                    'goods_id'=>$params['cp_extra']['skuId'],
+                    'user_id'=>$user_id??0,
+                    'mobile'=>$params['phone_num'],
+                    'quantity'=>$params['cp_extra']['quantity']
+                ]);
+            }
             return [
                 "outOrderNo" => $order_number,
                 "orderEntrySchema" => [
                     "path" => "page/index/index",
                     "params" => json_encode(['order_number' => $order_number])
                 ],
-                "payNotifyUrl" => config('douyin.payNotifyUrl')
+                "payNotifyUrl" => $payNotifyUrl??''
             ];
         } catch (\Exception $e) {
             throw new \Exception($e->getMessage());
         }
     }
-
-
-
+    // ++++++++++++++++++++ 首次创单
     /**
-     * 提交订单
+     * 预下单扩展点-子
      * @param array $params goods_id  user_info.mobile  user_id quantity
      * @return array|false
      */
@@ -271,7 +287,7 @@ class DouYinService
             if($goods->isEmpty()){
                 throw new \Exception('产品不存在!');
             }
-            if(empty($params['user_info']['mobile'])){
+            if(empty($params['mobile'])){
                 throw new \Exception('请先补充您的联系方式后在提交订单');
             }
             // TODO tmp防抖1m
@@ -283,7 +299,7 @@ class DouYinService
             //生成订单
             $create_data = [
                 'user_id' => $params['user_id'],
-                'mobile' => $params['user_info']['mobile'],
+                'mobile' => $params['mobile'],
                 'title' => $goods['goods_name'],
 
                 'goods_id'=>$goods['id'],
@@ -300,125 +316,120 @@ class DouYinService
             throw new \Exception($e->getMessage());
         }
     }
-
-    public static function getByteAuthorization($order_number)
+    // 支付成功回调
+    public static function payNotify($params)
     {
-        /*{
-            "skuList": [{
-                "skuId": "商品ID",
-                "price": 100,//单价-分
-                "quantity": 1,
-                "title": "商品标题",
-                "imageList": ["https://cdn.weixiu.kyjlkj.com/uploads/images/20240914/202409141528015aeaa2357.png"],
-                "type": 701,
-                "tagGroupId": "tag_group_7272625659887960076"
-            }],
-            "outOrderNo": "202411121413333930",
-            "totalAmount": 100,//分
-            "orderEntrySchema": {
-                    "path": "page/path/index",
-                "params": '{"id":1234, "name":"hello"}'
-            },
-            "payNotifyUrl": "https://weixiudev.kyjlkj.com/api/dou_yin/payNotify"
-        }*/
-        try {
-            $douyinOrder = DouyinOrder::where('order_number',$order_number)->findOrEmpty();
-            if($douyinOrder->isEmpty()){
-                throw new \Exception('订单不存在!');
+        Log::write(json_encode($params));
+        // 查询抖音订单是否完成支付
+        if ($params['status'] === 'SUCCESS') {
+            $transaction_id = $params['order_id']??'';
+            $paid_amount = bcdiv(bcsub($params['total_amount'] ,$params['discount_amount']), '100', 2)??0;
+            $out_order_no = $params['out_order_no'];
+            $pay_time = $params['event_time']??time();
+            $order = DouyinOrder::where('order_number', $out_order_no)->findOrEmpty();
+            if(!$order->isEmpty()){
+                // 更新充值订单状态
+                $order->transaction_id = $transaction_id;
+                $order->order_status = 2;
+                $order->pay_time = $pay_time;
+                $order->paid_amount = $paid_amount;
+
+                $user = User::where('id',$order->user_id)->findOrEmpty()->toArray();
+                $form_detail = [
+                    'user_name' => $user['real_name']??'',
+                    'mobile' => $user['mobile'],
+                    'transaction_id' => $transaction_id,
+                    'out_trade_no' => $out_order_no,
+                    'paid_amount' => $paid_amount,
+                    'params' => $params,
+                ];
+                $consultation = ExternalConsultation::create([
+                    'external_platform_id' => self::EXTERNAL_PLATFORM_ID,
+                    'form_detail' => json_encode($form_detail),
+                    'user_name' => $user['real_name']??'',
+                    'mobile' => $user['mobile'],
+                    'goods_id' => $order->goods_id,
+                    'amount' => $paid_amount
+                ]);
+                $order->consultation_id = $consultation->id;
+                $order->save();
+                return true;
             }
-            $order = $douyinOrder->toArray();
-            $goods_image = Goods::where('id',$order['goods_id'])->value('goods_image')??'';
-
-            $data = [
-                "skuList" => [
-                    [
-                        "skuId" => (string)$order['goods_id'],
-                        "price" => $order['unit_price'] * 100,
-                        "quantity" => $order['quantity'],
-                        "title" => $order['title'],
-                        "imageList" => [$goods_image],
-                        "type" => 701,
-                        "tagGroupId" => "tag_group_7272625659887960076"
-                    ]
-                ],
-                "outOrderNo" => $order['order_number'],
-                "totalAmount" => $order['total_amount'] * 100,
-                "orderEntrySchema" => [
-                    "path" => "page/index/index",
-                    "params" => json_encode(['order_number' => $order['order_number']])
-                ],
-                "payNotifyUrl" => config('douyin.payNotifyUrl'),
-            ];
-            $byteAuthorization = self::byteAuthorization(config('douyin.privateKeyStr'), json_encode($data), config('douyin.appId'), self::randStr(10), time(), 1);
-            return ['byteAuthorization'=>$byteAuthorization,'data'=>json_encode($data)];
-        } catch (\Exception $e) {
-            throw new \Exception($e->getMessage());
         }
+        return false;
     }
-
-    public static function cancelOrder($params)
+    // ++++++++++++++++++++ 首次创单 end
+    // ++++++++++++++++++++ 尾款创单
+    /**
+     * 预下单扩展点-子
+     * @param array $params goods_id  user_info.mobile  user_id quantity
+     * @return array|false
+     */
+    public static function tailOrder($params)
     {
-        // $params['order_number']
         Db::startTrans();
         try {
-            $order = DouyinOrder::where('order_number', $params['order_number'])->findOrEmpty();
-            if(!$order->isEmpty()){
-                if($order->order_status == 1 && $order->pay_status == 0){
-                    $order->order_status = 4;
-                    $order->save();
-                }else{
-                    throw new \Exception('订单状态不可取消!');
-                }
+            $amount = bcdiv(bcsub($params['total_amount'] ,$params['discount_amount']), '100', 2)??0;
+            $work_id = DouyinOrder::where(['id'=>$params['douyinOrderId']])->value('work_id');
+            $sn = '';
+            $rechargeOrder = RechargeOrder::where(['work_id'=>$work_id,'payment_type'=>2])->findOrEmpty();
+            if($rechargeOrder->isEmpty()){
+                //新增待支付尾款
+                $sn = generate_sn(RechargeOrder::class, 'sn');
+                $order_data = [
+                    'order_type' => 0,
+                    'sn' => $sn,
+                    'order_terminal' => 1,
+                    'work_id' => $work_id,
+                    'user_id' => $params['user_id'],
+
+                    'payment_type' => 2,
+                    'order_total' => $amount,
+                    'order_amount' => $amount,
+
+                    'pay_status' => 0,
+                    'paid_amount' => 0,
+                    'pay_way' => 4
+                ];
+                RechargeOrder::create($order_data);
             }
             Db::commit();
-            return $order['id'];
+            return $sn;
         } catch (\Exception $e) {
             Db::rollback();
             throw new \Exception($e->getMessage());
         }
     }
-
-    public static function payNotify($params)
+    // 尾款支付成功回调
+    public static function payTailNotify($params)
     {
-            Log::write(json_encode($params));
-            // 查询抖音订单是否完成支付
-            if ($params['status'] === 'SUCCESS') {
-                $transaction_id = $params['order_id']??'';
-                $paid_amount = bcdiv(bcsub($params['total_amount'] ,$params['discount_amount']), '100', 2)??0;
-                $out_order_no = $params['out_order_no'];
-                $pay_time = $params['event_time']??time();
-                $order = DouyinOrder::where('order_number', $out_order_no)->findOrEmpty();
-                if(!$order->isEmpty()){
-                    // 更新充值订单状态
-                    $order->transaction_id = $transaction_id;
-                    $order->order_status = 2;
-                    $order->pay_time = $pay_time;
-                    $order->paid_amount = $paid_amount;
-
-                    $user = User::where('id',$order->user_id)->findOrEmpty()->toArray();
-                    $form_detail = [
-                        'user_name' => $user['real_name']??'',
-                        'mobile' => $user['mobile'],
-                        'transaction_id' => $transaction_id,
-                        'out_trade_no' => $out_order_no,
-                        'paid_amount' => $paid_amount,
-                        'params' => $params,
-                    ];
-                    $consultation = ExternalConsultation::create([
-                        'external_platform_id' => self::$external_platform_id,
-                        'form_detail' => json_encode($form_detail),
-                        'user_name' => $user['real_name']??'',
-                        'mobile' => $user['mobile'],
-                        'goods_id' => $order->goods_id,
-                        'amount' => $paid_amount
-                    ]);
-                    $order->consultation_id = $consultation->id;
-                    $order->save();
-                    return true;
-                }
+        Log::write(json_encode($params));
+        // 查询抖音订单是否完成支付
+        if ($params['status'] === 'SUCCESS') {
+            $transaction_id = $params['order_id']??'';
+            $paid_amount = bcdiv(bcsub($params['total_amount'] ,$params['discount_amount']), '100', 2)??0;
+            $out_order_no = $params['out_order_no'];
+            $pay_time = $params['event_time']??time();
+
+            $rechargeOrder = RechargeOrder::where(['sn'=>$out_order_no,'payment_type'=>2])->findOrEmpty();
+            if(!$rechargeOrder->isEmpty()){
+                // 更新充值订单状态
+                $rechargeOrder->transaction_id = $transaction_id;
+                $rechargeOrder->pay_status = 1;
+                $rechargeOrder->pay_time = $pay_time;
+                $rechargeOrder->paid_amount = $paid_amount;
+                $rechargeOrder->save();
+                return true;
             }
-            return false;
+        }
+        return false;
     }
+
+    // ++++++++++++++++++++ 尾款创单 end
+
+
+    // ******************************** 订单预约/改约
+    // 创建预约单
     public static function reservation($params)
     {
         /*$lon_lat = get_address_lat_lng($params['user_address']);
@@ -441,20 +452,58 @@ class DouYinService
                 if (false === $result) {
                     throw new \Exception('预约失败');
                 }
-                $consultationOrder = ExternalConsultationOrder::where('consultation_id', $order->consultation_id)->where('goods_id', $order->goods_id)->where('amount', $order->paid_amount)
-                    ->findOrEmpty()->toArray();
-                $work_status = ServiceWork::where('id', $consultationOrder['work_id'])->value('work_status');
-                $order->work_id = $consultationOrder['work_id'];
-                $order->fulfillment_status = $work_status;
-                $order->save();
+                if(!empty($result)){
+                    $consultationOrder = ExternalConsultationOrder::where('consultation_id', $order->consultation_id)->where('goods_id', $order->goods_id)->where('amount', $order->paid_amount)
+                        ->findOrEmpty()->toArray();
+                    $work_status = ServiceWork::where('id', $consultationOrder['work_id'])->value('work_status');
+                    $order->work_id = $consultationOrder['work_id'];
+                    $order->fulfillment_status = $work_status;
+                    $order->save();
+                }
             }
             Db::commit();
-            return $order['id'];
+
+            // 抖音创建预约单
+            $url = 'api/apps/trade/v2/book/create_book';
+            $resData = self::toDyRequestUrl($url,self::getCreateBookParams($params));
+
+            //book_id result
+            // 抖音预约接单结果回调
+            $bookurl = 'api/apps/trade/v2/book/book_result_callback';
+            $res = self::toDyRequestUrl($bookurl,[
+                'book_id' => $resData['book_id']??'',
+                'result' => 1,
+            ]);
+
+            return $order['id']??0;
         } catch (\Exception $e) {
             Db::rollback();
             throw new \Exception($e->getMessage());
         }
     }
+    public static function getCreateBookParams($params)
+    {
+        try {
+            $data = [
+                "order_id"=> $params['order_number']??'',
+                "out_book_no"=> time()??'',
+                "open_id"=> $params['open_id']??'',
+                "item_book_info_list" => [
+                    [
+                        "poi_id" => '7511543640776017961',
+                        "shop_name" => '亿蜂快修·武汉市',
+                        "ext_shop_id" => self::EXTERNAL_PLATFORM_ID,
+                        "goods_id" => (string)$params['external_goods_sn'],
+                        "book_start_time" => $params['book_start_time']??'',
+                        "book_end_time" => $params['book_end_time']??'',
+                    ]
+                ]
+            ];
+            return $data;
+        } catch (\Exception $e) {
+            return [];
+        }
+    }
     public static function upReservation($params)
     {
         // $params['order_number']
@@ -478,52 +527,28 @@ class DouYinService
         }
     }
 
-    public static function getOrderDetail($params)
+    // ******************************** 订单退款
+    public static function cancelOrder($params)
     {
-        //抖音订单信息/商品信息/预约信息(地址、时间、履约状态与信息)
-        // $params['order_number']  user_id
-        $order = DouyinOrder::with(['goods','serviceWork','douyinRefundOrder'])->where('order_number', $params['order_number'])->where('user_id', $params['user_id'])->findOrEmpty();
-        if($order->isEmpty()){
-            return [];
-        }
-        $orderInfo = $order->toArray();
-        empty($orderInfo['goods']) && $orderInfo['goods'] = [];
-        empty($orderInfo['serviceWork']) && $orderInfo['serviceWork'] = [];
-        empty($orderInfo['douyinRefundOrder']) && $orderInfo['douyinRefundOrder'] = [];
-
-        $work_status = $orderInfo['serviceWork']['work_status']??0;
-        $performance = [];
-        // tmp
-        switch ($work_status){
-            case 0:
-                $performance[] = ['status' => '待派单','title' => '待派单','time' => date('Y-m-d H:i:s',time())];
-                break;
-            case 1:
-            case 2:
-            case 3:
-                $performance[] = ['status' => '待派单','title' => '待派单','time' => date('Y-m-d H:i:s',time())];
-                $performance[] = ['status' => '已派单','title' => '已派单','time' => date('Y-m-d H:i:s',time())];
-                break;
-            case 4:
-            case 5:
-            case 6:
-                $performance[] = ['status' => '待派单','title' => '待派单','time' => date('Y-m-d H:i:s',time())];
-                $performance[] = ['status' => '已派单','title' => '已派单','time' => date('Y-m-d H:i:s',time())];
-                $performance[] = ['status' => '服务中','title' => '服务中','time' => date('Y-m-d H:i:s',time())];
-                break;
-            case 7:
-            case 8:
-                $performance[] = ['status' => '待派单','title' => '待派单','time' => date('Y-m-d H:i:s',time())];
-                $performance[] = ['status' => '已派单','title' => '已派单','time' => date('Y-m-d H:i:s',time())];
-                $performance[] = ['status' => '服务中','title' => '服务中','time' => date('Y-m-d H:i:s',time())];
-                $performance[] = ['status' => '已完结','title' => '已完结','time' => date('Y-m-d H:i:s',time())];
-                break;
+        // $params['order_number']
+        Db::startTrans();
+        try {
+            $order = DouyinOrder::where('order_number', $params['order_number'])->findOrEmpty();
+            if(!$order->isEmpty()){
+                if($order->order_status == 1 && $order->pay_status == 0){
+                    $order->order_status = 4;
+                    $order->save();
+                }else{
+                    throw new \Exception('订单状态不可取消!');
+                }
+            }
+            Db::commit();
+            return $order['id'];
+        } catch (\Exception $e) {
+            Db::rollback();
+            throw new \Exception($e->getMessage());
         }
-        $orderInfo['performance'] = $performance;
-
-        return $orderInfo;
     }
-
     public static function refund($params)
     {
         Db::startTrans();
@@ -557,7 +582,6 @@ class DouYinService
             throw new \Exception($e->getMessage());
         }
     }
-
     public static function refundExamine($params)
     {
         Db::startTrans();
@@ -612,8 +636,7 @@ class DouYinService
             $douyinRefundOrder = DouyinRefundOrder::where('order_number', $order_number)->order('id', 'desc')->findOrEmpty();
 
             //通过后向抖音申请退款
-            //getClientToken()
-            $url = config('douyin.host').'api/trade_basic/v1/developer/refund_create/';
+            $url = 'api/trade_basic/v1/developer/refund_create/';
             $data = [
                 "order_id" => $orderInfo['transaction_id'],
                 "out_refund_no" => $douyinRefundOrder->refund_number,
@@ -623,7 +646,7 @@ class DouYinService
                     "params" => json_encode(['refund_number'=>$douyinRefundOrder->refund_number])
                 ],
                 "refund_total_amount " => $douyinRefundOrder->refund_amount * 100,
-                "notify_url" => config('douyin.refundNotifyUrl'),
+                //"notify_url" => config('douyin.refundNotifyUrl'),
                 "refund_reason" => [
                     [
                         "code" => 101,
@@ -631,9 +654,9 @@ class DouYinService
                     ]
                 ]
             ];
-            $res = http_request($url,$data,['Content-Type' => 'application/json;charset=utf-8','access_token' => self::getClientToken()]);
-            if(isset($res['err_msg']) && $res['err_msg'] === 'success'){
-                $douyinRefundOrder->transaction_id = $res['data']['refund_id'];
+            $resData = self::toDyRequestUrl($url,$data);
+            if(isset($resData['data']) && $resData['data']){
+                $douyinRefundOrder->transaction_id = $resData['data']['refund_id'];
                 $douyinRefundOrder->save();
             }
             return true;
@@ -673,63 +696,21 @@ class DouYinService
     }
 
 
-
-
-
-    public static function byteAuthorization($privateKeyStr, $data, $appId, $nonceStr, $timestamp, $keyVersion) {
-        $byteAuthorization = '';
-        // 读取私钥
-        $privateKey = openssl_pkey_get_private($privateKeyStr);
-        if (!$privateKey) {
-            throw new \Exception("Invalid private key");
-        }
-        // 生成签名
-        $signature = self::getSignature("POST", "/requestOrder", $timestamp, $nonceStr, $data, $privateKey);
-        if ($signature === false) {
-            return null;
-        }
-        // 构造 byteAuthorization
-        $byteAuthorization = sprintf("SHA256-RSA2048 appid=%s,nonce_str=%s,timestamp=%s,key_version=%s,signature=%s", $appId, $nonceStr, $timestamp, $keyVersion, $signature);
-        return $byteAuthorization;
-    }
-
-    public static function getSignature($method, $url, $timestamp, $nonce, $data, $privateKey) {
-        Log::info("method:{$method}\n url:{$url}\n timestamp:{$timestamp}\n nonce:{$nonce}\n data:{$data}");
-        $targetStr = $method. "\n" . $url. "\n" . $timestamp. "\n" . $nonce. "\n" . $data. "\n";
-        openssl_sign($targetStr, $sign, $privateKey, OPENSSL_ALGO_SHA256);
-        $sign = base64_encode($sign);
-        return $sign;
-    }
-
-    public static function randStr($length = 8) {
-        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
-        $str = '';
-        for ($i = 0; $i < $length; $i++) {
-            $str .= $chars[mt_rand(0, strlen($chars) - 1)];
-        }
-        return $str;
-    }
-    public static function getClientToken($isRefresh = false) {
-        $url = config('douyin.host').'oauth/client_token/';
-        $cache_name = 'dy_client_token';
-        $cache_data = cache($cache_name);
-        if(empty($cache_data) || $cache_data == null || $isRefresh){
-            $data = [
-                'client_key'=> config('douyin.appId'),
-                'client_secret'=> config('douyin.appSecret'),
-                'grant_type'=> "client_credential"
-            ];
-            $res = http_request($url,$data,['Content-Type' => 'application/json;charset=utf-8']);
-            Log::info(json_encode($res));
-            if($res['message'] === 'success'){
-                cache($cache_name, json_encode($res['data']), (time()+$res['data']['expires_in']-1));
-                $cache_data = $res['data'];
-            }
+    public static function toDyRequestUrl($url,$data)
+    {
+        $toData = [
+            'url' => $url,
+            'data' => $data
+        ];
+        $res = http_request(env('internal_api.api_url_host').'platf/dou_yin/toDyRequestUrl',$toData,['Content-Type' => 'application/json;charset=utf-8']);
+        Log::info(json_encode($res));
+        if($res['code'] === 0){
+            Log::info("toDyRequestUrl:".json_encode($res));
+            return $res['data'];
+        }else{
+            Log::info("toDyRequestUrl:".$res['msg']);
+            throw new \Exception($res['msg']);
         }
-        return $cache_data['access_token'];
     }
 
-
-
-
 }