DouYinService.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. <?php
  2. namespace app\api\service;
  3. use app\adminapi\logic\external\ExternalConsultationLogic;
  4. use app\api\logic\ServiceOrderLogic;
  5. use app\common\model\Config;
  6. use app\common\model\external\DouyinOrder;
  7. use app\common\model\external\DouyinRefundOrder;
  8. use app\common\model\external\DouyinUserAuth;
  9. use app\common\model\external\ExternalConsultation;
  10. use app\common\model\external\ExternalConsultationOrder;
  11. use app\common\model\external\ExternalPlatformGoods;
  12. use app\common\model\goods\Goods;
  13. use app\common\model\goods_category\GoodsCategory;
  14. use app\common\model\recharge\RechargeOrder;
  15. use app\common\model\user\User;
  16. use app\common\model\user\UserAuth;
  17. use app\common\model\works\ServiceWork;
  18. use app\common\service\ConfigService;
  19. use app\common\service\FileService;
  20. use think\facade\Db;
  21. use think\facade\Log;
  22. class DouYinService
  23. {
  24. protected static int $terminal = \app\common\enum\user\UserTerminalEnum::DOUYIN;
  25. protected static int $external_platform_id = 6;
  26. protected CONST EXTERNAL_PLATFORM_ID = 6;
  27. // ********************************* 注册登录
  28. public static function register(array $params)
  29. {
  30. $userSn = User::createUserSn();
  31. $params['password'] = !empty($params['password'])?$params['password']:rand(100000,999999);
  32. $passwordSalt = \think\facade\Config::get('project.unique_identification');
  33. $password = create_password($params['password'], $passwordSalt);
  34. $avatar = ConfigService::get('default_image', 'user_avatar');
  35. $user = User::create([
  36. 'sn' => $userSn,
  37. 'avatar' => $avatar,
  38. 'nickname' => '用户' . $userSn,
  39. 'account' => $params['account'],
  40. 'mobile' => !empty($params['mobile'])?$params['mobile']:'',
  41. 'password' => $password,
  42. 'channel' => self::$terminal,
  43. 'user_type' => $params['user_type']??0,
  44. ]);
  45. return $user;
  46. }
  47. public static function phoneLogin(array $params)
  48. {
  49. try {
  50. $where = ['mobile' => $params['mobile']];
  51. $params['account'] = $params['mobile'];
  52. $user = User::where($where)->findOrEmpty();
  53. if ($user->isEmpty()) {
  54. //直接注册用户
  55. $params['channel'] = self::$terminal;
  56. $user = self::register($params);
  57. }
  58. //更新登录信息
  59. $user->login_time = time();
  60. $user->login_ip = request()->ip();
  61. $user->save();
  62. $userInfo = UserTokenService::setToken($user->id, self::$terminal);
  63. //返回登录信息
  64. $avatar = $user->avatar ?: Config::get('project.default_image.user_avatar');
  65. $avatar = FileService::getFileUrl($avatar);
  66. return [
  67. 'nickname' => $userInfo['nickname'],
  68. 'sn' => $userInfo['sn'],
  69. 'mobile' => $userInfo['mobile'],
  70. 'avatar' => $avatar,
  71. 'token' => $userInfo['token'],
  72. ];
  73. } catch (\Exception $e) {
  74. throw new \Exception($e->getMessage());
  75. }
  76. }
  77. public static function getDouyinUserByOpenId(array $openId)
  78. {
  79. try {
  80. $user = DouyinUserAuth::where('openid',$openId)->findOrEmpty();
  81. if ($user->isEmpty()) {
  82. //直接注册用户
  83. $params['channel'] = self::$terminal;
  84. $user = self::register($params);
  85. }
  86. //更新登录信息
  87. $user->login_time = time();
  88. $user->login_ip = request()->ip();
  89. $user->save();
  90. $userInfo = UserTokenService::setToken($user->id, self::$terminal);
  91. //返回登录信息
  92. $avatar = $user->avatar ?: Config::get('project.default_image.user_avatar');
  93. $avatar = FileService::getFileUrl($avatar);
  94. return [
  95. 'nickname' => $userInfo['nickname'],
  96. 'sn' => $userInfo['sn'],
  97. 'mobile' => $userInfo['mobile'],
  98. 'avatar' => $avatar,
  99. 'token' => $userInfo['token'],
  100. ];
  101. } catch (\Exception $e) {
  102. throw new \Exception($e->getMessage());
  103. }
  104. }
  105. // **************************** 商品管理 goods_category_id goods_id external_platform_id
  106. public static function addProduct($params)
  107. {
  108. $send_url = env('internal_api.api_url_host').'platf/dou_yin/addGoods';
  109. $res = http_request($send_url,http_build_query($params));
  110. Log::info('addProduct:'
  111. .'url:'.$send_url
  112. .'|data:'.json_encode($params,JSON_UNESCAPED_UNICODE)
  113. .'|res:'.json_encode([$res],JSON_UNESCAPED_UNICODE)
  114. );
  115. return $res?:[];
  116. }
  117. // ******************************** 订单业务
  118. public static function getOrderDetail($params)
  119. {
  120. //抖音订单信息/商品信息/预约信息(地址、时间、履约状态与信息)
  121. // $params['order_number'] user_id
  122. $order = DouyinOrder::with(['goods','serviceWork','douyinRefundOrder'])->where('order_number', $params['order_number'])->where('user_id', $params['user_id'])->findOrEmpty();
  123. if($order->isEmpty()){
  124. return [];
  125. }
  126. $orderInfo = $order->toArray();
  127. empty($orderInfo['goods']) && $orderInfo['goods'] = [];
  128. empty($orderInfo['serviceWork']) && $orderInfo['serviceWork'] = [];
  129. empty($orderInfo['douyinRefundOrder']) && $orderInfo['douyinRefundOrder'] = [];
  130. $work_status = $orderInfo['serviceWork']['work_status']??0;
  131. $performance = [];
  132. // tmp
  133. switch ($work_status){
  134. case 0:
  135. $performance[] = ['status' => '待派单','title' => '待派单','time' => date('Y-m-d H:i:s',time())];
  136. break;
  137. case 1:
  138. case 2:
  139. case 3:
  140. $performance[] = ['status' => '待派单','title' => '待派单','time' => date('Y-m-d H:i:s',time())];
  141. $performance[] = ['status' => '已派单','title' => '已派单','time' => date('Y-m-d H:i:s',time())];
  142. break;
  143. case 4:
  144. case 5:
  145. case 6:
  146. $performance[] = ['status' => '待派单','title' => '待派单','time' => date('Y-m-d H:i:s',time())];
  147. $performance[] = ['status' => '已派单','title' => '已派单','time' => date('Y-m-d H:i:s',time())];
  148. $performance[] = ['status' => '服务中','title' => '服务中','time' => date('Y-m-d H:i:s',time())];
  149. break;
  150. case 7:
  151. case 8:
  152. $performance[] = ['status' => '待派单','title' => '待派单','time' => date('Y-m-d H:i:s',time())];
  153. $performance[] = ['status' => '已派单','title' => '已派单','time' => date('Y-m-d H:i:s',time())];
  154. $performance[] = ['status' => '服务中','title' => '服务中','time' => date('Y-m-d H:i:s',time())];
  155. $performance[] = ['status' => '已完结','title' => '已完结','time' => date('Y-m-d H:i:s',time())];
  156. break;
  157. }
  158. $orderInfo['performance'] = $performance;
  159. return $orderInfo;
  160. }
  161. // 预下单接口 - 前端 首次/尾款
  162. public static function getPluginCreateOrderData($goods_id, $quantity, $douyinOrderId,$params)
  163. {
  164. try {
  165. $goods = Goods::where('id',$goods_id)->findOrEmpty();
  166. if($goods->isEmpty()){
  167. throw new \Exception('商品不存在!');
  168. }
  169. $goods = $goods->toArray();
  170. $platformGoods = ExternalPlatformGoods::where('goods_id', $goods_id)->where('external_platform_id', self::EXTERNAL_PLATFORM_ID)->findOrEmpty();
  171. if($platformGoods->isEmpty()){
  172. throw new \Exception('外部商品不存在!');
  173. }
  174. $platformGoods = $platformGoods->toArray();
  175. $quantity = $quantity?:1;
  176. $appointment_time = strtotime($params['appointment_time']??date('Y-m-d H:i:s',time()));
  177. $bookStartTime = $appointment_time * 1000;
  178. $bookEndTime = ($appointment_time + (2 * 86400)) * 1000;
  179. $data = [
  180. /*"goodsList" => [
  181. "quantity" => $quantity,
  182. "price" => $platformGoods['service_fee'] * 100,
  183. "goodsName" => $goods['goods_name'],
  184. "goodsPhoto" => $goods['goods_image']??'',
  185. "goodsId" => '',
  186. "goodsType" => 1
  187. ],*/
  188. "skuList" => [
  189. [
  190. "quantity" => (int)$quantity,
  191. "skuId" => (string)$platformGoods['external_goods_sn'],
  192. "skuType" => 1, // 1:商品库商品 2:非商品库商品(融合预约品走加价时,固定传2)
  193. "price" => $platformGoods['service_fee'] * 100,
  194. ]
  195. ],
  196. "bookInfo" => [
  197. "itemBookInfoList"=>[
  198. [
  199. "poiId" => '7511543640776017961',
  200. "shopName" => '亿蜂快修·武汉市',
  201. "outShopId" => (string)self::EXTERNAL_PLATFORM_ID,
  202. "skuId" => (string)$platformGoods['external_goods_sn'],
  203. "bookStartTime" => $bookStartTime?:'',
  204. "bookEndTime" => $bookEndTime?:'',
  205. ]
  206. ]
  207. ],
  208. "payment" => [
  209. "totalAmount" => $quantity * $platformGoods['service_fee'] * 100,
  210. ],
  211. //"callbackUrl" => $callbackUrl?:'',
  212. "callbackData" => [
  213. "outShopId" => self::EXTERNAL_PLATFORM_ID,
  214. "skuId" => (string)$platformGoods['external_goods_sn'],
  215. "quantity" => $quantity,
  216. "user_id" => $params['user_id'],
  217. "douyinOrderId" => $douyinOrderId?:0,
  218. ],
  219. /*"tradeOption" => json_encode([
  220. "life_trade_flag" => 1,
  221. "order_relation_info" => [
  222. "related_order_id" => 0, // 加价时上个订单号
  223. "relation_type" => 'multi_buy_as_one'
  224. ]
  225. ])*/
  226. ];
  227. if($douyinOrderId){ // 说明是来自首单订单即要创建尾款
  228. $data['callbackUrl'] = env('douyin.pay_tail_notify_url')??'';
  229. $data['tradeOption'] = [
  230. "life_trade_flag" => 1,
  231. "order_relation_info" => [
  232. "related_order_id" => $douyinOrderId?:0, // 加价时上个订单号
  233. "relation_type" => 'multi_buy_as_one'
  234. ]
  235. ];
  236. }
  237. /*else{ // 创建首单订单
  238. $data['callbackUrl'] = '';
  239. }*/
  240. return $data;
  241. } catch (\Exception $e) {
  242. throw new \Exception($e->getMessage());
  243. }
  244. }
  245. // ++++++++++++++++++++ 首次/尾款 扩展点
  246. /**
  247. * 预下单扩展点
  248. * @param $params
  249. * @throws \Exception
  250. * @author liugc <466014217@qq.com>
  251. * @date 2025/6/4 14:03
  252. */
  253. public static function submitOrderNotify($params = [])
  254. {
  255. try {
  256. $params['external_platform_id'] = self::EXTERNAL_PLATFORM_ID;
  257. // order_id goods total_amount discount cp_extra create_order_time phone_num contact_name open_id
  258. $user_id = $params['cp_extra']['user_id'];
  259. $user = User::where('id',$user_id)->findOrEmpty();
  260. if($params['cp_extra']['douyinOrderId']>0){ // 说明是尾款单
  261. // 创建尾款单
  262. $order_number = self::tailOrder([
  263. 'douyinOrderId'=>$params['cp_extra']['douyinOrderId'],
  264. 'user_id'=>$user_id??0,
  265. 'total_amount'=>$params['total_amount'],
  266. 'discount_amount'=>$params['discount_amount'],
  267. ]);
  268. //$payNotifyUrl = 'payTailNotify';
  269. }else{
  270. // 创建首单 goods_id user_info.mobile user_id quantity
  271. $order_number = self::submitOrder([
  272. 'open_id'=>$params['open_id'],
  273. 'order_id'=>$params['order_id'], // 抖音订单号
  274. 'goods_id'=>$params['cp_extra']['skuId'],
  275. 'user_id'=>$user_id??0,
  276. 'mobile'=>$user['mobile']??'',
  277. 'quantity'=>$params['cp_extra']['quantity']
  278. ]);
  279. }
  280. return [
  281. "outOrderNo" => $order_number,
  282. "orderEntrySchema" => [
  283. "path" => "page/index/index",
  284. "params" => json_encode(['order_number' => $order_number])
  285. ],
  286. "payNotifyUrl" => $payNotifyUrl??''
  287. ];
  288. } catch (\Exception $e) {
  289. throw new \Exception($e->getMessage());
  290. }
  291. }
  292. // ++++++++++++++++++++ 首次创单
  293. /**
  294. * 预下单扩展点-子
  295. * @param array $params goods_id user_info.mobile user_id quantity
  296. * @return array|false
  297. */
  298. public static function submitOrder($params)
  299. {
  300. Db::startTrans();
  301. try {
  302. $goods = Goods::findOrEmpty($params['goods_id']);
  303. if($goods->isEmpty()){
  304. throw new \Exception('产品不存在!');
  305. }
  306. $platformGoods = ExternalPlatformGoods::where('goods_id', $params['goods_id'])->where('external_platform_id', self::EXTERNAL_PLATFORM_ID)->findOrEmpty();
  307. if($platformGoods->isEmpty()){
  308. throw new \Exception('产品不存在!');
  309. }
  310. if(empty($params['mobile'])){
  311. throw new \Exception('请先补充您的联系方式后在提交订单');
  312. }
  313. // TODO tmp防抖1m
  314. $isExist = DouyinOrder::where(['user_id'=>$params['user_id'],'goods_id'=>$goods['id']])->where('create_time','>',(time() - 60))->findOrEmpty();
  315. if(!$isExist->isEmpty()){
  316. throw new \Exception('请勿重复下单!');
  317. }
  318. $quantity = $params['quantity']??1;
  319. //生成订单
  320. $create_data = [
  321. 'user_id' => $params['user_id'],
  322. 'mobile' => $params['mobile'],
  323. 'open_id' => $params['open_id'],
  324. 'goods_id'=>$goods['id'],
  325. 'title' => $goods['goods_name'],
  326. 'unit_price' => $platformGoods['service_fee'],
  327. 'quantity' => $quantity,
  328. 'total_amount' => $platformGoods['service_fee'] * $quantity,
  329. 'dy_order_id' => $params['order_id']??'',
  330. 'order_number' => generate_sn(DouyinOrder::class, 'order_number'),
  331. ];
  332. $order = DouyinOrder::create($create_data);
  333. Db::commit();
  334. return $create_data['order_number'];
  335. } catch (\Exception $e) {
  336. Db::rollback();
  337. throw new \Exception($e->getMessage());
  338. }
  339. }
  340. // 支付成功回调
  341. public static function payNotify($params)
  342. {
  343. Log::write(json_encode($params));
  344. // 查询抖音订单是否完成支付
  345. if ($params['status'] === 'SUCCESS') {
  346. $transaction_id = $params['order_id']??'';
  347. $paid_amount = bcdiv(bcsub($params['total_amount'] ,$params['discount_amount']), '100', 2)??0;
  348. $out_order_no = $params['out_order_no'];
  349. $pay_time = $params['event_time']??time();
  350. $order = DouyinOrder::where('order_number', $out_order_no)->findOrEmpty();
  351. if(!$order->isEmpty()){
  352. // 更新充值订单状态
  353. $order->transaction_id = $transaction_id;
  354. $order->order_status = 2;
  355. $order->pay_time = $pay_time;
  356. $order->paid_amount = $paid_amount;
  357. $user = User::where('id',$order->user_id)->findOrEmpty()->toArray();
  358. $form_detail = [
  359. 'user_name' => $user['real_name']??'',
  360. 'mobile' => $user['mobile'],
  361. 'transaction_id' => $transaction_id,
  362. 'out_trade_no' => $out_order_no,
  363. 'paid_amount' => $paid_amount,
  364. 'params' => $params,
  365. ];
  366. $consultation = ExternalConsultation::create([
  367. 'external_platform_id' => self::EXTERNAL_PLATFORM_ID,
  368. 'form_detail' => json_encode($form_detail),
  369. 'user_name' => $user['real_name']??'',
  370. 'mobile' => $user['mobile'],
  371. 'goods_id' => $order->goods_id,
  372. 'amount' => $paid_amount
  373. ]);
  374. $order->consultation_id = $consultation->id;
  375. $order->save();
  376. return true;
  377. }
  378. }
  379. return false;
  380. }
  381. // ++++++++++++++++++++ 首次创单 end
  382. // ++++++++++++++++++++ 尾款创单
  383. /**
  384. * 预下单扩展点-子
  385. * @param array $params goods_id user_info.mobile user_id quantity
  386. * @return array|false
  387. */
  388. public static function tailOrder($params)
  389. {
  390. Db::startTrans();
  391. try {
  392. $amount = bcdiv(bcsub($params['total_amount'] ,$params['discount_amount']), '100', 2)??0;
  393. $work_id = DouyinOrder::where(['id'=>$params['douyinOrderId']])->value('work_id');
  394. $sn = '';
  395. $rechargeOrder = RechargeOrder::where(['work_id'=>$work_id,'payment_type'=>2])->findOrEmpty();
  396. if($rechargeOrder->isEmpty()){
  397. //新增待支付尾款
  398. $sn = generate_sn(RechargeOrder::class, 'sn');
  399. $order_data = [
  400. 'order_type' => 0,
  401. 'sn' => $sn,
  402. 'order_terminal' => 1,
  403. 'work_id' => $work_id,
  404. 'user_id' => $params['user_id'],
  405. 'payment_type' => 2,
  406. 'order_total' => $amount,
  407. 'order_amount' => $amount,
  408. 'pay_status' => 0,
  409. 'paid_amount' => 0,
  410. 'pay_way' => 4
  411. ];
  412. RechargeOrder::create($order_data);
  413. }
  414. Db::commit();
  415. return $sn;
  416. } catch (\Exception $e) {
  417. Db::rollback();
  418. throw new \Exception($e->getMessage());
  419. }
  420. }
  421. // 尾款支付成功回调
  422. public static function payTailNotify($params)
  423. {
  424. Log::write(json_encode($params));
  425. // 查询抖音订单是否完成支付
  426. if ($params['status'] === 'SUCCESS') {
  427. $transaction_id = $params['order_id']??'';
  428. $paid_amount = bcdiv(bcsub($params['total_amount'] ,$params['discount_amount']), '100', 2)??0;
  429. $out_order_no = $params['out_order_no'];
  430. $pay_time = $params['event_time']??time();
  431. $rechargeOrder = RechargeOrder::where(['sn'=>$out_order_no,'payment_type'=>2])->findOrEmpty();
  432. if(!$rechargeOrder->isEmpty()){
  433. // 更新充值订单状态
  434. $rechargeOrder->transaction_id = $transaction_id;
  435. $rechargeOrder->pay_status = 1;
  436. $rechargeOrder->pay_time = $pay_time;
  437. $rechargeOrder->paid_amount = $paid_amount;
  438. $rechargeOrder->save();
  439. return true;
  440. }
  441. }
  442. return false;
  443. }
  444. // ++++++++++++++++++++ 尾款创单 end
  445. // ******************************** 订单预约/改约
  446. // 创建预约单
  447. public static function reservation($params)
  448. {
  449. /*$lon_lat = get_address_lat_lng($params['user_address']);
  450. $params['lon'] = $lon_lat['lon'];
  451. $params['lat'] = $lon_lat['lat'];*/
  452. // $params['order_number']
  453. Db::startTrans();
  454. try {
  455. $order = DouyinOrder::where('order_number', $params['order_number'])->findOrEmpty();
  456. if(!$order->isEmpty()){
  457. $consultation = ExternalConsultation::where('id', $order->consultation_id)->findOrEmpty()->toArray();
  458. $consultation['user_name'] = $params['user_name']??$consultation['user_name'];
  459. $consultation['mobile'] = $params['mobile']??$consultation['mobile'];
  460. $consultation['user_address'] = $params['user_address'];
  461. $consultation['lon'] = $params['lon'];
  462. $consultation['lat'] = $params['lat'];
  463. $consultation['appointment_time'] = $params['appointment_time'];
  464. $consultationOrderId = ExternalConsultationLogic::order($consultation);
  465. if (false === $consultationOrderId) {
  466. throw new \Exception('预约失败');
  467. }
  468. if(!empty($consultationOrderId)){
  469. $consultationOrder = ExternalConsultationOrder::where('id', $consultationOrderId)->findOrEmpty()->toArray();
  470. $work_status = ServiceWork::where('id', $consultationOrder['work_id'])->value('work_status');
  471. $order->work_id = $consultationOrder['work_id'];
  472. $order->fulfillment_status = $work_status;
  473. $order->save();
  474. }
  475. $params['dy_order_id'] = $order->dy_order_id;
  476. $params['consultationOrderId'] = $consultationOrderId;
  477. $params['open_id'] = $order->open_id;
  478. $params['goods_id'] = $order->goods_id;
  479. }
  480. Db::commit();
  481. // 抖音创建预约单
  482. $url = 'api/apps/trade/v2/book/create_book';
  483. $resData = self::toDyRequestUrl($url,self::getCreateBookParams($params));
  484. //book_id result
  485. // 抖音预约接单结果回调
  486. $bookurl = 'api/apps/trade/v2/book/book_result_callback';
  487. $res = self::toDyRequestUrl($bookurl,[
  488. 'book_id' => $resData['book_id']??'',
  489. 'result' => 1,
  490. ]);
  491. return $order['id']??0;
  492. } catch (\Exception $e) {
  493. Db::rollback();
  494. throw new \Exception($e->getMessage());
  495. }
  496. }
  497. public static function getCreateBookParams($params)
  498. {
  499. try {
  500. $platformGoods = ExternalPlatformGoods::where('goods_id', $params['goods_id'])->where('external_platform_id', self::EXTERNAL_PLATFORM_ID)->findOrEmpty();
  501. if($platformGoods->isEmpty()){
  502. throw new \Exception('产品不存在!');
  503. }
  504. $appointment_time = strtotime($params['appointment_time']);
  505. $book_start_time = $appointment_time * 1000;
  506. $book_end_time = ($appointment_time + (2 * 86400)) * 1000;
  507. $data = [
  508. "order_id"=> (string)$params['dy_order_id']??'',
  509. "out_book_no"=> (string)$params['consultationOrderId']??'',
  510. "open_id"=> (string)$params['open_id']??'',
  511. "item_book_info_list" => [
  512. [
  513. "poi_id" => '7511543640776017961',
  514. "shop_name" => '亿蜂快修·武汉市',
  515. "ext_shop_id" => (string)self::EXTERNAL_PLATFORM_ID,
  516. "goods_id" => (string)$platformGoods->external_goods_sn,
  517. "book_start_time" => (int)$book_start_time??0,
  518. "book_end_time" => (int)$book_end_time??0,
  519. ]
  520. ]
  521. ];
  522. return $data;
  523. } catch (\Exception $e) {
  524. return [];
  525. }
  526. }
  527. public static function upReservation($params)
  528. {
  529. // $params['order_number']
  530. Db::startTrans();
  531. try {
  532. $order = DouyinOrder::where('order_number', $params['order_number'])->findOrEmpty();
  533. if(!$order->isEmpty()){
  534. // sn appointment_time
  535. $result = ServiceOrderLogic::approvalChangeAppointment(['sn'=>RechargeOrder::where('work_id', $order->work_id)->value('sn'),'appointment_time'=>$params['appointment_time']]);
  536. if (false === $result) {
  537. throw new \Exception(ServiceOrderLogic::getError());
  538. }
  539. $order->fulfillment_status = ServiceWork::where('id', $order->work_id)->value('work_status');
  540. $order->save();
  541. }
  542. Db::commit();
  543. return $order['id'];
  544. } catch (\Exception $e) {
  545. Db::rollback();
  546. throw new \Exception($e->getMessage());
  547. }
  548. }
  549. // ******************************** 订单退款
  550. public static function cancelOrder($params)
  551. {
  552. // $params['order_number']
  553. Db::startTrans();
  554. try {
  555. $order = DouyinOrder::where('order_number', $params['order_number'])->findOrEmpty();
  556. if(!$order->isEmpty()){
  557. if($order->order_status == 1 && $order->pay_status == 0){
  558. $order->order_status = 4;
  559. $order->save();
  560. }else{
  561. throw new \Exception('订单状态不可取消!');
  562. }
  563. }
  564. Db::commit();
  565. return $order['id'];
  566. } catch (\Exception $e) {
  567. Db::rollback();
  568. throw new \Exception($e->getMessage());
  569. }
  570. }
  571. public static function refund($params)
  572. {
  573. Db::startTrans();
  574. try {
  575. // $params['order_number'] user_id
  576. $order = DouyinOrder::with(['goods','serviceWork'])->where('order_number', $params['order_number'])->where('user_id', $params['user_id'])->findOrEmpty();
  577. if($order->isEmpty()){
  578. throw new \Exception('订单不存在');
  579. }
  580. $orderInfo = $order->toArray();
  581. $work_status = $orderInfo['serviceWork']['work_status']??0;
  582. if(3 < $work_status){
  583. throw new \Exception('该订单禁止退款');
  584. }
  585. DouyinRefundOrder::create([
  586. 'refund_number' => generate_sn(DouyinRefundOrder::class, 'refund_number'),
  587. 'order_number' => $orderInfo['order_number'],
  588. 'transaction_id' => $orderInfo['transaction_id'],
  589. 'reason' => $params['reason']??'',
  590. 'refund_status' => 0,
  591. 'user_id' => $orderInfo['user_id'],
  592. 'refund_amount' => $orderInfo['paid_amount'],
  593. ]);
  594. Db::commit();
  595. return true;
  596. } catch (\Exception $e) {
  597. Db::rollback();
  598. throw new \Exception($e->getMessage());
  599. }
  600. }
  601. public static function refundExamine($params)
  602. {
  603. Db::startTrans();
  604. try {
  605. // $params['order_number']
  606. $order = DouyinOrder::with(['goods','serviceWork'])->where('order_number', $params['order_number'])->findOrEmpty();
  607. if($order->isEmpty()){
  608. throw new \Exception('订单不存在');
  609. }
  610. $orderInfo = $order->toArray();
  611. //$refund_number = $params['refund_number']??'';
  612. $douyinRefundOrder = DouyinRefundOrder::where('order_number', $params['order_number'])->order('id', 'desc')->findOrEmpty();
  613. if($params['is_examine_ok'] === 'pass'){
  614. $douyinRefundOrder->refund_status = 2;
  615. RechargeOrder::where('work_id', $orderInfo['work_id'])->update([
  616. 'pay_status' => 2,
  617. 'pay_time' => 0,
  618. 'paid_amount' => 0,
  619. ]);
  620. ServiceWork::where('id', $orderInfo['work_id'])->update([
  621. 'work_status' => 0,
  622. 'user_confirm_status' => 0,
  623. 'service_status' => 4,
  624. 'work_pay_status' => 0
  625. ]);
  626. }else{
  627. $douyinRefundOrder->refund_status = 1;
  628. }
  629. $douyinRefundOrder->save();
  630. Db::commit();
  631. if($params['is_examine_ok'] === 'pass'){
  632. //通过后向抖音申请退款
  633. self::sendRefundCreate($params['order_number']);
  634. }
  635. return true;
  636. } catch (\Exception $e) {
  637. Db::rollback();
  638. throw new \Exception($e->getMessage());
  639. }
  640. }
  641. public static function sendRefundCreate($order_number)
  642. {
  643. try {
  644. // $params['order_number']
  645. $order = DouyinOrder::with(['goods','serviceWork'])->where('order_number', $order_number)->findOrEmpty();
  646. if($order->isEmpty()){
  647. throw new \Exception('订单不存在');
  648. }
  649. $orderInfo = $order->toArray();
  650. $douyinRefundOrder = DouyinRefundOrder::where('order_number', $order_number)->order('id', 'desc')->findOrEmpty();
  651. //通过后向抖音申请退款
  652. $url = 'api/trade_basic/v1/developer/refund_create/';
  653. $data = [
  654. "order_id" => $orderInfo['transaction_id'],
  655. "out_refund_no" => $douyinRefundOrder->refund_number,
  656. "cp_extra" => $orderInfo['id'].'|'.$douyinRefundOrder->id,
  657. "order_entry_schema" => [
  658. "path" => "page/index/index",
  659. "params" => json_encode(['refund_number'=>$douyinRefundOrder->refund_number])
  660. ],
  661. "refund_total_amount " => $douyinRefundOrder->refund_amount * 100,
  662. //"notify_url" => config('douyin.refundNotifyUrl'),
  663. "refund_reason" => [
  664. [
  665. "code" => 101,
  666. "text" => "不想要了"
  667. ]
  668. ]
  669. ];
  670. $resData = self::toDyRequestUrl($url,$data);
  671. if(isset($resData['data']) && $resData['data']){
  672. $douyinRefundOrder->transaction_id = $resData['data']['refund_id'];
  673. $douyinRefundOrder->save();
  674. }
  675. return true;
  676. } catch (\Exception $e) {
  677. Log::info($e->getMessage());
  678. return false;
  679. }
  680. }
  681. public static function refundNotify($params)
  682. {
  683. Db::startTrans();
  684. try {
  685. $douyinRefundOrder = DouyinRefundOrder::where('refund_number', $params['out_refund_no'])->findOrEmpty();
  686. if($douyinRefundOrder->isEmpty()){
  687. throw new \Exception('退款订单不存在');
  688. }
  689. if($douyinRefundOrder->refund_status == 0){
  690. if($params['status'] === 'SUCCESS'){
  691. $douyinRefundOrder->refund_status = 3;
  692. DouyinOrder::where('order_number', $douyinRefundOrder->order_number)->update([
  693. 'order_status' => 4,
  694. 'pay_status' => 3,
  695. ]);
  696. }elseif($params['status'] === 'FAIL'){
  697. $douyinRefundOrder->refund_status = 4;
  698. }else{
  699. throw new \Exception('退款状态未知');
  700. }
  701. $douyinRefundOrder->save();
  702. }
  703. Db::commit();
  704. return true;
  705. } catch (\Exception $e) {
  706. Db::rollback();
  707. throw new \Exception($e->getMessage());
  708. }
  709. }
  710. public static function toDyRequestUrl($url,$data,$headers = [],$resFunction = 'extraErrorCodeReturn',$isHost = 0)
  711. {
  712. $toData = [
  713. 'url' => $url,
  714. 'data' => $data,
  715. 'headers' => $headers,
  716. 'resFunction' => $resFunction,
  717. 'isHost' => $isHost
  718. ];
  719. $res = http_request(env('internal_api.api_url_host').'platf/dou_yin/toDyRequestUrl',$toData,['Content-Type' => 'application/json;charset=utf-8']);
  720. Log::info(json_encode($res));
  721. if($res['code'] === 0){
  722. Log::info("toDyRequestUrl:".json_encode($res));
  723. return $res['data'];
  724. }else{
  725. Log::info("toDyRequestUrl:".$res['msg']);
  726. throw new \Exception($res['msg']);
  727. }
  728. }
  729. }