Order.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. <?php
  2. namespace app\data\controller\api\auth;
  3. use app\data\controller\api\Auth;
  4. use app\data\service\ExpressService;
  5. use app\data\service\GoodsService;
  6. use app\data\service\OrderService;
  7. use app\data\service\PaymentService;
  8. use app\data\service\UserAdminService;
  9. use think\admin\extend\CodeExtend;
  10. use think\exception\HttpResponseException;
  11. /**
  12. * 用户订单数据接口
  13. * Class Order
  14. * @package app\data\controller\api\auth
  15. */
  16. class Order extends Auth
  17. {
  18. /**
  19. * 控制器初始化
  20. */
  21. protected function initialize()
  22. {
  23. parent::initialize();
  24. if (empty($this->user['status'])) {
  25. $this->error('账户已被冻结,不能操作订单数据哦!');
  26. }
  27. }
  28. /**
  29. * 获取订单列表
  30. * @throws \think\db\exception\DataNotFoundException
  31. * @throws \think\db\exception\DbException
  32. * @throws \think\db\exception\ModelNotFoundException
  33. */
  34. public function get()
  35. {
  36. $map = ['uid' => $this->uuid, 'deleted_status' => 0];
  37. $query = $this->_query('ShopOrder')->in('status')->equal('order_no');
  38. $result = $query->where($map)->order('id desc')->page(true, false, false, 20);
  39. if (count($result['list']) > 0) OrderService::instance()->buildData($result['list']);
  40. $this->success('获取订单数据成功!', $result);
  41. }
  42. /**
  43. * 用户创建订单
  44. * @throws \think\db\exception\DataNotFoundException
  45. * @throws \think\db\exception\DbException
  46. * @throws \think\db\exception\ModelNotFoundException
  47. */
  48. public function add()
  49. {
  50. // 检查用户状态
  51. $this->checkUserStatus();
  52. // 商品规则
  53. $rules = $this->request->post('items', '');
  54. if (empty($rules)) $this->error('商品不能为空');
  55. // 订单数据
  56. [$items, $order, $truckType, $allowPayments] = [[], [], -1, null];
  57. $order['uid'] = $this->uuid;
  58. $order['order_no'] = CodeExtend::uniqidDate(18, 'N');
  59. // 推荐人处理
  60. $order['puid1'] = input('from', $this->user['pid1']);
  61. if ($order['puid1'] == $this->uuid) $order['puid1'] = 0;
  62. if ($order['puid1'] > 0) {
  63. $map = ['id' => $order['puid1'], 'status' => 1];
  64. $order['puid2'] = $this->app->db->name('DataUser')->where($map)->value('pid2');
  65. if (is_null($order['puid2'])) $this->error('推荐人异常');
  66. }
  67. // 订单商品处理
  68. foreach (explode('||', $rules) as $rule) {
  69. [$code, $spec, $count] = explode('@', $rule);
  70. // 商品信息检查
  71. $goodsInfo = $this->app->db->name('ShopGoods')->where(['code' => $code, 'status' => 1, 'deleted' => 0])->find();
  72. $goodsItem = $this->app->db->name('ShopGoodsItem')->where(['status' => 1, 'goods_code' => $code, 'goods_spec' => $spec])->find();
  73. if (empty($goodsInfo) || empty($goodsItem)) $this->error('商品查询异常');
  74. // 商品类型检查
  75. if ($truckType < 0) $truckType = $goodsInfo['truck_type'];
  76. if ($truckType !== $goodsInfo['truck_type']) $this->error('不能混合下单');
  77. // 限制购买数量
  78. if (isset($goods['limit_max_num']) && $goods['limit_max_num'] > 0) {
  79. $map = [['a.uid', '=', $this->uuid], ['a.status', 'in', [2, 3, 4, 5]], ['b.goods_code', '=', $goods['code']]];
  80. $buys = $this->app->db->name('StoreOrder')->alias('a')->join('store_order_item b', 'a.order_no=b.order_no')->where($map)->sum('b.stock_sales');
  81. if ($buys + $count > $goods['limit_max_num']) $this->error('超过限购数量');
  82. }
  83. // 限制购买身份
  84. if ($goodsInfo['limit_low_vip'] > $this->user['vip_code']) $this->error('用户等级不够');
  85. // 商品库存检查
  86. if ($goodsItem['stock_sales'] + $count > $goodsItem['stock_total']) $this->error('商品库存不足');
  87. // 支付支付处理
  88. $_allowPayments = [];
  89. foreach (str2arr($goodsInfo['payment']) as $code) {
  90. if (is_null($allowPayments) || in_array($code, $allowPayments)) $_allowPayments[] = $code;
  91. }
  92. if (empty($_allowPayments)) {
  93. $this->error('订单无法统一支付');
  94. } else {
  95. $allowPayments = $_allowPayments;
  96. }
  97. // 商品折扣处理
  98. [$discountId, $discountRate] = OrderService::instance()->discount($goodsInfo['discount_id'], $this->user['vip_code']);
  99. // 订单详情处理
  100. $items[] = [
  101. 'uid' => $order['uid'],
  102. 'order_no' => $order['order_no'],
  103. // 商品信息字段
  104. 'goods_name' => $goodsInfo['name'],
  105. 'goods_cover' => $goodsInfo['cover'],
  106. 'goods_payment' => $goodsInfo['payment'],
  107. 'goods_sku' => $goodsItem['goods_sku'],
  108. 'goods_code' => $goodsItem['goods_code'],
  109. 'goods_spec' => $goodsItem['goods_spec'],
  110. // 库存数量处理
  111. 'stock_sales' => $count,
  112. // 快递发货数据
  113. 'truck_type' => $goodsInfo['truck_type'],
  114. 'truck_code' => $goodsInfo['truck_code'],
  115. 'truck_number' => $goodsInfo['rebate_type'] > 0 ? $goodsItem['number_express'] * $count : 0,
  116. // 商品费用字段
  117. 'price_market' => $goodsItem['price_market'],
  118. 'price_selling' => $goodsItem['price_selling'],
  119. 'total_market' => $goodsItem['price_market'] * $count,
  120. 'total_selling' => $goodsItem['price_selling'] * $count,
  121. // 奖励金额积分
  122. 'reward_balance' => $goodsItem['reward_balance'] * $count,
  123. 'reward_integral' => $goodsItem['reward_integral'] * $count,
  124. // 绑定用户等级
  125. 'vip_name' => $this->user['vip_name'],
  126. 'vip_code' => $this->user['vip_code'],
  127. // 是否入会礼包
  128. 'vip_entry' => $goodsInfo['vip_entry'],
  129. 'vip_upgrade' => $goodsInfo['vip_upgrade'],
  130. // 是否参与返利
  131. 'rebate_type' => $goodsInfo['rebate_type'],
  132. 'rebate_amount' => $goodsInfo['rebate_type'] > 0 ? $goodsItem['price_selling'] * $count : 0,
  133. // 等级优惠方案
  134. 'discount_id' => $discountId,
  135. 'discount_rate' => $discountRate,
  136. 'discount_amount' => $discountRate * $goodsItem['price_selling'] * $count / 100,
  137. ];
  138. }
  139. try {
  140. $order['payment_allow'] = arr2str($allowPayments);
  141. $order['rebate_amount'] = array_sum(array_column($items, 'rebate_amount'));
  142. $order['reward_balance'] = array_sum(array_column($items, 'reward_balance'));
  143. // 订单发货类型
  144. $order['status'] = $truckType ? 1 : 2;
  145. $order['truck_type'] = $truckType;
  146. // 统计商品数量
  147. $order['number_goods'] = array_sum(array_column($items, 'stock_sales'));
  148. $order['number_express'] = array_sum(array_column($items, 'truck_number'));
  149. // 统计商品金额
  150. $order['amount_goods'] = array_sum(array_column($items, 'total_selling'));
  151. // 优惠后的金额
  152. $order['amount_discount'] = array_sum(array_column($items, 'discount_amount'));
  153. // 订单随机免减
  154. $order['amount_reduct'] = OrderService::instance()->getReduct();
  155. if ($order['amount_reduct'] > $order['amount_goods']) {
  156. $order['amount_reduct'] = $order['amount_goods'];
  157. }
  158. // 统计订单金额
  159. $order['amount_real'] = $order['amount_discount'] - $order['amount_reduct'];
  160. $order['amount_total'] = $order['amount_goods'];
  161. // 写入商品数据
  162. $this->app->db->transaction(function () use ($order, $items) {
  163. $this->app->db->name('ShopOrder')->insert($order);
  164. $this->app->db->name('ShopOrderItem')->insertAll($items);
  165. });
  166. // 同步商品库存销量
  167. foreach (array_unique(array_column($items, 'goods_code')) as $code) {
  168. GoodsService::instance()->stock($code);
  169. }
  170. // 触发订单创建事件
  171. $this->app->event->trigger('ShopOrderCreate', $order['order_no']);
  172. // 组装订单商品数据
  173. $order['items'] = $items;
  174. // 返回处理成功数据
  175. $this->success('商品下单成功', $order);
  176. } catch (HttpResponseException $exception) {
  177. throw $exception;
  178. } catch (\Exception $exception) {
  179. $this->error("商品下单失败,{$exception->getMessage()}");
  180. }
  181. }
  182. /**
  183. * 获取用户折扣
  184. */
  185. public function discount()
  186. {
  187. $data = $this->_vali(['discount.require' => '折扣编号不能为空!']);
  188. [, $rate] = OrderService::instance()->discount(intval($data['discount']), $this->user['vip_code']);
  189. $this->success('获取用户折扣', ['rate' => $rate]);
  190. }
  191. /**
  192. * 模拟计算订单运费
  193. * @throws \think\db\exception\DataNotFoundException
  194. * @throws \think\db\exception\DbException
  195. * @throws \think\db\exception\ModelNotFoundException
  196. */
  197. public function express()
  198. {
  199. $data = $this->_vali([
  200. 'uid.value' => $this->uuid,
  201. 'code.require' => '地址不能为空',
  202. 'order_no.require' => '单号不能为空',
  203. ]);
  204. // 用户收货地址
  205. $map = ['uid' => $this->uuid, 'code' => $data['code']];
  206. $addr = $this->app->db->name('DataUserAddress')->where($map)->find();
  207. if (empty($addr)) $this->error('收货地址异常');
  208. // 订单状态检查
  209. $map = ['uid' => $this->uuid, 'order_no' => $data['order_no']];
  210. $tCount = $this->app->db->name('ShopOrderItem')->where($map)->sum('truck_number');
  211. // 根据地址计算运费
  212. $map = ['status' => 1, 'deleted' => 0, 'order_no' => $data['order_no']];
  213. $tCode = $this->app->db->name('ShopOrderItem')->where($map)->column('truck_code');
  214. [$amount, , , $remark] = ExpressService::instance()->amount($tCode, $addr['province'], $addr['city'], $tCount);
  215. $this->success('计算运费成功', ['amount' => $amount, 'remark' => $remark]);
  216. }
  217. /**
  218. * 订单信息完成
  219. * @throws \think\db\exception\DataNotFoundException
  220. * @throws \think\db\exception\DbException
  221. * @throws \think\db\exception\ModelNotFoundException
  222. */
  223. public function perfect()
  224. {
  225. $data = $this->_vali([
  226. 'uid.value' => $this->uuid,
  227. 'code.require' => '地址不能为空',
  228. 'order_no.require' => '单号不能为空',
  229. ]);
  230. // 用户收货地址
  231. $map = ['uid' => $this->uuid, 'code' => $data['code'], 'deleted' => 0];
  232. $addr = $this->app->db->name('DataUserAddress')->where($map)->find();
  233. if (empty($addr)) $this->error('收货地址异常');
  234. // 订单状态检查
  235. $map1 = ['uid' => $this->uuid, 'order_no' => $data['order_no']];
  236. $order = $this->app->db->name('ShopOrder')->where($map1)->whereIn('status', [1, 2])->find();
  237. if (empty($order)) $this->error('不能修改地址');
  238. if (empty($order['truck_type'])) $this->success('无需快递配送', ['order_no' => $order['order_no']]);
  239. // 根据地址计算运费
  240. $map2 = ['status' => 1, 'deleted' => 0, 'order_no' => $data['order_no']];
  241. $tCount = $this->app->db->name('ShopOrderItem')->where($map1)->sum('truck_number');
  242. $tCodes = $this->app->db->name('ShopOrderItem')->where($map2)->column('truck_code');
  243. [$amount, $tCount, $tCode, $remark] = ExpressService::instance()->amount($tCodes, $addr['province'], $addr['city'], $tCount);
  244. // 创建订单发货信息
  245. $express = [
  246. 'template_code' => $tCode, 'template_count' => $tCount, 'uid' => $this->uuid,
  247. 'template_remark' => $remark, 'template_amount' => $amount, 'status' => 1,
  248. ];
  249. $express['order_no'] = $data['order_no'];
  250. $express['address_code'] = $data['code'];
  251. $express['address_name'] = $addr['name'];
  252. $express['address_phone'] = $addr['phone'];
  253. $express['address_idcode'] = $addr['idcode'];
  254. $express['address_idimg1'] = $addr['idimg1'];
  255. $express['address_idimg2'] = $addr['idimg2'];
  256. $express['address_province'] = $addr['province'];
  257. $express['address_city'] = $addr['city'];
  258. $express['address_area'] = $addr['area'];
  259. $express['address_content'] = $addr['address'];
  260. $express['address_datetime'] = date('Y-m-d H:i:s');
  261. data_save('ShopOrderSend', $express, 'order_no');
  262. // 组装更新订单数据
  263. $update = ['status' => 2, 'amount_express' => $express['template_amount']];
  264. // 重新计算订单金额
  265. $update['amount_real'] = $order['amount_discount'] + $amount - $order['amount_reduct'];
  266. $update['amount_total'] = $order['amount_goods'] + $amount;
  267. // 支付金额不能为零
  268. if ($update['amount_real'] <= 0) $update['amount_real'] = 0.00;
  269. if ($update['amount_total'] <= 0) $update['amount_total'] = 0.00;
  270. // 更新用户订单数据
  271. $map = ['uid' => $this->uuid, 'order_no' => $data['order_no']];
  272. if ($this->app->db->name('ShopOrder')->where($map)->update($update) !== false) {
  273. // 触发订单确认事件
  274. $this->app->event->trigger('ShopOrderPerfect', $order['order_no']);
  275. // 返回处理成功数据
  276. $this->success('订单确认成功', ['order_no' => $order['order_no']]);
  277. } else {
  278. $this->error('订单确认失败');
  279. }
  280. }
  281. /**
  282. * 获取支付支付数据
  283. * @throws \think\db\exception\DataNotFoundException
  284. * @throws \think\db\exception\DbException
  285. * @throws \think\db\exception\ModelNotFoundException
  286. */
  287. public function channel()
  288. {
  289. $data = $this->_vali(['uid.value' => $this->uuid, 'order_no.require' => '单号不能为空']);
  290. $payments = $this->app->db->name('ShopOrder')->where($data)->value('payment_allow');
  291. if (empty($payments)) $this->error('获取订单支付参数失败');
  292. [$map, $types] = [['status' => 1, 'deleted' => 0], []];
  293. foreach (PaymentService::TYPES as $type => $arr) if (in_array($this->type, $arr['bind'])) $types[] = $type;
  294. $query = $this->app->db->name('ShopPayment')->where($map)->whereIn('type', $types)->whereIn('code', str2arr($payments));
  295. $this->success('获取支付参数数据', $query->order('sort desc,id desc')->field('type,code,name,cover')->select()->toArray());
  296. }
  297. /**
  298. * 获取订单支付状态
  299. * @throws \think\db\exception\DataNotFoundException
  300. * @throws \think\db\exception\DbException
  301. * @throws \think\db\exception\ModelNotFoundException
  302. */
  303. public function payment()
  304. {
  305. $data = $this->_vali([
  306. 'uid.value' => $this->uuid,
  307. 'order_no.require' => '单号不能为空',
  308. 'order_remark.default' => '',
  309. 'payment_code.require' => '支付不能为空',
  310. 'payment_back.default' => '', # 支付回跳地址
  311. 'payment_image.default' => '', # 支付凭证图片
  312. ]);
  313. [$map, $order] = $this->getOrderData();
  314. if ($order['status'] !== 2) $this->error('不能发起支付');
  315. if ($order['payment_status'] > 0) $this->error('已经完成支付');
  316. // 更新订单备注
  317. if (!empty($data['order_remark'])) {
  318. $this->app->db->name('ShopOrder')->where($map)->update([
  319. 'order_remark' => $data['order_remark'],
  320. ]);
  321. }
  322. // 自动处理用户字段
  323. $openid = '';
  324. if (in_array($this->type, [UserAdminService::API_TYPE_WXAPP, UserAdminService::API_TYPE_WECHAT])) {
  325. $openid = $this->user[UserAdminService::TYPES[$this->type]['auth']] ?? '';
  326. if (empty($openid)) $this->error("发起支付失败");
  327. }
  328. try {
  329. // 返回订单数据及支付发起参数
  330. $type = $order['amount_real'] <= 0 ? 'empty' : $data['payment_code'];
  331. $param = PaymentService::instance($type)->create($openid, $order['order_no'], $order['amount_real'], '商城订单支付', '', $data['payment_back'], $data['payment_image']);
  332. $this->success('获取支付参数', ['order' => $this->app->db->name('ShopOrder')->where($map)->find() ?: new \stdClass(), 'param' => $param]);
  333. } catch (HttpResponseException $exception) {
  334. throw $exception;
  335. } catch (\Exception $exception) {
  336. $this->error($exception->getMessage());
  337. }
  338. }
  339. /**
  340. * 主动取消未支付的订单
  341. * @throws \think\db\exception\DataNotFoundException
  342. * @throws \think\db\exception\DbException
  343. * @throws \think\db\exception\ModelNotFoundException
  344. */
  345. public function cancel()
  346. {
  347. [$map, $order] = $this->getOrderData();
  348. if (in_array($order['status'], [1, 2, 3])) {
  349. $result = $this->app->db->name('ShopOrder')->where($map)->update([
  350. 'status' => 0,
  351. 'cancel_status' => 1,
  352. 'cancel_remark' => '用户主动取消订单',
  353. 'cancel_datetime' => date('Y-m-d H:i:s'),
  354. ]);
  355. if ($result !== false && OrderService::instance()->stock($order['order_no'])) {
  356. // 触发订单取消事件
  357. $this->app->event->trigger('ShopOrderCancel', $order['order_no']);
  358. // 返回处理成功数据
  359. $this->success('订单取消成功');
  360. } else {
  361. $this->error('订单取消失败');
  362. }
  363. } else {
  364. $this->error('订单不可取消');
  365. }
  366. }
  367. /**
  368. * 用户主动删除已取消的订单
  369. * @throws \think\db\exception\DataNotFoundException
  370. * @throws \think\db\exception\DbException
  371. * @throws \think\db\exception\ModelNotFoundException
  372. */
  373. public function remove()
  374. {
  375. [$map, $order] = $this->getOrderData();
  376. if (empty($order)) $this->error('读取订单失败');
  377. if (in_array($order['status'], [0])) {
  378. $result = $this->app->db->name('ShopOrder')->where($map)->update([
  379. 'status' => 0,
  380. 'deleted_status' => 1,
  381. 'deleted_remark' => '用户主动删除订单',
  382. 'deleted_datetime' => date('Y-m-d H:i:s'),
  383. ]);
  384. if ($result !== false) {
  385. // 触发订单删除事件
  386. $this->app->event->trigger('ShopOrderRemove', $order['order_no']);
  387. // 返回处理成功数据
  388. $this->success('订单删除成功');
  389. } else {
  390. $this->error('订单删除失败');
  391. }
  392. } else {
  393. $this->error('订单不可删除');
  394. }
  395. }
  396. /**
  397. * 订单确认收货
  398. * @throws \think\db\exception\DataNotFoundException
  399. * @throws \think\db\exception\DbException
  400. * @throws \think\db\exception\ModelNotFoundException
  401. */
  402. public function confirm()
  403. {
  404. [$map, $order] = $this->getOrderData();
  405. if (in_array($order['status'], [5])) {
  406. if ($this->app->db->name('ShopOrder')->where($map)->update(['status' => 6]) !== false) {
  407. // 触发订单确认事件
  408. $this->app->event->trigger('ShopOrderConfirm', $order['order_no']);
  409. // 返回处理成功数据
  410. $this->success('订单确认成功');
  411. } else {
  412. $this->error('订单确认失败');
  413. }
  414. } else {
  415. $this->error('订单确认失败');
  416. }
  417. }
  418. /**
  419. * 获取输入订单
  420. * @return array [map, order]
  421. * @throws \think\db\exception\DataNotFoundException
  422. * @throws \think\db\exception\DbException
  423. * @throws \think\db\exception\ModelNotFoundException
  424. */
  425. private function getOrderData(): array
  426. {
  427. $map = $this->_vali(['uid.value' => $this->uuid, 'order_no.require' => '单号不能为空']);
  428. $order = $this->app->db->name('ShopOrder')->where($map)->find();
  429. if (empty($order)) $this->error('读取订单失败');
  430. return [$map, $order];
  431. }
  432. /**
  433. * 订单状态统计
  434. */
  435. public function total()
  436. {
  437. $data = ['t0' => 0, 't1' => 0, 't2' => 0, 't3' => 0, 't4' => 0, 't5' => 0, 't6' => 0];
  438. $query = $this->app->db->name('ShopOrder')->where(['uid' => $this->uuid, 'deleted_status' => 0]);
  439. foreach ($query->field('status,count(1) count')->group('status')->cursor() as $item) {
  440. $data["t{$item['status']}"] = $item['count'];
  441. }
  442. $this->success('获取订单统计', $data);
  443. }
  444. /**
  445. * 物流追踪查询
  446. */
  447. public function track()
  448. {
  449. try {
  450. $data = $this->_vali(['code.require' => '快递不能为空', 'number.require' => '单号不能为空']);
  451. $result = ExpressService::instance()->query($data['code'], $data['number']);
  452. empty($result['code']) ? $this->error($result['info']) : $this->success('快递追踪信息', $result);
  453. } catch (HttpResponseException $exception) {
  454. throw $exception;
  455. } catch (\Exception $exception) {
  456. $this->error($exception->getMessage());
  457. }
  458. }
  459. }