TOrderServiceImpl.java 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092
  1. package com.ylx.massage.service.impl;
  2. import cn.hutool.core.collection.CollectionUtil;
  3. import cn.hutool.json.JSONUtil;
  4. import com.alibaba.fastjson.JSONArray;
  5. import com.alibaba.fastjson.JSONObject;
  6. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  7. import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
  8. import com.baomidou.mybatisplus.core.toolkit.StringUtils;
  9. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  10. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  11. import com.ylx.common.config.WechatAccountConfig;
  12. import com.ylx.common.constant.MassageConstants;
  13. import com.ylx.common.core.domain.R;
  14. import com.ylx.common.exception.ServiceException;
  15. import com.ylx.common.utils.SecurityUtils;
  16. import com.ylx.massage.domain.*;
  17. import com.ylx.massage.domain.vo.CouponReceiveVo;
  18. import com.ylx.massage.domain.vo.HomeBlock;
  19. import com.ylx.massage.domain.vo.OrderVerificationVo;
  20. import com.ylx.massage.enums.BillTypeEnum;
  21. import com.ylx.massage.enums.DiscountTypeEnum;
  22. import com.ylx.massage.enums.JsStatusEnum;
  23. import com.ylx.massage.enums.OrderStatusEnum;
  24. import com.ylx.massage.mapper.TOrderMapper;
  25. import com.ylx.massage.service.*;
  26. import com.ylx.massage.utils.*;
  27. import lombok.extern.slf4j.Slf4j;
  28. import org.apache.commons.compress.utils.Lists;
  29. import org.springframework.stereotype.Service;
  30. import org.springframework.transaction.annotation.Transactional;
  31. import javax.annotation.Resource;
  32. import java.math.BigDecimal;
  33. import java.math.RoundingMode;
  34. import java.time.LocalDateTime;
  35. import java.util.*;
  36. import java.util.stream.Collectors;
  37. /**
  38. * 订单表 服务实现类
  39. */
  40. @Service
  41. @Slf4j
  42. public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> implements TOrderService {
  43. @Resource
  44. private TOrderMapper orderMapper;
  45. @Resource
  46. private WechatAccountConfig wxPayProperties;
  47. @Resource
  48. private LocationUtil locationUtil;
  49. @Resource
  50. private TWxUserService wxUserService;
  51. @Resource
  52. private TRechargeService rechargeService;
  53. @Resource
  54. private TXiangmuService xiangmuService;
  55. @Resource
  56. private OrderNumberGenerator generator;
  57. @Resource
  58. private TJsService jsService;
  59. @Resource
  60. private TAddressService addressService;
  61. @Resource
  62. private TConsumptionLogService consumptionLogService;
  63. @Resource
  64. private MassageUtil massageUtil;
  65. @Resource
  66. private CouponReceiveService couponReceiveService;
  67. @Resource
  68. private CouponService couponService;
  69. @Resource
  70. private WeChatUtil weChatUtil;
  71. @Resource
  72. private RefundVoucherService refundVoucherService;
  73. @Resource
  74. private OrderValidationService orderValidationService;
  75. @Resource
  76. private OrderNotificationService orderNotificationService;
  77. @Resource
  78. private OrderAllocationLogService allocationLogService;
  79. /**
  80. * 判断是否免车费
  81. * 时间段判断:
  82. * - 白天时段(7:30-20:00):距离 ≤ 技师白天免车费里程 → 免费
  83. * - 夜间时段(20:00-7:30):距离 ≤ 技师夜间免车费里程 → 免费
  84. *
  85. * @param js
  86. * @param distance
  87. * @return Boolean
  88. */
  89. public Boolean isFree(TJs js, BigDecimal distance) {
  90. Date date = new Date();
  91. //白天免车费(07.30-20.00)
  92. long current = Long.parseLong(DateTimeUtils.numTime(date));
  93. if (current >= MassageConstants.START_FREE && current <= MassageConstants.END_FREE) {
  94. if (js.getDaytimeMileage().compareTo(distance) >= 0) {
  95. //免车费
  96. return Boolean.TRUE;
  97. } else {
  98. return Boolean.FALSE;
  99. }
  100. } else {
  101. //夜间免车费(20.00-07.30)
  102. if (js.getNigthMileage().compareTo(distance) >= 0) {
  103. //免车费
  104. return Boolean.TRUE;
  105. } else {
  106. return Boolean.FALSE;
  107. }
  108. }
  109. }
  110. /**
  111. * 添加订单
  112. *
  113. * @param order
  114. * @return TOrder
  115. */
  116. @Override
  117. @Transactional(rollbackFor = Exception.class)
  118. public TOrder addOrder(TOrder order) {
  119. String jsId = order.getcJsId();
  120. // 1. 基础参数校验
  121. if (StringUtils.isBlank(jsId)) {
  122. throw new ServiceException("请选择技师");
  123. }
  124. if (order.getcGoods().isEmpty()) {
  125. throw new ServiceException("请选择项目");
  126. }
  127. TJs js = jsService.getById(jsId);
  128. if (js == null) {
  129. throw new ServiceException("技师不存在");
  130. }
  131. Integer techType = js.getTechType();
  132. // 虚拟技师
  133. if(techType.equals(1)){
  134. //虚拟技师订单
  135. order.setVirtualOrderFlag(1);
  136. //虚拟技师订单未分配
  137. order.setVirtualOrderAllocation(1);
  138. }else{
  139. //真实订单
  140. order.setVirtualOrderFlag(0);
  141. order.setVirtualOrderAllocation(0);
  142. }
  143. // 2. 【新增】订单状态锁校验 - 检查技师是否可以接单
  144. // 确保技师没有进行中的订单,保证服务时间互斥
  145. log.info("开始校验技师 {} 是否可以接单,订单号:{}", order.getcJsId(), order.getOrderNo());
  146. orderValidationService.canAcceptOrder(order.getcJsId(), order);
  147. log.info("技师 {} 接单校验通过,继续创建订单", order.getcJsId());
  148. //优惠卷减免
  149. // List<CouponReceiveVo> coupons = couponReceiveService.getByOpenId(order.getcOpenId());
  150. // BigDecimal preferential = this.setCoupon(coupons);
  151. // order.setPreferential(preferential);
  152. // 生成订单号
  153. order.setOrderNo(generator.generateNextOrderNumber(OrderNumberGenerator.KEY_PREFIX_ORDER));
  154. //订单价格
  155. List<TXiangmu> list = JSONObject.parseArray(order.getcGoods().toJSONString(), TXiangmu.class);
  156. BigDecimal sum = list.stream().map(TXiangmu::getSum).reduce(BigDecimal.ZERO, BigDecimal::add);
  157. //订单总金额
  158. order.setdTotalMoney(sum);
  159. //获取用户默认地址
  160. TAddress address = addressService.getByOpenId(order.getcOpenId());
  161. if (address == null) {
  162. throw new ServiceException("请先添加地址");
  163. }
  164. //添加技师位置信息
  165. locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), Double.parseDouble(js.getLongitude().toString()), Double.parseDouble(js.getLatitude().toString()));
  166. //添加用户位置信息
  167. locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, order.getcOpenId() + order.getOrderNo(), Double.parseDouble(address.getLongitude().toString()), Double.parseDouble(address.getLatitude().toString()));
  168. //计算距离
  169. double distance = locationUtil.getDistance(js.getcOpenId() + order.getOrderNo(), order.getcOpenId() + order.getOrderNo());
  170. locationUtil.remove(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), order.getcOpenId() + order.getOrderNo());
  171. order.setDistance(new BigDecimal(distance));
  172. //计算车费
  173. if (order.getDistance() != null && order.getDistance().compareTo(BigDecimal.ZERO) > 0 && StringUtils.isBlank(order.getParentNo())) {
  174. //判断是否可以免车费
  175. if (!this.isFree(js, order.getDistance())) {
  176. BigDecimal bigDecimal = massageUtil.calculateTaxiFare(order.getDistance(), js.getDeptId());
  177. order.setFare(bigDecimal.setScale(MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP));
  178. }
  179. }
  180. //总价 = 订单 + 车费
  181. order.setTotalPrice(sum.add(Optional.ofNullable(order.getFare()).orElse(BigDecimal.ZERO)));
  182. if (order.getParentNo() != null && order.getOrderType() == 2) {
  183. //升级订单 补差价
  184. TOrder partOrder = this.getByNo(order.getParentNo());
  185. order.setPriceDifference(order.getTotalPrice().subtract(partOrder.getTotalPrice()));
  186. }
  187. order.setAddress(address.getAddress());
  188. order.setName(address.getName());
  189. order.setLatitude(address.getLatitude());
  190. order.setLongitude(address.getLongitude());
  191. order.setcPhone(address.getPhone());
  192. order.setcName(address.getUserName());
  193. order.setAtlasAdd(address.getAtlasAdd());
  194. order.setDeptId(js.getDeptId());
  195. order.setDeptName(js.getCity());
  196. //设置订单状态:待支付
  197. order.setnStatus(OrderStatusEnum.WAIT_PAY.getCode());
  198. order.setDtCreateTime(LocalDateTime.now());
  199. Date date = DateTimeUtils.addMinute(new Date(), 10);
  200. order.setcTime(DateTimeUtils.formatDate(date, "yyyy-MM-dd HH:mm:ss"));
  201. save(order);
  202. return order;
  203. }
  204. private BigDecimal setCoupon(List<CouponReceiveVo> coupons) {
  205. //过滤过期的优惠券
  206. coupons = coupons.stream().filter(coupon -> coupon.getExpirationTime().after(new Date())).collect(Collectors.toList());
  207. //无门槛优惠券
  208. List<CouponReceiveVo> collect = coupons.stream().filter(coupon -> coupon.getDiscountType().equals(DiscountTypeEnum.NO_THRESHOLD.getCode())).collect(Collectors.toList());
  209. //支付成功 后 删除优惠卷
  210. // couponReceiveService.removeCoupons(collect);
  211. //计算优惠金额
  212. return collect.stream().map(CouponReceiveVo::getDiscountValue).reduce(BigDecimal.ZERO, BigDecimal::add);
  213. }
  214. @Override
  215. public void payNotifyOrder(String outTradeNo) {
  216. //查询未支付的订单
  217. LambdaQueryWrapper<TOrder> queryWrapper = new LambdaQueryWrapper<>();
  218. queryWrapper.eq(TOrder::getOrderNo, outTradeNo).eq(TOrder::getnStatus, OrderStatusEnum.WAIT_PAY.getCode());
  219. TOrder orderNew = this.getOne(queryWrapper);
  220. if (orderNew == null) {
  221. log.error("订单 {} 未支付状态不存在", outTradeNo);
  222. return;
  223. }
  224. // 设置微信支付
  225. orderNew.setPayType(1);
  226. TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId());
  227. orderPayManage(user, orderNew);
  228. }
  229. @Override
  230. public Object updateAddressById(TOrder borrow) {
  231. TOrder order = this.getById(borrow.getcId());
  232. if (borrow.getLatitude() != null && borrow.getLatitude() != 0 && borrow.getLongitude() != null && borrow.getLongitude() != 0) {
  233. order.setAtlasAdd(borrow.getAtlasAdd());
  234. order.setcName(borrow.getcName());
  235. order.setcPhone(borrow.getcPhone());
  236. order.setName(borrow.getName());
  237. order.setAddress(borrow.getAddress());
  238. order.setLatitude(borrow.getLatitude());
  239. order.setLongitude(borrow.getLongitude());
  240. TJs js = jsService.getById(order.getcJsId());
  241. //添加位置信息
  242. locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), Double.parseDouble(js.getLongitude().toString()), Double.parseDouble(js.getLatitude().toString()));
  243. locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, order.getcOpenId() + order.getOrderNo(), Double.parseDouble(borrow.getLongitude().toString()), Double.parseDouble(borrow.getLatitude().toString()));
  244. double distance = locationUtil.getDistance(js.getcOpenId() + order.getOrderNo(), order.getcOpenId() + order.getOrderNo());
  245. locationUtil.remove(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), order.getcOpenId() + order.getOrderNo());
  246. order.setDistance(new BigDecimal(distance));
  247. //计算车费
  248. if (order.getDistance() != null && order.getDistance().compareTo(BigDecimal.ZERO) > 0) {
  249. //判断是否可以免车费
  250. if (!this.isFree(js, order.getDistance())) {
  251. BigDecimal bigDecimal = massageUtil.calculateTaxiFare(order.getDistance(), order.getDeptId());
  252. order.setFare(bigDecimal.setScale(MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP));
  253. }
  254. }
  255. order.setTotalPrice(order.getdTotalMoney().add(Optional.ofNullable(order.getFare()).orElse(BigDecimal.ZERO)));
  256. this.updateById(order);
  257. }
  258. return order;
  259. }
  260. @Override
  261. public Object depart(TOrder order) {
  262. LambdaQueryWrapper<TOrder> wrapper = new LambdaQueryWrapper<>();
  263. wrapper.eq(TOrder::getcId, order.getcId()).eq(TOrder::getnStatus, OrderStatusEnum.RECEIVED_ORDER.getCode());
  264. //order.setnStatus(OrderStatusEnum.DEPART.getCode());
  265. order.setDepartTime(new Date());
  266. order.setDepartLatitude(Optional.ofNullable(order.getDepartLatitude()).orElse(BigDecimal.ZERO));
  267. order.setDepartLongitude(Optional.ofNullable(order.getDepartLongitude()).orElse(BigDecimal.ZERO));
  268. return this.update(order, wrapper);
  269. }
  270. @Override
  271. public Integer getOrderNum(String jsid, Date startDate, Date endDate) {
  272. return orderMapper.getOrderNum(jsid, startDate, endDate);
  273. }
  274. @Override
  275. public Integer getAddNum(String jsid, Date startDate, Date endDate) {
  276. return orderMapper.getAddNum(jsid, startDate, endDate);
  277. }
  278. @Override
  279. public Integer getUpgradeNum(String jsid, Date startDate, Date endDate) {
  280. return orderMapper.getUpgradeNum(jsid, startDate, endDate);
  281. }
  282. @Override
  283. public BigDecimal getTurnover(String jsid, Date startDate, Date endDate) {
  284. return orderMapper.getTurnover(jsid, startDate, endDate);
  285. }
  286. @Override
  287. @Transactional(rollbackFor = Exception.class)
  288. public TOrder transferOrder(TOrder order) {
  289. // ========== 第1步:参数校验 ==========
  290. if (StringUtils.isBlank(order.getcId())) {
  291. throw new ServiceException("订单id不能为空");
  292. }
  293. if (StringUtils.isBlank(order.getcJsId())) {
  294. throw new ServiceException("转单技师ID不能为空");
  295. }
  296. // 定义操作结果(默认为失败)
  297. Integer operationResult = 1; // 1-失败
  298. // 定义操作记录所需的变量
  299. String orderId = null;
  300. String orderNo = null;
  301. String oldTechnicianId = null;
  302. String oldTechnicianName = null;
  303. Integer oldTechnicianStatusBefore = null;
  304. Integer oldTechnicianStatusAfter = null;
  305. String newTechnicianId = null;
  306. String newTechnicianName = null;
  307. Integer newTechnicianStatusBefore = null;
  308. Integer newTechnicianStatusAfter = null;
  309. Integer orderStatusBefore = null;
  310. Integer orderStatusAfter = null;
  311. String operatorId = null;
  312. String operatorName = null;
  313. String operationReason = "虚拟订单分配";
  314. try {
  315. // ========== 第2步:查询原订单信息 ==========
  316. TOrder oldOrder = this.getById(order.getcId());
  317. if (oldOrder == null) {
  318. throw new ServiceException("订单不存在");
  319. }
  320. //原技师ID
  321. oldTechnicianId = oldOrder.getcJsId();
  322. //新技师ID
  323. newTechnicianId = order.getcJsId();
  324. // 记录订单操作前状态
  325. orderStatusBefore = oldOrder.getnStatus();
  326. orderId = oldOrder.getcId();
  327. orderNo = oldOrder.getOrderNo();
  328. log.info("开始转单操作 - 订单号:{}, 原技师ID:{}, 新技师ID:{}, 订单状态:{}", oldOrder.getOrderNo(), oldTechnicianId, newTechnicianId, orderStatusBefore);
  329. // ========== 第3步:查询原技师信息 ==========
  330. TJs oldTechnician = jsService.getById(oldTechnicianId);
  331. if (oldTechnician == null) {
  332. throw new ServiceException("原技师不存在");
  333. }
  334. oldTechnicianName = oldTechnician.getcName();
  335. oldTechnicianStatusBefore = oldTechnician.getnStatus();
  336. // ========== 第4步:查询新技师信息 ==========
  337. TJs newTechnician = jsService.getById(newTechnicianId);
  338. if (newTechnician == null) {
  339. throw new ServiceException("新技师不存在");
  340. }
  341. newTechnicianName = newTechnician.getcName();
  342. newTechnicianStatusBefore = newTechnician.getnStatus();
  343. // ========== 第5步:更新订单技师信息 ==========
  344. oldOrder.setOldJsId(oldTechnicianId); // 保存原技师ID
  345. oldOrder.setcJsId(newTechnicianId); // 更新为新技师ID
  346. log.info("更新订单技师 - 订单号:{}, 原技师:[ID:{}, 姓名:{}], 新技师:[ID:{}, 姓名:{}]", oldOrder.getOrderNo(), oldTechnicianId, oldTechnicianName, newTechnicianId, newTechnicianName);
  347. if (!this.updateById(oldOrder)) {
  348. throw new ServiceException("转单失败:更新订单技师信息失败");
  349. }
  350. // 记录订单操作后状态(转单后状态通常保持不变)
  351. orderStatusAfter = oldOrder.getnStatus();
  352. // ========== 第6步:更新新技师状态(可服务 → 服务中)==========
  353. TJs newJsUpdate = new TJs();
  354. newJsUpdate.setId(newTechnicianId);
  355. newJsUpdate.setnStatus(JsStatusEnum.JS_SERVICE.getCode());
  356. if (!jsService.updateById(newJsUpdate)) {
  357. throw new ServiceException("转单失败:更新新技师状态失败");
  358. }
  359. newTechnicianStatusAfter = JsStatusEnum.JS_SERVICE.getCode();
  360. // ========== 第7步:更新原技师状态(服务中 → 可服务)==========
  361. TJs oldJsUpdate = new TJs();
  362. oldJsUpdate.setId(oldTechnicianId);
  363. oldJsUpdate.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
  364. if (!jsService.updateById(oldJsUpdate)) {
  365. throw new ServiceException("转单失败:更新原技师状态失败");
  366. }
  367. oldTechnicianStatusAfter = JsStatusEnum.JS_SERVICEABLE.getCode();
  368. log.info("更新技师状态完成 - 新技师:{} {}→{}, 原技师:{} {}→{}",
  369. newTechnicianName, getStatusName(newTechnicianStatusBefore), getStatusName(newTechnicianStatusAfter),
  370. oldTechnicianName, getStatusName(oldTechnicianStatusBefore), getStatusName(oldTechnicianStatusAfter));
  371. // ========== 第8步:获取操作人信息 ==========
  372. operatorId = SecurityUtils.getUserId() != null ? SecurityUtils.getUserId().toString() : "ADMIN";
  373. operatorName = SecurityUtils.getUsername() != null ? SecurityUtils.getUsername() : "系统管理员";
  374. // ========== 第9步:转单成功,设置操作结果为成功 ==========
  375. operationResult = 0; // 0-成功
  376. log.info("转单操作完成 - 订单号:{}", oldOrder.getOrderNo());
  377. return oldOrder;
  378. } catch (ServiceException e) {
  379. // 业务异常,操作失败
  380. log.error("转单操作失败 - 订单号:{}, 错误信息:{}", orderNo, e.getMessage());
  381. operationResult = 1; // 1-失败
  382. throw e;
  383. } catch (Exception e) {
  384. // 系统异常,操作失败
  385. log.error("转单操作异常 - 订单号:{}, 异常信息:{}", orderNo, e.getMessage(), e);
  386. operationResult = 1; // 1-失败
  387. throw new ServiceException("转单操作异常:" + e.getMessage());
  388. } finally {
  389. // ========== 第10步:记录转单操作日志(无论成功或失败都记录)==========
  390. try {
  391. // 只有在获取到基本信息后才记录日志
  392. if (orderId != null && orderNo != null && oldTechnicianId != null && newTechnicianId != null) {
  393. allocationLogService.recordTransferOrder(
  394. orderId, // orderId
  395. orderNo, // orderNo
  396. oldTechnicianId, // oldTechnicianId
  397. oldTechnicianName, // oldTechnicianName
  398. oldTechnicianStatusBefore, // oldTechnicianStatusBefore
  399. oldTechnicianStatusAfter, // oldTechnicianStatusAfter
  400. newTechnicianId, // newTechnicianId
  401. newTechnicianName, // newTechnicianName
  402. newTechnicianStatusBefore, // newTechnicianStatusBefore
  403. newTechnicianStatusAfter, // newTechnicianStatusAfter
  404. orderStatusBefore, // orderStatusBefore
  405. orderStatusAfter, // orderStatusAfter
  406. operatorId, // operatorId
  407. operatorName, // operatorName
  408. operationReason, // operationReason
  409. operationResult // operationResult(0-成功,1-失败)
  410. );
  411. String resultDesc = operationResult == 0 ? "成功" : "失败";
  412. log.info("转单操作记录已保存 - 订单号:{}, 操作结果:{}", orderNo, resultDesc);
  413. }
  414. } catch (Exception e) {
  415. // 记录日志失败不影响转单操作
  416. log.error("记录转单操作日志失败 - 订单号:{}, 错误信息:{}", orderNo, e.getMessage(), e);
  417. }
  418. }
  419. }
  420. /**
  421. * 获取技师状态名称
  422. *
  423. * @param status 状态码
  424. * @return String 状态名称
  425. */
  426. private String getStatusName(Integer status) {
  427. if (status == null) {
  428. return "未知";
  429. }
  430. switch (status) {
  431. case 0:
  432. return "可服务";
  433. case 1:
  434. return "服务中";
  435. case 2:
  436. return "不可服务";
  437. default:
  438. return "未知(" + status + ")";
  439. }
  440. }
  441. @Override
  442. public List<HomeBlock> getBlock(Date start, Date end, String deptId) {
  443. return orderMapper.getBlock(start, end, deptId);
  444. }
  445. @Override
  446. public OrderVerificationVo verification(TOrder order) {
  447. if (StringUtils.isBlank(order.getCouponReceiveId())) {
  448. throw new ServiceException("认领优惠券id为空");
  449. }
  450. if (StringUtils.isBlank(order.getcId())) {
  451. throw new ServiceException("订单id为空");
  452. }
  453. OrderVerificationVo orderVerificationVo = new OrderVerificationVo();
  454. TOrder tOrder = this.getById(order.getcId());
  455. orderVerificationVo.setCouponReceiveId(order.getCouponReceiveId());
  456. CouponReceive couponReceive = couponReceiveService.getById(order.getCouponReceiveId());
  457. Coupon coupon = couponService.getById(couponReceive.getCouponId());
  458. log.info("订单信息,{}", tOrder);
  459. log.info("优惠卷信息,{}", coupon);
  460. //折扣券
  461. if (coupon.getDiscountType() == 2) {
  462. //判断门槛金额
  463. if (tOrder.getTotalPrice().compareTo(coupon.getThresholdAmount()) >= 0) {
  464. //折扣值
  465. BigDecimal divide = coupon.getRebValue().divide(new BigDecimal(10));
  466. //优惠后的金额 = 订单总金额*折扣值
  467. BigDecimal bigDecimal = tOrder.getTotalPrice().multiply(divide).setScale(MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP);
  468. //优惠值
  469. orderVerificationVo.setPreferential(tOrder.getTotalPrice().subtract(bigDecimal));
  470. orderVerificationVo.setTotalPrice(bigDecimal);
  471. } else {
  472. throw new ServiceException("不满足优惠券门槛金额");
  473. }
  474. } else {
  475. if (tOrder.getTotalPrice().compareTo(coupon.getThresholdAmount()) >= 0) {
  476. //优惠值
  477. orderVerificationVo.setPreferential(coupon.getDiscountValue());
  478. orderVerificationVo.setTotalPrice(tOrder.getTotalPrice().subtract(coupon.getDiscountValue()));
  479. } else {
  480. throw new ServiceException("不满足优惠券门槛金额");
  481. }
  482. }
  483. if (orderVerificationVo.getTotalPrice().compareTo(BigDecimal.ZERO) < 0) {
  484. throw new ServiceException("当前项目不可用");
  485. }
  486. return orderVerificationVo;
  487. }
  488. private TOrder getOrder(TOrder tOrder) {
  489. if (updateById(tOrder)) {
  490. return tOrder;
  491. } else {
  492. throw new ServiceException("优惠券核销失败");
  493. }
  494. }
  495. /**
  496. * 支付订单
  497. *
  498. * @param order
  499. * @return R
  500. */
  501. @Override
  502. public R payOrder(TOrder order) throws Exception {
  503. // 根据订单ID查询订单信息
  504. TOrder orderNew = getById(order.getcId());
  505. if (!orderNew.getnStatus().equals(OrderStatusEnum.WAIT_PAY.getCode())) {
  506. throw new ServiceException("该订单已经支付或者超时被取消");
  507. }
  508. TJs js = jsService.getById(orderNew.getcJsId());
  509. if (StringUtils.isBlank(orderNew.getParentNo())) {
  510. if (null == js || js.getnStatus().equals(JsStatusEnum.JS_SERVICE.getCode())) {
  511. throw new ServiceException("该技师已在服务中请重新下单");
  512. }
  513. }
  514. orderNew.setPayType(order.getPayType());
  515. //优惠券核销
  516. if (StringUtils.isNotBlank(order.getCouponReceiveId())) {
  517. orderNew.setCouponReceiveId(order.getCouponReceiveId());
  518. orderNew.setPreferential(order.getPreferential());
  519. orderNew.setTotalPrice(order.getTotalPrice());
  520. if (!updateById(orderNew)) {
  521. throw new ServiceException("支付失败");
  522. }
  523. }
  524. //判断支付方式
  525. if (order.getPayType().equals(MassageConstants.INTEGER_ONE)) {
  526. //微信支付
  527. R resp = rechargeService.getPay(orderNew.getOrderNo(), orderNew.getTotalPrice(), orderNew.getcOpenId(), BillTypeEnum.WX_PAY.getInfo(), BillTypeEnum.WX_PAY.getCode().toString());
  528. //添加待接单消息通知(技师侧)
  529. orderNotificationService.sendPendingRemindNotification(orderNew);
  530. return resp;
  531. }
  532. TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId());
  533. if (null == user) {
  534. throw new ServiceException("用户不存在");
  535. }
  536. //现金支付
  537. if (order.getPayType().equals(MassageConstants.INTEGER_THREE)) {
  538. //现金支付
  539. orderPayManage(user, orderNew);
  540. //添加待接单消息通知(技师侧)
  541. orderNotificationService.sendPendingRemindNotification(orderNew);
  542. return R.ok();
  543. }
  544. if (user.getdBalance().compareTo(orderNew.getTotalPrice()) < MassageConstants.INTEGER_ZERO) {
  545. throw new ServiceException("账户金额不够请充值");
  546. } else {
  547. orderPayManage(user, orderNew);
  548. //添加待接单消息通知(技师侧)
  549. orderNotificationService.sendPendingRemindNotification(orderNew);
  550. return R.ok();
  551. }
  552. }
  553. /**
  554. * 新订单通知
  555. *
  556. * @param order
  557. */
  558. public void newOrderNotification(TOrder order) {
  559. cn.hutool.json.JSONObject param = JSONUtil.createObj();
  560. //订单号
  561. param.set("character_string9", JSONUtil.createObj().set("value", order.getOrderNo()));
  562. //电话
  563. param.set("phone_number14", JSONUtil.createObj().set("value", order.getcPhone()));
  564. param.set("thing18", JSONUtil.createObj().set("value", order.getcName()));
  565. param.set("time6", JSONUtil.createObj().set("value", DateTimeUtils.formatDate(new Date(), DateTimeUtils.DATE_NUMBER_YEAR_MONTH_FORMAT)));
  566. param.set("thing27", JSONUtil.createObj().set("value", order.getName()));
  567. TJs js = jsService.getById(order.getcJsId());
  568. weChatUtil.notification(js.getcOpenId(), wxPayProperties.getTemplateId1(), param);
  569. }
  570. /**
  571. * 订单支付管理
  572. *
  573. * @param user
  574. * @param orderNew
  575. */
  576. @Transactional(rollbackFor = Exception.class)
  577. public void orderPayManage(TWxUser user, TOrder orderNew) {
  578. //更新优惠卷状态
  579. if (StringUtils.isNotBlank(orderNew.getCouponReceiveId())) {
  580. CouponReceive couponReceive = new CouponReceive();
  581. couponReceive.setId(orderNew.getCouponReceiveId());
  582. couponReceive.setUseState(MassageConstants.INTEGER_TWO);
  583. if (!couponReceiveService.updateById(couponReceive)) {
  584. log.error("优惠券状态更新失败id:,{}", orderNew.getCouponReceiveId());
  585. }
  586. }
  587. // 更新用户金额 及下单此时
  588. TWxUser paramUser = new TWxUser();
  589. paramUser.setcOpenid(user.getcOpenid());
  590. // 余额支付
  591. if (orderNew.getPayType().equals(MassageConstants.INTEGER_TWO)) {
  592. paramUser.setdBalance(user.getdBalance().subtract(orderNew.getTotalPrice()));
  593. }
  594. paramUser.setdMoney(user.getdMoney().add(orderNew.getTotalPrice()));
  595. paramUser.setnNum(user.getnNum() + MassageConstants.INTEGER_ONE);
  596. paramUser.setId(user.getId());
  597. wxUserService.updateById(paramUser);
  598. //增加消费记录
  599. TConsumptionLog tConsumptionLog = new TConsumptionLog();
  600. tConsumptionLog.setAmount(orderNew.getTotalPrice().negate());
  601. tConsumptionLog.setBillNo(orderNew.getOrderNo());
  602. tConsumptionLog.setOpenId(orderNew.getcOpenId());
  603. if (orderNew.getPayType().equals(MassageConstants.INTEGER_TWO)) {
  604. tConsumptionLog.setBillType(BillTypeEnum.BALANCE_PAYMENT.getCode());
  605. tConsumptionLog.setNote("余额支付");
  606. } else if(orderNew.getPayType().equals(MassageConstants.INTEGER_ONE)){
  607. tConsumptionLog.setBillType(BillTypeEnum.WX_PAY.getCode());
  608. tConsumptionLog.setNote("微信支付");
  609. } else {
  610. tConsumptionLog.setBillType(BillTypeEnum.CASH_PAYMENT.getCode());
  611. tConsumptionLog.setNote("现金支付");
  612. }
  613. consumptionLogService.save(tConsumptionLog);
  614. // 更新项目数据
  615. JSONArray objects = orderNew.getcGoods();
  616. objects.forEach(item -> {
  617. UpdateWrapper<TXiangmu> wrapper = new UpdateWrapper<>();
  618. // 获取参数
  619. wrapper.lambda().eq(TXiangmu::getcId, ((JSONObject) item).getString("cId"));
  620. // 设置数量
  621. wrapper.setSql(" n_sale_number = n_sale_number + " + ((JSONObject) item).getInteger("number"));
  622. xiangmuService.update(wrapper);
  623. });
  624. TOrder orderParam = new TOrder();
  625. orderParam.setPayType(orderNew.getPayType());
  626. orderParam.setcId(orderNew.getcId());
  627. orderParam.setnStatus(OrderStatusEnum.WAIT_JD.getCode());
  628. orderParam.setPayTime(new Date());
  629. //加钟的订单支付完直接服务中
  630. if (StringUtils.isNotBlank(orderNew.getParentNo())) {
  631. orderParam.setnStatus(OrderStatusEnum.SERVICE.getCode());
  632. }
  633. // orderParam.setnStatus(OrderStatusEnum.SERVICE.getCode());
  634. //更新及技师状态
  635. updateJs(orderNew);
  636. updateById(orderParam);
  637. //
  638. this.newOrderNotification(orderNew);
  639. //电话通知
  640. TJs js = jsService.getById(orderNew.getcJsId());
  641. Sendvoice.sendPhone(js.getcPhone());
  642. }
  643. /**
  644. * 拒绝订单
  645. *
  646. * @param order
  647. */
  648. @Override
  649. public Boolean jujue(TOrder order) {
  650. TOrder orderNew = getById(order.getcId());
  651. TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId());
  652. // 更新用户金额 及下单此时
  653. TWxUser paramUser = new TWxUser();
  654. paramUser.setcOpenid(user.getcOpenid());
  655. paramUser.setId(user.getId());
  656. // 余额记录
  657. TConsumptionLog tConsumptionLog = new TConsumptionLog();
  658. tConsumptionLog.setAmount(orderNew.getTotalPrice());
  659. tConsumptionLog.setBillNo(orderNew.getOrderNo());
  660. tConsumptionLog.setOpenId(orderNew.getcOpenId());
  661. if (orderNew.getPayType() == 2) {
  662. // 金额归还对应账户
  663. paramUser.setdBalance(user.getdBalance().add(orderNew.getTotalPrice()));
  664. tConsumptionLog.setBillType(BillTypeEnum.REFUSE_ACCEPT_REFUND.getCode());
  665. tConsumptionLog.setNote("拒绝接单退款到余额");
  666. } else {
  667. // 微信支付
  668. // 生成退款单退款
  669. RefundVoucher refundVoucher = new RefundVoucher();
  670. refundVoucher.setRefundNo(generator.generateNextOrderNumber(OrderNumberGenerator.KEY_PREFIX_REFUND));
  671. refundVoucher.setOrderNo(orderNew.getOrderNo());
  672. refundVoucher.setMoney(orderNew.getTotalPrice());
  673. refundVoucher.setOpenId(orderNew.getcOpenId());
  674. refundVoucher.setReStatus(MassageConstants.INTEGER_ZERO);
  675. refundVoucher.setReason("技师拒绝接单");
  676. refundVoucherService.save(refundVoucher);
  677. tConsumptionLog.setBillType(BillTypeEnum.REFUSE_ACCEPT_REFUND.getCode());
  678. tConsumptionLog.setNote("拒绝接单退款到余额");
  679. // 微信退款原路返回
  680. rechargeService.refund(refundVoucher.getRefundNo(), null, orderNew.getOrderNo(), orderNew.getTotalPrice());
  681. }
  682. consumptionLogService.save(tConsumptionLog);
  683. //退优惠卷
  684. if (StringUtils.isNotBlank(orderNew.getCouponReceiveId())) {
  685. CouponReceive couponReceive = couponReceiveService.getById(orderNew.getCouponReceiveId());
  686. couponReceive.setUseState(MassageConstants.INTEGER_ZERO);
  687. couponReceiveService.updateById(couponReceive);
  688. }
  689. log.info("余额支付退款user:{}", user);
  690. // 消费金额对应减少
  691. paramUser.setdMoney(user.getdMoney().subtract(orderNew.getTotalPrice()));
  692. // 下单次数减一
  693. paramUser.setnNum(user.getnNum() - MassageConstants.INTEGER_ONE);
  694. wxUserService.updateById(paramUser);
  695. // 更新项目数据
  696. JSONArray objects = orderNew.getcGoods();
  697. objects.forEach(item -> {
  698. UpdateWrapper<TXiangmu> wrapper = new UpdateWrapper<>();
  699. // 获取参数
  700. wrapper.lambda().eq(TXiangmu::getcId, ((JSONObject) item).getString("cId"));
  701. // 设置数量
  702. wrapper.setSql(" n_sale_number = n_sale_number - " + ((JSONObject) item).getInteger("number"));
  703. xiangmuService.update(wrapper);
  704. });
  705. TOrder orderParam = new TOrder();
  706. orderParam.setcId(orderNew.getcId());
  707. orderParam.setnStatus(OrderStatusEnum.REFUSE.getCode());
  708. orderParam.setReasonRefusal(order.getReasonRefusal());
  709. updateJs(orderNew);
  710. return updateById(orderParam);
  711. }
  712. /**
  713. * 确认服务完成
  714. *
  715. * @param order
  716. * @return Boolean
  717. */
  718. @Override
  719. @Transactional(rollbackFor = Exception.class)
  720. public Boolean confirm(TOrder order) {
  721. // 获取订单信息
  722. TOrder orderNew = getById(order.getcId());
  723. if (!orderNew.getnStatus().equals(OrderStatusEnum.SERVICE.getCode())) {
  724. throw new ServiceException("订单状态不是服务中");
  725. }
  726. // 更新技师信息
  727. TJs jsParam = new TJs();
  728. jsParam.setId(orderNew.getcJsId());
  729. jsParam.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
  730. //判断热度标识
  731. List<TOrder> list = list(new LambdaQueryWrapper<TOrder>().eq(TOrder::getcJsId, orderNew.getcJsId())
  732. .ge(TOrder::getDtCreateTime, DateTimeUtils.addDays(new Date(), -3))
  733. .ge(TOrder::getnStatus, OrderStatusEnum.WAIT_EVALUATE.getCode()));
  734. if (list.size() >= 2) {
  735. // 设置热度标识:1
  736. jsParam.setnB3(MassageConstants.INTEGER_ONE);
  737. }
  738. // 更新技师状态
  739. jsService.updateById(jsParam);
  740. // 更新技师钱包金额
  741. TJs jsById = jsService.getById(orderNew.getcJsId());
  742. // 获取技师抽成
  743. BigDecimal multiply = orderNew.getTotalPrice().multiply(new BigDecimal(jsById.getnBili()));
  744. multiply = multiply.divide(new BigDecimal(100), MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP);
  745. // 获取技师所对应的用户
  746. TWxUser jsUser = wxUserService.getByOpenId(jsById.getcOpenId());
  747. // 更新余额
  748. jsUser.setdBalance(jsUser.getdBalance().add(multiply));
  749. // 更新总钱数
  750. jsUser.setdAllMoney(jsUser.getdAllMoney().add(multiply));
  751. wxUserService.updateById(jsUser);
  752. //增加消费记录
  753. TConsumptionLog tConsumptionLog = new TConsumptionLog();
  754. tConsumptionLog.setAmount(multiply);
  755. tConsumptionLog.setBillNo(orderNew.getOrderNo());
  756. tConsumptionLog.setOpenId(jsUser.getcOpenid());
  757. tConsumptionLog.setBillType(BillTypeEnum.INCOME.getCode());
  758. tConsumptionLog.setNote("技师收益");
  759. consumptionLogService.save(tConsumptionLog);
  760. // 如果该技师有推荐人员 一级
  761. if (StringUtils.isNotBlank(jsUser.getcUpUser())) {
  762. // 获取技师上级对应的用户
  763. TWxUser jsUp = wxUserService.getByOpenId(jsUser.getcUpUser());
  764. extracted(orderNew, jsUp);
  765. //二级
  766. if (StringUtils.isNotBlank(jsUp.getcUpUser())) {
  767. TWxUser jsUpTwo = wxUserService.getByOpenId(jsUp.getcUpUser());
  768. extracted(orderNew, jsUpTwo);
  769. //三级
  770. if (StringUtils.isNotBlank(jsUpTwo.getcUpUser())) {
  771. TWxUser jsUpThree = wxUserService.getByOpenId(jsUpTwo.getcUpUser());
  772. extracted(orderNew, jsUpThree);
  773. }
  774. }
  775. }
  776. // 更新订单
  777. // 订单状态:待评价
  778. orderNew.setnStatus(OrderStatusEnum.WAIT_EVALUATE.getCode());
  779. orderNew.setEndTime(LocalDateTime.now());
  780. updateById(orderNew);
  781. // 添加订单完成消息通知
  782. orderNotificationService.sendCompletedNotification(orderNew);
  783. return true;
  784. }
  785. private void extracted(TOrder orderNew, TWxUser jsUp) {
  786. log.info("TOrderServiceImpl->extracted->jsUp,{}", JSONUtil.toJsonStr(jsUp));
  787. log.info("TOrderServiceImpl->extracted->orderNew,{}",JSONUtil.toJsonStr(orderNew));
  788. BigDecimal up = orderNew.getdTotalMoney().multiply(new BigDecimal("10"));
  789. up = up.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
  790. // 更新余额
  791. jsUp.setdBalance(jsUp.getdBalance().add(up));
  792. // 更新总钱数
  793. jsUp.setdAllMoney(jsUp.getdAllMoney().add(up));
  794. jsUp.setDistributionAmount(up);
  795. wxUserService.updateById(jsUp);
  796. //记录分销收益
  797. TConsumptionLog tConsumptionLog = new TConsumptionLog();
  798. tConsumptionLog.setAmount(up);
  799. tConsumptionLog.setBillNo(orderNew.getOrderNo());
  800. tConsumptionLog.setOpenId(jsUp.getcOpenid());
  801. tConsumptionLog.setBillType(BillTypeEnum.DISTRIBUTION.getCode());
  802. tConsumptionLog.setNote("分销收益");
  803. consumptionLogService.save(tConsumptionLog);
  804. }
  805. /**
  806. * 取消订单
  807. *
  808. * @param order
  809. * @return Boolean
  810. */
  811. @Override
  812. @Transactional(rollbackFor = Exception.class)
  813. public Boolean cancle(TOrder order) {
  814. // 获取订单信息
  815. // 根据orderid查询订单信息
  816. TOrder orderNew = getById(order.getcId());
  817. //待接单
  818. if (Objects.equals(orderNew.getnStatus(), OrderStatusEnum.WAIT_JD.getCode())) {
  819. TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId());
  820. // 更新用户金额 及下单此时
  821. TWxUser paramUser = new TWxUser();
  822. paramUser.setId(user.getId());
  823. paramUser.setcOpenid(user.getcOpenid());
  824. TConsumptionLog tConsumptionLog = new TConsumptionLog();
  825. tConsumptionLog.setAmount(orderNew.getTotalPrice());
  826. tConsumptionLog.setBillNo(orderNew.getOrderNo());
  827. tConsumptionLog.setOpenId(orderNew.getcOpenId());
  828. // 余额支付
  829. if (orderNew.getPayType() == 2) {
  830. // 金额归还对应账户
  831. paramUser.setdBalance(user.getdBalance().add(orderNew.getTotalPrice()));
  832. // 余额记录
  833. tConsumptionLog.setBillType(BillTypeEnum.CANCEL_ACCEPT_REFUND.getCode());
  834. tConsumptionLog.setNote("取消订单退款到余额");
  835. //自己取消的不退优惠卷
  836. } else {
  837. // 微信支付
  838. // 生成退款单退款
  839. RefundVoucher refundVoucher = new RefundVoucher();
  840. refundVoucher.setRefundNo(generator.generateNextOrderNumber(OrderNumberGenerator.KEY_PREFIX_REFUND));
  841. refundVoucher.setOrderNo(orderNew.getOrderNo());
  842. refundVoucher.setMoney(orderNew.getTotalPrice());
  843. refundVoucher.setOpenId(orderNew.getcOpenId());
  844. refundVoucher.setReStatus(MassageConstants.INTEGER_ZERO);
  845. refundVoucher.setReason("技师拒绝接单");
  846. refundVoucherService.save(refundVoucher);
  847. tConsumptionLog.setBillType(BillTypeEnum.CANCEL_WX_REFUND.getCode());
  848. tConsumptionLog.setNote("取消订单退款到微信");
  849. // 微信退款原路返回
  850. rechargeService.refund(refundVoucher.getRefundNo(), null, orderNew.getOrderNo(), orderNew.getTotalPrice());
  851. }
  852. consumptionLogService.save(tConsumptionLog);
  853. // 消费金额对应减少
  854. paramUser.setdMoney(user.getdMoney().subtract(orderNew.getTotalPrice()));
  855. // 下单次数减一
  856. paramUser.setnNum(user.getnNum() - MassageConstants.INTEGER_ONE);
  857. wxUserService.updateById(paramUser);
  858. // 更新项目数据
  859. JSONArray objects = orderNew.getcGoods();
  860. objects.forEach(item -> {
  861. UpdateWrapper<TXiangmu> wrapper = new UpdateWrapper<>();
  862. // 获取参数
  863. wrapper.lambda().eq(TXiangmu::getcId, ((JSONObject) item).getString("cId"));
  864. // 设置数量
  865. wrapper.setSql(" n_sale_number = n_sale_number - " + ((JSONObject) item).getInteger("number"));
  866. xiangmuService.update(wrapper);
  867. });
  868. TOrder orderParam = new TOrder();
  869. orderParam.setcId(orderNew.getcId());
  870. orderParam.setnStatus(OrderStatusEnum.CANCEL.getCode());
  871. //更新技师状态
  872. TJs tJs = new TJs();
  873. tJs.setId(orderNew.getcJsId());
  874. tJs.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
  875. jsService.updateById(tJs);
  876. updateById(orderParam);
  877. // 添加取消订单通知(用户侧)
  878. orderNotificationService.sendCancelledNotification(orderNew);
  879. // 添加取消订单通知(技师侧)
  880. orderNotificationService.sendTechnicianCancelledNotification(orderNew);
  881. return true;
  882. } else if (Objects.equals(orderNew.getnStatus(), OrderStatusEnum.WAIT_PAY.getCode())) {//待付款
  883. TOrder orderParam = new TOrder();
  884. orderParam.setcId(orderNew.getcId());
  885. orderParam.setnStatus(OrderStatusEnum.CANCEL.getCode());
  886. updateById(orderParam);
  887. // 添加取消订单通知
  888. orderNotificationService.sendCancelledNotification(orderNew);
  889. // 添加取消订单通知(技师侧)
  890. orderNotificationService.sendTechnicianCancelledNotification(orderNew);
  891. return true;
  892. } else {
  893. return false;
  894. }
  895. }
  896. @Override
  897. public TOrder getByNo(String orderNo) {
  898. LambdaQueryWrapper<TOrder> objectLambdaQueryWrapper = new LambdaQueryWrapper<>();
  899. return this.getOne(objectLambdaQueryWrapper.eq(TOrder::getOrderNo, orderNo));
  900. }
  901. // private TOrder gettOrder(TOrder order) {
  902. // LambdaUpdateWrapper<TOrder> objectLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
  903. // objectLambdaUpdateWrapper.eq(TOrder::getOrderNo, order.getOrderNo());
  904. // return this.getOne(objectLambdaUpdateWrapper);
  905. // }
  906. @Override
  907. public Page<TOrder> getAll(Page<TOrder> page, TOrder order) {
  908. Page<TOrder> orderPage = orderMapper.getAll(page, order);
  909. if (orderPage != null && CollectionUtil.isNotEmpty(orderPage.getRecords())) {
  910. ArrayList<TOrder> ordersList = Lists.newArrayList();
  911. orderPage.getRecords().forEach(orders -> {
  912. orders.setStatusName(OrderStatusEnum.getDescByCode(orders.getnStatus()));
  913. orders.setJsPhone(orders.getJs().getcPhone());
  914. orders.setJsName(orders.getJs().getcName());
  915. if (StringUtils.isEmpty(orders.getcTime())) {
  916. orders.setRemainingTime(0L);
  917. }
  918. if (StringUtils.isNotBlank(orders.getcTime()) && DateTimeUtils.dateStringToStamp(orders.getcTime()) > DateTimeUtils.dateToStamp(new Date())) {
  919. orders.setRemainingTime((DateTimeUtils.dateStringToStamp(orders.getcTime()) - DateTimeUtils.dateToStamp(new Date())) / 1000);
  920. }
  921. if (StringUtils.isNotBlank(orders.getcTime()) && DateTimeUtils.dateStringToStamp(orders.getcTime()) < DateTimeUtils.dateToStamp(new Date())) {
  922. orders.setRemainingTime(0L);
  923. }
  924. if (StringUtils.isNotBlank(orders.getOldJsId())) {
  925. orders.setOldJs(jsService.getById(orders.getOldJsId()));
  926. }
  927. ordersList.add(orders);
  928. });
  929. orderPage.setRecords(ordersList);
  930. }
  931. return orderPage;
  932. }
  933. @Override
  934. @Transactional(rollbackFor = Exception.class)
  935. public void takingOrders(TOrder order) {
  936. if (order == null || StringUtils.isBlank(order.getcId())) {
  937. throw new IllegalArgumentException("订单对象不能为空");
  938. }
  939. TOrder orderNew = this.getById(order.getcId());
  940. // 【新增】订单状态锁校验 - 检查技师是否可以接单
  941. log.info("开始校验技师 {} 是否可以接单,订单号:{}", orderNew.getcJsId(), orderNew.getOrderNo());
  942. orderValidationService.canAcceptOrder(orderNew.getcJsId(), orderNew);
  943. log.info("技师 {} 接单校验通过,继续接单流程", orderNew.getcJsId());
  944. // 检查订单对应的技师是否存在
  945. // updateJs (orderNew);
  946. // 更新订单状态
  947. TOrder orderParam = new TOrder();
  948. orderParam.setcId(order.getcId());
  949. //设置订单状态:已接单
  950. orderParam.setnStatus(OrderStatusEnum.RECEIVED_ORDER.getCode());
  951. orderParam.setAcceptanceTime(LocalDateTime.now());
  952. this.updateById(orderParam);
  953. // 已接单消息通知(用户侧)
  954. orderNotificationService.sendReceivedNotification(orderNew);
  955. // 已接单消息通知(技师侧)
  956. orderNotificationService.sendTechnicianReceivedNotification(orderNew);
  957. }
  958. private void updateJs(TOrder orderNew) {
  959. TJs js = jsService.getById(orderNew.getcJsId());
  960. if (js == null) {
  961. throw new IllegalStateException("无法找到对应的技师");
  962. }
  963. if (Objects.equals(js.getnStatus(), JsStatusEnum.JS_SERVICEABLE.getCode())) {
  964. // 更新技师状态
  965. js.setnStatus(JsStatusEnum.JS_SERVICE.getCode());
  966. // 确保js.getnNum()不为null,避免 NullPointerException
  967. int num = js.getnNum() == null ? 0 : js.getnNum();
  968. js.setnNum(num + MassageConstants.INTEGER_ONE);
  969. } else {
  970. // 更新技师状态
  971. js.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
  972. // 确保js.getnNum()不为null,避免 NullPointerException
  973. int num = js.getnNum() == null ? 0 : js.getnNum();
  974. js.setnNum(num - MassageConstants.INTEGER_ONE);
  975. }
  976. jsService.updateById(js);
  977. }
  978. }