Order.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. <?php
  2. namespace app\controller\merchant;
  3. use app\extra\basic\Base;
  4. use app\extra\dyLife\data\BaseData;
  5. use app\extra\dyLife\data\OrderData;
  6. use app\extra\tools\LifeWeb;
  7. use app\middleware\AuthMiddleware;
  8. use app\model\saas\SaasOrder;
  9. use app\model\saas\SaasOrderAddress;
  10. use app\model\saas\SaasOrderExpress;
  11. use app\model\saas\SaasOrderItem;
  12. use app\model\saas\SaasOrderVerify;
  13. use app\model\saas\SaasStore;
  14. use app\model\saas\SaasUserOpen;
  15. use app\model\system\SystemImport;
  16. use app\service\saas\GoodsService;
  17. use app\service\saas\OrderService;
  18. use DI\Attribute\Inject;
  19. use LinFly\Annotation\Attributes\Route\Controller;
  20. use LinFly\Annotation\Attributes\Route\GetMapping;
  21. use LinFly\Annotation\Attributes\Route\Middleware;
  22. use LinFly\Annotation\Attributes\Route\PostMapping;
  23. use support\Request;
  24. use support\Response;
  25. #[Controller("/api/merchant/order"),Middleware(AuthMiddleware::class)]
  26. class Order extends Base
  27. {
  28. #[Inject]
  29. protected OrderService $service;
  30. #[Inject]
  31. protected SaasOrder $model;
  32. #[Inject]
  33. protected GoodsService $goodsService;
  34. #[GetMapping('list')]
  35. public function getOrderList(Request $request): Response
  36. {
  37. try {
  38. $param = $request->all();
  39. $param['poi_id'] = $request->user['store_id'];
  40. $productType = $this->goodsService->productType();
  41. $data = $this->service->setModel()->getList($param,['product' => function($query) use($productType){
  42. $query->field("product_id,product_name,product_type,is_new,price,line_price")->append(['types'])->withAttr(['types' => function($query,$resp) use($productType){
  43. $productTypeArr = [];
  44. foreach ($productType as $val) {
  45. $productTypeArr[$val['key']] = $val['name'];
  46. }
  47. return $productTypeArr[$resp['product_type']]??'';
  48. }]);
  49. },'user' => function($query){
  50. $query->field("openid,nickname,avatar");
  51. },"star" => function($query){
  52. $query->field("uid,nick_name,avatar_url,unique_id");
  53. }],true,['end_time','refund_num'],["end_time" => function ($resp,$next) {
  54. return timeDiff(strtotime("+30 minutes",strtotime($next['create_at'])),time());
  55. },'refund_num' => function($resp,$next){
  56. if ($next['status'] == 6) {
  57. return (new SaasOrderItem)->where(['order_sn' => $next['order_sn'],'status' => 2])->count()??0;
  58. }
  59. return 0;
  60. }]);
  61. return successTrans("success.data",pageFormat($data),200);
  62. } catch (\Throwable $throwable) {
  63. return error($throwable->getMessage());
  64. }
  65. }
  66. #[GetMapping('detail')]
  67. public function getOrderDetail(Request $request): Response
  68. {
  69. try {
  70. $param = $this->_valid([
  71. "order.require" => trans("empty.require"),
  72. ],$request->method());
  73. if (!is_array($param)) return error($param);
  74. $order = $this->model->where("out_order_no",$param['order'])->with(['product','poi','user','address' => function($query){
  75. $query->with(['goods']);
  76. },'express'])->findOrEmpty();
  77. if ($order->isEmpty()) return errorTrans("empty.data");
  78. if ($order['poi_id'] <> $request->user['store_id'] && $request->user['is_super'] == 0) return errorTrans("empty.data");
  79. $order['end_time'] = timeDiff(strtotime("+30 minutes",strtotime($order['create_at'])),time());
  80. return successTrans("success.data",$order->toArray());
  81. } catch (\Throwable $throwable) {
  82. return error($throwable->getMessage());
  83. }
  84. }
  85. #[PostMapping('save')]
  86. public function setOrderData(Request $request): Response
  87. {
  88. try {
  89. $param = $this->_valid([
  90. "order.require" => trans("empty.require"),
  91. "field.require" => trans("empty.require"),
  92. "val.require" => trans("empty.require"),
  93. ],$request->method());
  94. if (!is_array($param)) return error($param);
  95. $order = $this->model->where("out_order_no",$param['order'])->with(['product','poi','user','address'])->findOrEmpty();
  96. if ($order->isEmpty()) return errorTrans("empty.data");
  97. if ($order['poi_id'] <> $request->user['store_id'] && $request->user['is_super'] == 0) return errorTrans("empty.data");
  98. $update = [];
  99. if ($param['field'] == 'status' && $param['val'] == 1) {
  100. $update['express_status'] = 0;
  101. (new SaasOrderAddress)->where(["openid" => $order['openid'],'order_sn' => $order['out_order_no']])->delete();
  102. }
  103. if ($param['field'] == 'is_error' && $param['val'] == 1) {
  104. $update['express_status'] = 0;
  105. $update['error_at'] = getDateFull();
  106. }
  107. $update[$param['field']] = $param['val'];
  108. $state = $order->save($update);
  109. if (!$state) return errorTrans("error.data");
  110. return successTrans("success.data");
  111. } catch (\Throwable $throwable) {
  112. return error($throwable->getMessage());
  113. }
  114. }
  115. /**
  116. * 小程序手动核销
  117. * @param Request $request
  118. * @return Response
  119. */
  120. #[PostMapping('doneMini')]
  121. public function doneOrderMini(Request $request): Response
  122. {
  123. try {
  124. $param = $this->_valid([
  125. "order.require" => trans("empty.require")
  126. ],$request->method());
  127. if (!is_array($param)) return error($param);
  128. $order = $this->model->where("out_order_no",$param['order'])->with(['product','poi','user'])->findOrEmpty();
  129. if ($order->isEmpty()) return errorTrans("empty.data");
  130. if ($order['status'] <> 7) return error("该订单状态不支持核销");
  131. if ($request->user['store_id'] <> $order['poi_id'] && $request->user['is_super'] == 0) return errorTrans("empty.data");
  132. $orderExpress = (new SaasOrderExpress)->where("order_sn",$order['order_sn'])->findOrEmpty();
  133. if ($orderExpress->isEmpty()) return error("订单尚未发货");
  134. if (strtotime("+24 hour",strtotime($order['error_at'])) < time()) return error("新订单需24小时后方可核销");
  135. [$state,$expressData] = (new LifeWeb)->getExpressData($orderExpress['express_sn'],$orderExpress['mobile']);
  136. if (!$state) return error("查询物流失败,请联系管理员");
  137. if (!in_array($expressData['logisticsStatusDesc'],['派件中',"已签收"])) return error("当前物流状态不允许核销");
  138. $startTime = $order['poi']['order_start']??'09:00';
  139. $endTime = $order['poi']['order_end']??'22:30';
  140. $startTimeStamp = strtotime(date("Y-m-d {$startTime}:00"));
  141. $endTimeStamp = strtotime(date("Y-m-d {$endTime}:00"));
  142. if (time() > $startTimeStamp && time() < $endTimeStamp) {
  143. $data = (new OrderData)->config([
  144. "appid" => sConf("wechat.mini_appid"),
  145. "secret" => sConf("wechat.mini_secret")
  146. ])->token()->verifyOrder($order->toArray());
  147. if (empty($data['verify_results'])) return error($data['description']); // 核销失败
  148. $order->status = 2;
  149. $order->express_status = 3;
  150. $order->done_at = getDateFull();
  151. $order->save();
  152. (new SaasOrderItem)->where(['order_sn'=>$order['order_sn'],'status'=>1])->update(['status'=>3]);
  153. (new SaasOrderVerify)->insertGetId([
  154. "poi_id" => $order['poi_id'],
  155. "order_sn" => $order['order_sn'],
  156. "money" => $order['price'],
  157. "certificate_id" => $data['verify_results'][0]['certificate_id']??'0',
  158. "verify_id" => $data['verify_results'][0]['verify_id']??'0',
  159. ]);
  160. return success("核销成功");
  161. } else {
  162. return error("当前时间段不支持核销");
  163. }
  164. } catch (\Throwable $throwable) {
  165. return error($throwable->getMessage());
  166. }
  167. }
  168. /**
  169. * 手动核销
  170. * @param Request $request
  171. * @return Response
  172. */
  173. #[PostMapping('done')]
  174. public function doneOrder(Request $request): Response
  175. {
  176. try {
  177. $param = $this->_valid([
  178. "id.require" => trans("empty.require")
  179. ],$request->method());
  180. if (!is_array($param)) return error($param);
  181. $order = $this->model->where("order_sn",$param['id'])->with(['product','poi','user'])->findOrEmpty();
  182. if ($order->isEmpty()) return errorTrans("empty.data");
  183. if ($order['poi_id'] <> $request->user['store_id']) return errorTrans("empty.data");
  184. if ($order['status'] <> 1) return error("该订单状态不支持核销");
  185. $store = (new SaasStore)->where("poi_id",$request->user['store_id'])->findOrEmpty();
  186. if ($store->isEmpty()) return errorTrans("error.data");
  187. // 获取到订单encrypted_code
  188. $orderCode = (new OrderData)->config([
  189. "appid" => sConf("wechat.appid"),
  190. "secret" => sConf("wechat.secret"),
  191. "account" => sConf("wechat.shop_id"),
  192. ])->token()->getCertificate($order['order_sn'],$store['store_id']);
  193. if (empty($orderCode['certificates'])) return error("获取订单数据失败");
  194. if (!isset($orderCode['certificates'][0]['encrypted_code'])) return error("获取订单数据失败");
  195. if ($orderCode['certificates'][0]['status'] <> 1) return error("该订单状态不支持核销");
  196. // 核销订单
  197. $orderDone = (new OrderData)->config([
  198. "appid" => sConf("wechat.appid"),
  199. "secret" => sConf("wechat.secret"),
  200. "account" => sConf("wechat.shop_id"),
  201. ])->token()->verifyCertificate([
  202. "account_id" => $store['store_id'],
  203. "pay_sn" => $order['pay_sn'],
  204. "encrypted_code" => $orderCode['certificates'][0]['encrypted_code'],
  205. "order_id" => $order['order_sn'],
  206. "poi_id" => $order['poi_id']
  207. ]);
  208. if (empty($orderDone['verify_results'])) return error("核销失败");
  209. if (!isset($orderDone['verify_results'][0]['verify_id'])) return error("核销失败");
  210. if (!isset($orderDone['verify_results'][0]['certificate_id'])) return error("核销失败");
  211. $order->status = 2;
  212. $order->verify_id = $orderDone['verify_results'][0]['verify_id'];
  213. $order->certificate_id = $orderDone['verify_results'][0]['certificate_id'];
  214. $order->done_at = getDateFull();
  215. $state = $order->save();
  216. if (!$state) return error("核销失败");
  217. return success("核销成功");
  218. } catch (\Throwable $throwable) {
  219. return error($throwable->getMessage());
  220. }
  221. }
  222. /**
  223. * 三级城市信息
  224. * @param Request $request
  225. * @return Response
  226. */
  227. #[GetMapping("city")]
  228. public function getCityJson(Request $request): Response
  229. {
  230. try {
  231. $data = json_decode(file_get_contents(base_path()."/city.json"),true);
  232. return successTrans("success.data",$data);
  233. } catch (\Throwable $throwable) {
  234. return error($throwable->getMessage());
  235. }
  236. }
  237. #[PostMapping('address')]
  238. public function setOrderAddress(Request $request): Response
  239. {
  240. try {
  241. $param = $this->_valid([
  242. "nickname.require" => trans("empty.require"),
  243. "mobile.require" => trans("empty.require"),
  244. "mobile.mobile" => trans("error.mobile"),
  245. "city.require" => trans("empty.require"),
  246. "address.require" => trans("empty.require"),
  247. "cityLabel.require" => trans("empty.require"),
  248. "order.require" => trans("empty.require"),
  249. ],$request->method());
  250. if (!is_array($param)) return error($param);
  251. $order = (new SaasOrder)->where("out_order_no",$param["order"])->with(['poi'])->findOrEmpty();
  252. if ($order->isEmpty()) return error("关联订单错误");
  253. if ($order['status'] <> 1) return error("关联订单错误");
  254. $address = (new SaasOrderAddress)->where(['openid' => $order['openid'],'order_sn' => $param['order']])->findOrEmpty();
  255. if (!$address->isEmpty()) return error("请勿重复提交");
  256. $param['city_text'] = empty($param['cityLabel'])?'':implode("",$param['cityLabel']);
  257. $address->insertGetId([
  258. "openid" => $order['openid'],
  259. "order_sn" => $param['order'],
  260. "city_code" => empty($param['city'])?[]:json_encode($param['city']),
  261. "city_text" => $param['city_text'],
  262. "mobile" => $param['mobile'],
  263. "address" => $param['address'],
  264. "nickname" => $param['nickname'],
  265. ]);
  266. $order->status = 2;
  267. $order->express_status = 1;
  268. $state = $order->save();
  269. if (!$state) return errorTrans("error.data");
  270. return successTrans("success.data");
  271. } catch (\Throwable $throwable) {
  272. return error($throwable->getMessage());
  273. }
  274. }
  275. #[PostMapping("refund")]
  276. public function setOrderRefund(Request $request): Response
  277. {
  278. try {
  279. $param = $this->_valid([
  280. "order.require" => trans("empty.require"),
  281. ],$request->method());
  282. if (!is_array($param)) return error($param);
  283. $order = (new SaasOrder)->where("out_order_no",$param["order"])->with(['poi'])->findOrEmpty();
  284. if ($order->isEmpty()) return error("关联订单错误");
  285. if ($order['status'] <> 1) return error("关联订单错误");
  286. if (empty($order['item_order_id'])) return error("订单数据不完整");
  287. $data = (new OrderData)->config([
  288. "appid" => sConf("wechat.mini_appid"),
  289. "secret" => sConf("wechat.mini_secret"),
  290. ])->token()->refundOrderOp($order->toArray());
  291. echo getDateFull()."==={$order["order_sn"]}===手动发起退款\n";
  292. print_r($data);
  293. if (empty($data['refund_id'])) return error("发起退款失败");
  294. $order->status = 3;
  295. $order->refund_at = getDateFull();
  296. $state = $order->save();
  297. if (!$state) return errorTrans("error.data");
  298. return successTrans("success.data");
  299. } catch (\Throwable $throwable) {
  300. return error($throwable->getMessage());
  301. }
  302. }
  303. #[PostMapping("black")]
  304. public function setOrderBlack(Request $request): Response
  305. {
  306. try {
  307. $param = $this->_valid([
  308. "order.require" => trans("empty.require"),
  309. ],$request->method());
  310. if (!is_array($param)) return error($param);
  311. $order = (new SaasOrder)->where("out_order_no",$param["order"])->with(['user'])->findOrEmpty();
  312. if ($order->isEmpty()) return error("关联订单错误");
  313. if ($order['user']['is_black'] == 1) return error("该用户已在黑名单,无需重复操作");
  314. $state = (new SaasUserOpen)->where("openid",$order['openid'])->save(['is_black' => 1,'poi_id' => $order['poi_id']]);
  315. if (!$state) return errorTrans("error.data");
  316. return successTrans("success.data");
  317. } catch (\Throwable $throwable) {
  318. return error($throwable->getMessage());
  319. }
  320. }
  321. #[PostMapping("express")]
  322. public function setExpressData(Request $request): Response
  323. {
  324. try {
  325. $param = $this->_valid([
  326. "order.require" => trans("empty.require"),
  327. "express_sn.require" => trans("empty.require"),
  328. "express_name.require" => trans("empty.require"),
  329. ],$request->method());
  330. if (!is_array($param)) return error($param);
  331. $order = $this->model->where("out_order_no",$param["order"])->findOrEmpty();
  332. if ($order->isEmpty()) return error("关联订单错误");
  333. $express = (new SaasOrderExpress)->where("order_sn",$order["order_sn"])->findOrEmpty();
  334. if (!$express->isEmpty()) return error("该订单已完成发货,请勿重复操作");
  335. $usedOrder = (new SaasOrderExpress)->where('express_sn', $param['express_sn'])
  336. ->where('order_sn', '<>', $order["order_sn"]) // 排除自己
  337. ->where('status', 0) // 只查未发货的
  338. ->findOrEmpty();
  339. if (!$usedOrder->isEmpty() && $usedOrder['openid'] != $order['openid']) {
  340. return error("该单号已被其他用户使用");
  341. }
  342. $state = $express->setAutoData([
  343. "openid" => $order["openid"],
  344. "order_sn" => $order["order_sn"],
  345. "express_sn" => $param["express_sn"],
  346. "express_name" => $param["express_name"],
  347. "mobile" => $param["mobile"]??$order['mobile'],
  348. ]);
  349. if (!$state) return errorTrans("error.data");
  350. $order->status = 7;
  351. $order->express_status = 2;
  352. $order->save();
  353. return successTrans("success.data");
  354. } catch (\Throwable $throwable) {
  355. return error($throwable->getMessage());
  356. }
  357. }
  358. }