IntegralOrderLogic.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. <?php
  2. namespace app\shopapi\logic;
  3. use app\common\enum\{AccountLogEnum, IntegralGoodsEnum, IntegralOrderEnum, IntegralOrderRefundEnum, PayEnum};
  4. use app\common\logic\{AccountLogLogic, BaseLogic, IntegralOrderRefundLogic, PayNotifyLogic};
  5. use app\common\service\ConfigService;
  6. use expressage\Kd100;
  7. use expressage\Kdniao;
  8. use app\common\model\{Express, IntegralGoods, IntegralOrder, User, UserAddress};
  9. use app\common\service\FileService;
  10. use think\facade\Db;
  11. /**
  12. * 积分商城订单
  13. * Class IntegralOrderLogic
  14. * @package app\api\logic
  15. */
  16. class IntegralOrderLogic extends BaseLogic
  17. {
  18. /**
  19. * @notes 结算订单
  20. * @param $params
  21. * @return array
  22. * @author 段誉
  23. * @date 2022/3/31 11:34
  24. */
  25. public static function settlement($params)
  26. {
  27. // 用户地址
  28. $address = UserAddress::getOneAddress($params['user_id'], $params['address_id'] ?? 0);
  29. // 积分商品信息
  30. $goods = IntegralGoods::withoutField(['content'])->findOrEmpty($params['id'])->toArray();
  31. // 订单需支付总金额
  32. $orderAmount = 0;
  33. // 积分商品金额
  34. $goodsPrice = 0;
  35. // 兑换方式为纯积分
  36. if ($goods['exchange_way'] == IntegralGoodsEnum::EXCHANGE_WAY_HYBRID) {
  37. // 订单需支付总金额
  38. $goodsPrice = $goods['need_money'] * $params['num'];
  39. $orderAmount = $goodsPrice;
  40. }
  41. // 订单需支付总积分
  42. $orderIntegral = $goods['need_integral'] * $params['num'];
  43. // 运费
  44. $expressPrice = 0;
  45. // 快递配送 && 快递统一运费 && 运费>0
  46. if ($goods['delivery_way'] == IntegralGoodsEnum::DELIVERY_EXPRESS
  47. && $goods['express_type'] == IntegralGoodsEnum::EXPRESS_TYPE_UNIFIED
  48. && $goods['express_money'] > 0
  49. ) {
  50. $orderAmount = $orderAmount + $goods['express_money'];
  51. $expressPrice = $goods['express_money'];
  52. }
  53. return [
  54. 'address' => $address,
  55. 'goods' => $goods,
  56. 'need_pay' => $orderAmount > 0 ? 1 : 0,
  57. 'exchange_way' => $goods['exchange_way'],
  58. 'delivery_way' => $goods['delivery_way'],
  59. 'total_num' => intval($params['num']),
  60. 'express_price' => $expressPrice, // 运费
  61. 'goods_price' => round($goodsPrice, 2), // 商品金额(不包含运费)
  62. 'order_amount' => round($orderAmount, 2), // 订单需要的金额(包含运费)
  63. 'order_integral' => $orderIntegral, // 订单需支付的积分
  64. ];
  65. }
  66. /**
  67. * @notes 提交订单
  68. * @param $params
  69. * @return array|false
  70. * @author 段誉
  71. * @date 2022/3/31 14:24
  72. */
  73. public static function submitOrder($params)
  74. {
  75. Db::startTrans();
  76. try {
  77. // 结算详情(支付积分,支付金额)
  78. $settle = self::settlement($params);
  79. $settle['goods']['image'] = FileService::setFileUrl($settle['goods']['image']);
  80. // 提交前验证
  81. $user = User::findOrEmpty($params['user_id']);
  82. if ($user['user_integral'] < $settle['order_integral']) {
  83. throw new \Exception('积分不足');
  84. }
  85. if ($settle['total_num'] <= 0) {
  86. throw new \Exception('请选择商品数量');
  87. }
  88. // 提交订单
  89. $order = IntegralOrder::create([
  90. 'sn' => generate_sn((new IntegralOrder()), 'sn'),
  91. 'user_id' => $params['user_id'],
  92. 'order_source' => $params['terminal'],
  93. 'delivery_way' => $settle['goods']['delivery_way'],
  94. 'exchange_type' => $settle['goods']['type'],
  95. 'exchange_way' => $settle['goods']['exchange_way'],
  96. 'order_amount' => $settle['order_amount'],
  97. 'order_integral' => $settle['order_integral'],
  98. 'total_num' => $settle['total_num'],
  99. 'goods_price' => $settle['goods_price'],
  100. 'express_price' => $settle['express_price'],
  101. 'user_remark' => $params['user_remark'] ?? '',
  102. 'goods_snap' => $settle['goods'],
  103. 'address' => [
  104. 'contact' => $settle['address']['contact'],
  105. 'province' => $settle['address']['province_id'],
  106. 'city' => $settle['address']['city_id'],
  107. 'district' => $settle['address']['district_id'],
  108. 'address' => $settle['address']['address'],
  109. 'mobile' => $settle['address']['mobile'],
  110. ]
  111. ]);
  112. // 扣减应付积分
  113. if ($settle['order_integral'] > 0) {
  114. User::where(['id' => $params['user_id']])
  115. ->dec('user_integral', $settle['order_integral'])
  116. ->update();
  117. AccountLogLogic::add(
  118. $params['user_id'],
  119. AccountLogEnum::INTEGRAL_DEC_INTEGRAL_ORDER,
  120. AccountLogEnum::DEC,
  121. $settle['order_integral'], $order['sn']
  122. );
  123. }
  124. // 兑换方式-积分 且没有运费 扣减积分后 直接支付完成
  125. if ($settle['goods']['exchange_way'] == IntegralGoodsEnum::EXCHANGE_WAY_INTEGRAL && $settle['order_amount'] <= 0) {
  126. PayNotifyLogic::handle('integral', $order['sn']);
  127. }
  128. Db::commit();
  129. return ['order_id' => $order['id'], 'type' => 'integral'];
  130. } catch (\Exception $e) {
  131. Db::rollback();
  132. self::$error = $e->getMessage();
  133. return false;
  134. }
  135. }
  136. /**
  137. * @notes 订单详情
  138. * @param $id
  139. * @return array
  140. * @author 段誉
  141. * @date 2022/3/2 10:22
  142. */
  143. public static function detail($id)
  144. {
  145. $order = IntegralOrder::where(['id' => $id])
  146. ->withoutField(['content', 'order_source', 'transaction_id', 'refund_amount'])
  147. ->append(['delivery_address', 'pay_way_desc', 'order_status_desc', 'btns'])
  148. ->findOrEmpty()->toArray();
  149. $goodsSnap = $order['goods_snap'];
  150. unset($order['goods_snap']);
  151. $order['goods'] = [
  152. 'image' => FileService::getFileUrl($goodsSnap['image']),
  153. 'name' => $goodsSnap['name'],
  154. 'exchange_way' => $goodsSnap['exchange_way'],
  155. 'need_integral' => $goodsSnap['need_integral'],
  156. 'need_money' => $goodsSnap['need_money'],
  157. 'total_num' => $order['total_num'],
  158. ];
  159. return $order;
  160. }
  161. static function wxReceiveDetail($id, $user_id)
  162. {
  163. $order = IntegralOrder::where('id', $id)->where('user_id', $user_id)->findOrEmpty()->toArray();
  164. return [
  165. 'transaction_id' => $order['transaction_id'] ?? '',
  166. ];
  167. }
  168. /**
  169. * @notes 确认收货
  170. * @param $id
  171. * @param $userId
  172. * @author 段誉
  173. * @date 2022/3/31 15:04
  174. */
  175. public static function confirm($id, $userId)
  176. {
  177. //更新订单状态
  178. IntegralOrder::update([
  179. 'order_status' => IntegralOrderEnum::ORDER_STATUS_COMPLETE,
  180. 'confirm_time' => time(),
  181. ], ['id' => $id, 'user_id' => $userId]);
  182. }
  183. /**
  184. * @notes 删除订单
  185. * @param $id
  186. * @author 段誉
  187. * @date 2022/3/31 15:06
  188. */
  189. public static function del($id)
  190. {
  191. IntegralOrder::destroy($id);
  192. }
  193. /**
  194. * @notes 取消订单
  195. * @param $id
  196. * @return bool
  197. * @throws \think\db\exception\DataNotFoundException
  198. * @throws \think\db\exception\DbException
  199. * @throws \think\db\exception\ModelNotFoundException
  200. * @author 段誉
  201. * @date 2022/4/1 11:27
  202. */
  203. public static function cancel($id)
  204. {
  205. Db::startTrans();
  206. try {
  207. $order = IntegralOrder::findOrEmpty($id);
  208. // 更新订单状态, 退回库存, 扣减销量
  209. IntegralOrderRefundLogic::cancelOrder($id);
  210. // 退回已支付积分
  211. IntegralOrderRefundLogic::refundOrderIntegral($id);
  212. // 退回订单已支付积分或已支付金额
  213. if ($order['pay_status'] == PayEnum::ISPAID) {
  214. IntegralOrderRefundLogic::refundOrderAmount($id);
  215. }
  216. Db::commit();
  217. return true;
  218. } catch (\Exception $e) {
  219. Db::rollback();
  220. self::$error = $e->getMessage();
  221. IntegralOrderRefundLogic::addRefundLog(
  222. $order, $order['order_amount'],
  223. IntegralOrderRefundEnum::STATUS_FAIL,
  224. $e->getMessage()
  225. );
  226. return false;
  227. }
  228. }
  229. /**
  230. * @notes 物流轨迹
  231. * @param $id
  232. * @return array
  233. * @author 段誉
  234. * @date 2022/3/31 18:53
  235. */
  236. public static function orderTraces($id)
  237. {
  238. $field = [
  239. 'o.order_status', 'o.total_num', 'o.confirm_time', 'o.address',
  240. 'o.pay_time', 'o.express_time', 'o.create_time', 'o.goods_snap',
  241. 'd.express_name', 'd.invoice_no', 'd.send_type', 'd.express_id'
  242. ];
  243. // 获取订单信息,物流信息
  244. $order = IntegralOrder::alias('o')->field($field)
  245. ->join('integral_delivery d', 'd.order_id = o.id')
  246. ->where(['o.id' => $id])
  247. ->append(['delivery_address'])
  248. ->findOrEmpty();
  249. if ($order->isEmpty() || $order['send_type'] != 1) {
  250. return [];
  251. }
  252. $traces = [
  253. 'order' => [
  254. 'goods_image' => FileService::getFileUrl($order['goods_snap']['image']),
  255. 'goods_count' => $order['total_num'],
  256. 'express_name' => $order['express_name'],
  257. 'invoice_no' => $order['invoice_no'],
  258. 'order_status' => $order['order_status'],
  259. 'send_type' => $order['send_type'],
  260. ],
  261. 'take' => [
  262. 'contact' => $order['address']['contact'],
  263. 'mobile' => $order['address']['mobile'],
  264. 'address' => $order['delivery_address'],
  265. ],
  266. 'finish' => [
  267. 'title' => '交易完成',
  268. 'tips' => ($order['order_status'] == IntegralOrderEnum::ORDER_STATUS_COMPLETE) ? '订单交易完成' : '',
  269. 'time' => ($order['order_status'] == IntegralOrderEnum::ORDER_STATUS_COMPLETE) ? $order['confirm_time'] : '',
  270. ],
  271. 'delivery' => [
  272. 'title' => '运输中',
  273. 'traces' => self::getTracesData($order)
  274. ],
  275. 'shipment' => self::getTracesShipment($order),
  276. 'pay' => [
  277. 'title' => '已支付',
  278. 'tips' => '订单支付成功,等待商家发货',
  279. 'time' => $order['pay_time']
  280. ],
  281. 'buy' => [
  282. 'title' => '已下单',
  283. 'tips' => '订单提交成功',
  284. 'time' => $order['create_time']
  285. ]
  286. ];
  287. return $traces;
  288. }
  289. /**
  290. * @notes 获取物流轨迹数据
  291. * @param $order
  292. * @return array|false
  293. * @author 段誉
  294. * @date 2022/3/31 18:41
  295. */
  296. public static function getTracesData($order)
  297. {
  298. // 获取物流查询配置, 发起查询申请
  299. $expressType = ConfigService::get('logistics_config', 'express_type', '');
  300. $expressBird = unserialize(ConfigService::get('logistics_config', 'express_bird', ''));
  301. $expressHundred = unserialize(ConfigService::get('logistics_config', 'express_hundred', ''));
  302. // (没有物流配置 || 发货方式不是快递配送 || 订单为发货) 不查询快递
  303. if (empty($expressType)
  304. || $order['send_type'] != 1
  305. || $order['order_status'] <= IntegralOrderEnum::ORDER_STATUS_DELIVERY
  306. || ($expressType === 'express_bird' && empty($expressBird))
  307. || ($expressType === 'express_hundred' && empty($expressHundred))
  308. ) {
  309. return [];
  310. }
  311. if ($expressType === 'express_bird') {
  312. $expressHandle = (new Kdniao($expressBird['ebussiness_id'], $expressBird['app_key']));
  313. $expressField = 'codebird';
  314. } else {
  315. $expressHandle = (new Kd100($expressHundred['customer'], $expressHundred['app_key']));
  316. $expressField = 'code100';
  317. }
  318. //快递编码
  319. $expressCode = Express::where('id', $order['express_id'])->value($expressField);
  320. //获取物流轨迹
  321. if ($expressCode === 'SF' && $expressType === 'express_bird') {
  322. $expressHandle->logistics($expressCode, $order['invoice_no'], substr($order['address']->mobile, -4));
  323. } else {
  324. $expressHandle->logistics($expressCode, $order['invoice_no']);
  325. }
  326. $traces = $expressHandle->logisticsFormat();
  327. if ($traces != false) {
  328. foreach ($traces as &$item) {
  329. $item = array_values(array_unique($item));
  330. }
  331. }
  332. return $traces;
  333. }
  334. /**
  335. * @notes 订单物流-待收货信息
  336. * @param $order
  337. * @return string[]
  338. * @author 段誉
  339. * @date 2022/3/3 17:30
  340. */
  341. public static function getTracesShipment($order)
  342. {
  343. $shipment = [
  344. 'title' => '已发货',
  345. 'tips' => '',
  346. 'time' => '',
  347. ];
  348. //待收货
  349. if ($order['order_status'] == IntegralOrderEnum::ORDER_STATUS_GOODS) {
  350. $shipment['tips'] = '商品已出库';
  351. $shipment['time'] = date('Y-m-d H:i:s', $order['shipping_time']);
  352. }
  353. return $shipment;
  354. }
  355. }