PosOrderQsOprateController.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. package com.ruoyi.app.order;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONArray;
  4. import com.alibaba.fastjson.JSONObject;
  5. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  6. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  7. import com.ruoyi.app.order.dto.OrderPushBodyDto;
  8. import com.ruoyi.app.service.UserService;
  9. import com.ruoyi.app.utils.PayPush;
  10. import com.ruoyi.app.utils.event.PushEventService;
  11. import com.ruoyi.common.annotation.Anonymous;
  12. import com.ruoyi.common.core.controller.BaseController;
  13. import com.ruoyi.common.core.domain.AjaxResult;
  14. import com.ruoyi.common.exception.ServiceException;
  15. import com.ruoyi.common.utils.MessageUtils;
  16. import com.ruoyi.common.utils.StringUtils;
  17. import com.ruoyi.framework.manager.AsyncManager;
  18. import com.ruoyi.system.domain.InfoUser;
  19. import com.ruoyi.system.domain.PosOrder;
  20. import com.ruoyi.system.domain.PosStore;
  21. import com.ruoyi.system.domain.UserBilling;
  22. import com.ruoyi.system.service.IInfoUserService;
  23. import com.ruoyi.system.service.IPosOrderService;
  24. import com.ruoyi.system.service.IPosStoreService;
  25. import com.ruoyi.system.service.IUserBillingService;
  26. import com.ruoyi.system.utils.Auth;
  27. import com.ruoyi.system.utils.JwtUtil;
  28. import org.redisson.api.RLock;
  29. import org.redisson.api.RedissonClient;
  30. import org.springframework.beans.factory.annotation.Autowired;
  31. import org.springframework.context.i18n.LocaleContextHolder;
  32. import org.springframework.transaction.annotation.Transactional;
  33. import org.springframework.transaction.support.TransactionSynchronization;
  34. import org.springframework.transaction.support.TransactionSynchronizationManager;
  35. import org.springframework.web.bind.annotation.*;
  36. import java.util.*;
  37. import java.util.concurrent.TimeUnit;
  38. import java.util.stream.Collectors;
  39. import com.ruoyi.app.order.dto.PickupOrderInput;
  40. @RestController
  41. @RequestMapping("/system/orderQsOprate")
  42. public class PosOrderQsOprateController extends BaseController {
  43. @Autowired
  44. private IPosOrderService posOrderService;
  45. @Autowired
  46. private IInfoUserService infoUserService;
  47. @Autowired
  48. private PushEventService pushEventService;
  49. @Autowired
  50. private RedissonClient redissonClient;
  51. @Autowired
  52. private UserService userService;
  53. @Autowired
  54. private IUserBillingService userBillingService;
  55. @Autowired
  56. private IPosStoreService posStoreService;
  57. /**
  58. * 骑手接单:校验 type=0 且 afterSaleStatus=0,设置 deliveryStatus=1
  59. */
  60. @Anonymous
  61. @Auth
  62. @GetMapping("/acceptOrder")
  63. @Transactional(rollbackFor = Exception.class)
  64. public AjaxResult acceptOrder(@RequestHeader String token, @RequestParam Long id) {
  65. JwtUtil jwtUtil = new JwtUtil();
  66. PayPush push = new PayPush();
  67. String qsId = jwtUtil.getusid(token);
  68. PosOrder order = posOrderService.getOne(new LambdaQueryWrapper<PosOrder>().eq(PosOrder::getId, id));
  69. if (order == null) {
  70. throw new ServiceException(MessageUtils.message("no.order.not.found"));
  71. }
  72. if (order.getType() != null && order.getType() != 0L) {
  73. throw new ServiceException("仅外送订单支持骑手接单");
  74. }
  75. if (order.getAfterSaleStatus() != null && order.getAfterSaleStatus() > 0) {
  76. throw new ServiceException("订单存在售后申请,无法操作");
  77. }
  78. PosOrder posOrder = new PosOrder();
  79. posOrder.setId(order.getId());
  80. posOrder.setDeliveryStatus(1L);
  81. posOrder.setQsId(Long.valueOf(qsId));
  82. return setOrderQsState(posOrder, qsId, push);
  83. }
  84. /**
  85. * 骑手取餐:校验 type=0 且 afterSaleStatus=0,设置 deliveryStatus=2
  86. */
  87. @Anonymous
  88. @Auth
  89. @PostMapping("/pickupOrder")
  90. @Transactional(rollbackFor = Exception.class)
  91. public AjaxResult pickupOrder(@RequestHeader String token, @RequestBody PickupOrderInput input) {
  92. JwtUtil jwtUtil = new JwtUtil();
  93. PayPush push = new PayPush();
  94. String qsId = jwtUtil.getusid(token);
  95. PosOrder order = posOrderService.getOne(new LambdaQueryWrapper<PosOrder>().eq(PosOrder::getId, input.getId()));
  96. if (order == null) {
  97. throw new ServiceException(MessageUtils.message("no.order.not.found"));
  98. }
  99. if (order.getAfterSaleStatus() != null && order.getAfterSaleStatus() > 0) {
  100. throw new ServiceException("订单存在售后申请,无法操作");
  101. }
  102. PosOrder posOrder = new PosOrder();
  103. posOrder.setId(order.getId());
  104. posOrder.setDeliveryStatus(2L);
  105. posOrder.setQsImg(input.getQsImg());
  106. return setOrderQsState(posOrder, qsId, push);
  107. }
  108. /**
  109. * 骑手送达:设置 deliveryStatus=3,同时设置 state=3
  110. */
  111. @Anonymous
  112. @Auth
  113. @GetMapping("/deliverOrder")
  114. @Transactional(rollbackFor = Exception.class)
  115. public AjaxResult deliverOrder(@RequestHeader String token, @RequestParam Long id) {
  116. JwtUtil jwtUtil = new JwtUtil();
  117. PayPush push = new PayPush();
  118. String qsId = jwtUtil.getusid(token);
  119. PosOrder order = posOrderService.getOne(new LambdaQueryWrapper<PosOrder>().eq(PosOrder::getId, id));
  120. if (order == null) {
  121. throw new ServiceException(MessageUtils.message("no.order.not.found"));
  122. }
  123. PosOrder posOrder = new PosOrder();
  124. posOrder.setId(order.getId());
  125. posOrder.setDeliveryStatus(3L);
  126. posOrder.setState(3L);
  127. posOrder.setSdTime(new Date());
  128. return setOrderQsState(posOrder, qsId, push);
  129. }
  130. //骑手接单、取餐、送达
  131. private AjaxResult setOrderQsState(PosOrder posOrder, String qsId, PayPush push) {
  132. String lockKey = "order:qs:lock:" + posOrder.getId();
  133. RLock lock = redissonClient.getLock(lockKey);
  134. try {
  135. var local = LocaleContextHolder.getLocale();
  136. logger.info("骑手接单local信息:" + local.getLanguage());
  137. logger.info(JSON.toJSONString(local));
  138. // 尝试获取锁
  139. if (lock.tryLock(20L, 10L, TimeUnit.SECONDS)) {
  140. boolean releaseInfinally = true;
  141. try {
  142. userService.checkUserStatus(Long.valueOf(qsId));
  143. PosOrder orst = posOrderService.getById(posOrder.getId());
  144. if (orst.getDeliveryStatus() != null && orst.getDeliveryStatus() == 1L && posOrder.getDeliveryStatus() != null && posOrder.getDeliveryStatus() == 1L) {
  145. throw new ServiceException(MessageUtils.message("no.order.snatched"));
  146. }
  147. if (posOrder.getDeliveryStatus() != null && posOrder.getDeliveryStatus() == 1L) {
  148. QueryWrapper<PosOrder> wrapper = new QueryWrapper<>();
  149. wrapper.eq("qs_id", qsId);
  150. wrapper.in("delivery_status", 1, 2);
  151. List<PosOrder> orsts = posOrderService.list(wrapper);
  152. if (orsts.size() > 0) {
  153. throw new ServiceException(MessageUtils.message("no.exist.undelivered.order"));
  154. }
  155. }
  156. String userMessage = MessageUtils.message("no.message.push.delivery.personnel.receiving.order");
  157. if (posOrder.getDeliveryStatus() != null && posOrder.getDeliveryStatus() == 2L) {
  158. userMessage = MessageUtils.message("no.message.push.delivery.personnel.qspsz.order");
  159. } else if (posOrder.getDeliveryStatus() != null && posOrder.getDeliveryStatus() == 3L) {
  160. userMessage = MessageUtils.message("no.message.push.delivery.personnel.qsysd.order");
  161. }
  162. boolean org = posOrderService.saveOrUpdate(posOrder);
  163. if (org) {
  164. //到付订单,骑手送达时设置用户账单为完成
  165. if (posOrder.getDeliveryStatus() != null && posOrder.getDeliveryStatus() == 3L && "1".equals(orst.getCollectPayment())) {
  166. updateUserBill(orst.getDdId(), orst.getUserId(), orst.getQsId());
  167. }
  168. InfoUser user = infoUserService.getById(orst.getUserId());
  169. InfoUser shu = infoUserService.getById(orst.getShId());
  170. String finalUserMessage = userMessage;
  171. releaseInfinally = false;
  172. TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
  173. @Override
  174. public void afterCommit() {
  175. if (lock.isHeldByCurrentThread()) {
  176. lock.unlock();
  177. }
  178. // 给用户推送(骑手已接单/配送中/已送达)
  179. if (!StringUtils.isEmpty(user.getCid())) {
  180. String userTitle = "no.message.push.message";
  181. String userContent = "no.message.push.delivery.personnel.receiving.order";
  182. if (posOrder.getDeliveryStatus() != null && posOrder.getDeliveryStatus() == 2L) {
  183. userContent = "no.message.push.delivery.personnel.qspsz.order";
  184. } else if (posOrder.getDeliveryStatus() != null && posOrder.getDeliveryStatus() == 3L) {
  185. userContent = "no.message.push.delivery.personnel.qsysd.order";
  186. }
  187. final String finalUserContent = userContent;
  188. String userBody = OrderPushBodyDto.getJson(String.valueOf(orst.getDdId()), String.valueOf(posOrder.getState()));
  189. Long userUserId = user.getUserId();
  190. String userCid = user.getCid();
  191. String userCidType = "";
  192. String userDdId = String.valueOf(orst.getDdId());
  193. AsyncManager.me().execute(new TimerTask() {
  194. @Override
  195. public void run() {
  196. PayPush.userPushHandleLocal(push, pushEventService, userUserId, userCid, userTitle, finalUserContent, userBody, userCidType, userDdId);
  197. }
  198. });
  199. }
  200. // 给商家推送(骑手已接单时)
  201. if (!StringUtils.isEmpty(shu.getCid()) && posOrder.getDeliveryStatus() != null && posOrder.getDeliveryStatus() == 1L) {
  202. String shTitle = "no.message.push.message";
  203. String shContent = "no.message.push.delivery.personnel.receiving.order";
  204. String shBody = OrderPushBodyDto.getJson(String.valueOf(orst.getDdId()), String.valueOf(posOrder.getState()));
  205. Long shUserId = shu.getUserId();
  206. String shCid = shu.getCid();
  207. String shCidType = "";
  208. String shDdId = String.valueOf(orst.getDdId());
  209. AsyncManager.me().execute(new TimerTask() {
  210. @Override
  211. public void run() {
  212. PayPush.shPushHandleLocal(push, pushEventService, shUserId, shCid, shTitle, shContent, shBody, shCidType, shDdId);
  213. }
  214. });
  215. }
  216. }
  217. });
  218. return success(MessageUtils.message("no.modify.success"), posOrder.getId());
  219. } else {
  220. return error();
  221. }
  222. } finally {
  223. if (releaseInfinally && lock.isHeldByCurrentThread()) {
  224. lock.unlock();
  225. }
  226. }
  227. } else {
  228. throw new ServiceException(MessageUtils.message("no.system.busy.try.again"));
  229. }
  230. } catch (InterruptedException e) {
  231. Thread.currentThread().interrupt();
  232. throw new ServiceException(MessageUtils.message("no.operation.interrupted.try.again"));
  233. }
  234. }
  235. /**
  236. * 更新用户用户账单
  237. *
  238. * @param ddId
  239. * @param userId
  240. * @param qsId
  241. */
  242. private void updateUserBill(String ddId, Long userId, Long qsId) {
  243. QueryWrapper<UserBilling> queryWrapper = new QueryWrapper<>();
  244. queryWrapper.eq("dd_id", ddId);
  245. queryWrapper.eq("user_id", userId);
  246. queryWrapper.eq("type", "3");
  247. UserBilling userBill = userBillingService.getOne(queryWrapper);
  248. if (userBill != null) {
  249. userBill.setState("0");
  250. userBill.setIllustrate("Rider Payment Order");
  251. userBill.setBalancePay("1");
  252. userBill.setPaymentId(String.valueOf(qsId));
  253. userBillingService.saveOrUpdate(userBill);
  254. }
  255. }
  256. /**
  257. * 骑手端订单列表(Tab分页)
  258. */
  259. @Anonymous
  260. @Auth
  261. @GetMapping("/orderList")
  262. public AjaxResult orderList(@RequestHeader String token, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) String tab, @RequestParam(required = false) java.math.BigDecimal longitude, @RequestParam(required = false) java.math.BigDecimal latitude) {
  263. JwtUtil jwtUtil = new JwtUtil();
  264. String qsId = jwtUtil.getusid(token);
  265. Long riderId = Long.valueOf(qsId);
  266. LambdaQueryWrapper<PosOrder> wrapper = new LambdaQueryWrapper<>();
  267. wrapper.eq(PosOrder::getIsDisplay,true);
  268. switch (tab) {
  269. case "newTask":
  270. wrapper.eq(PosOrder::getType, 0L).eq(PosOrder::getDeliveryStatus, 0L).eq(PosOrder::getState, 2L).eq(PosOrder::getAfterSaleStatus, 0L);
  271. if (longitude != null && latitude != null) {
  272. wrapper.last("ORDER BY ST_Distance_Sphere(point(longitude, latitude), point(" + longitude + ", " + latitude + ")) ASC");
  273. } else {
  274. wrapper.orderByDesc(PosOrder::getCretim);
  275. }
  276. break;
  277. case "toPickup":
  278. wrapper.eq(PosOrder::getDeliveryStatus, 1L).eq(PosOrder::getQsId, riderId).eq(PosOrder::getAfterSaleStatus, 0L).orderByAsc(PosOrder::getCretim);
  279. break;
  280. case "delivering":
  281. wrapper.eq(PosOrder::getDeliveryStatus, 2L).eq(PosOrder::getQsId, riderId).eq(PosOrder::getAfterSaleStatus, 0L).orderByAsc(PosOrder::getCretim);
  282. break;
  283. case "completed":
  284. wrapper.eq(PosOrder::getState, 3L).eq(PosOrder::getAfterSaleStatus, 0L).eq(PosOrder::getQsId, riderId).orderByDesc(PosOrder::getCretim);
  285. break;
  286. case "cancelled":
  287. wrapper.eq(PosOrder::getState, 4L).eq(PosOrder::getQsId, riderId).eq(PosOrder::getAfterSaleStatus, 0L).orderByDesc(PosOrder::getCretim);
  288. break;
  289. case "refund":
  290. wrapper.gt(PosOrder::getAfterSaleStatus, 0L).eq(PosOrder::getQsId, riderId).orderByDesc(PosOrder::getCretim);
  291. break;
  292. default:
  293. break;
  294. }
  295. com.baomidou.mybatisplus.extension.plugins.pagination.Page<PosOrder> pageParam = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(page, size);
  296. com.baomidou.mybatisplus.extension.plugins.pagination.Page<PosOrder> result = posOrderService.page(pageParam, wrapper);
  297. Set<Long> mdIds = result.getRecords().stream().map(PosOrder::getMdId).filter(Objects::nonNull).collect(Collectors.toSet());
  298. Map<Integer, PosStore> storeMap = mdIds.isEmpty() ? Collections.emptyMap() :
  299. posStoreService.listByIds(mdIds).stream().collect(Collectors.toMap(PosStore::getId, s -> s));
  300. JSONArray arr=new JSONArray();
  301. for (PosOrder order : result.getRecords()) {
  302. // 自动序列化所有PosOrder字段,新增字段无需手动添加
  303. JSONObject org = JSON.parseObject(
  304. JSON.toJSONStringWithDateFormat(order, "yyyy-MM-dd HH:mm:ss")
  305. );
  306. org.put("juli",null);
  307. // 计算距离(km)
  308. if (order.getLongitude() != null && order.getLatitude() != null && longitude != null && latitude != null) {
  309. double lat1 = Math.toRadians(order.getLatitude().doubleValue());
  310. double lat2 = Math.toRadians(latitude.doubleValue());
  311. double dlat = lat2 - lat1;
  312. double dlng = Math.toRadians(longitude.doubleValue() - order.getLongitude().doubleValue());
  313. double a = Math.sin(dlat / 2) * Math.sin(dlat / 2)
  314. + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dlng / 2) * Math.sin(dlng / 2);
  315. double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  316. // order.setJuli(Math.round(6371 * c * 100.0) / 100.0);
  317. org.put("juli",Math.round(6371 * c * 100.0) / 100.0);
  318. }
  319. // 以下为非PosOrder字段或需要特殊处理的字段
  320. org.put("store", storeMap.get(order.getMdId().intValue()));
  321. arr.add(org);
  322. }
  323. JSONObject org=new JSONObject();
  324. org.put("records",arr);
  325. org.put("total",result.getTotal());
  326. org.put("current",result.getCurrent());
  327. return success(org);
  328. }
  329. }