Order.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  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','express'])->findOrEmpty();
  75. if ($order->isEmpty()) return errorTrans("empty.data");
  76. if ($order['poi_id'] <> $request->user['store_id'] && $request->user['is_super'] == 0) return errorTrans("empty.data");
  77. $order['end_time'] = timeDiff(strtotime("+30 minutes",strtotime($order['create_at'])),time());
  78. return successTrans("success.data",$order->toArray());
  79. } catch (\Throwable $throwable) {
  80. return error($throwable->getMessage());
  81. }
  82. }
  83. #[PostMapping('save')]
  84. public function setOrderData(Request $request): Response
  85. {
  86. try {
  87. $param = $this->_valid([
  88. "order.require" => trans("empty.require"),
  89. "field.require" => trans("empty.require"),
  90. "val.require" => trans("empty.require"),
  91. ],$request->method());
  92. if (!is_array($param)) return error($param);
  93. $order = $this->model->where("out_order_no",$param['order'])->with(['product','poi','user','address'])->findOrEmpty();
  94. if ($order->isEmpty()) return errorTrans("empty.data");
  95. if ($order['poi_id'] <> $request->user['store_id'] && $request->user['is_super'] == 0) return errorTrans("empty.data");
  96. $update = [];
  97. if ($param['field'] == 'status' && $param['val'] == 1) {
  98. $update['express_status'] = 0;
  99. (new SaasOrderAddress)->where(["openid" => $order['openid'],'order_sn' => $order['out_order_no']])->delete();
  100. }
  101. if ($param['field'] == 'is_error' && $param['val'] == 1) {
  102. $update['express_status'] = 0;
  103. $update['error_at'] = getDateFull();
  104. }
  105. $update[$param['field']] = $param['val'];
  106. $state = $order->save($update);
  107. if (!$state) return errorTrans("error.data");
  108. return successTrans("success.data");
  109. } catch (\Throwable $throwable) {
  110. return error($throwable->getMessage());
  111. }
  112. }
  113. /**
  114. * 小程序手动核销
  115. * @param Request $request
  116. * @return Response
  117. */
  118. #[PostMapping('doneMini')]
  119. public function doneOrderMini(Request $request): Response
  120. {
  121. try {
  122. $param = $this->_valid([
  123. "order.require" => trans("empty.require")
  124. ],$request->method());
  125. if (!is_array($param)) return error($param);
  126. $order = $this->model->where("out_order_no",$param['order'])->with(['product','poi','user'])->findOrEmpty();
  127. if ($order->isEmpty()) return errorTrans("empty.data");
  128. if ($order['status'] <> 7) return error("该订单状态不支持核销");
  129. if ($request->user['store_id'] <> $order['poi_id'] && $request->user['is_super'] == 0) return errorTrans("empty.data");
  130. $orderExpress = (new SaasOrderExpress)->where("order_sn",$order['order_sn'])->findOrEmpty();
  131. if ($orderExpress->isEmpty()) return error("订单尚未发货");
  132. if (strtotime("+24 hour",strtotime($order['error_at'])) < time()) return error("新订单需24小时后方可核销");
  133. [$state,$expressData] = (new LifeWeb)->getExpressData($orderExpress['express_sn'],$orderExpress['mobile']);
  134. if (!$state) return error("查询物流失败,请联系管理员");
  135. if (!in_array($expressData['logisticsStatusDesc'],['派件中',"已签收"])) return error("当前物流状态不允许核销");
  136. $startTime = $order['poi']['order_start']??'09:00';
  137. $endTime = $order['poi']['order_end']??'22:30';
  138. $startTimeStamp = strtotime(date("Y-m-d {$startTime}:00"));
  139. $endTimeStamp = strtotime(date("Y-m-d {$endTime}:00"));
  140. if (time() > $startTimeStamp && time() < $endTimeStamp) {
  141. $data = (new OrderData)->config([
  142. "appid" => sConf("wechat.mini_appid"),
  143. "secret" => sConf("wechat.mini_secret")
  144. ])->token()->verifyOrder($order->toArray());
  145. if (empty($data['verify_results'])) return error($data['description']); // 核销失败
  146. $order->status = 2;
  147. $order->express_status = 3;
  148. $order->done_at = getDateFull();
  149. $order->save();
  150. (new SaasOrderItem)->where(['order_sn'=>$order['order_sn'],'status'=>1])->update(['status'=>3]);
  151. (new SaasOrderVerify)->insertGetId([
  152. "poi_id" => $order['poi_id'],
  153. "order_sn" => $order['order_sn'],
  154. "money" => $order['price'],
  155. "certificate_id" => $data['verify_results'][0]['certificate_id']??'0',
  156. "verify_id" => $data['verify_results'][0]['verify_id']??'0',
  157. ]);
  158. return success("核销成功");
  159. } else {
  160. return error("当前时间段不支持核销");
  161. }
  162. } catch (\Throwable $throwable) {
  163. return error($throwable->getMessage());
  164. }
  165. }
  166. /**
  167. * 手动核销
  168. * @param Request $request
  169. * @return Response
  170. */
  171. #[PostMapping('done')]
  172. public function doneOrder(Request $request): Response
  173. {
  174. try {
  175. $param = $this->_valid([
  176. "id.require" => trans("empty.require")
  177. ],$request->method());
  178. if (!is_array($param)) return error($param);
  179. $order = $this->model->where("order_sn",$param['id'])->with(['product','poi','user'])->findOrEmpty();
  180. if ($order->isEmpty()) return errorTrans("empty.data");
  181. if ($order['poi_id'] <> $request->user['store_id']) return errorTrans("empty.data");
  182. if ($order['status'] <> 1) return error("该订单状态不支持核销");
  183. $store = (new SaasStore)->where("poi_id",$request->user['store_id'])->findOrEmpty();
  184. if ($store->isEmpty()) return errorTrans("error.data");
  185. // 获取到订单encrypted_code
  186. $orderCode = (new OrderData)->config([
  187. "appid" => sConf("wechat.appid"),
  188. "secret" => sConf("wechat.secret"),
  189. "account" => sConf("wechat.shop_id"),
  190. ])->token()->getCertificate($order['order_sn'],$store['store_id']);
  191. if (empty($orderCode['certificates'])) return error("获取订单数据失败");
  192. if (!isset($orderCode['certificates'][0]['encrypted_code'])) return error("获取订单数据失败");
  193. if ($orderCode['certificates'][0]['status'] <> 1) return error("该订单状态不支持核销");
  194. // 核销订单
  195. $orderDone = (new OrderData)->config([
  196. "appid" => sConf("wechat.appid"),
  197. "secret" => sConf("wechat.secret"),
  198. "account" => sConf("wechat.shop_id"),
  199. ])->token()->verifyCertificate([
  200. "account_id" => $store['store_id'],
  201. "pay_sn" => $order['pay_sn'],
  202. "encrypted_code" => $orderCode['certificates'][0]['encrypted_code'],
  203. "order_id" => $order['order_sn'],
  204. "poi_id" => $order['poi_id']
  205. ]);
  206. if (empty($orderDone['verify_results'])) return error("核销失败");
  207. if (!isset($orderDone['verify_results'][0]['verify_id'])) return error("核销失败");
  208. if (!isset($orderDone['verify_results'][0]['certificate_id'])) return error("核销失败");
  209. $order->status = 2;
  210. $order->verify_id = $orderDone['verify_results'][0]['verify_id'];
  211. $order->certificate_id = $orderDone['verify_results'][0]['certificate_id'];
  212. $order->done_at = getDateFull();
  213. $state = $order->save();
  214. if (!$state) return error("核销失败");
  215. return success("核销成功");
  216. } catch (\Throwable $throwable) {
  217. return error($throwable->getMessage());
  218. }
  219. }
  220. /**
  221. * 三级城市信息
  222. * @param Request $request
  223. * @return Response
  224. */
  225. #[GetMapping("city")]
  226. public function getCityJson(Request $request): Response
  227. {
  228. try {
  229. $data = json_decode(file_get_contents(base_path()."/city.json"),true);
  230. return successTrans("success.data",$data);
  231. } catch (\Throwable $throwable) {
  232. return error($throwable->getMessage());
  233. }
  234. }
  235. #[PostMapping('address')]
  236. public function setOrderAddress(Request $request): Response
  237. {
  238. try {
  239. $param = $this->_valid([
  240. "nickname.require" => trans("empty.require"),
  241. "mobile.require" => trans("empty.require"),
  242. "mobile.mobile" => trans("error.mobile"),
  243. "city.require" => trans("empty.require"),
  244. "address.require" => trans("empty.require"),
  245. "cityLabel.require" => trans("empty.require"),
  246. "order.require" => trans("empty.require"),
  247. ],$request->method());
  248. if (!is_array($param)) return error($param);
  249. $order = (new SaasOrder)->where("out_order_no",$param["order"])->with(['poi'])->findOrEmpty();
  250. if ($order->isEmpty()) return error("关联订单错误");
  251. if ($order['status'] <> 1) return error("关联订单错误");
  252. $address = (new SaasOrderAddress)->where(['openid' => $order['openid'],'order_sn' => $param['order']])->findOrEmpty();
  253. if (!$address->isEmpty()) return error("请勿重复提交");
  254. $param['city_text'] = empty($param['cityLabel'])?'':implode("",$param['cityLabel']);
  255. $address->insertGetId([
  256. "openid" => $order['openid'],
  257. "order_sn" => $param['order'],
  258. "city_code" => empty($param['city'])?[]:json_encode($param['city']),
  259. "city_text" => $param['city_text'],
  260. "mobile" => $param['mobile'],
  261. "address" => $param['address'],
  262. "nickname" => $param['nickname'],
  263. ]);
  264. $order->status = 2;
  265. $order->express_status = 1;
  266. $state = $order->save();
  267. if (!$state) return errorTrans("error.data");
  268. return successTrans("success.data");
  269. } catch (\Throwable $throwable) {
  270. return error($throwable->getMessage());
  271. }
  272. }
  273. #[PostMapping("refund")]
  274. public function setOrderRefund(Request $request): Response
  275. {
  276. try {
  277. $param = $this->_valid([
  278. "order.require" => trans("empty.require"),
  279. ],$request->method());
  280. if (!is_array($param)) return error($param);
  281. $order = (new SaasOrder)->where("out_order_no",$param["order"])->with(['poi'])->findOrEmpty();
  282. if ($order->isEmpty()) return error("关联订单错误");
  283. if ($order['status'] <> 1) return error("关联订单错误");
  284. if (empty($order['item_order_id'])) return error("订单数据不完整");
  285. $data = (new OrderData)->config([
  286. "appid" => sConf("wechat.mini_appid"),
  287. "secret" => sConf("wechat.mini_secret"),
  288. ])->token()->refundOrderOp($order->toArray());
  289. echo getDateFull()."==={$order["order_sn"]}===手动发起退款\n";
  290. print_r($data);
  291. if (empty($data['refund_id'])) return error("发起退款失败");
  292. $order->status = 3;
  293. $order->refund_at = getDateFull();
  294. $state = $order->save();
  295. if (!$state) return errorTrans("error.data");
  296. return successTrans("success.data");
  297. } catch (\Throwable $throwable) {
  298. return error($throwable->getMessage());
  299. }
  300. }
  301. #[PostMapping("black")]
  302. public function setOrderBlack(Request $request): Response
  303. {
  304. try {
  305. $param = $this->_valid([
  306. "order.require" => trans("empty.require"),
  307. ],$request->method());
  308. if (!is_array($param)) return error($param);
  309. $order = (new SaasOrder)->where("out_order_no",$param["order"])->with(['user'])->findOrEmpty();
  310. if ($order->isEmpty()) return error("关联订单错误");
  311. if ($order['user']['is_black'] == 1) return error("该用户已在黑名单,无需重复操作");
  312. $state = (new SaasUserOpen)->where("openid",$order['openid'])->save(['is_black' => 1,'poi_id' => $order['poi_id']]);
  313. if (!$state) return errorTrans("error.data");
  314. return successTrans("success.data");
  315. } catch (\Throwable $throwable) {
  316. return error($throwable->getMessage());
  317. }
  318. }
  319. #[PostMapping("express")]
  320. public function setExpressData(Request $request): Response
  321. {
  322. try {
  323. $param = $this->_valid([
  324. "order.require" => trans("empty.require"),
  325. "express_sn.require" => trans("empty.require"),
  326. "express_name.require" => trans("empty.require"),
  327. ],$request->method());
  328. if (!is_array($param)) return error($param);
  329. $order = $this->model->where("out_order_no",$param["order"])->findOrEmpty();
  330. if ($order->isEmpty()) return error("关联订单错误");
  331. $express = (new SaasOrderExpress)->where("order_sn",$order["order_sn"])->findOrEmpty();
  332. if (!$express->isEmpty()) return error("该订单已完成发货,请勿重复操作");
  333. $usedOrder = (new SaasOrderExpress)->where('express_sn', $param['express_sn'])
  334. ->where('order_sn', '<>', $order["order_sn"]) // 排除自己
  335. ->where('status', 0) // 只查未发货的
  336. ->findOrEmpty();
  337. if (!$usedOrder->isEmpty() && $usedOrder['openid'] != $order['openid']) {
  338. return error("该单号已被其他用户使用");
  339. }
  340. $state = $express->setAutoData([
  341. "openid" => $order["openid"],
  342. "order_sn" => $order["order_sn"],
  343. "express_sn" => $param["express_sn"],
  344. "express_name" => $param["express_name"],
  345. "mobile" => $param["mobile"]??$order['mobile'],
  346. ]);
  347. if (!$state) return errorTrans("error.data");
  348. $order->status = 7;
  349. $order->express_status = 2;
  350. $order->save();
  351. return successTrans("success.data");
  352. } catch (\Throwable $throwable) {
  353. return error($throwable->getMessage());
  354. }
  355. }
  356. }