UserOrderController.java 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  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.OrderCreateInput;
  8. import com.ruoyi.app.order.dto.OrderCreatItem;
  9. import com.ruoyi.app.order.dto.OrderPushBodyDto;
  10. import com.ruoyi.app.utils.DateUtil;
  11. import com.ruoyi.app.utils.OperatingUtil;
  12. import com.ruoyi.app.utils.PayPush;
  13. import com.ruoyi.app.utils.event.PushEventService;
  14. import com.github.pagehelper.PageHelper;
  15. import com.ruoyi.common.annotation.Anonymous;
  16. import com.ruoyi.common.core.controller.BaseController;
  17. import com.ruoyi.common.core.domain.AjaxResult;
  18. import com.ruoyi.common.core.page.TableDataInfo;
  19. import com.ruoyi.common.exception.ServiceException;
  20. import com.ruoyi.common.utils.MessageUtils;
  21. import com.ruoyi.common.utils.StringUtils;
  22. import com.ruoyi.framework.manager.AsyncManager;
  23. import com.ruoyi.system.domain.*;
  24. import com.ruoyi.system.mapper.PosOrderPromotionMapper;
  25. import com.ruoyi.system.service.*;
  26. import com.ruoyi.system.utils.Auth;
  27. import com.ruoyi.system.utils.GetArea;
  28. import com.ruoyi.system.utils.JwtUtil;
  29. import com.ruoyi.system.utils.OrderLogHelper;
  30. import com.ruoyi.system.dto.PromotionCalcResponse;
  31. import com.ruoyi.system.domain.dto.ApplyInvoiceDto;
  32. import jakarta.validation.Valid;
  33. import org.springframework.web.bind.annotation.*;
  34. import org.springframework.beans.factory.annotation.Autowired;
  35. import org.springframework.transaction.annotation.Transactional;
  36. import java.text.SimpleDateFormat;
  37. import java.util.Date;
  38. import java.util.List;
  39. import java.util.Optional;
  40. import java.util.TimerTask;
  41. import java.util.stream.Collectors;
  42. /**
  43. * 用户订单Controller
  44. *
  45. * @author ruoyi
  46. * @date 2020-04-01
  47. */
  48. @RestController
  49. @RequestMapping("/system/userOrder")
  50. public class UserOrderController extends BaseController {
  51. @Autowired
  52. private IOrderParentService orderParentService;
  53. @Autowired
  54. private IPosOrderService posOrderService;
  55. @Autowired
  56. private IPosStoreService posStoreService;
  57. @Autowired
  58. private IInfoAddressService infoAddressService;
  59. @Autowired
  60. private IOperatingHoursService operatingHoursService;
  61. @Autowired
  62. private IUserFootprintService userFootprintService;
  63. @Autowired
  64. private IInfoUserService infoUserService;
  65. @Autowired
  66. private PushEventService pushEventService;
  67. @Autowired
  68. private OrderLogHelper orderLogHelper;
  69. @Autowired
  70. private OrderPromotionHelper orderPromotionHelper;
  71. @Autowired
  72. private PosOrderPromotionMapper posOrderPromotionMapper;
  73. @Autowired
  74. private OrderInvoiceService orderInvoiceService;
  75. @Autowired
  76. private IInfoInvoiceService infoInvoiceService;
  77. /**
  78. * 申请电子发票(订单完成后,客户主动申请:B2C 邮箱 / B2B 统编 / 载具)
  79. */
  80. @PostMapping("/applyInvoice")
  81. @Auth
  82. @Anonymous
  83. public AjaxResult applyInvoice(@RequestHeader String token, @RequestBody @Valid ApplyInvoiceDto dto) {
  84. Long userId = Long.valueOf(new JwtUtil().getusid(token));
  85. return AjaxResult.success(orderInvoiceService.applyInvoice(dto, userId));
  86. }
  87. /**
  88. * 查询订单电子发票(状态 / 发票号 / 凭证);门店不可开票时 invoiceStatus=null
  89. */
  90. @GetMapping("/getInvoice")
  91. @Auth
  92. @Anonymous
  93. public AjaxResult getInvoice(@RequestHeader String token, @RequestParam Long orderId) {
  94. Long userId = Long.valueOf(new JwtUtil().getusid(token));
  95. return AjaxResult.success(orderInvoiceService.getInvoice(orderId, userId));
  96. }
  97. /**
  98. * 捐赠机构列表(客户端选择器,仅 enabled=1,按机构名/简称/捐赠码搜索;分页)
  99. */
  100. @GetMapping("/loveOrg/list")
  101. @Auth
  102. @Anonymous
  103. public TableDataInfo loveOrgList(@RequestHeader String token,
  104. @RequestParam(defaultValue = "1") int pageNum,
  105. @RequestParam(defaultValue = "20") int pageSize,
  106. @RequestParam(required = false) String keyword) {
  107. PageHelper.startPage(pageNum, pageSize);
  108. return getDataTable(orderInvoiceService.listLoveOrg(keyword));
  109. }
  110. /**
  111. * 会员发票夹(010 Phase 11):我的发票列表,按 userId 查已开发票(不含码图)。
  112. */
  113. @GetMapping("/myInvoices")
  114. @Auth
  115. @Anonymous
  116. public TableDataInfo myInvoices(@RequestHeader String token,
  117. @RequestParam(defaultValue = "1") int pageNum,
  118. @RequestParam(defaultValue = "10") int pageSize) {
  119. Long userId = Long.valueOf(new JwtUtil().getusid(token));
  120. PageHelper.startPage(pageNum, pageSize);
  121. List<com.ruoyi.system.domain.vo.PosOrderInvoiceVo> list = orderInvoiceService.listMyInvoices(userId);
  122. return getDataTable(list);
  123. }
  124. /**
  125. * 创建订单
  126. */
  127. @PostMapping("/createOrder")
  128. @Auth
  129. @Anonymous
  130. @Transactional(rollbackFor = Exception.class)
  131. public AjaxResult createOrder(@RequestHeader String token, @RequestBody OrderCreateInput input)
  132. {
  133. JwtUtil jwtUtil = new JwtUtil();
  134. String id = jwtUtil.getusid(token);
  135. String shdz=null;
  136. if(input.getType()==0){
  137. InfoAddress usadd = infoAddressService.getById(input.getShdzId());
  138. if (usadd == null) {
  139. throw new ServiceException(MessageUtils.message("no.address.not.exist"));
  140. }
  141. shdz= JSON.toJSONString(usadd);
  142. }
  143. // === 发票意图(010 Phase 11)===
  144. // 校验:载具/捐赠/统编验真前置,不通过拦截下单(联网校验用第一个可开票门店的 ezPay 凭证)
  145. if (!StringUtils.isEmpty(input.getInvoiceChoice()) && !"PAPER".equals(input.getInvoiceChoice())) {
  146. Long invoiceStoreId = input.getItems() == null ? null : input.getItems().stream()
  147. .map(OrderCreatItem::getMdId)
  148. .filter(md -> orderInvoiceService.canInvoice(md))
  149. .findFirst().orElse(null);
  150. if (invoiceStoreId == null) {
  151. throw new ServiceException("所选门店暂不支持电子发票");
  152. }
  153. orderInvoiceService.validateCheckoutInvoiceIntent(input.getInvoiceChoice(), input.getCarrierNum(),
  154. input.getLoveCode(), input.getBuyerUbn(), input.getBuyerEmail(), invoiceStoreId);
  155. }
  156. createOrderParent(input,Long.valueOf(id),shdz);
  157. createOrderChild(input,Long.valueOf(id),Long.valueOf(id),shdz);
  158. // saveAsDefault:把载具/统编存为默认抬头(同一用户仅一条 is_default=1)
  159. if (Boolean.TRUE.equals(input.getSaveAsDefault()) && !StringUtils.isEmpty(input.getInvoiceChoice())) {
  160. saveDefaultInvoiceProfile(input, Long.valueOf(id));
  161. }
  162. OrderParent result = orderParentService.getOne(new LambdaQueryWrapper<OrderParent>().eq(OrderParent::getDdId, input.getDdId().toString()));
  163. System.out.println("创建返回信息:"+result);
  164. return super.success(result);
  165. }
  166. //创建父订单
  167. private void createOrderParent(OrderCreateInput input,Long UserId,String shdz){
  168. OrderParent orderParent = new OrderParent();
  169. orderParent.setUserId(UserId);
  170. orderParent.setOrderStatus(0L);
  171. orderParent.setTotalAmount(input.getTotalAmount());
  172. orderParent.setActualPayAmount(input.getActualPayAmount());
  173. orderParent.setPaymentMethod(input.getPaymentMethod());
  174. // orderParent.setPaymentTime(new Date());
  175. orderParent.setCreateTime(new Date());
  176. // orderParent.setUpdateTime(new Date());
  177. orderParent.setDdId(input.getDdId().toString());
  178. orderParent.setTableId(input.getTableId());
  179. orderParent.setShdzId(input.getShdzId());
  180. orderParent.setShAddress(shdz);
  181. orderParent.setType(input.getType());
  182. orderParent.setRemarks(input.getRemarks());
  183. orderParentService.save(orderParent);
  184. }
  185. //创建子订单
  186. private void createOrderChild(OrderCreateInput input,Long orderParentId,Long userId,String shdz){
  187. // 检查items是否为空
  188. if (input.getItems() == null || input.getItems().isEmpty()) {
  189. throw new ServiceException(MessageUtils.message("订单商品不能为空"));
  190. }
  191. String ddId = input.getDdId().toString();
  192. List<Long> mdIdList = input.getItems().stream().map(OrderCreatItem::getMdId).collect(Collectors.toList());
  193. // 将Long类型的门店ID转换为Integer类型用于查询(因为PosStore.getId()返回Integer)
  194. List<Integer> mdIdIntegerList = mdIdList.stream().map(Long::intValue).collect(Collectors.toList());
  195. List<PosStore> storeList = posStoreService.list(new LambdaQueryWrapper<PosStore>().in(PosStore::getId, mdIdIntegerList));
  196. LambdaQueryWrapper<OperatingHours> wrapper = new LambdaQueryWrapper<>();
  197. wrapper.in(OperatingHours::getMdId, mdIdList);
  198. List<OperatingHours> hourslist = operatingHoursService.list(wrapper);
  199. boolean isMultiStore = input.getItems().size() > 1;
  200. // 循环items,为每个item创建一条PosOrder
  201. int index = 0;
  202. for (OrderCreatItem item : input.getItems()) {
  203. // 检查门店是否开放
  204. checkStoreOpen(storeList,hourslist,item);
  205. index++;
  206. // 单店铺:子订单号与父订单一致;多店铺:子订单号追加序号
  207. String subddId = isMultiStore ? ddId + String.format("%03d", index) : ddId;
  208. PosOrder posOrder = new PosOrder();
  209. posOrder.setIsDisplay(false);
  210. handleIsDisplay(posOrder);
  211. posOrder.setShdzId(input.getShdzId());
  212. // 设置子订单的基本信息
  213. posOrder.setDdId(subddId);
  214. posOrder.setParentDdId(input.getDdId().toString()); // 设置父订单ID
  215. posOrder.setCretim(new Date());
  216. // 根据item设置订单信息
  217. posOrder.setShId(item.getShId());
  218. posOrder.setMdId(item.getMdId());
  219. posOrder.setShdzId(input.getShdzId());
  220. posOrder.setUserId(userId);
  221. posOrder.setAmount(item.getAmount());
  222. posOrder.setRemarks(item.getRemarks());
  223. posOrder.setType(item.getType());
  224. //外卖设置配送状态为
  225. if(0L==posOrder.getType()){
  226. posOrder.setDeliveryStatus(0L);
  227. }
  228. posOrder.setDelryTime(item.getDelryTime());
  229. posOrder.setFood(JSON.toJSONString(item.getFood()));
  230. if("1".equals(input.getPaymentMethod()) || "2".equals(input.getPaymentMethod())){
  231. posOrder.setCollectPayment("1");
  232. }
  233. // 设置支付相关信息(从父订单继承)
  234. posOrder.setPayType(input.getPaymentMethod());
  235. //设置桌号(从父订单继承)
  236. posOrder.setTableId(input.getTableId());
  237. posOrder.setTableNo(input.getTableNo());
  238. posOrder.setLogo(item.getLogo());
  239. posOrder.setPosName(item.getPosName());
  240. posOrder.setJvli(item.getJvli());
  241. posOrder.setFreight(item.getFreight());
  242. posOrder.setShdzId(input.getShdzId());
  243. posOrder.setShAddress(shdz);
  244. posOrder.setFoodAmount(item.getFoodAmount());
  245. posOrder.setPickUpTime(input.getPickUpTime());
  246. posOrder.setReservePhone(input.getReservePhone());
  247. // 新状态字段初始化
  248. // 堂食订单(type=2)自动接单,跳过商家确认
  249. if (item.getType() != null && item.getType() == 2) {
  250. posOrder.setState(1L);
  251. } else {
  252. posOrder.setState(0L);
  253. }
  254. posOrder.setAfterSaleStatus(0L);
  255. posOrder.setDeliveryStatus(null);
  256. // 外送到付(payType=1):payStatus=1;其他:payStatus=0
  257. if (item.getType() != null && item.getType() == 0 && "1".equals(input.getPaymentMethod())) {
  258. posOrder.setPayStatus(1L);
  259. } else {
  260. posOrder.setPayStatus(0L);
  261. }
  262. Optional<PosStore> storeOptional = storeList.stream()
  263. .filter(x -> item.getMdId().equals(Long.valueOf(x.getId())))
  264. .findFirst();
  265. if (storeOptional.isEmpty()) {
  266. throw new ServiceException(MessageUtils.message("no.mendian.not.exist"));
  267. }
  268. PosStore store = storeOptional.get();
  269. posOrder.setLongitude(store.getLongitude());
  270. posOrder.setLatitude(store.getLatitude());
  271. //设置订单分类
  272. storeList.stream().filter(s -> Long.valueOf(s.getId()).equals(item.getMdId())).findFirst().ifPresent(st -> {
  273. posOrder.setOrderCategory(String.valueOf(st.getType()));
  274. });
  275. // 保存子订单
  276. // --- 促销:算价 + 校验 + 回填优惠字段(save之前) ---
  277. PromotionCalcResponse calc = orderPromotionHelper.calcPromotion(item, userId);
  278. orderPromotionHelper.validatePromotionAmount(calc, item.getPromoReduceAmount());
  279. orderPromotionHelper.fillPromotionFields(posOrder, calc);
  280. createFootprint(userId, item.getMdId());
  281. setPickUpNum(posOrder);
  282. posOrderService.save(posOrder);
  283. // 发票意图落 pos_order_invoice 意图行(010 Phase 11,方案B):仅开发票门店插,
  284. // issueTriggered=0 未触发;出餐 autoIssue 在该行落结果并置 issueTriggered=1
  285. if (!StringUtils.isEmpty(input.getInvoiceChoice()) && orderInvoiceService.canInvoice(item.getMdId())) {
  286. String choice = input.getInvoiceChoice();
  287. PosOrderInvoice intent = new PosOrderInvoice();
  288. intent.setOrderId(posOrder.getId());
  289. intent.setOrderNo(subddId);
  290. intent.setStoreId(item.getMdId());
  291. intent.setInvoiceChoice(choice);
  292. intent.setCarrierNum(input.getCarrierNum());
  293. intent.setBuyerUbn(input.getBuyerUbn());
  294. intent.setBuyerName(input.getBuyerName());
  295. intent.setBuyerEmail(input.getBuyerEmail());
  296. intent.setLoveCode(input.getLoveCode());
  297. if ("PHONE_BARCODE".equals(choice)) {
  298. intent.setCarrierType("0");
  299. } else if ("CITIZEN".equals(choice)) {
  300. intent.setCarrierType("1");
  301. } else if ("MEMBER".equals(choice)) {
  302. // 【决定 2026-08-05】会员载具走 PrintFlag=Y 纸本,不传 ezPay 电子载具(见 OrderInvoiceService.buildAutoIssueDto 决策备注);carrierType 留空
  303. } else {
  304. intent.setCarrierType(input.getCarrierType());
  305. }
  306. orderInvoiceService.captureInvoiceIntent(intent);
  307. }
  308. // --- 促销:写入快照 + 标记券已使用(save之后,需要posOrder.id) ---
  309. if (calc != null) {
  310. orderPromotionHelper.savePromotionSnapshots(posOrder.getId(), calc);
  311. if (calc.getCouponId() != null) {
  312. orderPromotionHelper.markCouponUsed(calc.getCouponId(), posOrder.getId(), userId);
  313. }
  314. }
  315. // 创建订单成功,添加操作日志
  316. InfoUser uUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, userId));
  317. String uName = uUser != null ? uUser.getNickName() : "";
  318. orderLogHelper.log(subddId, 4, userId, uName, "用户" + uName + "创建订单");
  319. // 创建订单成功,给商家推送"您有新订单了"
  320. InfoUser shUser = infoUserService.getById(item.getShId());
  321. if (shUser != null && !StringUtils.isEmpty(shUser.getCid())) {
  322. PayPush push = new PayPush();
  323. String title = "no.message.push.message";
  324. String content = "no.message.push.new.order";
  325. String body = OrderPushBodyDto.getJson(subddId, String.valueOf(posOrder.getState()), 0);
  326. Long shUserId = shUser.getUserId();
  327. String cid = shUser.getCid();
  328. AsyncManager.me().execute(new TimerTask() {
  329. @Override
  330. public void run() {
  331. PayPush.shPushHandleLocal(push, pushEventService, shUserId, cid, title, content, body, "", subddId);
  332. }
  333. });
  334. }
  335. }
  336. }
  337. //保存默认发票抬头(010 Phase 11:结账勾「同意设置为默认」;同一用户仅一条 is_default=1)
  338. private void saveDefaultInvoiceProfile(OrderCreateInput input, Long userId) {
  339. String choice = input.getInvoiceChoice();
  340. if ("PAPER".equals(choice) || "LOVE_CODE".equals(choice) || "MEMBER".equals(choice)) {
  341. return; // 纸本/捐赠/会员载具不存抬头(会员载具=会员手机号,无需记忆)
  342. }
  343. InfoInvoice profile = new InfoInvoice();
  344. profile.setUserId(userId);
  345. profile.setIsDefault(1);
  346. switch (choice) {
  347. case "PHONE_BARCODE":
  348. profile.setCategory("B2C");
  349. profile.setTitleName("手机条码载具");
  350. profile.setBuyerName("手机条码载具");
  351. profile.setCarrierType("0");
  352. profile.setCarrierNum(input.getCarrierNum());
  353. break;
  354. case "CITIZEN":
  355. profile.setCategory("B2C");
  356. profile.setTitleName("自然人凭证载具");
  357. profile.setBuyerName("自然人凭证载具");
  358. profile.setCarrierType("1");
  359. profile.setCarrierNum(input.getCarrierNum());
  360. break;
  361. case "COMPANY":
  362. profile.setCategory("B2B");
  363. profile.setTitleName(input.getBuyerName());
  364. profile.setBuyerName(input.getBuyerName());
  365. profile.setBuyerUbn(input.getBuyerUbn());
  366. break;
  367. default:
  368. return;
  369. }
  370. // 先清掉该用户其它默认(同一用户仅一条为 1),再存新的
  371. InfoInvoice reset = new InfoInvoice();
  372. reset.setIsDefault(0);
  373. infoInvoiceService.update(reset, new LambdaQueryWrapper<InfoInvoice>()
  374. .eq(InfoInvoice::getUserId, userId)
  375. .eq(InfoInvoice::getIsDefault, 1));
  376. infoInvoiceService.save(profile);
  377. }
  378. //生成取餐码,根据门店id,当天取餐码从1开始累加
  379. private void setPickUpNum(PosOrder order) {
  380. if (order == null || order.getMdId() == null) {
  381. return;
  382. }
  383. try {
  384. // 获取当前日期,格式为 yyyy-MM-dd
  385. SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
  386. String today = dateFormat.format(new Date());
  387. // 查询当天该门店的最大取餐码
  388. LambdaQueryWrapper<PosOrder> queryWrapper = new LambdaQueryWrapper<>();
  389. queryWrapper.eq(PosOrder::getMdId, order.getMdId());
  390. queryWrapper.like(PosOrder::getCretim, today);
  391. queryWrapper.orderByDesc(PosOrder::getPickUpNum);
  392. queryWrapper.last("LIMIT 1");
  393. PosOrder lastOrder = posOrderService.getOne(queryWrapper);
  394. long nextNum = 1L;
  395. if (lastOrder != null && lastOrder.getPickUpNum() != null) {
  396. // 直接使用Long类型的取餐码
  397. nextNum = lastOrder.getPickUpNum() + 1;
  398. }
  399. // 生成新的取餐码:直接使用Long数字
  400. order.setPickUpNum(nextNum);
  401. logger.info("生成取餐码成功:门店ID={}, 取餐码={}", order.getMdId(), nextNum);
  402. } catch (Exception e) {
  403. logger.error("生成取餐码失败:门店ID={}, 错误信息={}", order.getMdId(), e.getMessage(), e);
  404. // 生成失败时使用时间戳作为备用方案
  405. Long fallbackPickUpNum = System.currentTimeMillis() % 10000;
  406. order.setPickUpNum(fallbackPickUpNum);
  407. }
  408. }
  409. //设置显示状态
  410. private void handleIsDisplay(PosOrder posOrder){
  411. if (StringUtils.isEmpty(posOrder.getDelryTime())) {
  412. posOrder.setIsDisplay(true);
  413. posOrder.setDisplayTime(new Date());
  414. }
  415. //预约单不在时间范围内不显示
  416. if (StringUtils.isNotEmpty(posOrder.getDelryTime())) {
  417. int minute = DateUtil.getAppoinmentOrderHandleTime();
  418. boolean isNear = DateUtil.isNearOrPastStartTime(posOrder.getDelryTime(), minute);
  419. if (isNear) {
  420. posOrder.setIsDisplay(true);
  421. posOrder.setDisplayTime(new Date());
  422. }
  423. }
  424. }
  425. private void createFootprint(Long uid,Long mdId){
  426. QueryWrapper<UserFootprint> Wrapper = new QueryWrapper<>();
  427. Wrapper.eq("user_id", uid);
  428. Wrapper.eq("md_id", mdId);
  429. UserFootprint foot = userFootprintService.getOne(Wrapper);
  430. DateUtil dateString = new DateUtil();
  431. if (foot == null) {
  432. UserFootprint usf = new UserFootprint();
  433. usf.setUserId(uid);
  434. usf.setMdId(mdId);
  435. usf.setCretim(dateString.GetDate());
  436. userFootprintService.saveOrUpdate(usf);
  437. } else {
  438. foot.setCretim(dateString.GetDate());
  439. userFootprintService.saveOrUpdate(foot);
  440. }
  441. }
  442. /**
  443. * 检查门店是否开放
  444. */
  445. private void checkStoreOpen( List<PosStore> storeList,List<OperatingHours> hourslist, OrderCreatItem item) {
  446. List<OperatingHours> storeHourslist = hourslist.stream().filter(x-> x.getMdId().equals(item.getMdId())).collect(Collectors.toList());
  447. PosStore store = storeList.stream()
  448. .filter(x -> Long.valueOf(x.getId()).equals(item.getMdId()))
  449. .findFirst()
  450. .orElseThrow(() -> new ServiceException(MessageUtils.message("no.mendian.not.exist")));
  451. if (storeHourslist.isEmpty()) {
  452. throw new ServiceException(store.getPosName()+MessageUtils.message("no.mendian.not.set.business.hours"));
  453. }
  454. if (store.getState() == 1) {
  455. throw new ServiceException(store.getPosName()+MessageUtils.message("no.mendian.is.closed"));
  456. }
  457. boolean isOperating = OperatingUtil.storeIsOperatingOfDay(storeHourslist);
  458. if (!isOperating) {
  459. throw new ServiceException(store.getPosName()+MessageUtils.message("no.mendian.is.closed"));
  460. }
  461. }
  462. /**
  463. * 获取父订单详情
  464. */
  465. @GetMapping("/getParentOrderDetail")
  466. @Auth
  467. @Anonymous
  468. public AjaxResult getParentOrderDetail(@RequestHeader String token, @RequestParam String ddId){
  469. OrderParent result = orderParentService.getOne(new LambdaQueryWrapper<OrderParent>().eq(OrderParent::getDdId,ddId));
  470. List<PosOrder> posOrders = posOrderService.list(new LambdaQueryWrapper<PosOrder>().eq(PosOrder::getParentDdId,ddId).orderBy(true,true,PosOrder::getCretim));
  471. result.setItems(posOrders);
  472. // 转为JSONObject以追加非实体字段(promotions)
  473. JSONObject org = JSON.parseObject(JSON.toJSONStringWithDateFormat(result, "yyyy-MM-dd HH:mm:ss"));
  474. JSONArray items = org.getJSONArray("items");
  475. if (items != null) {
  476. for (int i = 0; i < items.size(); i++) {
  477. JSONObject item = items.getJSONObject(i);
  478. Long orderId = item.getLong("id");
  479. List<PosOrderPromotion> promotions = posOrderPromotionMapper.selectByOrderId(orderId);
  480. item.put("promotions", promotions);
  481. }
  482. }
  483. return super.success(org);
  484. }
  485. /**
  486. * 计算两个经纬度之间的距离
  487. */
  488. @GetMapping("/getDistance")
  489. @Anonymous
  490. public AjaxResult getDistance(@RequestParam Double lat1, @RequestParam Double lng1,
  491. @RequestParam Double lat2, @RequestParam Double lng2) {
  492. double distance = GetArea.getDistance(lat1, lng1, lat2, lng2);
  493. return success(distance);
  494. }
  495. /**
  496. * 用户端订单列表(Tab分页)
  497. */
  498. @GetMapping("/orderList")
  499. @Auth
  500. @Anonymous
  501. public AjaxResult orderList(@RequestHeader String token,
  502. @RequestParam(defaultValue = "1") int page,
  503. @RequestParam(defaultValue = "10") int size,
  504. @RequestParam(required = false) String tab,
  505. @RequestParam(required = false) String type) {
  506. JwtUtil jwtUtil = new JwtUtil();
  507. String userId = jwtUtil.getusid(token);
  508. LambdaQueryWrapper<PosOrder> wrapper = new LambdaQueryWrapper<>();
  509. wrapper.eq(PosOrder::getUserId, Long.valueOf(userId));
  510. // 类型筛选
  511. if (type != null && !type.isEmpty()) {
  512. wrapper.eq(PosOrder::getType, Long.valueOf(type));
  513. }
  514. switch (tab) {
  515. case "unpaid":
  516. // 排除已取消(state=4)和售后中(afterSaleStatus>0)的订单
  517. wrapper.eq(PosOrder::getPayStatus, 0L).eq(PosOrder::getType, 0L).ne(PosOrder::getState, 4L).eq(PosOrder::getAfterSaleStatus, 0L);
  518. break;
  519. case "active":
  520. wrapper.in(PosOrder::getState, 0L, 1L, 2L)
  521. .eq(PosOrder::getAfterSaleStatus, 0L)
  522. .and(w -> w.eq(PosOrder::getPayStatus, 1L).or().in(PosOrder::getType, 1L, 2L));
  523. break;
  524. case "completed":
  525. wrapper.eq(PosOrder::getState, 3L).eq(PosOrder::getAfterSaleStatus, 0L);
  526. break;
  527. case "cancelled":
  528. wrapper.eq(PosOrder::getState, 4L).eq(PosOrder::getAfterSaleStatus, 0L);
  529. break;
  530. case "refund":
  531. wrapper.gt(PosOrder::getAfterSaleStatus, 0L);
  532. break;
  533. default:
  534. break;
  535. }
  536. wrapper.orderByDesc(PosOrder::getCretim);
  537. // 分页
  538. com.baomidou.mybatisplus.extension.plugins.pagination.Page<PosOrder> pageParam =
  539. new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(page, size);
  540. com.baomidou.mybatisplus.extension.plugins.pagination.Page<PosOrder> result = posOrderService.page(pageParam, wrapper);
  541. // 附加关联数据
  542. for (PosOrder order : result.getRecords()) {
  543. enrichOrderData(order);
  544. }
  545. java.util.Map<String, Object> data = new java.util.LinkedHashMap<>();
  546. data.put("records", result.getRecords());
  547. data.put("total", result.getTotal());
  548. data.put("page", page);
  549. data.put("size", size);
  550. return success(result);
  551. }
  552. private void enrichOrderData(PosOrder order) {
  553. try {
  554. if (order.getShId() != null) {
  555. InfoUser shanghu = infoUserService.getById(order.getShId());
  556. order.setPriceAll(shanghu != null ? String.valueOf(shanghu.getNickName()) : null);
  557. }
  558. } catch (Exception ignored) {}
  559. }
  560. /**
  561. * 确认取餐(自取类型订单,state=2 → state=3)
  562. */
  563. @Transactional(rollbackFor = Exception.class)
  564. @GetMapping("/confirmPickup")
  565. @Auth
  566. @Anonymous
  567. public AjaxResult confirmPickup(@RequestHeader String token, @RequestParam Long id) {
  568. JwtUtil jwtUtil = new JwtUtil();
  569. String userId = jwtUtil.getusid(token);
  570. PosOrder order = posOrderService.getOne(
  571. new LambdaQueryWrapper<PosOrder>()
  572. .eq(PosOrder::getId, id)
  573. .eq(PosOrder::getUserId, Long.valueOf(userId))
  574. );
  575. if (order == null) {
  576. return error("订单不存在");
  577. }
  578. if (order.getType() == null || order.getType() != 1) {
  579. return error("仅自取订单可确认取餐");
  580. }
  581. if (order.getState() == null || order.getState() != 2) {
  582. return error("当前订单状态不可确认取餐");
  583. }
  584. if (order.getPayStatus() == null || order.getPayStatus() != 1) {
  585. return error("订单未支付,不可确认取餐");
  586. }
  587. if (order.getAfterSaleStatus() != null && order.getAfterSaleStatus() > 0) {
  588. return error("订单存在售后处理中,不可确认取餐");
  589. }
  590. order.setState(3L);
  591. posOrderService.updateById(order);
  592. // 确认取餐成功,添加操作日志
  593. InfoUser uUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, Long.valueOf(userId)));
  594. String uName = uUser != null ? uUser.getNickName() : "";
  595. orderLogHelper.log(String.valueOf(order.getDdId()), 4, Long.valueOf(userId), uName, "用户" + uName + "确认取餐");
  596. return success("确认取餐成功");
  597. }
  598. /**
  599. * 用户确认完成外送订单(骑手已送达后,state=2 → state=3)
  600. */
  601. @Transactional(rollbackFor = Exception.class)
  602. @GetMapping("/confirmDelivery")
  603. @Auth
  604. @Anonymous
  605. public AjaxResult confirmDelivery(@RequestHeader String token, @RequestParam Long id) {
  606. JwtUtil jwtUtil = new JwtUtil();
  607. String userId = jwtUtil.getusid(token);
  608. PosOrder order = posOrderService.getOne(
  609. new LambdaQueryWrapper<PosOrder>()
  610. .eq(PosOrder::getId, id)
  611. .eq(PosOrder::getUserId, Long.valueOf(userId))
  612. );
  613. if (order == null) {
  614. return error("订单不存在");
  615. }
  616. if (order.getState() == null || order.getState() != 2L) {
  617. return error("当前订单状态不可确认完成");
  618. }
  619. if (order.getDeliveryStatus() == null || order.getDeliveryStatus() != 3L) {
  620. return error("骑手尚未送达,不可确认完成");
  621. }
  622. if (order.getAfterSaleStatus() != null && order.getAfterSaleStatus() > 0) {
  623. return error("订单存在售后处理中,不可确认完成");
  624. }
  625. PosOrder update = new PosOrder();
  626. update.setId(order.getId());
  627. update.setState(3L);
  628. posOrderService.updateById(update);
  629. // 用户确认完成,添加操作日志
  630. InfoUser uUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, Long.valueOf(userId)));
  631. String uName = uUser != null ? uUser.getNickName() : "";
  632. orderLogHelper.log(String.valueOf(order.getDdId()), 4, Long.valueOf(userId), uName, "用户" + uName + "确认完成订单");
  633. return success("确认完成成功");
  634. }
  635. /**
  636. * 取消订单(未接单前,state=0 → state=4)
  637. */
  638. @GetMapping("/cancelOrder")
  639. @Auth
  640. @Anonymous
  641. public AjaxResult cancelOrder(@RequestHeader String token, @RequestParam Long id) {
  642. JwtUtil jwtUtil = new JwtUtil();
  643. String userId = jwtUtil.getusid(token);
  644. PosOrder order = posOrderService.getOne(
  645. new LambdaQueryWrapper<PosOrder>()
  646. .eq(PosOrder::getId, id)
  647. .eq(PosOrder::getUserId, Long.valueOf(userId))
  648. );
  649. if (order == null) {
  650. return error("订单不存在");
  651. }
  652. if (order.getState() == null || order.getState() != 0) {
  653. return error("仅待处理状态的订单可取消");
  654. }
  655. // OMG 已支付订单取消触发退款(信用卡自动退刷;ATM/超商记人工;失败不阻断取消,可重试)
  656. if (com.ruoyi.app.pay.OmgPayController.PAY_TYPE_OMG.equals(order.getPayType())
  657. && order.getPayStatus() != null && order.getPayStatus() == 1L) {
  658. try {
  659. com.ruoyi.common.utils.spring.SpringUtils.getBean(com.ruoyi.app.pay.OmgPayController.class).refundOrder(order);
  660. } catch (Exception ignore) {
  661. }
  662. }
  663. order.setState(4L);
  664. posOrderService.updateById(order);
  665. // 取消订单成功,添加操作日志
  666. InfoUser uUser = infoUserService.getOne(new LambdaQueryWrapper<InfoUser>().eq(InfoUser::getUserId, Long.valueOf(userId)));
  667. String uName = uUser != null ? uUser.getNickName() : "";
  668. orderLogHelper.log(String.valueOf(order.getDdId()), 4, Long.valueOf(userId), uName, "用户" + uName + "取消订单");
  669. return success("取消订单成功");
  670. }
  671. }