DouYinService.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  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. $data = [
  177. /*"goodsList" => [
  178. "quantity" => $quantity,
  179. "price" => $platformGoods['service_fee'] * 100,
  180. "goodsName" => $goods['goods_name'],
  181. "goodsPhoto" => $goods['goods_image']??'',
  182. "goodsId" => '',
  183. "goodsType" => 1
  184. ],*/
  185. "skuList" => [
  186. [
  187. "quantity" => $quantity,
  188. "skuId" => (string)$platformGoods['external_goods_sn'],
  189. "skuType" => 1, // 1:商品库商品 2:非商品库商品(融合预约品走加价时,固定传2)
  190. "price" => $platformGoods['service_fee'] * 100,
  191. ]
  192. ],
  193. /*"bookInfo" => [
  194. "itemBookInfoList"=>[
  195. [
  196. "poiId" => '7511543640776017961',
  197. "shopName" => '亿蜂快修·武汉市',
  198. "outShopId" => self::EXTERNAL_PLATFORM_ID,
  199. "skuId" => (string)$platformGoods['external_goods_sn'],
  200. "bookStartTime" => $bookStartTime?:'',
  201. "bookEndTime" => $bookEndTime?:'',
  202. ]
  203. ]
  204. ],*/
  205. "payment" => [
  206. "totalAmount" => $quantity * $platformGoods['service_fee'] * 100,
  207. ],
  208. //"callbackUrl" => $callbackUrl?:'',
  209. "callbackData" => [
  210. "outShopId" => self::EXTERNAL_PLATFORM_ID,
  211. "skuId" => (string)$platformGoods['external_goods_sn'],
  212. "quantity" => $quantity,
  213. "user_id" => $params['user_id'],
  214. "douyinOrderId" => $douyinOrderId?:0,
  215. ],
  216. /*"tradeOption" => json_encode([
  217. "life_trade_flag" => 1,
  218. "order_relation_info" => [
  219. "related_order_id" => 0, // 加价时上个订单号
  220. "relation_type" => 'multi_buy_as_one'
  221. ]
  222. ])*/
  223. ];
  224. if($douyinOrderId){ // 说明是来自首单订单即要创建尾款
  225. $data['callbackUrl'] = '';
  226. $data['tradeOption'] = [
  227. "life_trade_flag" => 1,
  228. "order_relation_info" => [
  229. "related_order_id" => '', // 加价时上个订单号
  230. "relation_type" => 'multi_buy_as_one'
  231. ]
  232. ];
  233. }
  234. /*else{ // 创建首单订单
  235. $data['callbackUrl'] = '';
  236. }*/
  237. return $data;
  238. } catch (\Exception $e) {
  239. throw new \Exception($e->getMessage());
  240. }
  241. }
  242. // ++++++++++++++++++++ 首次/尾款 扩展点
  243. /**
  244. * 预下单扩展点
  245. * @param $params
  246. * @throws \Exception
  247. * @author liugc <466014217@qq.com>
  248. * @date 2025/6/4 14:03
  249. */
  250. public static function submitOrderNotify($params = [])
  251. {
  252. try {
  253. $params['external_platform_id'] = self::EXTERNAL_PLATFORM_ID;
  254. // order_id goods total_amount discount cp_extra create_order_time phone_num contact_name open_id
  255. $user_id = $params['cp_extra']['user_id'];
  256. $user = User::where('id',$user_id)->findOrEmpty();
  257. if($params['cp_extra']['douyinOrderId']>0){ // 说明是尾款单
  258. // 创建尾款单
  259. $order_number = self::tailOrder([
  260. 'douyinOrderId'=>$params['cp_extra']['douyinOrderId'],
  261. 'user_id'=>$user_id??0,
  262. 'total_amount'=>$params['total_amount'],
  263. 'discount_amount'=>$params['discount_amount'],
  264. ]);
  265. //$payNotifyUrl = 'payTailNotify';
  266. }else{
  267. // 创建首单 goods_id user_info.mobile user_id quantity
  268. $order_number = self::submitOrder([
  269. 'open_id'=>$params['open_id'],
  270. 'order_id'=>$params['order_id'], // 抖音订单号
  271. 'goods_id'=>$params['cp_extra']['skuId'],
  272. 'user_id'=>$user_id??0,
  273. 'mobile'=>$user['mobile']??'',
  274. 'quantity'=>$params['cp_extra']['quantity']
  275. ]);
  276. }
  277. return [
  278. "outOrderNo" => $order_number,
  279. "orderEntrySchema" => [
  280. "path" => "page/index/index",
  281. "params" => json_encode(['order_number' => $order_number])
  282. ],
  283. "payNotifyUrl" => $payNotifyUrl??''
  284. ];
  285. } catch (\Exception $e) {
  286. throw new \Exception($e->getMessage());
  287. }
  288. }
  289. // ++++++++++++++++++++ 首次创单
  290. /**
  291. * 预下单扩展点-子
  292. * @param array $params goods_id user_info.mobile user_id quantity
  293. * @return array|false
  294. */
  295. public static function submitOrder($params)
  296. {
  297. Db::startTrans();
  298. try {
  299. $goods = Goods::findOrEmpty($params['goods_id']);
  300. if($goods->isEmpty()){
  301. throw new \Exception('产品不存在!');
  302. }
  303. $platformGoods = ExternalPlatformGoods::where('goods_id', $params['goods_id'])->where('external_platform_id', self::EXTERNAL_PLATFORM_ID)->findOrEmpty();
  304. if($platformGoods->isEmpty()){
  305. throw new \Exception('产品不存在!');
  306. }
  307. if(empty($params['mobile'])){
  308. throw new \Exception('请先补充您的联系方式后在提交订单');
  309. }
  310. // TODO tmp防抖1m
  311. $isExist = DouyinOrder::where(['user_id'=>$params['user_id'],'goods_id'=>$goods['id']])->where('create_time','>',(time() - 60))->findOrEmpty();
  312. if(!$isExist->isEmpty()){
  313. throw new \Exception('请勿重复下单!');
  314. }
  315. $quantity = $params['quantity']??1;
  316. //生成订单
  317. $create_data = [
  318. 'user_id' => $params['user_id'],
  319. 'mobile' => $params['mobile'],
  320. 'open_id' => $params['open_id'],
  321. 'goods_id'=>$goods['id'],
  322. 'title' => $goods['goods_name'],
  323. 'unit_price' => $platformGoods['service_fee'],
  324. 'quantity' => $quantity,
  325. 'total_amount' => $platformGoods['service_fee'] * $quantity,
  326. 'dy_order_id' => $params['order_id']??'',
  327. 'order_number' => generate_sn(DouyinOrder::class, 'order_number'),
  328. ];
  329. $order = DouyinOrder::create($create_data);
  330. Db::commit();
  331. return $create_data['order_number'];
  332. } catch (\Exception $e) {
  333. Db::rollback();
  334. throw new \Exception($e->getMessage());
  335. }
  336. }
  337. // 支付成功回调
  338. public static function payNotify($params)
  339. {
  340. Log::write(json_encode($params));
  341. // 查询抖音订单是否完成支付
  342. if ($params['status'] === 'SUCCESS') {
  343. $transaction_id = $params['order_id']??'';
  344. $paid_amount = bcdiv(bcsub($params['total_amount'] ,$params['discount_amount']), '100', 2)??0;
  345. $out_order_no = $params['out_order_no'];
  346. $pay_time = $params['event_time']??time();
  347. $order = DouyinOrder::where('order_number', $out_order_no)->findOrEmpty();
  348. if(!$order->isEmpty()){
  349. // 更新充值订单状态
  350. $order->transaction_id = $transaction_id;
  351. $order->order_status = 2;
  352. $order->pay_time = $pay_time;
  353. $order->paid_amount = $paid_amount;
  354. $user = User::where('id',$order->user_id)->findOrEmpty()->toArray();
  355. $form_detail = [
  356. 'user_name' => $user['real_name']??'',
  357. 'mobile' => $user['mobile'],
  358. 'transaction_id' => $transaction_id,
  359. 'out_trade_no' => $out_order_no,
  360. 'paid_amount' => $paid_amount,
  361. 'params' => $params,
  362. ];
  363. $consultation = ExternalConsultation::create([
  364. 'external_platform_id' => self::EXTERNAL_PLATFORM_ID,
  365. 'form_detail' => json_encode($form_detail),
  366. 'user_name' => $user['real_name']??'',
  367. 'mobile' => $user['mobile'],
  368. 'goods_id' => $order->goods_id,
  369. 'amount' => $paid_amount
  370. ]);
  371. $order->consultation_id = $consultation->id;
  372. $order->save();
  373. return true;
  374. }
  375. }
  376. return false;
  377. }
  378. // ++++++++++++++++++++ 首次创单 end
  379. // ++++++++++++++++++++ 尾款创单
  380. /**
  381. * 预下单扩展点-子
  382. * @param array $params goods_id user_info.mobile user_id quantity
  383. * @return array|false
  384. */
  385. public static function tailOrder($params)
  386. {
  387. Db::startTrans();
  388. try {
  389. $amount = bcdiv(bcsub($params['total_amount'] ,$params['discount_amount']), '100', 2)??0;
  390. $work_id = DouyinOrder::where(['id'=>$params['douyinOrderId']])->value('work_id');
  391. $sn = '';
  392. $rechargeOrder = RechargeOrder::where(['work_id'=>$work_id,'payment_type'=>2])->findOrEmpty();
  393. if($rechargeOrder->isEmpty()){
  394. //新增待支付尾款
  395. $sn = generate_sn(RechargeOrder::class, 'sn');
  396. $order_data = [
  397. 'order_type' => 0,
  398. 'sn' => $sn,
  399. 'order_terminal' => 1,
  400. 'work_id' => $work_id,
  401. 'user_id' => $params['user_id'],
  402. 'payment_type' => 2,
  403. 'order_total' => $amount,
  404. 'order_amount' => $amount,
  405. 'pay_status' => 0,
  406. 'paid_amount' => 0,
  407. 'pay_way' => 4
  408. ];
  409. RechargeOrder::create($order_data);
  410. }
  411. Db::commit();
  412. return $sn;
  413. } catch (\Exception $e) {
  414. Db::rollback();
  415. throw new \Exception($e->getMessage());
  416. }
  417. }
  418. // 尾款支付成功回调
  419. public static function payTailNotify($params)
  420. {
  421. Log::write(json_encode($params));
  422. // 查询抖音订单是否完成支付
  423. if ($params['status'] === 'SUCCESS') {
  424. $transaction_id = $params['order_id']??'';
  425. $paid_amount = bcdiv(bcsub($params['total_amount'] ,$params['discount_amount']), '100', 2)??0;
  426. $out_order_no = $params['out_order_no'];
  427. $pay_time = $params['event_time']??time();
  428. $rechargeOrder = RechargeOrder::where(['sn'=>$out_order_no,'payment_type'=>2])->findOrEmpty();
  429. if(!$rechargeOrder->isEmpty()){
  430. // 更新充值订单状态
  431. $rechargeOrder->transaction_id = $transaction_id;
  432. $rechargeOrder->pay_status = 1;
  433. $rechargeOrder->pay_time = $pay_time;
  434. $rechargeOrder->paid_amount = $paid_amount;
  435. $rechargeOrder->save();
  436. return true;
  437. }
  438. }
  439. return false;
  440. }
  441. // ++++++++++++++++++++ 尾款创单 end
  442. // ******************************** 订单预约/改约
  443. // 创建预约单
  444. public static function reservation($params)
  445. {
  446. /*$lon_lat = get_address_lat_lng($params['user_address']);
  447. $params['lon'] = $lon_lat['lon'];
  448. $params['lat'] = $lon_lat['lat'];*/
  449. // $params['order_number']
  450. Db::startTrans();
  451. try {
  452. $order = DouyinOrder::where('order_number', $params['order_number'])->findOrEmpty();
  453. if(!$order->isEmpty()){
  454. $consultation = ExternalConsultation::where('id', $order->consultation_id)->findOrEmpty()->toArray();
  455. $consultation['user_name'] = $params['user_name']??$consultation['user_name'];
  456. $consultation['mobile'] = $params['mobile']??$consultation['mobile'];
  457. $consultation['user_address'] = $params['user_address'];
  458. $consultation['lon'] = $params['lon'];
  459. $consultation['lat'] = $params['lat'];
  460. $consultation['appointment_time'] = $params['appointment_time'];
  461. $consultationOrderId = ExternalConsultationLogic::order($consultation);
  462. if (false === $consultationOrderId) {
  463. throw new \Exception('预约失败');
  464. }
  465. if(!empty($consultationOrderId)){
  466. $consultationOrder = ExternalConsultationOrder::where('id', $consultationOrderId)->findOrEmpty()->toArray();
  467. $work_status = ServiceWork::where('id', $consultationOrder['work_id'])->value('work_status');
  468. $order->work_id = $consultationOrder['work_id'];
  469. $order->fulfillment_status = $work_status;
  470. $order->save();
  471. }
  472. $params['dy_order_id'] = $order->dy_order_id;
  473. $params['consultationOrderId'] = $consultationOrderId;
  474. $params['open_id'] = $order->open_id;
  475. $params['goods_id'] = $order->goods_id;
  476. }
  477. Db::commit();
  478. // 抖音创建预约单
  479. $url = 'api/apps/trade/v2/book/create_book';
  480. $resData = self::toDyRequestUrl($url,self::getCreateBookParams($params));
  481. //book_id result
  482. // 抖音预约接单结果回调
  483. $bookurl = 'api/apps/trade/v2/book/book_result_callback';
  484. $res = self::toDyRequestUrl($bookurl,[
  485. 'book_id' => $resData['book_id']??'',
  486. 'result' => 1,
  487. ]);
  488. return $order['id']??0;
  489. } catch (\Exception $e) {
  490. Db::rollback();
  491. throw new \Exception($e->getMessage());
  492. }
  493. }
  494. public static function getCreateBookParams($params)
  495. {
  496. try {
  497. $platformGoods = ExternalPlatformGoods::where('goods_id', $params['goods_id'])->where('external_platform_id', self::EXTERNAL_PLATFORM_ID)->findOrEmpty();
  498. if($platformGoods->isEmpty()){
  499. throw new \Exception('产品不存在!');
  500. }
  501. $appointment_time = strtotime($params['appointment_time']);
  502. $book_start_time = $appointment_time * 1000;
  503. $book_end_time = ($appointment_time + (2 * 86400)) * 1000;
  504. $data = [
  505. "order_id"=> (string)$params['dy_order_id']??'',
  506. "out_book_no"=> (string)$params['consultationOrderId']??'',
  507. "open_id"=> (string)$params['open_id']??'',
  508. "item_book_info_list" => [
  509. [
  510. "poi_id" => '7511543640776017961',
  511. "shop_name" => '亿蜂快修·武汉市',
  512. "ext_shop_id" => (string)self::EXTERNAL_PLATFORM_ID,
  513. "goods_id" => (string)$platformGoods->external_goods_sn,
  514. "book_start_time" => (int)$book_start_time??0,
  515. "book_end_time" => (int)$book_end_time??0,
  516. ]
  517. ]
  518. ];
  519. return $data;
  520. } catch (\Exception $e) {
  521. return [];
  522. }
  523. }
  524. public static function upReservation($params)
  525. {
  526. // $params['order_number']
  527. Db::startTrans();
  528. try {
  529. $order = DouyinOrder::where('order_number', $params['order_number'])->findOrEmpty();
  530. if(!$order->isEmpty()){
  531. // sn appointment_time
  532. $result = ServiceOrderLogic::approvalChangeAppointment(['sn'=>RechargeOrder::where('work_id', $order->work_id)->value('sn'),'appointment_time'=>$params['appointment_time']]);
  533. if (false === $result) {
  534. throw new \Exception(ServiceOrderLogic::getError());
  535. }
  536. $order->fulfillment_status = ServiceWork::where('id', $order->work_id)->value('work_status');
  537. $order->save();
  538. }
  539. Db::commit();
  540. return $order['id'];
  541. } catch (\Exception $e) {
  542. Db::rollback();
  543. throw new \Exception($e->getMessage());
  544. }
  545. }
  546. // ******************************** 订单退款
  547. public static function cancelOrder($params)
  548. {
  549. // $params['order_number']
  550. Db::startTrans();
  551. try {
  552. $order = DouyinOrder::where('order_number', $params['order_number'])->findOrEmpty();
  553. if(!$order->isEmpty()){
  554. if($order->order_status == 1 && $order->pay_status == 0){
  555. $order->order_status = 4;
  556. $order->save();
  557. }else{
  558. throw new \Exception('订单状态不可取消!');
  559. }
  560. }
  561. Db::commit();
  562. return $order['id'];
  563. } catch (\Exception $e) {
  564. Db::rollback();
  565. throw new \Exception($e->getMessage());
  566. }
  567. }
  568. public static function refund($params)
  569. {
  570. Db::startTrans();
  571. try {
  572. // $params['order_number'] user_id
  573. $order = DouyinOrder::with(['goods','serviceWork'])->where('order_number', $params['order_number'])->where('user_id', $params['user_id'])->findOrEmpty();
  574. if($order->isEmpty()){
  575. throw new \Exception('订单不存在');
  576. }
  577. $orderInfo = $order->toArray();
  578. $work_status = $orderInfo['serviceWork']['work_status']??0;
  579. if(3 < $work_status){
  580. throw new \Exception('该订单禁止退款');
  581. }
  582. DouyinRefundOrder::create([
  583. 'refund_number' => generate_sn(DouyinRefundOrder::class, 'refund_number'),
  584. 'order_number' => $orderInfo['order_number'],
  585. 'transaction_id' => $orderInfo['transaction_id'],
  586. 'reason' => $params['reason']??'',
  587. 'refund_status' => 0,
  588. 'user_id' => $orderInfo['user_id'],
  589. 'refund_amount' => $orderInfo['paid_amount'],
  590. ]);
  591. Db::commit();
  592. return true;
  593. } catch (\Exception $e) {
  594. Db::rollback();
  595. throw new \Exception($e->getMessage());
  596. }
  597. }
  598. public static function refundExamine($params)
  599. {
  600. Db::startTrans();
  601. try {
  602. // $params['order_number']
  603. $order = DouyinOrder::with(['goods','serviceWork'])->where('order_number', $params['order_number'])->findOrEmpty();
  604. if($order->isEmpty()){
  605. throw new \Exception('订单不存在');
  606. }
  607. $orderInfo = $order->toArray();
  608. //$refund_number = $params['refund_number']??'';
  609. $douyinRefundOrder = DouyinRefundOrder::where('order_number', $params['order_number'])->order('id', 'desc')->findOrEmpty();
  610. if($params['is_examine_ok'] === 'pass'){
  611. $douyinRefundOrder->refund_status = 2;
  612. RechargeOrder::where('work_id', $orderInfo['work_id'])->update([
  613. 'pay_status' => 2,
  614. 'pay_time' => 0,
  615. 'paid_amount' => 0,
  616. ]);
  617. ServiceWork::where('id', $orderInfo['work_id'])->update([
  618. 'work_status' => 0,
  619. 'user_confirm_status' => 0,
  620. 'service_status' => 4,
  621. 'work_pay_status' => 0
  622. ]);
  623. }else{
  624. $douyinRefundOrder->refund_status = 1;
  625. }
  626. $douyinRefundOrder->save();
  627. Db::commit();
  628. if($params['is_examine_ok'] === 'pass'){
  629. //通过后向抖音申请退款
  630. self::sendRefundCreate($params['order_number']);
  631. }
  632. return true;
  633. } catch (\Exception $e) {
  634. Db::rollback();
  635. throw new \Exception($e->getMessage());
  636. }
  637. }
  638. public static function sendRefundCreate($order_number)
  639. {
  640. try {
  641. // $params['order_number']
  642. $order = DouyinOrder::with(['goods','serviceWork'])->where('order_number', $order_number)->findOrEmpty();
  643. if($order->isEmpty()){
  644. throw new \Exception('订单不存在');
  645. }
  646. $orderInfo = $order->toArray();
  647. $douyinRefundOrder = DouyinRefundOrder::where('order_number', $order_number)->order('id', 'desc')->findOrEmpty();
  648. //通过后向抖音申请退款
  649. $url = 'api/trade_basic/v1/developer/refund_create/';
  650. $data = [
  651. "order_id" => $orderInfo['transaction_id'],
  652. "out_refund_no" => $douyinRefundOrder->refund_number,
  653. "cp_extra" => $orderInfo['id'].'|'.$douyinRefundOrder->id,
  654. "order_entry_schema" => [
  655. "path" => "page/index/index",
  656. "params" => json_encode(['refund_number'=>$douyinRefundOrder->refund_number])
  657. ],
  658. "refund_total_amount " => $douyinRefundOrder->refund_amount * 100,
  659. //"notify_url" => config('douyin.refundNotifyUrl'),
  660. "refund_reason" => [
  661. [
  662. "code" => 101,
  663. "text" => "不想要了"
  664. ]
  665. ]
  666. ];
  667. $resData = self::toDyRequestUrl($url,$data);
  668. if(isset($resData['data']) && $resData['data']){
  669. $douyinRefundOrder->transaction_id = $resData['data']['refund_id'];
  670. $douyinRefundOrder->save();
  671. }
  672. return true;
  673. } catch (\Exception $e) {
  674. Log::info($e->getMessage());
  675. return false;
  676. }
  677. }
  678. public static function refundNotify($params)
  679. {
  680. Db::startTrans();
  681. try {
  682. $douyinRefundOrder = DouyinRefundOrder::where('refund_number', $params['out_refund_no'])->findOrEmpty();
  683. if($douyinRefundOrder->isEmpty()){
  684. throw new \Exception('退款订单不存在');
  685. }
  686. if($douyinRefundOrder->refund_status == 0){
  687. if($params['status'] === 'SUCCESS'){
  688. $douyinRefundOrder->refund_status = 3;
  689. DouyinOrder::where('order_number', $douyinRefundOrder->order_number)->update([
  690. 'order_status' => 4,
  691. 'pay_status' => 3,
  692. ]);
  693. }elseif($params['status'] === 'FAIL'){
  694. $douyinRefundOrder->refund_status = 4;
  695. }else{
  696. throw new \Exception('退款状态未知');
  697. }
  698. $douyinRefundOrder->save();
  699. }
  700. Db::commit();
  701. return true;
  702. } catch (\Exception $e) {
  703. Db::rollback();
  704. throw new \Exception($e->getMessage());
  705. }
  706. }
  707. public static function toDyRequestUrl($url,$data)
  708. {
  709. $toData = [
  710. 'url' => $url,
  711. 'data' => $data
  712. ];
  713. $res = http_request(env('internal_api.api_url_host').'platf/dou_yin/toDyRequestUrl',$toData,['Content-Type' => 'application/json;charset=utf-8']);
  714. Log::info(json_encode($res));
  715. if($res['code'] === 0){
  716. Log::info("toDyRequestUrl:".json_encode($res));
  717. return $res['data'];
  718. }else{
  719. Log::info("toDyRequestUrl:".$res['msg']);
  720. throw new \Exception($res['msg']);
  721. }
  722. }
  723. }