OrderService.php 38 KB

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