Order.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. <?php
  2. namespace addons\unishop\behavior;
  3. use addons\unishop\extend\Ali;
  4. use addons\unishop\extend\Hashids;
  5. use addons\unishop\extend\Wechat;
  6. use addons\unishop\model\Address;
  7. use addons\unishop\model\Config;
  8. use addons\unishop\model\DeliveryRule as DeliveryRuleModel;
  9. use addons\unishop\model\OrderProduct;
  10. use addons\unishop\model\Product;
  11. use app\admin\model\unishop\Coupon;
  12. use think\Db;
  13. use think\Exception;
  14. /**
  15. * 订单相关行为
  16. * Class Order
  17. * @package addons\unishop\behavior
  18. */
  19. class Order
  20. {
  21. /**
  22. * 创建订单之后
  23. * 行为一:根据订单减少商品库存 增加"已下单未支付数量"
  24. * 行为二:如果选了购物车的就删除购物车的信息
  25. * @param array $params 商品属性
  26. * @param array $extra [specNumber] => ['spec1' => 'number1','spec2' => 'number2']
  27. */
  28. public function createOrderAfter(&$params, $extra)
  29. {
  30. // 行为一
  31. $key = 0;
  32. $productExtend = new \addons\unishop\extend\Product;
  33. $prefix = \think\Config::get('database.prefix');
  34. if (Config::isPessimism()) {
  35. // 悲观锁
  36. foreach ($extra['specNumber'] as $spec => $number) {
  37. $result = 0;
  38. if (is_numeric($spec) && $params[$key]['use_spec'] == Product::SPEC_OFF) {
  39. $result = Db::execute('UPDATE ' . $prefix . "unishop_product SET no_buy_yet = no_buy_yet+{$number}, real_sales = real_sales+{$number}, stock = stock-{$number} WHERE id = {$params[$key]['id']}");
  40. } else if ($params[$key]['use_spec'] == Product::SPEC_ON) {
  41. $info = $productExtend->getBaseData($params[$key], $spec);
  42. // mysql<5.7.13时用
  43. //if (mysql < 5.7.13) {
  44. $spec = str_replace(',', '","', $spec);
  45. $search = '"stock":"' . $info['stock'] . '","value":["' . $spec . '"]';
  46. $stock = $info['stock'] - $number;
  47. $replace = '"stock":\"' . $stock . '\","value":["' . $spec . '"]';
  48. $sql = 'UPDATE ' . $prefix . "unishop_product SET no_buy_yet = no_buy_yet+{$number}, stock = stock-{$number}, real_sales = real_sales+{$number} ,`specTableList` = REPLACE(specTableList,'$search','$replace') WHERE id = {$params[$key]['id']}";
  49. $result = Db::execute($sql);
  50. //}
  51. //下面语句直接操作JSON
  52. //if (mysql >= 5.7.13) {
  53. //$info['stock'] -= $number;
  54. //$result = Db::execute("UPDATE fa_unishop_product SET no_buy_yet = no_buy_yet+{$number}, real_sales = real_sales+{$number}, stock = stock-{$number},specTableList = JSON_REPLACE(specTableList, '$[{$info['key']}].stock', {$info['stock']}) WHERE id = {$params[$key]['id']}");
  55. //}
  56. }
  57. if ($result == 0) { // 锁生效
  58. throw new Exception('下单失败,请重试');
  59. }
  60. $key++;
  61. }
  62. } else {
  63. // 乐观锁
  64. foreach ($extra['specNumber'] as $spec => $number) {
  65. $result = 0;
  66. if (is_numeric($spec) && $params[$key]['use_spec'] == Product::SPEC_OFF) {
  67. $result = Db::execute('UPDATE ' . $prefix . "unishop_product SET no_buy_yet = no_buy_yet+{$number}, real_sales = real_sales+{$number}, stock = stock-{$number} WHERE id = {$params[$key]['id']} AND stock = {$params[$key]['stock']}");
  68. } else if ($params[$key]['use_spec'] == Product::SPEC_ON) {
  69. $info = $productExtend->getBaseData($params[$key], $spec);
  70. // mysql<5.7.13时用
  71. //if (mysql < 5.7.13) {
  72. $spec = str_replace(',', '","', $spec);
  73. $search = '"stock":"' . $info['stock'] . '","value":["' . $spec . '"]';
  74. $stock = $info['stock'] - $number;
  75. $replace = '"stock":\"' . $stock . '\","value":["' . $spec . '"]';
  76. $sql = 'UPDATE ' . $prefix . "unishop_product SET no_buy_yet = no_buy_yet+{$number}, real_sales = real_sales+{$number}, stock = stock-{$number},`specTableList` = REPLACE(specTableList,'$search','$replace') WHERE id = {$params[$key]['id']} AND stock = {$params[$key]['stock']}";
  77. $result = Db::execute($sql);
  78. //}
  79. //下面语句直接操作JSON
  80. //if (mysql >= 5.7.13) {
  81. //$info['stock'] -= $number;
  82. //$result = Db::execute("UPDATE fa_unishop_product SET no_buy_yet = no_buy_yet+{$number}, real_sales = real_sales+{$number}, stock = stock-{$number},specTableList = JSON_REPLACE(specTableList, '$[{$info['key']}].stock', {$info['stock']}) WHERE id = {$params[$key]['id']} AND stock = {$params[$key]['stock']}");
  83. //}
  84. }
  85. if ($result == 0) { // 锁生效
  86. throw new Exception('下单失败,请重试');
  87. }
  88. $key++;
  89. }
  90. }
  91. // 行为二
  92. if (!empty($extra['cart'])) {
  93. $cart = $extra['cart'];
  94. Db::execute('DELETE FROM ' . $prefix . "unishop_cart WHERE id IN ($cart) AND user_id = $extra[userId]");
  95. }
  96. // More ...
  97. }
  98. /**
  99. * 检查是否符合创建订单的条件
  100. * 条件1:商品是否存在
  101. * 条件2:商品库存情况
  102. * 条件3:收货地址是否在范围内
  103. * 条件4:是否使用优惠券,优惠券能否可用
  104. * @param array $params
  105. * @param array $extra
  106. * @throws Exception
  107. * @throws \think\exception\DbException
  108. */
  109. public function createOrderBefore(&$params, $extra)
  110. {
  111. if(empty($extra['cart'])){
  112. $specs = explode(',', $extra['spec']);
  113. foreach ($specs as &$spec) {
  114. $spec = str_replace('|', ',', $spec);
  115. }
  116. $numbers = explode(',', $extra['number']);
  117. $productIds = explode(',', $extra['product_id']);
  118. if (count($specs) !== count($numbers) || count($specs) !== count($productIds)) {
  119. throw new Exception(__('Parameter error'));
  120. }
  121. }else{
  122. $carts = Db::name('unishop_cart')->field('id,product_id,spec,number')
  123. ->whereIn('id', $extra['cart'])
  124. ->order(['id' => 'desc'])
  125. ->select();
  126. // dump($carts);
  127. $numbers = array_column($carts,'number');
  128. $productIds = array_column($carts,'product_id');
  129. $specs = array_column($carts,'spec');
  130. foreach ($productIds as $key => $productId) {
  131. $productIds[$key] = Hashids::encodeHex($productId);
  132. }
  133. foreach ($specs as $key => $spec) {
  134. $specs[$key] = is_null($spec) ? '' : $spec;
  135. }
  136. }
  137. /*dump($numbers);
  138. dump($productIds);
  139. dump($specs);
  140. exit;*/
  141. // 订单价格
  142. $orderPrice = 0;
  143. // 条件一
  144. $products = [];
  145. foreach ($productIds as $key => &$productId) {
  146. $productId = Hashids::decodeHex($productId);
  147. $products[$key] = Db::name('unishop_product')
  148. ->where(['id' => $productId, 'switch' => Product::SWITCH_ON])
  149. ->lock(Config::isPessimism()) // Todo 是否使用悲观锁
  150. ->find();
  151. if (!$products[$key]) {
  152. throw new Exception(__('There are not exist or Offline'));
  153. }
  154. }
  155. if (count($products) == 0 || count($productIds) != count($products)) {
  156. throw new Exception(__('There are offline product'));
  157. }
  158. // 从购物车下单多个商品时,有同一个商品的不同规格导致减库存问题
  159. if (count($productIds) > 0) {
  160. $reduceStock = [];
  161. foreach ($products as $key => $value) {
  162. if (!isset($reduceStock[$value['id']])) {
  163. $reduceStock[$value['id']] = $numbers[$key];
  164. } else {
  165. $products[$key]['stock'] -= $reduceStock[$value['id']];
  166. $reduceStock[$value['id']] += $numbers[$key];
  167. }
  168. }
  169. }
  170. // 条件二
  171. foreach ($products as $key => $product) {
  172. $productInfo = (new \addons\unishop\extend\Product())->getBaseData($product, $specs[$key] ? $specs[$key] : '');
  173. if ($productInfo['stock'] < $numbers[$key]) {
  174. throw new Exception(__('Insufficient inventory,%s pieces left', $productInfo['stock']));
  175. }
  176. $orderPrice = bcadd($orderPrice, bcmul($productInfo['sales_price'], $numbers[$key], 2), 2);
  177. $baseProductInfo[] = $productInfo;
  178. }
  179. // 条件三
  180. /*$delivery = (new DeliveryRuleModel())->cityInScopeOfDelivery($extra['city_id'], $extra['delivery_id']);
  181. if (!$delivery) {
  182. throw new Exception(__('Your receiving address is not within the scope of delivery'));
  183. } else {
  184. if ($delivery['min'] > array_sum($numbers)) {
  185. throw new Exception(__('You must purchase at least %s item to use this shipping method', $delivery['min']));
  186. }
  187. }*/
  188. $delivery = [];
  189. /*$address = (new Address)->where(['id' => $extra['address_id'], 'user_id' => $extra['userId']])->find();
  190. if (!$address) {
  191. throw new Exception(__('Address not exist'));
  192. }*/
  193. $address = [];
  194. // 条件四
  195. /*if ($extra['coupon_id']) {
  196. $coupon = Coupon::get($extra['coupon_id']);
  197. if ($coupon['switch'] == Coupon::SWITCH_OFF || $coupon['deletetime'] || $coupon['starttime'] > time() || $coupon['endtime'] < time()) {
  198. throw new Exception('此优惠券不可用');
  199. }
  200. // 至少消费多少钱
  201. if ($coupon['least'] > $orderPrice) {
  202. throw new Exception('选中的优惠券不满足使用条件');
  203. }
  204. } else {
  205. $coupon = [];
  206. }*/
  207. $coupon = [];
  208. $params = [$products, $delivery, $coupon, $baseProductInfo, $address, $orderPrice, $specs, $numbers];
  209. }
  210. /**
  211. * 支付成功
  212. * 行为一:更改订单的支付状态,更新支付时间。
  213. * 行为二:减少商品的已下单但未支付的数量
  214. * @param $params
  215. * @param $extra
  216. * @throws \think\db\exception\DataNotFoundException
  217. * @throws \think\db\exception\ModelNotFoundException
  218. * @throws \think\exception\DbException
  219. */
  220. public function paidSuccess(&$params, $extra)
  221. {
  222. $order = &$params;
  223. $order->have_paid = time();// 更新支付时间为当前时间
  224. $order->pay_type = $extra['pay_type'];
  225. $order->save();
  226. $orderProductModel = new OrderProduct();
  227. $orderProducts = $orderProductModel
  228. ->with('product')
  229. ->where(['order_id' => $order->id])
  230. ->select();
  231. foreach ($orderProducts as $product) {
  232. if (isset($product->product)) {
  233. $product->product->no_buy_yet -= $product->number;
  234. $product->product->save();
  235. }
  236. }
  237. // More ...
  238. }
  239. /**
  240. * 支付失败
  241. * @param $params
  242. */
  243. public function paidFail(&$params)
  244. {
  245. $order = &$params;
  246. $order->have_paid = \addons\unishop\model\Order::PAID_NO;
  247. $order->save();
  248. // More ...
  249. }
  250. /**
  251. * 订单退款
  252. * 行为一:退款
  253. * @param array $params 订单数据
  254. */
  255. public function orderRefund(&$params)
  256. {
  257. $order = &$params;
  258. // 行为一
  259. switch ($order['pay_type']) {
  260. case \addons\unishop\model\Order::PAY_WXPAY:
  261. $app = Wechat::initEasyWechat('payment');
  262. $result = $app->refund->byOutTradeNumber($params['out_trade_no'], $params['out_trade_no'], bcmul($params['total_price'], 100), bcmul($params['refund']['amount'], 100), [
  263. // 可在此处传入其他参数,详细参数见微信支付文档
  264. 'refund_desc' => $params['refund']['reason_type'],
  265. ]);
  266. break;
  267. case \addons\unishop\model\Order::PAY_ALIPAY:
  268. $alipay = Ali::initAliPay();
  269. $order = [
  270. 'out_trade_no' => $params['out_trade_no'],
  271. 'refund_amount' => $params['total_price'],
  272. ];
  273. $result = $alipay->refund($order);
  274. break;
  275. }
  276. // More ...
  277. }
  278. }