Order.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. <?php
  2. namespace app\controller\api;
  3. use app\extra\basic\Base;
  4. use app\extra\dyLife\Crypt;
  5. use app\extra\dyMini\Pay;
  6. use app\middleware\AuthMiddleware;
  7. use app\model\saas\SaasComplaint;
  8. use app\model\saas\SaasOrder;
  9. use app\model\saas\SaasOrderItem;
  10. use app\model\saas\SaasOrderPrice;
  11. use app\service\saas\OrderService;
  12. use DI\Attribute\Inject;
  13. use LinFly\Annotation\Attributes\Route\Controller;
  14. use LinFly\Annotation\Attributes\Route\GetMapping;
  15. use LinFly\Annotation\Attributes\Route\Middleware;
  16. use LinFly\Annotation\Attributes\Route\PostMapping;
  17. use support\Request;
  18. use support\Response;
  19. #[Controller("/dy/order"),Middleware(AuthMiddleware::class)]
  20. class Order extends Base
  21. {
  22. #[Inject]
  23. protected SaasOrder $model;
  24. #[Inject]
  25. protected OrderService $service;
  26. #[GetMapping('list')]
  27. public function getDataList(Request $request): Response
  28. {
  29. try {
  30. $param = $request->all();
  31. if (!empty($param['size'])) {
  32. $param['pageSize'] = $param['size'];
  33. }
  34. $param['openid'] = $request->user['openid'];
  35. $data = $this->service->setModel()->getList($param,['product' => function($query){
  36. $query->field("product_id,product_name");
  37. }]);
  38. return successTrans("success.data",pageFormat($data));
  39. } catch (\Throwable $th) {
  40. return error($th->getMessage());
  41. }
  42. }
  43. #[GetMapping('chat')]
  44. public function getDataChat(Request $request): Response
  45. {
  46. try {
  47. $param = $request->all();
  48. if (!empty($param['size'])) {
  49. $param['pageSize'] = $param['size'];
  50. }
  51. $param['openid'] = $request->user['openid'];
  52. $param['statusIn'] = [1,2];
  53. $data = $this->service->setModel()->getList($param,['product' => function($query){
  54. $query->field("product_id,product_name");
  55. }]);
  56. return successTrans("success.data",pageFormat($data));
  57. } catch (\Throwable $th) {
  58. return error($th->getMessage());
  59. }
  60. }
  61. #[PostMapping("del")]
  62. public function delOrderData(Request $request): Response
  63. {
  64. try {
  65. $param = $this->_valid([
  66. "order.require" => trans("empty.require"),
  67. "type.require" => trans("empty.require"),
  68. ],$request->method());
  69. if (!is_array($param)) return error($param);
  70. $order = $this->model->where("order_sn",$param['order'])->with(['product','poi'])->findOrEmpty();
  71. if ($order->isEmpty()) return errorTrans("empty.data");
  72. if ($order['openid'] <> $request->user['openid']) return errorTrans("empty.data");
  73. switch ($param['type']) {
  74. case "del": // 删除订单
  75. if ($order['status'] <> 5) return error("该订单不支持删除");
  76. $state = $order->delete();
  77. break;
  78. case "cancel": // 取消订单
  79. if ($order['status'] <> 0) return error("该订单不支持取消");
  80. $order->status = 5;
  81. $state = $order->save();
  82. break;
  83. default:
  84. $state = false;
  85. break;
  86. }
  87. if (!$state) return errorTrans("error.data");
  88. return successTrans("success.data");
  89. } catch (\Throwable $throwable) {
  90. return error($throwable->getMessage());
  91. }
  92. }
  93. #[PostMapping("refund")]
  94. public function orderRefund(Request $request): Response
  95. {
  96. try {
  97. $param = $this->_valid([
  98. "order.require" => trans("empty.require"),
  99. "refund.require" => trans("empty.require"),
  100. ],$request->method());
  101. if (!is_array($param)) return error($param);
  102. $order = $this->model->where("order_sn",$param['order'])->with(['product','poi'])->findOrEmpty();
  103. if ($order->isEmpty()) return errorTrans("empty.data");
  104. if ($order['openid'] <> $request->user['openid']) return errorTrans("empty.data");
  105. if ($order['status'] <> 1) return error("当前订单不支持退款");
  106. $reason = explode(",",$param['refund']);
  107. $payParam = [
  108. "order_sn" => $order['order_sn'],
  109. "total" => $order['pay_money'],
  110. "reason" => $reason[0]??'不想要了',
  111. "out_refund_no" => $order['pay_sn'],
  112. "notify_url" => "https://tran.jsshuita.cn/notify/refund"
  113. ];
  114. $byteAuthorization = (new Pay)->config([
  115. "appid" => sConf("wechat.mini_appid"),
  116. "secret" => sConf("wechat.mini_secret"),
  117. "salt" => sConf("wechat.mch_salt"),
  118. ])->createRefund($payParam);
  119. if (!$byteAuthorization) return error("发起退款失败");
  120. $order->status = 4;
  121. $order->refund_apply = getDateFull();
  122. $state = $order->save();
  123. if (!$state) return errorTrans("error.data");
  124. return successTrans("提交申请成功");
  125. } catch (\Throwable $throwable) {
  126. return error($throwable->getMessage());
  127. }
  128. }
  129. #[GetMapping('detail')]
  130. public function getDataDetail(Request $request): Response
  131. {
  132. try {
  133. $param = $this->_valid([
  134. "order.require" => trans("empty.require"),
  135. "option.default" => ""
  136. ],$request->method());
  137. if (!is_array($param)) return error($param);
  138. echo getDateFull()."===>订单详情\n";
  139. echo json_encode($param)."\n";
  140. $order = $this->model->where("out_order_no",$param['order'])->with(['product','poi'])->append(['refund_num'])->withAttr(['refund_num' => function($data,$next){
  141. return (new SaasOrderItem)->where(['order_sn' => $next['order_sn'],'status' => 2])->count();
  142. }])->findOrEmpty();
  143. if ($order->isEmpty()) return errorTrans("empty.data");
  144. $order['end_time'] = timeDiff(strtotime("+30 minutes",strtotime($order['create_at'])),time());
  145. return $this->encode("ok",$order->toArray());
  146. } catch (\Throwable $th) {
  147. return error($th->getMessage());
  148. }
  149. }
  150. #[GetMapping("confirm")]
  151. public function confirmOrder(Request $request): Response
  152. {
  153. try {
  154. $param = $this->_valid([
  155. "order.require" => trans("empty.require")
  156. ],$request->method());
  157. if (!is_array($param)) return error($param);
  158. $order = $this->model->where("order_sn",$param['order'])->with(['product','poi'])->findOrEmpty();
  159. if ($order->isEmpty()) return errorTrans("empty.data");
  160. if ($order['status'] <> 0) return errorTrans("empty.data");
  161. return $this->encode("ok",$order->toArray());
  162. } catch (\Throwable $throwable) {
  163. return error($throwable->getMessage());
  164. }
  165. }
  166. /**
  167. * 差价支付
  168. * @param Request $request
  169. * @return Response
  170. */
  171. #[PostMapping("pricePay")]
  172. public function orderPricePay(Request $request): Response
  173. {
  174. try {
  175. $param = $this->_valid([
  176. "order.require" => trans("empty.require")
  177. ],$request->method());
  178. if (!is_array($param)) return error($param);
  179. $order = (new SaasOrderPrice)->where("order_sn",$param['order'])->findOrEmpty();
  180. $payParam = [
  181. "order_sn" => $order['order_sn'],
  182. "total" => $order['price'],
  183. "name" => $order['name'],
  184. "notify_url" => "https://tran.jsshuita.cn/notify/price"
  185. ];
  186. $byteAuthorization = (new Pay)->config([
  187. "appid" => sConf("wechat.mini_appid"),
  188. "secret" => sConf("wechat.mini_secret"),
  189. "salt" => sConf("wechat.mch_salt"),
  190. ])->createOrder($payParam);
  191. return success("ok",['pay' => $byteAuthorization]);
  192. } catch (\Throwable $throwable) {
  193. return error($throwable->getMessage());
  194. }
  195. }
  196. /**
  197. * 发起支付
  198. * @param Request $request
  199. * @return Response
  200. */
  201. #[PostMapping("toPay")]
  202. public function orderPay(Request $request): Response
  203. {
  204. try {
  205. $param = $this->_valid([
  206. "order.require" => trans("empty.require"),
  207. "mobile.default" => ""
  208. ],$request->method());
  209. if (!is_array($param)) return error($param);
  210. $order = $this->model->where("order_sn",$param['order'])->with(['product','poi'])->findOrEmpty();
  211. $payParam = [
  212. "order_sn" => $order['order_sn'],
  213. "total" => $order['price'],
  214. "name" => $order['product']['product_name'],
  215. "notify_url" => "https://tran.jsshuita.cn/notify/douyin"
  216. ];
  217. $byteAuthorization = (new Pay)->config([
  218. "appid" => sConf("wechat.mini_appid"),
  219. "secret" => sConf("wechat.mini_secret"),
  220. "salt" => sConf("wechat.mch_salt"),
  221. ])->createOrder($payParam);
  222. if (!empty($param['mobile'])) {
  223. $order->mobile = $param['mobile'];
  224. $order->save();
  225. }
  226. return success("ok",['pay' => $byteAuthorization]);
  227. } catch (\Throwable $throwable) {
  228. return error($throwable->getMessage());
  229. }
  230. }
  231. /**
  232. * 交易组件获取手机号码
  233. * @param Request $request
  234. * @return Response
  235. */
  236. #[PostMapping("mobile")]
  237. public function getOrderMobile(Request $request): Response
  238. {
  239. try {
  240. $param = $request->post();
  241. $mobile = [];
  242. if (!empty($param['code'])) {
  243. $mobileStr = (new Crypt)->config($this->getDyConfig())->token()->getMobile($param['code']);
  244. if (!empty($mobileStr)) {
  245. $mobile = $this->decrypt2code(sConf('wechat.min_private_key'), $mobileStr);
  246. }
  247. }
  248. $mobileStr = "";
  249. if (!empty($mobile['purePhoneNumber'])) {
  250. $mobileStr = $mobile['purePhoneNumber'];
  251. }
  252. return success("ok",['mobile' => $mobileStr]);
  253. } catch (\Throwable $throwable) {
  254. return error($throwable->getMessage());
  255. }
  256. }
  257. /**
  258. * 订单投诉
  259. * @param Request $request
  260. * @return Response
  261. */
  262. #[PostMapping("complaint")]
  263. public function setOrderComplaint(Request $request): Response
  264. {
  265. try {
  266. $param = $this->_valid([
  267. "content.require" => trans("empty.require"),
  268. "mobile.default" => "",
  269. "order.require" => trans("empty.require"),
  270. "text.require" => trans("empty.require"),
  271. ],$request->method());
  272. if (!is_array($param)) return error($param);
  273. $order = (new SaasOrder)->where("out_order_no",$param['order'])->findOrEmpty();
  274. if ($order->isEmpty()) return error("该笔交易不存在");
  275. if ($order['openid'] <> $request->user['openid']) return error("非法操作");
  276. $state = (new SaasComplaint)->insertGetId([
  277. "openid" => $request->user['openid'],
  278. "poi_id" => $order['poi_id'],
  279. "content" => $param['content'],
  280. "question" => $param['text'],
  281. "order_sn" => $param['order'],
  282. "mobile" => $param['mobile']??$order['mobile'],
  283. ]);
  284. if (!$state) return error("提交失败");
  285. return success("提交成功,请耐心等待");
  286. } catch (\Throwable $throwable) {
  287. return error($throwable->getMessage());
  288. }
  289. }
  290. protected function decrypt2code($private_key, $ciphertext_str) {
  291. // 解码 base64 密文
  292. $ciphertext = base64_decode($ciphertext_str);
  293. // 使用私钥解密
  294. openssl_private_decrypt($ciphertext, $plaintext, $private_key, OPENSSL_PKCS1_PADDING);
  295. if ($plaintext === false) {
  296. return [];
  297. }
  298. return json_decode($plaintext,true);
  299. }
  300. protected function decrypt($encrypted_data, $session_key, $iv) {
  301. $data = base64_decode($encrypted_data);
  302. $key = base64_decode($session_key);
  303. $iv_decoded = base64_decode($iv);
  304. // 使用 AES-256-CBC 模式解密
  305. $decrypted = openssl_decrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv_decoded);
  306. return $decrypted;
  307. }
  308. }