Orders.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. <?php
  2. namespace app\api\controller\mall;
  3. use app\common\controller\Api;
  4. use app\common\model\OrderInfo;
  5. use app\common\model\OrderVoucher;
  6. use app\common\model\Refund;
  7. use app\common\service\OrderService;
  8. use app\common\service\RefundService;
  9. use app\common\validate\TaxValidate;
  10. use Carbon\Carbon;
  11. use think\Cache;
  12. use think\Db;
  13. use app\common\model\Orders as Order;
  14. use Yansongda\Supports\Arr;
  15. /**
  16. * 商城订单接口
  17. * @inheritdoc
  18. */
  19. class Orders extends Api
  20. {
  21. use TaxValidate;
  22. protected $noNeedRight="*";
  23. public $taxApi=false;
  24. /**
  25. * 预下单
  26. * @ApiMethod (POST)
  27. * @ApiParams (name=frm,description="来源1直接下单2购物车")
  28. * @ApiParams (name=form,description="key为规格id,对象:num购买数量,num_install安装数量")
  29. * @ApiParams (name=coupon_use,description=是否使用优惠券)
  30. * @ApiParams (name=coupon_id,description=优惠券id)
  31. * @ApiReturnParams (name=amount_total,description=总价,包含没有优惠的所有的价格)
  32. * @ApiReturnParams (name=amount_pay,description=实际需要支付的价格)
  33. * @ApiReturnParams (name=amount_coupon,description=优惠券优惠的金额)
  34. * @ApiReturnParams (name=amount_coupon_kill,description=秒杀活动优惠的金额)
  35. * @ApiReturnParams (name=amount_install,description=安装费的金额)
  36. * @ApiReturnParams (name=amount_discount,description=总优惠金额)
  37. * @ApiReturnParams (name=num,description=总数量)
  38. * @ApiReturnParams (name=num_install,description=总安装数量)
  39. * @ApiReturnParams (name=goods,0.description=商品列表数组)
  40. * @ApiReturnParams (name=goods.0.num,description=购买数量)
  41. * @ApiReturnParams (name=goods.0.num_install,description=安装数量)
  42. * @ApiReturnParams (name=goods.0.sku,description=sku对象)
  43. * @ApiReturnParams (name=goods.0.goods,description=商品对象)
  44. * @ApiReturnParams (name=goods.0.amount_total,description=总价,包含没有优惠的所有的价格)
  45. * @ApiReturnParams (name=goods.0.amount_pay,description=实际需要支付的价格)
  46. * @ApiReturnParams (name=goods.0.amount_goods,description=商品总价)
  47. * @ApiReturnParams (name=goods.0.amount_goods_real,description=商品扣除优惠的实际总价)
  48. * @ApiReturnParams (name=goods.0.amount_coupon,description=优惠券优惠的金额)
  49. * @ApiReturnParams (name=goods.0.amount_coupon_kill,description=秒杀活动优惠的金额)
  50. * @ApiReturnParams (name=goods.0.amount_install,description=安装费的金额)
  51. */
  52. public function preview(){
  53. $data=$this->_validate([
  54. 'coupon_id|优惠券'=>['integer','requireIf:coupon_use,1'],
  55. 'form|商品'=>['require','array','min:1'],
  56. 'frm|来源'=>['require','in:1,2'],
  57. ]);
  58. $user=$this->auth->getUser();
  59. $goods=$data['form'];
  60. $service=new OrderService();
  61. $service->setUser($user);
  62. $service->setGoods($goods);
  63. $service->setFrm($data['frm']);
  64. $service->setCouponUse($data['coupon_use']??false);
  65. $service->setCouponId($data['coupon_id']??0);
  66. $info=$service->calculate();
  67. $this->success('',$info);
  68. }
  69. /**
  70. * 提交订单
  71. * @ApiMethod (POST)
  72. * @ApiParams (name=frm,description="来源1直接下单2购物车")
  73. * @ApiParams (name=form,description="key为规格id,对象:num购买数量,num_install安装数量,{1:{num:1,num_install:2}}")
  74. * @ApiParams (name=coupon_use,description=是否使用优惠券)
  75. * @ApiParams (name=coupon_id,description=优惠券id)
  76. * @ApiParams (name=remark,description=留言)
  77. * @ApiParams (name=address_id,description=收货地址id)
  78. * @ApiParams (name=is_tax,description=是1否0需要开票)
  79. * @ApiParams (name=customer_send_time,description="预定发货时间,datetime")
  80. * @ApiParams (name=tax,description=发票信息)
  81. * @ApiParams (name=tax.paper_type,description=1电子2纸质)
  82. * @ApiParams (name=tax.self_type,description=1专票2普票)
  83. * @ApiParams (name=tax.u_type,description=1个人2企业)
  84. * @ApiParams (name=tax.name,description="姓名u_type=1时候必须")
  85. * @ApiParams (name=tax.com_name,description="单位名称,self_type=1或2,u_type=2时候必须")
  86. * @ApiParams (name=tax.bank_name,description="开户行,self_type=1,u_type=2时候必须")
  87. * @ApiParams (name=tax.bank_no,description="开户行账号,self_type=1,u_type=2时候必须")
  88. * @ApiParams (name=tax.tax_no,description="税号,self_type=1或2,u_type=2时候必须")
  89. * @ApiParams (name=tax.reg_addr,description="注册地址,self_type=1,u_type=2时候必须")
  90. * @ApiParams (name=tax.reg_phone,description="注册电话,self_type=1,u_type=2时候必须")
  91. */
  92. public function create(){
  93. $data=$this->_validate([
  94. 'coupon_id|优惠券'=>['integer','requireIf:coupon_use,1'],
  95. 'form|商品'=>['require','array','min:1'],
  96. 'frm|来源'=>['require','in:1,2'],
  97. 'remark|备注'=>['max:50'],
  98. 'address_id|收货地址'=>['require','integer','gt:0'],
  99. 'customer_send_time|发货时间'=>['date'],
  100. ]);
  101. $this->validateTax();
  102. if(!empty($data['customer_send_time']) && strtotime($data['customer_send_time'])<Carbon::now()->startOfDay()->addDays(config('site.send_delay_day'))->timestamp){
  103. $this->error('发货时间过早');
  104. }
  105. $user=$this->auth->getUser();
  106. $address=$user->address()->find($data['address_id']);
  107. abortIf(!$address,'地址不存在');
  108. $goods=$data['form'];
  109. $service=new OrderService();
  110. $service->setUser($user);
  111. $service->setGoods($goods);
  112. $service->setFrm(1);
  113. $service->setSubmit(true);
  114. $service->setClearCart(true);
  115. $service->setCouponUse($data['coupon_use']??false);
  116. $service->setCouponId($data['coupon_id']??0);
  117. Db::startTrans();
  118. $info=$service->calculate();
  119. if($info['amount_pay']==0){
  120. $this->error('支付金额有误');
  121. }
  122. $order=new Order();
  123. $order['user_id']=$user['id'];
  124. $order['order_no']=order_no();
  125. $order['amount_total']=$info['amount_total'];
  126. $order['amount_pay']=$info['amount_pay'];
  127. $order['amount_install']=$info['amount_install'];
  128. $order['amount_coupon']=$info['amount_coupon'];
  129. $order['amount_coupon_kill']=$info['amount_coupon_kill'];
  130. $order['amount_coupon_level']=$info['amount_coupon_level'];
  131. $order['amount_discount']=$info['amount_discount'];
  132. $order['amount_cmn']=$info['amount_cmn'];
  133. $order['num']=$info['num'];
  134. $order['num_install']=$info['num_install'];
  135. $order['tax']=$data['tax']??null;
  136. $order->save();
  137. $amount_profit=0;
  138. foreach ($info['goods'] as $everyGoods){
  139. $orderInfo=OrderInfo::saveInfo($order,$everyGoods);
  140. $amount_profit=bcadd($amount_profit,$orderInfo['amount_profit']);
  141. }
  142. $order['amount_profit']=$amount_profit;
  143. $order['amount_profit_per']=bcmul(100,bcdiv($order['amount_profit'],$order['amount_pay']));
  144. $order->save();
  145. $order->address()->save($address->orderAddress());
  146. Db::commit();
  147. $this->success('',$this->orderDetail($order['id']));
  148. }
  149. /**
  150. * 订单详情
  151. * @ApiParams (name=id,description="订单ID")
  152. */
  153. public function show(){
  154. $data=$this->_validate([
  155. 'id'=>['require'],
  156. ]);
  157. $user=$this->auth->getUser();
  158. $order=$this->orderDetail($data['id']);
  159. $goodsBak=$order->info[0]->goodsBak;
  160. if(!($goodsBak['goods']['is_fix']??0)) {
  161. Cache::remember("user_order_show_{$user['id']}", time(), 86400);
  162. }
  163. $this->success('',$order);
  164. }
  165. protected function orderDetail($orderId){
  166. $order= Order::with(['info','info.refund','address','logistics','logistics.com','voucher'])->findOrFail($orderId);
  167. foreach ($order['info'] as $info){
  168. $info->append(['goods','sku']);
  169. Refund::makeRefundConfig($info);
  170. }
  171. return $order;
  172. }
  173. /**
  174. * 取消订单
  175. * @ApiParams (name=id,description="订单ID")
  176. */
  177. public function cancel(){
  178. $data=$this->_validate([
  179. 'id'=>['require'],
  180. ]);
  181. $user=$this->auth->getUser();
  182. $order=$user->orders()->findOrFail($data['id']);
  183. $order->allowCancel() && $order->makeCancel();
  184. $this->success();
  185. }
  186. /**
  187. * 用户订单列表
  188. * @ApiParams (name=page,description=分页)
  189. * @ApiParams (name=limit,description=分页)
  190. * @ApiParams (name=status,description=状态)
  191. * @ApiReturnParams (name=is_evaled,description=是否已评价)
  192. */
  193. public function users(){
  194. $user=$this->auth->getUser();
  195. $map=[];
  196. $status=input('status');
  197. if(is_numeric($status)){
  198. $map['status']=$status;
  199. }
  200. if($this->taxApi){
  201. $map['tax']=['neq','[]'];
  202. }
  203. $orders=$user->orders()
  204. ->where($map)
  205. ->with(['info','voucher','logistics','logistics.com'])
  206. ->order('id','desc')
  207. ->paginate(input('limit',15));
  208. foreach ($orders as $order){
  209. //$order['info']=$order->info()->with(['goodsBak'])->find();
  210. $order->append(['is_evaled']);
  211. }
  212. $this->success('',$orders);
  213. }
  214. /**
  215. * 支付订单
  216. * @ApiParams (name=id,description=订单ID)
  217. * @ApiParams (name=pay_type,description=支付方式1企业网银2微信3支付宝4银联5代付6线下支付)
  218. * @ApiReturnParams (name=qr,description=微信支付宝返回的二维码)
  219. * @ApiReturnParams (name=account_name,description=线下支付会返【账户名称】)
  220. * @ApiReturnParams (name=bank_no,description=线下支付会返【银行卡号】)
  221. * @ApiReturnParams (name=bank_name,description=线下支付会返【银行名称】)
  222. */
  223. public function pay(){
  224. $data=$this->_validate([
  225. 'id'=>['require'],
  226. 'pay_type|支付方式'=>['require','in:'.implode(',',array_keys(Order::getPayTypes()))]
  227. ]);
  228. $user=$this->auth->getUser();
  229. $order=Order::findOrFail($data['id']);
  230. if(!$order->is_wait_pay){
  231. $this->error('非待支付状态无法支付');
  232. }
  233. $data=$order->makePayInfo($data['pay_type']);
  234. $this->success('',$data);
  235. }
  236. /**
  237. * 申请售后或修改售后信息
  238. * @ApiParams (name=refund_type,description=1退款2退款退货3仅退货4取消订单5换货保修)
  239. * @ApiParams (name=num,description=退货数量)
  240. * @ApiParams (name=num_install,description=退安装数量)
  241. * @ApiParams (name=order_info_id,description=订单ID)
  242. * @ApiParams (name=refund_by,description=配置里的type里的key)
  243. * @ApiParams (name=reason1,description=配置里的reason里的key)
  244. * @ApiParams (name=reason2,description=原因2)
  245. * @ApiParams (name=amount,description=金额)
  246. * @ApiParams (name=fix_order_no,description=维修订单号,在拍维修费时需要)
  247. */
  248. public function refund(){
  249. $data=$this->_validate([
  250. 'order_info_id|订单'=>['require','integer','gt:0'],
  251. ]);
  252. $user=$this->auth->getUser();
  253. Db::startTrans();
  254. $orderInfo=$user->orderInfo()->lock(true)->findOrFail($data['order_info_id']);
  255. $order= Order::lock(true)->where('id',$orderInfo['order_id'])->find();
  256. if(!$order){
  257. Db::rollback();
  258. $this->error('订单不存在');
  259. }
  260. $order->allowRefund();
  261. $refund=$orderInfo->refund;
  262. if($refund && $refund['refund_status']==$refund::REFUND_ING){
  263. Db::rollback();
  264. $this->error('您已提交,不能再次提交');
  265. }
  266. $config=Refund::makeRefundConfig($orderInfo,false,false);
  267. if(empty($config)){
  268. Db::rollback();
  269. $this->error('该订单无法满足售后条件');
  270. }
  271. $this->_validate([
  272. 'refund_type|售后方式'=>['require','in:'.implode(',',array_filter(array_keys($config['refund_type'])))],
  273. //'num|数量'=>['require','integer','gt:0',"elt:{$orderInfo['num']}"],
  274. 'refund_by|退货方式'=>['require','integer','egt:0','in:'.implode(',',array_keys($config['type']))],
  275. 'reason1|退款退货原因'=>['require','integer','egt:0','in:'.implode(',',array_keys($config['reason']))],
  276. 'reason2|原因'=>['require','max:100'],
  277. ]);
  278. if($config['req_amount']){
  279. /*$this->_validate([
  280. 'amount|金额'=>['require','float','gt:0',"elt:{$orderInfo['amount_pay']}"],
  281. ]);*/
  282. $data['amount']=RefundService::setOrderInfo($orderInfo,$orderInfo['num'],$orderInfo['num_install'])->amount(true);
  283. $data['num']=$config['num'];
  284. $data['num_install']=$config['num_install'];
  285. }else{
  286. $data['amount']=0;
  287. $this->_validate([
  288. 'num_install|安装数量'=>['require','integer','gt:0',"elt:{$orderInfo['num_install']}"],
  289. ]);
  290. }
  291. $fix_order_id=0;
  292. if($config['req_order']){
  293. $this->_validate([
  294. 'fix_order_no|维修订单号'=>['require',],
  295. ]);
  296. $showTime=Cache::get("user_order_show_{$user['id']}",null);
  297. if(!$showTime){
  298. $this->error('请先支付维修单',$showTime);
  299. }
  300. $fixOrder=$user->orders()->statusPay()
  301. ->where('order_no',$data['fix_order_no'])
  302. ->where('pay_time','>',$showTime)
  303. ->find();
  304. if(!$fixOrder){
  305. $this->error('未找到维修订单',$showTime);
  306. }
  307. $goodsBak=$fixOrder->info[0]->goodsBak;
  308. if(!$fixOrder||($goodsBak['goods']['is_fix']??0)!=1){
  309. $this->error('请完成维修订单支付');
  310. }
  311. $fix_order_id=$fixOrder['id'];
  312. }
  313. if($data['refund_type']==Refund::REFUND_TYPE_ALL){
  314. $refundCount=$orderInfo->refunds()->where('refund_type',Refund::REFUND_TYPE_ALL)->count();
  315. if($refundCount>=3){
  316. Db::rollback();
  317. $this->error('申请超过3次无法再次申请');
  318. }
  319. }
  320. $newRefund=Refund::create([
  321. 'user_id'=>$user->id,
  322. 'order_id'=>$orderInfo['order_id'],
  323. 'order_info_id'=>$orderInfo['id'],
  324. 'goods_id'=>$orderInfo['goods_id'],
  325. 'refund_type'=>$data['refund_type'],
  326. 'refund_by'=>$data['refund_by'],
  327. 'num'=>$data['num'],
  328. 'num_install'=>$data['num_install'],
  329. 'amount'=>$data['amount'],
  330. 'reason1'=>$data['reason1'],
  331. 'reason2'=>$data['reason2'],
  332. 'fix_order_id'=>$fix_order_id,
  333. ]);
  334. $orderInfo['refund_id']=$newRefund['id'];
  335. $orderInfo->save();
  336. #未发货直接退款取消
  337. /* if($order->is_wait_send){
  338. $newRefund['refund_type']=Refund::REFUND_TYPE_MONEY;
  339. $newRefund['amount']=$orderInfo['amount_pay'];
  340. $newRefund['num']=$orderInfo['num'];
  341. $newRefund->save();
  342. $newRefund->makeAudit(1);
  343. }*/
  344. Db::commit();
  345. $this->success();
  346. }
  347. /**
  348. * 取消售后
  349. * @ApiParams (name=order_info_id,description=子订单ID)
  350. */
  351. public function refund_cancel(){
  352. $data=$this->_validate([
  353. 'order_info_id'=>['require'],
  354. ]);
  355. $user=$this->auth->getUser();
  356. $orderInfo=$user->orderInfo()->findOrFail($data['order_info_id']);
  357. $refund=$orderInfo->refund;
  358. if(!$refund){
  359. $this->error('售后不存在');
  360. }
  361. if(!$refund->allowCancel()){
  362. $this->error('该售后不允许取消');
  363. }
  364. $refund->makeCancel();
  365. $this->success();
  366. }
  367. /**
  368. * 确认收货
  369. * @ApiParams (name=id,description=订单ID)
  370. */
  371. public function commit(){
  372. $data=$this->_validate([
  373. 'id'=>['require','integer'],
  374. ]);
  375. $user=$this->auth->getUser();
  376. $order=$user->orders()->findOrFail($data['id']);
  377. if(!$order->allowOver()){
  378. $this->error('该订单不允许确认');
  379. }
  380. $order->makeOver();
  381. $this->success();
  382. }
  383. /**
  384. * 上传(重新上传)打款凭证
  385. * @ApiParams (name=id,description=订单ID)
  386. * @ApiParams (name=bank_no,description=银行卡号)
  387. * @ApiParams (name=bank_name,description=银行开户行)
  388. * @ApiParams (name=bank_username,description=银行持卡人姓名)
  389. * @ApiParams (name=image,description=凭证图片)
  390. */
  391. public function po_complete(){
  392. $user=$this->auth->getUser();
  393. $data=$this->_validate([
  394. 'id|订单'=>['require'],
  395. //'bank_no|银行卡号'=>['require'],
  396. //'bank_name|银行开户行'=>['require'],
  397. //'bank_username|银行持卡人姓名'=>['require'],
  398. //'image|凭证图片'=>['require'],
  399. ]);
  400. Db::startTrans();
  401. $voucher=OrderVoucher::where('order_id',$data['id'])->order('id','desc')->lock(true)->find();
  402. if($voucher && $voucher['status']==1){
  403. Db::rollback();
  404. $this->error('该凭证已通过无法再次提交');
  405. }
  406. $order=$user->orders()->lock(true)->findOrFail($data['id']);
  407. if($order->is_closed){
  408. Db::rollback();
  409. $this->error('订单已关闭');
  410. }
  411. if($order->is_payed && $order->pay_type!=$order::PT_OFF){
  412. $this->error('支付方式不支持提交');
  413. }
  414. $newData= Arr::only($data,['bank_no','bank_name','bank_username','image']);
  415. $newData['status']=0;
  416. if($voucher && $voucher['status']==0){
  417. $voucher->save($newData);
  418. }else{
  419. $newData['order_id']=$data['id'];
  420. $newData['user_id']=$user['id'];
  421. $voucher=OrderVoucher::create($newData);
  422. }
  423. if($order->is_payed){
  424. $voucher['status']=1;
  425. $voucher['is_auto']=1;
  426. $voucher->save();
  427. }
  428. $order['order_voucher_id']=$voucher['id'];
  429. $order['pay_type']= Order::PT_OFF;
  430. $order->save();
  431. Db::commit();
  432. $this->success('',$voucher);
  433. }
  434. /**
  435. * 售后列表
  436. * @ApiParams (name=page,description=分页)
  437. * @ApiParams (name=limit,description=分页)
  438. * @ApiReturnParams (name=id,description=退款记录ID)
  439. * @ApiReturnParams (name=refund_status,description="退款状态10申请处理中20申请通过30申请驳回40已取消")
  440. * @ApiReturnParams (name=refund_type,description="退款退货类型1仅退款2退款退货3仅退货")
  441. * @ApiReturnParams (name=refund_by,description="退货方式")
  442. * @ApiReturnParams (name=reason1,description="退款退货原因")
  443. * @ApiReturnParams (name=reason2,description="退款退货原因")
  444. * @ApiReturnParams (name=num,description="退款退货数量")
  445. * @ApiReturnParams (name=amount,description="退款退货金额")
  446. * @ApiReturnParams (name=create_time,description="创建时间")
  447. * @ApiReturnParams (name=audit_remark,description="审核备注")
  448. * @ApiReturnParams (name=order_no,description="退款单号")
  449. * @ApiReturnParams (name=pay_status,description="退款状态1成功2失败")
  450. * @ApiReturnParams (name=pay_remark,description="支付备注")
  451. */
  452. public function service_list(){
  453. $user=$this->auth->getUser();
  454. $list=$user->refund()
  455. ->order('id','desc')
  456. ->with(['order_info','orders'])
  457. ->paginate(input('limit',15));
  458. $this->success('',$list);
  459. }
  460. /**
  461. * 订单物流查询
  462. * @ApiParams (name=id,description=订单ID)
  463. * @ApiReturnParams (name=time,description="时间")
  464. * @ApiReturnParams (name=content,description="内容")
  465. */
  466. public function logistics_order(){
  467. $user=$this->auth->getUser();
  468. $data=$this->_validate([
  469. 'id'=>['require'],
  470. ]);
  471. $order=$user->orders()->findOrFail($data['id']);
  472. $logistics=$order->logistics;
  473. if(!$logistics){
  474. $this->error('未上传物流信息');
  475. }
  476. $info['logistics']=logistics()
  477. ->setNo($logistics['trans_no'])
  478. ->setLogistics($logistics->com)
  479. ->setPhone($order->address->mobile)
  480. ->query();
  481. $this->success('',$info);
  482. }
  483. /**
  484. * 订单物流地图轨迹
  485. * @ApiParams (name=id,description=订单ID)
  486. * @ApiReturnParams (name=trailUrl,description="轨迹地图链接")
  487. */
  488. public function logistics_map(){
  489. $user=$this->auth->getUser();
  490. $data=$this->_validate([
  491. 'id'=>['require'],
  492. ]);
  493. $order=$user->orders()->findOrFail($data['id']);
  494. $logistics=$order->logistics;
  495. if(!$logistics){
  496. $this->error('未上传物流信息');
  497. }
  498. $info['logistics']=logistics()
  499. ->setNo($logistics['trans_no'])
  500. ->setLogistics($logistics->com)
  501. ->setPhone($order->address->mobile)
  502. ->setExtend([
  503. 'from' =>$logistics->from(),
  504. 'to'=>$order->address->to(),
  505. ])
  506. ->maptrack();
  507. $this->success('',$info);
  508. }
  509. }