OrderService.php 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905
  1. <?php
  2. namespace app\api\service;
  3. use app\admin\model\Admin;
  4. use app\admin\model\membership\Config;
  5. use app\admin\model\system\GrantVoucher;
  6. use app\api\model\massager\Comment;
  7. use app\api\model\massager\Massager;
  8. use app\api\model\order\Order;
  9. use app\api\model\service\Service;
  10. use app\api\model\Store;
  11. use app\api\model\system\Message;
  12. use app\api\model\User;
  13. use app\api\model\user\Area;
  14. use app\api\model\user\Voucher;
  15. use app\api\model\user\Wallet;
  16. use redis\RedLock;
  17. use think\Db;
  18. use think\Exception;
  19. class OrderService extends BaseService
  20. {
  21. private $model;
  22. private $massagerModel;
  23. private $storeModel;
  24. private $userModel;
  25. private $serviceModel;
  26. private $orderServiceModel;
  27. private $userAreaModel;
  28. private $userVoucherModel;
  29. private $massagerCommentModel;
  30. private $thirdPayService;
  31. private $walletService;
  32. public $lockKeys = [
  33. 'order_create' => "Order:create:"
  34. ];
  35. public function __construct()
  36. {
  37. $this->model = new Order();
  38. $this->massagerModel = new Massager();
  39. $this->storeModel = new Store();
  40. $this->userModel = new User();
  41. $this->serviceModel = new Service();
  42. $this->orderServiceModel = new \app\api\model\order\Service();
  43. $this->userAreaModel = new Area();
  44. $this->userVoucherModel = new Voucher();
  45. $this->massagerCommentModel = new Comment();
  46. $this->thirdPayService = new ThirdPayService();
  47. $this->walletService = new WalletService();
  48. }
  49. /**
  50. * 刷新订单金额
  51. * @param Order $order
  52. * @param $open_membership
  53. * @param null $total_service_amount
  54. * @param null $trip_amount
  55. * @param null $voucher_amount
  56. * @return Order|null
  57. * @throws \think\exception\DbException
  58. */
  59. private function updateOrderAmount(Order $order, $open_membership = true, $total_service_amount = null, $trip_amount = null, $voucher_amount = null)
  60. {
  61. $user = User::where("id", $order->user_id)->find();
  62. if ($user && $user["vip_invalid_time"] > time()) {
  63. $open_membership = true;
  64. }
  65. $change_trip_amount = $order->trip_amount;
  66. $change_voucher_amount = $order->voucher_amount;
  67. if (null != $total_service_amount)
  68. $change_total_service_amount = $total_service_amount;
  69. else {
  70. $services = $this->orderServiceModel->where("order_id", $order['id'])->select();
  71. $change_total_service_amount = array_reduce($services, function ($p, $cur) {
  72. $p += ($cur["amount"] * $cur["quantity"]);
  73. return $p;
  74. }, 0);
  75. }
  76. if (null !== $trip_amount)
  77. $change_trip_amount = $trip_amount;
  78. if (null !== $voucher_amount)
  79. $change_voucher_amount = $voucher_amount;
  80. $config = null;
  81. $membership_discount_rate = 100;
  82. $membership_discount_amount = 0;
  83. $membership_amount = 0;
  84. $total_amount = fixed2Float($change_total_service_amount + $change_trip_amount);
  85. if ($open_membership) {
  86. $user = $this->userModel->findById($order["user_id"]);
  87. $config = (new Config())->order("real_price", "ASC")->find();
  88. if (!$user || is_null($user["vip_invalid_time"]) || $user["vip_invalid_time"] < time()) { // 需要支付会员费
  89. $membership_amount = $config["real_price"];
  90. }
  91. $membership_discount_rate = config("site.membership_discount_rate");
  92. // 会员折扣
  93. $membership_discount_amount = fixed2Float($change_total_service_amount - ($change_total_service_amount * ($membership_discount_rate / 100)));
  94. // 服务费用减去会员折扣
  95. $change_total_service_amount = fixed2Float($change_total_service_amount * ($membership_discount_rate / 100));
  96. }
  97. $this->model->update([
  98. "open_membership" => $open_membership ? 1 : 0,
  99. "membership_config_id" => $config ? $config["id"] : null,
  100. "membership_amount" => $membership_amount,
  101. "membership_discount_rate" => $membership_discount_rate,
  102. "membership_discount_amount" => $membership_discount_amount,
  103. "total_amount" => $total_amount,
  104. "total_real_amount" => fixed2Float(($change_total_service_amount + $change_trip_amount) - $change_voucher_amount),
  105. "total_service_amount" => $change_total_service_amount,
  106. "trip_amount" => $change_trip_amount,
  107. "voucher_amount" => $change_voucher_amount
  108. ], ["id" => $order->id]);
  109. return $this->model->get($order->id);
  110. }
  111. /**
  112. * 获取单个订单详情
  113. * @param $id
  114. * @return array|bool|false|\PDOStatement|string|\think\Model|null
  115. */
  116. public function getOrder($id)
  117. {
  118. return $this->model->order_detail($id);
  119. }
  120. /**
  121. * 获取订单列表
  122. * @param $user_id
  123. * @param array $status
  124. * @param int $page
  125. * @param int $size
  126. * @return array
  127. * @throws \think\exception\DbException
  128. */
  129. public function fetchOrder($user_id, $status, $page = 1, $size = 10)
  130. {
  131. $paginate = $this->model->fetchOrder($user_id, $status, $page, $size);
  132. $items = $paginate->items();
  133. foreach ($items as $item) {
  134. $item->getRelation("massager")->visible(["id", "name", "photo_images"]);
  135. }
  136. return [$items, $paginate->total()];
  137. }
  138. /**
  139. * 创建订单
  140. * @param $user
  141. * @param array $option_service_ids
  142. * @param array $option_services
  143. * @param $m_id
  144. * @param $store_id
  145. * @param bool $is_app
  146. * @return \SResult
  147. * @throws \think\db\exception\DataNotFoundException
  148. * @throws \think\db\exception\ModelNotFoundException
  149. * @throws \think\exception\DbException
  150. */
  151. public function create($user, array $option_service_ids, array $option_services, $m_id = null, $store_id = null, bool $is_app = true): \SResult
  152. {
  153. $city_code = null;
  154. if ($is_app) { // 线上订单
  155. $massager = $this->massagerModel->getMassager($m_id);
  156. if (null === $massager)
  157. return $this->fail("助教信息不存在!");
  158. $city_code = $massager["city_code"];
  159. } else { // 球房订单
  160. $store = $this->storeModel->get($store_id);
  161. if (!$store) {
  162. return $this->fail("球房信息出现错误!");
  163. }
  164. $city_code = $store["city_code"];
  165. }
  166. $services = $this->serviceModel->findByIds($option_service_ids, [
  167. 'status' => \E_BASE_STATUS::Normal
  168. ]);
  169. $no = $this->model->genOrderNo($user->id, $store_id);
  170. $user_area_id = null;
  171. $user_address = null;
  172. $last_order = $this->model
  173. ->where("user_id", $user->id)
  174. ->where("user_area_id", ">", 0)
  175. ->order("id", "DESC")
  176. ->find();
  177. if ($last_order) {
  178. $user_area_id = $last_order["user_area_id"];
  179. $user_address = $last_order["user_address"];
  180. }
  181. $order = $this->model->create([
  182. "no" => $no,
  183. "user_id" => $user->id,
  184. "massager_id" => $m_id,
  185. "store_id" => $store_id,
  186. "total_amount" => 0,
  187. "total_real_amount" => 0,
  188. "total_service_amount" => 0,
  189. "trip_amount" => 0,
  190. "voucher_amount" => 0,
  191. "city_code" => $city_code,
  192. "channel_id" => $user->channel_id,
  193. "user_area_id" => $user_area_id,
  194. "user_address" => $user_address,
  195. ]);
  196. Db::startTrans();
  197. try {
  198. $db_service = [];
  199. foreach ($services as $service) {
  200. $quantity = $option_services[$service['id']]['quantity'];
  201. $db_service[] = [
  202. "order_id" => $order->id,
  203. "store_id" => $store_id,
  204. "service_id" => $service['id'],
  205. "service_name" => $service['name'],
  206. "cover_image" => $service['cover_image'],
  207. "amount" => $service["real_price"],
  208. "duration_minute" => $service["duration_minute"],
  209. "quantity" => $quantity,
  210. "status" => \E_BASE_STATUS::Normal,
  211. "createtime" => time(),
  212. "updatetime" => time(),
  213. ];
  214. }
  215. $total_service_amount = array_reduce($db_service, function ($p, $cur) {
  216. $p += ($cur["amount"] * $cur["quantity"]);
  217. return $p;
  218. }, 0);
  219. $this->orderServiceModel->insertAll($db_service);
  220. $this->updateOrderAmount($order, false, $total_service_amount);
  221. Db::commit();
  222. } catch (Exception $e) {
  223. Db::rollback();
  224. $this->model->where('id', $order->id)->delete();
  225. return $this->fail($e->getMessage());
  226. }
  227. if ($user_area_id) {
  228. $this->calculateTripAmount($user["id"], $order["id"]);
  229. }
  230. return $this->ok($this->model->order_detail($order->id));
  231. }
  232. public function reorder($order_id)
  233. {
  234. $old_order = $this->model->order_detail($order_id);
  235. if (null === $old_order)
  236. return $this->fail("订单不存在");
  237. if (time() - $old_order["updatetime"] > 3 * 24 * 60 * 60)
  238. return $this->fail("加钟订单不能超过原始订单的三天期限");
  239. $no = $this->model->genOrderNo($old_order["user_id"], $old_order["store_id"]);
  240. $redLock = new RedLock();
  241. $lock = $redLock->lock($this->lockKeys['order_create'] . $no);
  242. if (!is_array($lock))
  243. return $this->fail("请稍后再试!");
  244. $new_order = $this->model->create([
  245. "no" => $no,
  246. "user_id" => $old_order["user_id"],
  247. "user_area_id" => $old_order["user_area_id"],
  248. "store_id" => $old_order["store_id"],
  249. "massager_id" => $old_order["massager_id"],
  250. "total_amount" => 0,
  251. "total_real_amount" => 0,
  252. "total_service_amount" => 0,
  253. "trip_amount" => 0,
  254. "voucher_amount" => 0,
  255. "city_code" => $old_order["city_code"],
  256. "channel_id" => $old_order["channel_id"],
  257. "reorder" => 1,
  258. "user_address" => $old_order["user_address"],
  259. "massager_address" => $old_order["massager_address"],
  260. "createtime" => time(),
  261. "updatetime" => time()
  262. ]);
  263. Db::startTrans();
  264. try {
  265. $db_service = [];
  266. foreach ($old_order["services"] as $item) {
  267. $service = $this->serviceModel->get($item['service_id']);
  268. $db_service[] = [
  269. "order_id" => $new_order["id"],
  270. "store_id" => $service['store_id'],
  271. "service_id" => $service['id'],
  272. "service_name" => $service['name'],
  273. "cover_image" => $service['cover_image'],
  274. "amount" => $service["real_price"],
  275. "duration_minute" => $service["duration_minute"],
  276. "quantity" => 1,
  277. "status" => \E_BASE_STATUS::Normal,
  278. "createtime" => time(),
  279. "updatetime" => time()
  280. ];
  281. }
  282. $total_service_amount = array_reduce($db_service, function ($p, $cur) {
  283. $p += ($cur["amount"] * $cur["quantity"]);
  284. return $p;
  285. }, 0);
  286. $this->orderServiceModel->insertAll($db_service);
  287. $this->updateOrderAmount($new_order, true, $total_service_amount);
  288. Db::commit();
  289. } catch (Exception $e) {
  290. Db::rollback();
  291. $this->model->where('id', $new_order->id)->delete();
  292. return $this->fail($e->getMessage());
  293. } finally {
  294. $redLock->unlock($lock);
  295. }
  296. return $this->ok($this->model->order_detail($new_order->id));
  297. }
  298. /**
  299. * bind 地址
  300. * @param $user_id
  301. * @param $order_id
  302. * @param $user_area_id
  303. * @return \SResult
  304. * @throws \think\exception\DbException
  305. */
  306. public function bindUserArea($user_id, $order_id, $user_area_id): \SResult
  307. {
  308. $order = $this->model->get($order_id);
  309. if (!$order)
  310. return $this->fail("订单不存在!");
  311. if ($order->user_id != $user_id)
  312. return $this->fail("非法修改!");
  313. if (\E_ORDER_STATUS::Default !== $order["status"])
  314. return $this->fail("订单当前状态不支持修改地点");
  315. if ($order->store_id > 0) {
  316. return $this->ok($this->model->order_detail($order->id));
  317. }
  318. $massager = $this->massagerModel->getMassager($order->massager_id);
  319. if (!$massager)
  320. return $this->fail("助教不存在, 请选择其他助教为您服务!");
  321. if (!is_numeric($massager->lng) || !is_numeric($massager->lat))
  322. return $this->fail("助教未完善地址信息无法计算距离, 请选择其他助教为您服务!");
  323. $userArea = $this->userAreaModel->getArea($user_id, $user_area_id);
  324. if (null === $userArea)
  325. return $this->fail("绑定的地址不存在!");
  326. // if ($userArea->city_code !== $order->city_code)
  327. // return $this->fail("距离太远,跨市无法下单!");
  328. $this->model->update([
  329. "user_area_id" => $user_area_id,
  330. "user_address" => $userArea->address . "-" . $userArea->door_no,
  331. ], ['id' => $order_id]);
  332. $this->calculateTripAmount($user_id, $order_id);
  333. return $this->ok($this->model->order_detail($order->id));
  334. }
  335. /**
  336. * 删减服务
  337. * @param int $order_id
  338. * @param array $option_service_ids
  339. * @param array $option_services
  340. * @return \SResult
  341. * @throws \think\exception\DbException
  342. */
  343. public function changeOrderService(int $order_id, array $option_service_ids, array $option_services)
  344. {
  345. $order = $this->model->get($order_id);
  346. if (!$order)
  347. return $this->fail("订单不存在!");
  348. if (\E_ORDER_STATUS::Default !== $order["status"])
  349. return $this->fail("订单当前状态不支持修改地点");
  350. if ($order["massager_id"] > 0) {
  351. $massager = $this->massagerModel->getMassager($order['massager_id']);
  352. }
  353. $services = $this->serviceModel->findByIds($option_service_ids, [
  354. 'status' => \E_BASE_STATUS::Normal
  355. ]);
  356. Db::startTrans();
  357. try {
  358. $this->orderServiceModel->where("order_id", $order_id)->delete();
  359. $db_service = [];
  360. foreach ($services as $service) {
  361. $quantity = $option_services[$service['id']]['quantity'];
  362. $db_service[] = [
  363. "order_id" => $order_id,
  364. "store_id" => $order["store_id"],
  365. "service_id" => $service['id'],
  366. "service_name" => $service['name'],
  367. "cover_image" => $service['cover_image'],
  368. "amount" => $service["real_price"],
  369. "duration_minute" => $service["duration_minute"],
  370. "quantity" => $quantity,
  371. "status" => \E_BASE_STATUS::Normal,
  372. "createtime" => time(),
  373. "updatetime" => time(),
  374. ];
  375. }
  376. $total_service_amount = array_reduce($db_service, function ($p, $cur) {
  377. $p += ($cur["amount"] * $cur["quantity"]);
  378. return $p;
  379. }, 0);
  380. $this->orderServiceModel->insertAll($db_service);
  381. $this->updateOrderAmount($order, $order["open_membership"], $total_service_amount);
  382. Db::commit();
  383. } catch (Exception $e) {
  384. Db::rollback();
  385. return $this->fail($e->getMessage());
  386. }
  387. return $this->ok($this->model->order_detail($order->id));
  388. }
  389. /**
  390. * 绑定优惠券
  391. * @param $user_id
  392. * @param $order_id
  393. * @param $user_voucher_id
  394. * @return \SResult
  395. * @throws \think\db\exception\DataNotFoundException
  396. * @throws \think\db\exception\ModelNotFoundException
  397. * @throws \think\exception\DbException
  398. */
  399. public function bindVoucher($user_id, $order_id, $user_voucher_id)
  400. {
  401. $order = $this->model->get($order_id);
  402. if (!$order)
  403. return $this->fail("订单不存在!");
  404. if ($order->user_id != $user_id)
  405. return $this->fail("非法修改!");
  406. if (\E_ORDER_STATUS::Default !== $order["status"])
  407. return $this->fail("订单当前状态不支持选择优惠券!");
  408. $voucher = $this->userVoucherModel->findById($user_voucher_id, ["user_id" => $user_id]);
  409. if (null === $voucher)
  410. return $this->fail("错误的优惠券");
  411. if ("normal" !== $voucher->status || time() > $voucher->expiretime || time() < $voucher->take_effect_time) {
  412. return $this->fail("该优惠券暂不可用!");
  413. }
  414. $order = $this->updateOrderAmount($order, $order["open_membership"]);
  415. if ($voucher->fullAmount > $order->total_amount) {
  416. return $this->fail("总金额不满足优惠券金额!");
  417. }
  418. if ($voucher->takeAmount >= $order->total_amount)
  419. return $this->fail("订单总金额小于优惠券立减金额");
  420. $this->updateOrderAmount($order, $order["open_membership"], null, null, $voucher->takeAmount);
  421. $this->model->update(["voucher_id" => $user_voucher_id], ["id" => $order->id]);
  422. return $this->ok($this->model->order_detail($order->id));
  423. }
  424. /**
  425. * 修改订单选项
  426. * @param $user_id
  427. * @param $order_id
  428. * @param $description
  429. * @param $trip_type
  430. * @param $service_start_date
  431. * @param $balance_deduction
  432. * @return \SResult
  433. * @throws \think\exception\DbException
  434. */
  435. public function modifyOrder($user_id, $order_id, $description = null, $trip_type = null, $service_start_date = null, $balance_deduction = null): \SResult
  436. {
  437. $order = $this->model->get($order_id);
  438. if (!$order)
  439. return $this->fail("订单不存在!");
  440. if ($order->user_id != $user_id)
  441. return $this->fail("非法修改!");
  442. if (\E_ORDER_STATUS::Default !== $order["status"])
  443. return $this->fail("订单状态不支持修改!");
  444. $update = ["updatetime" => time()];
  445. if (null !== $description)
  446. $update["description"] = $description;
  447. if (null !== $trip_type) {
  448. $update["trip_type"] = $trip_type;
  449. }
  450. if (null !== $service_start_date) {
  451. $update["service_start_date"] = $service_start_date;
  452. }
  453. if (null !== $balance_deduction && $balance_deduction >= 0) {
  454. if ($balance_deduction >= $order["total_real_amount"])
  455. return $this->fail("余额抵扣金额错误!");
  456. $update["balance_deduction"] = $balance_deduction;
  457. }
  458. if (0 < count($update))
  459. $this->model->update($update, ["id" => $order->id]);
  460. if (null !== $trip_type || null !== $service_start_date)
  461. $this->calculateTripAmount($user_id, $order_id);
  462. return $this->ok($this->model->order_detail($order->id));
  463. }
  464. /**
  465. * @param $user_id
  466. * @param $order_id
  467. * @param $open_membership
  468. * @return \SResult
  469. * @throws \think\exception\DbException
  470. */
  471. public function updateOrderOpenMembership($user_id, $order_id, $open_membership): \SResult
  472. {
  473. $order = $this->model->get($order_id);
  474. if (!$order)
  475. return $this->fail("订单不存在!");
  476. if ($order->user_id != $user_id)
  477. return $this->fail("非法修改!");
  478. $this->updateOrderAmount($order, $open_membership);
  479. return $this->ok($this->model->order_detail($order->id));
  480. }
  481. /**
  482. * 计算出行费用
  483. * @param $user_id
  484. * @param $order_id
  485. * @return \SResult
  486. * @throws \think\exception\DbException
  487. */
  488. public function calculateTripAmount($user_id, $order_id)
  489. {
  490. $order = $this->model->get($order_id);
  491. if (!$order)
  492. return $this->fail("订单不存在!");
  493. if ($order->store_id > 0) {
  494. return $this->ok($this->model->order_detail($order->id));
  495. }
  496. if (null === $order->service_start_date)
  497. return $this->fail("请先选择服务时间!");
  498. $h = (int)date("H", strtotime($order->service_start_date));
  499. $massager = $this->massagerModel->getMassager($order->massager_id);
  500. if (!$massager)
  501. return $this->fail("助教不存在, 请选择其他助教为您服务!");
  502. if (!is_numeric($massager->lng) || !is_numeric($massager->lat))
  503. return $this->fail("助教未完善地址信息无法计算距离, 请选择其他助教为您服务!");
  504. $userArea = $this->userAreaModel->getArea($user_id, $order->user_area_id);
  505. if (null === $userArea)
  506. return $this->fail("绑定的地址不存在!");
  507. $trip_amount = 0;
  508. // 道路距离计算m
  509. $distanceRes = BDService::shortestPathsAlgorithm("$massager->lat,$massager->lng", "$userArea->lat,$userArea->lng");
  510. if (0 === $distanceRes->code()) {
  511. return $this->fail($distanceRes->msg());
  512. }
  513. // km
  514. $distance = ($distanceRes->data() / 1000);
  515. $is_free_travel = (
  516. $massager->free_travel === 1
  517. && (
  518. is_null($massager["free_travel_km"])
  519. ||
  520. (
  521. $massager["free_travel_km"] > 0
  522. && $distance <= $massager["free_travel_km"]
  523. )
  524. )
  525. )
  526. || $order["reorder"] == 1;
  527. if (!$is_free_travel) { // 计算出行费
  528. if ($distance > $massager["radius"]) {
  529. return $this->fail("该助教设置了服务距离不超过{$massager["radius"]}km,请重新找助教下单~");
  530. }
  531. $trip_taxi_km = config("site.trip_taxi_km") ?? 3; // 起步公里
  532. $residue_km = $distance > $trip_taxi_km ? ceil($distance - $trip_taxi_km) : 0;
  533. if (\E_TRIP_TYPE::Taxi === $order->trip_type) {
  534. $is_night = in_array($h, [0, 1, 2, 3, 4, 5, 21, 22, 23, 24]);
  535. if ($is_night) {
  536. $trip_flag_fall = config("site.trip_taxi_night_flag_fall");
  537. $trip_money_of_km = config("site.trip_taxi_night_money_of_km");
  538. } else {
  539. $trip_flag_fall = config("site.trip_taxi_daytime_flag_fall");
  540. $trip_money_of_km = config("site.trip_taxi_daytime_money_of_km");
  541. }
  542. } else {
  543. $trip_flag_fall = config("site.trip_bus_flag_fall");
  544. $trip_money_of_km = config("site.trip_bus_money_of_km");
  545. }
  546. $trip_amount = ($trip_flag_fall + (($residue_km) * $trip_money_of_km)) * ((int)config("site.trip_charge_rules")); //site.trip_charge_rules 1单程 2往返
  547. }
  548. $this->updateOrderAmount($order, $order["open_membership"], null, $trip_amount, null);
  549. $this->model->update(["massager_address" => $massager->address, "distance" => $distance], ["id" => $order->id]);
  550. return $this->ok($this->model->order_detail($order->id));
  551. }
  552. private function checkCanServiceWorkRange($massager_id, $order_id, $service_date)
  553. {
  554. $order_date_range = $this->model->fetchByJudgeMassagerWork($massager_id, $order_id);
  555. $now_time = time();
  556. $service_time = strtotime($service_date);
  557. if ($service_time <= $now_time)
  558. return false;
  559. foreach ($order_date_range as $range) {
  560. if (!is_null($range["service_start_date"]) && !is_null($range["service_start_date"])) {
  561. if (strtotime($range["service_start_date"]) <= $service_time && strtotime($range["service_end_date"]) >= $service_time)
  562. return false;
  563. }
  564. }
  565. return true;
  566. }
  567. /**
  568. * 支付
  569. * @param $user_id
  570. * @param $order_id
  571. * @param $payment_type
  572. * @param $platform
  573. * @param $description
  574. * @return \SResult
  575. * @throws \think\exception\DbException
  576. */
  577. public function payment($user_id, $order_id, $payment_type, $platform, $description = null): \SResult
  578. {
  579. $orderLock = new RedLock();
  580. $oLock = $orderLock->lock(Order::OKey($order_id));
  581. if (!is_array($oLock))
  582. return $this->fail("请稍后再试");
  583. try {
  584. $order = $this->model
  585. ->where("id", $order_id)
  586. ->with("services")
  587. ->find();
  588. if (!$order)
  589. return $this->fail("订单不存在!");
  590. if ($user_id != $order["user_id"])
  591. return $this->fail("您不是该笔订单的发起人");
  592. $user = $this->userModel->findById($user_id);
  593. if (!$user)
  594. return $this->fail("用户异常!");
  595. if (\E_ORDER_STATUS::Default !== $order["status"])
  596. return $this->fail("订单状态不支持支付!");
  597. if (null === $order->service_start_date)
  598. return $this->fail("请先选择服务时间!");
  599. if ($order["massager_id"] > 0) {
  600. $massager = $this->massagerModel->getMassager($order->massager_id);
  601. if (!$massager)
  602. return $this->fail("该助教一分钟前已被他人预约请重新选择时间段!");
  603. $is_can_option = $this->checkCanServiceWorkRange($massager->id, $order_id, $order->service_start_date);
  604. if (!$is_can_option)
  605. return $this->fail("助教服务时间冲突,请重新选择服务时间!");
  606. $userArea = $this->userAreaModel->getArea($user_id, $order->user_area_id);
  607. if (null === $userArea)
  608. return $this->fail("请输入服务地址!");
  609. if (!is_numeric($massager->lng) || !is_numeric($massager->lat))
  610. return $this->fail("助教未完善地址信息无法计算距离, 请选择其他助教为您服务!");
  611. if ((0 === $massager->free_travel && $order["reorder"] == 0) && 0 == $order->trip_amount)
  612. return $this->fail("出行费用错误, 请先更新出行费用!");
  613. }
  614. $openid = null;
  615. if ($platform == "app")
  616. $openid = $user["app_openid"];
  617. if ($platform == "web")
  618. $openid = $user["web_openid"];
  619. if ($platform == "applet")
  620. $openid = $user["applet_openid"];
  621. if ($payment_type === \E_ORDER_PAY_TYPE::Wechat) {
  622. if ($platform != "app" && (is_null($openid) || mb_strlen($openid) === 0))
  623. return $this->fail("请先绑定微信再进行支付", 101);
  624. }
  625. $start_time = strtotime($order->service_start_date);
  626. $o_services = $order->getRelation("services");
  627. $t_minute = array_reduce($o_services, function ($p, $cur) {
  628. $p += ($cur->duration_minute * $cur->quantity);
  629. return $p;
  630. }, 0);
  631. // 余额抵扣
  632. if ($order["balance_deduction"] > 0 && $payment_type != \E_ORDER_PAY_TYPE::Balance) {
  633. if ($order["balance_deduction"] >= $order["total_real_amount"]) {
  634. return $this->fail("请使用余额支付!");
  635. }
  636. $userWalletModel = new Wallet();
  637. $wallet = $userWalletModel->getUserWallet($order->user_id);
  638. $after = fixed2Float($wallet["money"] - $order["balance_deduction"]);
  639. if ($after < 0) {
  640. return $this->fail("余额不足,无法抵扣!");
  641. }
  642. }
  643. $this->model->update([
  644. "parent_id" => $user["parent_id"],
  645. "pay_platform" => $platform,
  646. "service_end_date" => date("Y-m-d H:i:00", $start_time + ($t_minute * 60)),
  647. "description" => $description,
  648. "payment_type" => $payment_type
  649. ], ["id" => $order->id]);
  650. switch ($payment_type) {
  651. case \E_ORDER_PAY_TYPE::Wechat:
  652. $res = $this->thirdPayService->payWechat(
  653. $platform,
  654. $openid,
  655. $order["no"],
  656. fixed2Float($order["total_real_amount"] + $order["membership_amount"] - $order["balance_deduction"])
  657. );
  658. break;
  659. case \E_ORDER_PAY_TYPE::ALi:
  660. $res = $this->thirdPayService->payAli($order);
  661. break;
  662. default:
  663. $res = $this->walletService->payBalance($order);
  664. }
  665. (new CallbackService())->payOrderSuccess($order["no"]);
  666. return $res;
  667. } catch (Exception $e) {
  668. return $this->fail($e->getMessage());
  669. } finally {
  670. $orderLock->unlock($oLock);
  671. }
  672. }
  673. /**
  674. * 取消订单
  675. * @param $order_id
  676. * @param $reason
  677. * @param $platform
  678. * @return \SResult
  679. */
  680. public function cancel($order_id, $reason)
  681. {
  682. $orderLock = new RedLock();
  683. $oLock = $orderLock->lock(Order::OKey($order_id));
  684. if (!is_array($oLock))
  685. return $this->fail("请稍后再试");
  686. try {
  687. $order = $this->model->get($order_id);
  688. if (!$order)
  689. return $this->fail("订单不存在!");
  690. if (\E_ORDER_STATUS::Purchase !== $order["status"])
  691. return $this->fail([
  692. \E_ORDER_STATUS::Default => "默认状态不支持取消订单",
  693. \E_ORDER_STATUS::Proceed => "助教已接单,不支持取消订单",
  694. \E_ORDER_STATUS::WaitFeedback => "助教已接单,不支持取消订单",
  695. \E_ORDER_STATUS::Finish => "已经完成的订单不支持取消",
  696. \E_ORDER_STATUS::Reject => "订单已被拒绝,无法取消订单",
  697. \E_ORDER_STATUS::Cancel => "订单已经取消请刷新页面",
  698. \E_ORDER_STATUS::AdminCancel => "平台已经取消订单,请刷新订单页面",
  699. \E_ORDER_STATUS::AutoCancel => "超时未接单取消订单,请刷新订单页面",
  700. ][$order["status"]]);
  701. $order_exceed_time_dont_refund = config("site.order_exceed_time_dont_refund") ?? 1000;
  702. if (($order["createtime"] + $order_exceed_time_dont_refund * 24 * 60 * 60) < time()) {
  703. return $this->fail("订单超过{$order_exceed_time_dont_refund}天不能发起退款!");
  704. }
  705. switch ($order["payment_type"]) {
  706. case \E_ORDER_PAY_TYPE::Wechat:
  707. $res = $this->thirdPayService->refundByWx(
  708. $order["pay_platform"],
  709. $order["no"],
  710. "用户取消订单退款",
  711. $order["total_real_amount"] + $order["membership_amount"] - $order["balance_deduction"],
  712. $order["status"] == \E_ORDER_STATUS::Purchase ? $order["total_real_amount"] - $order["balance_deduction"] : ($order["total_real_amount"] - $order["trip_amount"] - $order["balance_deduction"]));
  713. break;
  714. case \E_ORDER_PAY_TYPE::ALi:
  715. $res = $this->thirdPayService->refundByAli($order);
  716. break;
  717. default:
  718. $res = $this->walletService->refundByBalance($order, \E_ORDER_STATUS::Cancel);
  719. Message::sendSystemMessage(
  720. \E_IDENTITY_TYPE::User,
  721. ["to_user_id" => $order->user_id],
  722. "取消订单通知",
  723. "您的订单【{$order->no}】已取消,订单金额/优惠券将原路返回您的账户!"
  724. );
  725. }
  726. $this->model->update([
  727. "reason" => $reason,
  728. "refund_amount" => $order["status"] == \E_ORDER_STATUS::Purchase ? $order["total_real_amount"] : ($order["total_real_amount"] - $order["trip_amount"]),
  729. "is_refund_trip" => $order["status"] == \E_ORDER_STATUS::Purchase ? 1 : 0
  730. ], ["id" => $order["id"]]);
  731. return $res;
  732. } catch (Exception $e) {
  733. return $this->fail($e->getMessage());
  734. } finally {
  735. $orderLock->unlock($oLock);
  736. }
  737. }
  738. //评价订单
  739. public function comment($order_id, $params)
  740. {
  741. $orderLock = new RedLock();
  742. $oLock = $orderLock->lock(Order::OKey($order_id));
  743. if (!is_array($oLock))
  744. return $this->fail("请稍后再试");
  745. $order = $this->model->get($order_id);
  746. if (!$order)
  747. return $this->fail("订单不存在!");
  748. if (\E_ORDER_STATUS::WaitFeedback !== $order["status"])
  749. return $this->fail("服务未完成,等待助教完成服务!");
  750. $user = $this->userModel->get($order->user_id);
  751. if (is_null($user))
  752. return $this->fail("下单用户不存在");
  753. Db::startTrans();
  754. try {
  755. $this->massagerCommentModel->save([
  756. "user_id" => $order->user_id,
  757. "massager_id" => $order->massager_id > 0 ? $order->massager_id : null,
  758. "store_id" => $order->store_id > 0 ? $order->store_id : null,
  759. "is_anonymity" => $params["is_anonymity"] ?? 1,
  760. "tags" => $params["tags"] ?? null,
  761. "star" => $params["star"] ?? 5,
  762. "content" => $params["content"],
  763. "status" => \E_BASE_STATUS::Normal,
  764. "order_id" => $order->id,
  765. "negative" => $params["negative"],
  766. "city_code" => $order->city_code,
  767. "createtime" => time(),
  768. "updatetime" => time()
  769. ]);
  770. if ($order["massager_id"] > 0) {
  771. if ($params["negative"] == 1) {
  772. $this->massagerModel->where("id", $order["massager_id"])->setInc("negative_count");
  773. $agency = (new Admin())->findAgency($order["city_code"]);
  774. if ($agency) {
  775. Message::sendSystemMessage(\E_IDENTITY_TYPE::Agency, [
  776. "to_agency_id" => $agency["id"]
  777. ], '投诉提醒', "订单: {$order["no"]} 已被用户投诉,请及时处理!");
  778. }
  779. }
  780. $total_count = $this->massagerCommentModel->where([
  781. 'massager_id' => $order->massager_id,
  782. 'status' => \E_BASE_STATUS::Normal
  783. ])->count();
  784. $total_count += 1;
  785. $gte_3_count = $this->massagerCommentModel
  786. ->where([
  787. 'massager_id' => $order->massager_id,
  788. "status" => \E_BASE_STATUS::Normal
  789. ])
  790. ->where("star", ">=", 3)
  791. ->count();
  792. if ($params['star'] >= 3)
  793. $gte_3_count += 1;
  794. $praise_rate = 100;
  795. if ($total_count > 0 && $gte_3_count > 0)
  796. $praise_rate = fixed2Float((($gte_3_count / $total_count)) * 100);
  797. $this->massagerModel
  798. ->where('id', $order->massager_id)
  799. ->inc("order_count")
  800. ->inc("comment_count")
  801. ->update(['praise_rate' => $praise_rate]);
  802. }
  803. $this->orderServiceModel->update([
  804. "finish" => 1
  805. ], ["order_id" => $order->id]);
  806. // $res = (new ProfitService())->profit($order, $user, false);
  807. // if (!$res->code())
  808. // throw new Exception($res->msg());
  809. $this->model->update([
  810. "parent_id" => $user["parent_id"],
  811. "status" => \E_ORDER_STATUS::Finish,
  812. "updatetime" => time()
  813. ], ["id" => $order_id]);
  814. GrantVoucher::grant_voucher("buy", $order["user_id"]);
  815. // $this->userModel->update(['is_use_award' => 1], ["id" => $order["user_id"]]);
  816. $chat_session = \db("fastchat_session")->where("
  817. (user_id = '{$order["user_id"]}||user' AND session_type = 6 and session_user_id = {$order['massager_id']})
  818. OR (user_id = '{$order['massager_id']}||massager' AND session_type = 0 and session_user_id = {$order['user_id']})
  819. ")->find();
  820. if ($chat_session) {
  821. \db("fastchat_record")->where("session_id", $chat_session["id"])->update(["is_del" => 1]);
  822. }
  823. Db::commit();
  824. return $this->ok(true);
  825. } catch (Exception $e) {
  826. Db::rollback();
  827. return $this->fail($e->getMessage());
  828. } finally {
  829. (new MassagerService())->updateInteriorScore($order["massager_id"], date("Y"), date("m"));
  830. $orderLock->unlock($oLock);
  831. }
  832. }
  833. public function appeal($user_id, $order_id, $appeal_reason)
  834. {
  835. $order = $this->model->get($order_id);
  836. if (!$order)
  837. return $this->fail("订单不存在!");
  838. if ($user_id != $order["user_id"])
  839. return $this->fail("订单创建人不是本人,无法申诉");
  840. if (!in_array($order["status"], [\E_ORDER_STATUS::Proceed, \E_ORDER_STATUS::WaitFeedback]))
  841. return $this->fail("订单状态为进行中或者等待评价才能申诉订单!");
  842. if (1 == $order["is_appeal"])
  843. return $this->fail("订单正在申述中,请等待后台管理员处理~");
  844. $this->model->update([
  845. "is_appeal" => 1,
  846. "appeal_reason" => $appeal_reason
  847. ], ["id" => $order_id]);
  848. return $this->ok(true);
  849. }
  850. }