BlindboxOrder.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. <?php
  2. /**
  3. * Niushop商城系统 - 团队十年电商经验汇集巨献!
  4. * =========================================================
  5. * Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
  6. * ----------------------------------------------
  7. * 官方网址: https://www.niushop.com
  8. * =========================================================
  9. */
  10. namespace addon\blindbox\model;
  11. use addon\blindbox\model\Blindbox as BlindboxModel;
  12. use addon\blindbox\model\BlindboxGoods as BlindboxGoodsModel;
  13. use app\model\BaseModel;
  14. use app\model\member\Member;
  15. use app\model\member\MemberAccount;
  16. use app\model\message\Message;
  17. use app\model\order\Config;
  18. use app\model\system\Cron;
  19. use app\model\system\Pay;
  20. use think\facade\Cache;
  21. /**
  22. * 订单
  23. */
  24. class BlindboxOrder extends BaseModel
  25. {
  26. // 订单待付款
  27. const ORDER_CREATE = 0;
  28. //已完成
  29. const ORDER_PAY = 1;
  30. //订单已取消
  31. const ORDER_CANCEL = 2;
  32. private $goods_money = 0;//商品金额
  33. private $balance_money = 0;//余额
  34. private $delivery_money = 0;//配送费用
  35. private $coupon_money = 0;//优惠券金额
  36. private $adjust_money = 0;//调整金额
  37. private $invoice_money = 0;//发票费用
  38. private $promotion_money = 0;//优惠金额
  39. private $order_money = 0;//订单金额
  40. private $pay_money = 0;//支付总价
  41. private $goods_num = 0; //商品种数
  42. private $member_balance_money = 0;//会员账户余额(计算过程中会逐次减少)
  43. private $pay_type = 'ONLINE_PAY';//支付方式
  44. private $invoice_delivery_money = 0;
  45. private $error = 0; //是否有错误
  46. private $error_msg = ''; //错误描述
  47. /**
  48. * 订单创建
  49. * @param unknown $data
  50. */
  51. public function create($data)
  52. {
  53. //查询出会员相关信息
  54. $calculate_data = $this->calculate($data);
  55. if (isset($calculate_data[ 'code' ]) && $calculate_data[ 'code' ] < 0)
  56. return $calculate_data;
  57. if ($this->error > 0) {
  58. return $this->error([ 'error_code' => $this->error ], $this->error_msg);
  59. }
  60. if (!empty($calculate_data[ 'invoice_type' ])) {
  61. if ($calculate_data[ 'invoice_type' ] == 1 && $calculate_data[ 'invoice_full_address' ] == "") {
  62. //物流,同城
  63. if ($calculate_data[ 'shop_goods_list' ][ 'delivery' ][ 'delivery_type' ] == "express" || $calculate_data[ 'shop_goods_list' ][ 'delivery' ][ 'delivery_type' ] == "local") {
  64. $calculate_data[ 'invoice_full_address' ] = $calculate_data[ 'member_address' ][ 'full_address' ] . $calculate_data[ 'member_address' ][ 'address' ];
  65. $calculate_data[ 'shop_goods_list' ][ 'invoice_full_address' ] = $calculate_data[ 'member_address' ][ 'full_address' ] . $calculate_data[ 'member_address' ][ 'address' ];
  66. }
  67. //门店
  68. if ($calculate_data[ 'shop_goods_list' ][ 'delivery' ][ 'delivery_type' ] == "store") {
  69. $delivery_store_info = json_decode($calculate_data[ 'shop_goods_list' ][ 'delivery_store_info' ], true);
  70. $calculate_data[ 'invoice_full_address' ] = $delivery_store_info[ 'full_address' ];
  71. $calculate_data[ 'shop_goods_list' ][ 'invoice_full_address' ] = $delivery_store_info[ 'full_address' ];
  72. }
  73. }
  74. }
  75. $pay = new Pay();
  76. $out_trade_no = $pay->createOutTradeNo($data[ 'member_id' ]);
  77. model("blindbox_order")->startTrans();
  78. $order_info = model("blindbox_order")->getInfo([ [ 'blindbox_id', '=', $data[ 'blindbox_id' ] ], [ 'blindbox_goods_id', '=', $data[ 'blindbox_goods_id' ] ], [ 'status', '=', 1 ] ]);
  79. if ($order_info) {
  80. return $this->error([ 'error_code' => $this->error ], "该盲盒已被抢");
  81. }
  82. //循环生成多个订单
  83. try {
  84. //订单主表
  85. $order_no = $this->createOrderNo($data[ 'site_id' ], $data[ 'member_id' ]);
  86. $data_order = [
  87. 'order_number' => $order_no,
  88. 'site_id' => $data[ 'site_id' ],
  89. 'out_trade_no' => $out_trade_no,
  90. 'member_id' => $data[ 'member_id' ],
  91. 'buyer_ip' => request()->ip(),
  92. 'price' => $data[ 'price' ],
  93. 'blindbox_id' => $data[ 'blindbox_id' ],
  94. 'blindbox_goods_id' => $data[ 'blindbox_goods_id' ],
  95. 'sku_id' => $calculate_data[ 'blindbox_goods_info' ][ 'sku_id' ],
  96. 'num' => $data[ 'goods_num' ],
  97. 'create_time' => time(),
  98. 'order_from' => $data[ 'order_from' ],
  99. 'order_from_name' => $data[ 'order_from_name' ],
  100. 'is_invoice' => $data[ 'is_invoice' ],
  101. 'invoice_type' => $data[ 'invoice_type' ],
  102. 'invoice_title' => $data[ 'invoice_title' ],
  103. 'taxpayer_number' => $data[ 'taxpayer_number' ],
  104. 'invoice_content' => $data[ 'invoice_content' ],
  105. 'invoice_full_address' => $data[ 'invoice_full_address' ],
  106. 'is_tax_invoice' => $data[ 'is_tax_invoice' ],
  107. 'invoice_email' => $data[ 'invoice_email' ],
  108. 'invoice_title_type' => $data[ 'invoice_title_type' ],
  109. ];
  110. $order_id = model("blindbox_order")->add($data_order);
  111. $config_model = new Config();
  112. $balance_config = $config_model->getBalanceConfig($data[ 'site_id' ]);
  113. //扣除余额(统一扣除)
  114. if ($calculate_data[ "balance_money" ] > 0 && $balance_config[ 'data' ][ 'value' ][ 'balance_show' ] == 1) {
  115. $this->pay_type = "BALANCE";
  116. $calculate_data[ 'order_id' ] = $order_id;
  117. $balance_result = $this->useBalance($calculate_data, $data[ 'site_id' ]);
  118. if ($balance_result[ "code" ] < 0) {
  119. model("blindbox_order")->rollback();
  120. return $balance_result;
  121. }
  122. } else {
  123. $this->pay_money = $data[ 'price' ];
  124. }
  125. //生成支付单据
  126. $pay_body = '购买盲盒';
  127. $pay->addPay($data[ 'site_id' ], $out_trade_no, $this->pay_type, $pay_body, $pay_body, $this->pay_money, '', 'BlindboxGoodsOrderPayNotify', '');
  128. $this->addOrderCronClose($order_id, $data[ 'site_id' ]);//增加关闭订单自动事件
  129. //订单生成的消息
  130. $message_model = new Message();
  131. $message_model->sendMessage([ 'keywords' => "ORDER_CREATE", 'order_id' => $order_id, 'site_id' => $data[ 'site_id' ] ]);
  132. model("blindbox_order")->commit();
  133. return $this->success($out_trade_no);
  134. } catch (\Exception $e) {
  135. model("blindbox_order")->rollback();
  136. return $this->error('', $e->getMessage() . $e->getFile() . $e->getLine());
  137. }
  138. }
  139. /**
  140. * 订单计算
  141. * @param unknown $data
  142. */
  143. public function calculate($data)
  144. {
  145. $blindbox_model = new BlindboxModel();
  146. $blindbox_goods_model = new BlindboxGoodsModel();
  147. $data = $this->initMemberAccount($data);//初始化会员账户
  148. $blindbox_data = $blindbox_model->getBlindboxInfo([ [ 'site_id', '=', $data[ 'site_id' ] ], [ 'blindbox_id', '=', $data[ 'blindbox_id' ] ], [ 'blindbox_status', '=', 1 ] ])[ 'data' ] ?? [];
  149. if (empty($blindbox_data)) {
  150. return $this->error([], "未查询到盲盒活动");
  151. }
  152. $blindbox_goods_data = $blindbox_goods_model->getBlindboxGoodsInfo([ [ 'site_id', '=', $data[ 'site_id' ] ], [ 'id', '=', $data[ 'blindbox_goods_id' ] ] ])[ 'data' ] ?? [];
  153. if (!empty($blindbox_goods_data)) {
  154. if ($blindbox_goods_data[ 'status' ] == 1) {
  155. return $this->error([], "该盲盒已被别的用户开启");
  156. }
  157. }
  158. $data[ "blindbox_info" ] = $blindbox_data;
  159. $data[ "blindbox_goods_info" ] = $blindbox_goods_data;
  160. //余额付款
  161. if ($data[ 'is_balance' ] > 0 && $blindbox_data[ 'is_balance' ] == 1) {
  162. $this->member_balance_money = $data[ "member_account" ][ "balance_total" ] ?? 0;
  163. $balance_money = $data[ 'member_account' ][ 'balance' ] - $data[ 'price' ];
  164. if ($balance_money > 0) {
  165. $this->balance_money = $data[ 'price' ];
  166. } else {
  167. $this->balance_money = $data[ 'member_account' ][ 'balance' ];
  168. }
  169. }
  170. //总结计算
  171. $data[ 'delivery_money' ] = $this->delivery_money;
  172. $data[ 'coupon_money' ] = $this->coupon_money;
  173. $data[ 'adjust_money' ] = $this->adjust_money;
  174. $data[ 'invoice_money' ] = $this->invoice_money;
  175. $data[ 'invoice_delivery_money' ] = $this->invoice_delivery_money;
  176. $data[ 'promotion_money' ] = $this->promotion_money;
  177. $data[ 'order_money' ] = $this->order_money;
  178. $data[ 'balance_money' ] = $this->balance_money;
  179. $data[ 'pay_money' ] = $this->pay_money;
  180. $data[ 'goods_money' ] = $this->goods_money;
  181. $data[ 'goods_num' ] = $this->goods_num;
  182. return $data;
  183. }
  184. /**
  185. * 初始化会员账户
  186. * @param $data
  187. * @return mixed
  188. */
  189. public function initMemberAccount($data)
  190. {
  191. $member_model = new Member();
  192. $member_info_result = $member_model->getMemberDetail($data[ "member_id" ], $data[ "site_id" ]);
  193. $member_info = $member_info_result[ "data" ];
  194. if (!empty($member_info)) {
  195. if (!empty($member_info[ "pay_password" ])) {
  196. $is_pay_password = 1;
  197. } else {
  198. $is_pay_password = 0;
  199. }
  200. unset($member_info[ "pay_password" ]);
  201. $member_info[ "is_pay_password" ] = $is_pay_password;
  202. $data[ 'member_account' ] = $member_info;
  203. }
  204. return $data;
  205. }
  206. /**
  207. * 生成订单编号
  208. *
  209. * @param array $site_id
  210. */
  211. public function createOrderNo($site_id, $member_id = 0)
  212. {
  213. $time_str = date('YmdHi');
  214. $max_no = Cache::get($site_id . "_" . $member_id . "_" . $time_str);
  215. if (!isset($max_no) || empty($max_no)) {
  216. $max_no = 1;
  217. } else {
  218. $max_no = $max_no + 1;
  219. }
  220. $order_no = $time_str . $member_id . sprintf("%03d", $max_no);
  221. Cache::set($site_id . "_" . $member_id . "_" . $time_str, $max_no);
  222. return $order_no;
  223. }
  224. /**
  225. * 使用余额
  226. * @param $data
  227. * @param $site_id
  228. * @param string $from_type
  229. * @return array
  230. */
  231. public function useBalance($data, $site_id, $from_type = 'order')
  232. {
  233. $this->pay_type = "BALANCE";
  234. $member_model = new Member();
  235. $balance_money = $data[ "member_account" ][ "balance_money" ]; //现金余额
  236. $balance = $data[ "member_account" ][ "balance" ]; //储值余额
  237. $member_account_model = new MemberAccount();
  238. $surplus_banance = $data[ "balance_money" ];
  239. //优先扣除储值余额
  240. if ($balance > 0) {
  241. if ($balance >= $surplus_banance) {
  242. $real_balance = $surplus_banance;
  243. } else {
  244. $real_balance = $balance;
  245. }
  246. $result = $member_account_model->addMemberAccount($site_id, $data[ "member_id" ], "balance", -$real_balance, $from_type, $data[ 'order_id' ], "订单消费扣除");
  247. $surplus_banance -= $real_balance;
  248. }
  249. if ($surplus_banance > 0) {
  250. $result = $member_account_model->addMemberAccount($site_id, $data[ "member_id" ], "balance_money", -$surplus_banance, $from_type, $data[ 'order_id' ], "订单消费扣除");
  251. }
  252. $this->pay_money = $data[ "balance_money" ] - ( $real_balance + $surplus_banance ); //计算出实际支付金额
  253. return $result;
  254. }
  255. /**
  256. * 增加订单自动关闭事件
  257. * @param $order_id
  258. */
  259. public function addOrderCronClose($order_id, $site_id)
  260. {
  261. //计算订单自动关闭时间
  262. $config_model = new Config();
  263. $order_config_result = $config_model->getOrderEventTimeConfig($site_id);
  264. $order_config = $order_config_result[ "data" ];
  265. $now_time = time();
  266. if ($order_config[ "value" ][ "auto_close" ] > 0) {
  267. $execute_time = $now_time + $order_config[ "value" ][ "auto_close" ] * 60; //自动关闭时间
  268. $cron_model = new Cron();
  269. $cron_model->addCron(1, 0, "订单自动关闭", "BlindboxGoodsOrderClose", $execute_time, $order_id);
  270. }
  271. }
  272. /**
  273. * 盲盒订单详情
  274. * @param array $condition
  275. * @param string $field
  276. * @param string $alias
  277. * @param array $join
  278. * @return array
  279. */
  280. public function getOrderInfo($condition = [], $field = '*', $alias = 'a', $join = [])
  281. {
  282. $res = model('blindbox_order')->getInfo($condition, $field, $alias, $join);
  283. return $this->success($res);
  284. }
  285. /**
  286. * 订单线上支付回调
  287. * @param $data
  288. * @return array
  289. */
  290. public function orderOnlinePay($data)
  291. {
  292. $out_trade_no = $data[ "out_trade_no" ];
  293. $order_info = model("blindbox_order")->getInfo([ [ 'out_trade_no', '=', $out_trade_no ] ]);
  294. if ($order_info[ 'status' ] != self::ORDER_CREATE) {
  295. return $this->success();
  296. }
  297. $order_model = new \app\model\order\Order();
  298. $pay_type_list = $order_model->getPayType();
  299. model('blindbox_order')->startTrans();
  300. try {
  301. //修改订单状态
  302. $order_data = [
  303. 'status' => self::ORDER_PAY,
  304. 'pay_time' => time(),
  305. 'pay_type' => $data[ 'pay_type' ],
  306. 'pay_type_name' => $pay_type_list[ $data[ 'pay_type' ] ],
  307. ];
  308. model("blindbox_order")->update($order_data, [ [ 'order_id', '=', $order_info[ 'order_id' ] ] ]);
  309. $cron_model = new Cron();
  310. $cron_model->deleteCron([ [ 'event', '=', 'BlindboxGoodsOrderClose' ], [ 'relate_id', '=', $order_info[ 'order_id' ] ] ]);
  311. //支付成功减库存
  312. model('blindbox')->setInc([ [ 'blindbox_id', '=', $order_info[ 'blindbox_id' ] ] ], 'blindbox_num');
  313. model('blindbox')->setDec([ [ 'blindbox_id', '=', $order_info[ 'blindbox_id' ] ] ], 'blindbox_inventory');
  314. model('blindbox_goods')->update([ 'member_id' => $order_info[ 'member_id' ], 'status' => 1 ], [ [ 'id', '=', $order_info[ 'blindbox_goods_id' ] ] ]);
  315. $group_data = [
  316. 'blindbox_id' => $order_info[ 'blindbox_id' ],
  317. 'blindbox_goods_id' => $order_info[ 'blindbox_goods_id' ],
  318. 'member_id' => $order_info[ 'member_id' ],
  319. 'site_id' => $order_info[ 'site_id' ],
  320. 'sku_id' => $order_info[ 'sku_id' ],
  321. 'create_time' => time(),
  322. 'order_id' => $order_info[ 'order_id' ],
  323. ];
  324. event('GiftCardOrderPay', $order_info);
  325. model('blindbox_member_group')->add($group_data);
  326. model('blindbox_order')->commit();
  327. return $this->success();
  328. } catch (\Exception $e) {
  329. model('blindbox_order')->rollback();
  330. return $this->error('', $e->getMessage());
  331. }
  332. }
  333. /**
  334. * 关闭盲盒订单
  335. * @param $order_id
  336. * @return array|\multitype
  337. */
  338. public function cronCloseOrder($order_id)
  339. {
  340. $order_info = model('blindbox_order')->getInfo([ [ 'order_id', '=', $order_id ] ], 'status,card_id,num');
  341. if (!empty($order_info)) {
  342. if ($order_info[ 'status' ] == 0) {
  343. $res = model('blindbox_order')->update([ 'status' => 2 ], [ [ 'order_id', '=', $order_id ] ]);
  344. return $this->success($res);
  345. } else {
  346. return $this->error("", "该订单已支付或关闭");
  347. }
  348. } else {
  349. return $this->error("", "该订单不存在");
  350. }
  351. }
  352. }