You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

309 lines
12 KiB

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