Order.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. <?php
  2. namespace app\api\controller\inspection;
  3. use app\api\controller\inspection\Base;
  4. use app\common\Enum\PayEnum;
  5. use app\common\Service\OrderService;
  6. use app\common\Enum\OrderEnum;
  7. use app\common\Service\OrderActionService;
  8. use app\common\Enum\OrderActionEnum;
  9. use app\common\Enum\OrderGoodsEnum;
  10. use app\common\model\OrderGoods;
  11. use app\common\Service\Order\OrderShipService;
  12. use think\Db;
  13. use app\common\library\easywechatPlus\WechatMiniProgramShop;
  14. use app\common\facade\Wechat;
  15. /**
  16. * 验货端订单接口
  17. *
  18. * 包含功能:
  19. * - 订单详情查询
  20. * - 订单列表查询
  21. * - 订单发货确认
  22. * - 订单统计功能(按日/月/年统计)
  23. * - 订单趋势数据
  24. *
  25. * 统计功能说明:
  26. * - 验收订单:已验货通过的订单
  27. * - 退回订单:验货不通过的订单
  28. * - 售后订单:退款相关的订单
  29. */
  30. class Order extends Base
  31. {
  32. protected $noNeedLogin = [];
  33. //订单详情
  34. public function detail()
  35. {
  36. // 验证请求参数
  37. $validate = new \app\api\validate\Order();
  38. $orderId = $this->request->get('orderId');
  39. $params = ['orderId' => $orderId];
  40. if (!$validate->scene('detail')->check($params)) {
  41. $this->error($validate->getError());
  42. }
  43. // 获取当前验货员所属的供应商ID
  44. $supplierId = $this->application->supplier_id;
  45. if (!$supplierId) {
  46. $this->error('您未绑定供应商');
  47. }
  48. // 使用供应商订单详情方法,只获取该供应商的商品
  49. $order = OrderService::getSupplierOrderDetail($orderId, $supplierId, 0);
  50. if (empty($order)) {
  51. $this->error('未找到订单或该订单不包含您供应商的商品');
  52. }
  53. /** @var \app\common\model\Order $order */
  54. //$order->append(['order_status_text']);
  55. $address = OrderService::getAddressInfo($orderId);
  56. $order->address = $address;
  57. // $order->append(['status_text']);
  58. // $order->hidden(explode(',', 'method,transactionid,updatetime,deletetime'));
  59. // $order->expiretime = $order->expiretime - time();
  60. $order->order_status_text = OrderEnum::STATUS_TEXT_MAP_INSPECTION[$order->order_status];
  61. $this->success('', $order);
  62. }
  63. //订单列表
  64. public function index()
  65. {
  66. // 验证请求参数
  67. $validate = new \app\api\validate\inspection\Order();
  68. $param = $this->request->param();
  69. $param['time_range'] = '1'; // 添加触发时间范围验证的字段
  70. if (!$validate->scene('lists')->check($param)) {
  71. $this->error($validate->getError());
  72. }
  73. // 设置默认值
  74. $userId = 0;
  75. $param['page'] = $param['page'] ?? 1;
  76. $param['page_size'] = $param['page_size'] ?? 10;
  77. $status = $param['status'] ?? 0; // 默认为0(全部订单)
  78. $param['keywords'] = $param['keywords'] ?? '';
  79. $status = OrderEnum::SHOW_INSPECTION_TYPE_STATUS_MAP[$status];
  80. // 查询对应的工厂 信息
  81. $supplierId = $this->application->supplier_id;
  82. $list = OrderService::getSupplierOrderList($supplierId ,$userId, $param, $status);
  83. foreach ($list as $item) {
  84. // $item->append(['order_status_text']);
  85. $field = 'id,order_sn,amount,goods_price,order_amount,express_name,express_no,order_goods,order_status_text,order_status';
  86. $item->visible(explode(',', $field));
  87. $item->order_status_text = OrderEnum::STATUS_TEXT_MAP_INSPECTION[$item->order_status];
  88. }
  89. $this->success('获取成功', $list);
  90. }
  91. //确认发货
  92. public function send()
  93. {
  94. // 验证请求参数
  95. $userId = 0;
  96. $validate = new \app\api\validate\inspection\Order();
  97. $params = [
  98. 'order_id' => $this->request->post('order_id'),
  99. 'order_goods_id' => $this->request->post('order_goods_id'),
  100. 'express_id' => $this->request->post('express_id'),
  101. 'express_no' => $this->request->post('express_no'),
  102. 'express_image' => $this->request->post('express_image/a', [])
  103. ];
  104. if (!$validate->scene('send')->check($params)) {
  105. $this->error($validate->getError());
  106. }
  107. //根据ID 查询快递公司
  108. $expressId = intval($params['express_id'] ?? 0);
  109. $express = OrderShipService::getExpressById($expressId);
  110. if (empty($express)) {
  111. $this->error('快递公司不存在');
  112. }
  113. $orderId = intval($params['order_id'] ?? 0);
  114. $orderGoodsId = intval($params['order_goods_id'] ?? 0);
  115. if (empty($orderId)) {
  116. $this->error('订单ID不能为空');
  117. }
  118. if (empty($orderGoodsId)) {
  119. $this->error('订单商品ID不能为空');
  120. }
  121. // 验证订单商品是否可以发货
  122. $orderData = OrderService::validateOrderGoodsForDelivery($orderId, $orderGoodsId);
  123. if (!$orderData) {
  124. $this->error('订单或订单商品不存在,或不允许发货');
  125. }
  126. $order = $orderData['order'];
  127. $orderGoods = $orderData['order_goods'];
  128. $expressName = $express->name;
  129. $expressCode = $express->code;
  130. $expressNo = trim($params['express_no'] ?? '');
  131. $expressImage = $params['express_image'] ?? [];
  132. // 事务处理
  133. Db::startTrans();
  134. try{
  135. // 创建包裹
  136. $orderExpress = OrderShipService::createOrderExpress($orderId, $order['user_id'], $expressName, $expressCode, $expressNo, 'input', '', [], $expressImage);
  137. if (!$orderExpress) {
  138. Db::rollback();
  139. $this->error('创建包裹失败');
  140. }
  141. // 1. 更新订单商品发货状态
  142. $result = OrderService::updateOrderGoodsDeliveryStatus($orderGoodsId, $expressName, $expressNo, $expressImage,$orderExpress->id);
  143. if (!$result) {
  144. Db::rollback();
  145. $this->error('更新订单商品发货状态失败');
  146. }
  147. // 2. 检查并更新订单状态
  148. OrderService::updateOrderStatusByDeliveryResult($orderId);
  149. // 3. 记录操作日志
  150. $orderActionService = new OrderActionService();
  151. $orderActionService->addInspectionAction(
  152. $order->order_sn,
  153. OrderActionEnum::ACTION_SHIP,
  154. $userId,
  155. '订单商品发货,商品:' . $orderGoods->goods_title . ',快递公司:' . $expressName . ',快递单号:' . $expressNo,
  156. $userId,
  157. [
  158. 'order_goods_id' => $orderGoodsId,
  159. 'goods_title' => $orderGoods->goods_title,
  160. 'express_name' => $expressName,
  161. 'express_no' => $expressNo,
  162. ]
  163. );
  164. // 提交事务
  165. Db::commit();
  166. } catch (\Exception $e) {
  167. // 回滚事务
  168. Db::rollback();
  169. $this->error('发货失败'.$e->getMessage());
  170. }
  171. // 4. 同步微信小程序订单发货状态 todo 队列处理
  172. $data = [
  173. 'order' => $order,
  174. 'items' => $orderGoods,
  175. 'express' => $orderExpress,
  176. 'delivery_type' => 'express',
  177. ];
  178. $this->syncWechatMiniProgramOrderDispatch($data,'send');
  179. $this->success('发货成功');
  180. }
  181. private function syncWechatMiniProgramOrderDispatch($params =[],$type='send'){
  182. $order = $params['order'];
  183. $items = $params['items'];
  184. $express = $params['express'];
  185. $dispatch_type = $params['delivery_type'];
  186. $uploadshoppingInfo = new WechatMiniProgramShop(Wechat::miniProgram());
  187. // 微信小程序,并且存在微信发货管理权限时,才推送发货消息
  188. if ($order['platform'] == 'WechatMiniProgram' && $uploadshoppingInfo->isTradeManaged()) {
  189. $hasNosend = OrderGoods::where('order_id', $order['id'])
  190. // ->where('refund_status', OrderGoodsEnum::REFUND_STATUS_NOREFUND)
  191. ->where('delivery_status', OrderGoodsEnum::DELIVERY_STATUS_NOSEND)->count();
  192. if ($type == 'send') {
  193. // if (!$hasNosend && in_array('wechat', $order->pay_type)) {
  194. if (!$hasNosend && $order->pay_type == PayEnum::METHOD_WECHAT) {
  195. // 所有 items 都已经发货,将交易信息上传微信
  196. $uploadshoppingInfo->uploadShippingInfos($order);
  197. }
  198. } else {
  199. // 修改物流单
  200. // if (!$hasNosend && in_array('wechat', $order->pay_types)) {
  201. if (!$hasNosend && $order->pay_type == PayEnum::METHOD_WECHAT) {
  202. // 所有 items 都已经发货,将交易信息上传微信
  203. $uploadshoppingInfo->uploadShippingInfos($order, $express, 'change');
  204. }
  205. }
  206. }
  207. // 添加自动确认收货队列,这个队列只自动确认 本次发货的 items
  208. // $confirm_days = Config::getConfigField('shop.order.auto_confirm');
  209. // $confirm_days = $confirm_days > 0 ? $confirm_days : 0;
  210. // if ($confirm_days) {
  211. // // 小于等于0, 不自动确认收货
  212. // \think\Queue::later(($confirm_days * 86400), '\addons\shopro\job\OrderAutoOper@autoConfirm', $params, 'shopro');
  213. // }
  214. }
  215. // 统计订单
  216. public function statistics()
  217. {
  218. // 验证请求参数
  219. $validate = new \app\api\validate\Order();
  220. $params = [
  221. 'type' => $this->request->param('type', 'day'),
  222. 'date' => $this->request->param('date', date('Y-m-d'))
  223. ];
  224. if (!$validate->scene('statistics')->check($params)) {
  225. $this->error($validate->getError());
  226. }
  227. // 获取验证后的参数
  228. $type = $params['type'];
  229. $date = $params['date'];
  230. // 根据类型处理日期和时间范围
  231. switch ($type) {
  232. case 'day':
  233. $startTime = strtotime($date . ' 00:00:00');
  234. $endTime = strtotime($date . ' 23:59:59');
  235. $displayDate = date('m-d', $startTime);
  236. // 前一天
  237. $prevStartTime = strtotime($date . ' 00:00:00') - 86400;
  238. $prevEndTime = strtotime($date . ' 23:59:59') - 86400;
  239. break;
  240. case 'month':
  241. $startTime = strtotime(date('Y-m-01 00:00:00', strtotime($date)));
  242. $endTime = strtotime(date('Y-m-t 23:59:59', strtotime($date)));
  243. $displayDate = date('Y-m', $startTime);
  244. // 前一个月
  245. $prevStartTime = strtotime(date('Y-m-01 00:00:00', strtotime($date . ' -1 month')));
  246. $prevEndTime = strtotime(date('Y-m-t 23:59:59', strtotime($date . ' -1 month')));
  247. break;
  248. case 'year':
  249. $startTime = strtotime(date('Y-01-01 00:00:00', strtotime($date)));
  250. $endTime = strtotime(date('Y-12-31 23:59:59', strtotime($date)));
  251. $displayDate = date('Y', $startTime) . '年';
  252. // 前一年
  253. $prevStartTime = strtotime(date('Y-01-01 00:00:00', strtotime($date . ' -1 year')));
  254. $prevEndTime = strtotime(date('Y-12-31 23:59:59', strtotime($date . ' -1 year')));
  255. break;
  256. }
  257. // 获取当前用户ID
  258. $userId = $this->auth->id;
  259. // 如果需要根据工厂统计,可以在这里添加工厂相关逻辑
  260. // 目前先按用户统计,后续可以根据实际的工厂关联字段进行调整
  261. // 当前期间统计
  262. $currentStats = $this->getOrderStatistics($userId, $startTime, $endTime);
  263. // 上期统计(用于对比)
  264. $prevStats = $this->getOrderStatistics($userId, $prevStartTime, $prevEndTime);
  265. // 计算增长率
  266. $growthRate = [
  267. 'inspection_rate' => $this->calculateGrowthRate($prevStats['inspection_count'], $currentStats['inspection_count']),
  268. 'return_rate' => $this->calculateGrowthRate($prevStats['return_count'], $currentStats['return_count']),
  269. 'aftersale_rate' => $this->calculateGrowthRate($prevStats['aftersale_count'], $currentStats['aftersale_count'])
  270. ];
  271. // 返回统计数据
  272. $data = [
  273. 'date' => $displayDate,
  274. 'type' => $type,
  275. 'current_period' => $currentStats,
  276. 'previous_period' => $prevStats,
  277. 'growth_rate' => $growthRate,
  278. 'statistics' => [
  279. 'inspection_count' => $currentStats['inspection_count'],
  280. 'return_count' => $currentStats['return_count'],
  281. 'aftersale_count' => $currentStats['aftersale_count']
  282. ]
  283. ];
  284. $this->success('统计数据获取成功', $data);
  285. }
  286. /**
  287. * 获取订单统计数据
  288. */
  289. private function getOrderStatistics($userId, $startTime, $endTime)
  290. {
  291. // 统计验收订单(验货通过)
  292. $inspectionCount = \app\common\model\Order::where('user_id', $userId)
  293. ->where('order_status', OrderEnum::STATUS_INSPECTION_PASS)
  294. ->where('createtime', '>=', $startTime)
  295. ->where('createtime', '<=', $endTime)
  296. ->count();
  297. // 统计退回订单(验货不通过)
  298. $returnCount = \app\common\model\Order::where('user_id', $userId)
  299. ->where('order_status', OrderEnum::STATUS_INSPECTION_FAIL)
  300. ->where('createtime', '>=', $startTime)
  301. ->where('createtime', '<=', $endTime)
  302. ->count();
  303. // 统计待验收订单
  304. $aftersaleCount = \app\common\model\Order::where('user_id', $userId)
  305. ->whereIn('order_status', [OrderEnum::STATUS_PAY])
  306. ->where('createtime', '>=', $startTime)
  307. ->where('createtime', '<=', $endTime)
  308. ->count();
  309. return [
  310. 'inspection_count' => $inspectionCount,
  311. 'return_count' => $returnCount,
  312. 'aftersale_count' => $aftersaleCount
  313. ];
  314. }
  315. /**
  316. * 计算增长率
  317. */
  318. private function calculateGrowthRate($prevValue, $currentValue)
  319. {
  320. if ($prevValue == 0) {
  321. return $currentValue > 0 ? 100 : 0;
  322. }
  323. return round((($currentValue - $prevValue) / $prevValue) * 100, 2);
  324. }
  325. /**
  326. * 获取统计数据的时间范围选择
  327. */
  328. public function getStatisticsDateRange()
  329. {
  330. // 获取最早和最晚的订单时间
  331. $userId = $this->auth->id;
  332. $earliest = \app\common\model\Order::where('user_id', $userId)
  333. ->order('createtime', 'asc')
  334. ->value('createtime');
  335. $latest = \app\common\model\Order::where('user_id', $userId)
  336. ->order('createtime', 'desc')
  337. ->value('createtime');
  338. $data = [
  339. 'earliest_date' => $earliest ? date('Y-m-d', $earliest) : date('Y-m-d'),
  340. 'latest_date' => $latest ? date('Y-m-d', $latest) : date('Y-m-d'),
  341. 'current_date' => date('Y-m-d'),
  342. 'current_month' => date('Y-m'),
  343. 'current_year' => date('Y')
  344. ];
  345. $this->success('日期范围获取成功', $data);
  346. }
  347. /**
  348. * 获取历史趋势数据
  349. */
  350. public function getTrendData()
  351. {
  352. // 验证请求参数
  353. $validate = new \app\api\validate\Order();
  354. $params = [
  355. 'type' => $this->request->param('type', 'day'),
  356. 'days' => $this->request->param('days', 7)
  357. ];
  358. if (!$validate->scene('trend')->check($params)) {
  359. $this->error($validate->getError());
  360. }
  361. // 获取验证后的参数
  362. $type = $params['type'];
  363. $days = $params['days'];
  364. $userId = $this->auth->id;
  365. $trendData = [];
  366. for ($i = $days - 1; $i >= 0; $i--) {
  367. $date = date('Y-m-d', strtotime("-{$i} days"));
  368. $startTime = strtotime($date . ' 00:00:00');
  369. $endTime = strtotime($date . ' 23:59:59');
  370. $stats = $this->getOrderStatistics($userId, $startTime, $endTime);
  371. $trendData[] = [
  372. 'date' => $date,
  373. 'display_date' => date('m-d', $startTime),
  374. 'inspection_count' => $stats['inspection_count'],
  375. 'return_count' => $stats['return_count'],
  376. 'aftersale_count' => $stats['aftersale_count']
  377. ];
  378. }
  379. $this->success('趋势数据获取成功', $trendData);
  380. }
  381. // 获取快递公司列表
  382. public function getExpressCompany(){
  383. $list = OrderShipService::getExpressCompany();
  384. $this->success('快递公司列表获取成功', $list);
  385. }
  386. }