TOrderServiceImpl.java 70 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634
  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.*;
  18. import com.ylx.massage.enums.*;
  19. import com.ylx.massage.mapper.TOrderMapper;
  20. import com.ylx.massage.service.*;
  21. import com.ylx.massage.utils.*;
  22. import lombok.extern.slf4j.Slf4j;
  23. import lombok.var;
  24. import org.apache.commons.compress.utils.Lists;
  25. import org.springframework.beans.factory.annotation.Value;
  26. import org.springframework.stereotype.Service;
  27. import org.springframework.transaction.annotation.Transactional;
  28. import javax.annotation.Resource;
  29. import java.math.BigDecimal;
  30. import java.math.RoundingMode;
  31. import java.time.*;
  32. import java.util.*;
  33. import java.util.stream.Collectors;
  34. /**
  35. * 订单表 服务实现类
  36. */
  37. @Service
  38. @Slf4j
  39. public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> implements TOrderService {
  40. @Resource
  41. private TOrderMapper orderMapper;
  42. @Resource
  43. private WechatAccountConfig wxPayProperties;
  44. @Resource
  45. private LocationUtil locationUtil;
  46. @Resource
  47. private TWxUserService wxUserService;
  48. @Resource
  49. private TRechargeService rechargeService;
  50. @Resource
  51. private TXiangmuService xiangmuService;
  52. @Resource
  53. private OrderNumberGenerator generator;
  54. @Resource
  55. private TJsService jsService;
  56. @Resource
  57. private TAddressService addressService;
  58. @Resource
  59. private TConsumptionLogService consumptionLogService;
  60. @Resource
  61. private MassageUtil massageUtil;
  62. @Resource
  63. private CouponReceiveService couponReceiveService;
  64. @Resource
  65. private CouponService couponService;
  66. @Resource
  67. private WeChatUtil weChatUtil;
  68. @Resource
  69. private RefundVoucherService refundVoucherService;
  70. @Resource
  71. private OrderValidationService orderValidationService;
  72. @Resource
  73. private OrderNotificationService orderNotificationService;
  74. @Resource
  75. private OrderAllocationLogService allocationLogService;
  76. @Resource
  77. private CancelOrderApplicationService cancelOrderApplicationService;
  78. @Resource
  79. private TTxRecordService recordService;
  80. @Value("${percent}")
  81. private String percent;
  82. @Value("${hPercent}")
  83. private String hPercent;
  84. /**
  85. * 判断是否免车费
  86. * 时间段判断:
  87. * - 白天时段(7:30-20:00):距离 ≤ 技师白天免车费里程 → 免费
  88. * - 夜间时段(20:00-7:30):距离 ≤ 技师夜间免车费里程 → 免费
  89. *
  90. * @param js
  91. * @param distance
  92. * @return Boolean
  93. */
  94. public Boolean isFree(TJs js, BigDecimal distance) {
  95. Date date = new Date();
  96. //白天免车费(07.30-20.00)
  97. long current = Long.parseLong(DateTimeUtils.numTime(date));
  98. if (current >= MassageConstants.START_FREE && current <= MassageConstants.END_FREE) {
  99. if (js.getDaytimeMileage().compareTo(distance) >= 0) {
  100. //免车费
  101. return Boolean.TRUE;
  102. } else {
  103. return Boolean.FALSE;
  104. }
  105. } else {
  106. //夜间免车费(20.00-07.30)
  107. if (js.getNigthMileage().compareTo(distance) >= 0) {
  108. //免车费
  109. return Boolean.TRUE;
  110. } else {
  111. return Boolean.FALSE;
  112. }
  113. }
  114. }
  115. /**
  116. * 添加订单
  117. *
  118. * @param order
  119. * @return TOrder
  120. */
  121. @Override
  122. @Transactional(rollbackFor = Exception.class)
  123. public TOrder addOrder(TOrder order) {
  124. String jsId = order.getcJsId();
  125. // 1. 基础参数校验
  126. if (StringUtils.isBlank(jsId)) {
  127. throw new ServiceException("请选择技师");
  128. }
  129. if (order.getcGoods().isEmpty()) {
  130. throw new ServiceException("请选择项目");
  131. }
  132. TJs js = jsService.getById(jsId);
  133. if (js == null) {
  134. throw new ServiceException("技师不存在");
  135. }
  136. Integer techType = js.getTechType();
  137. // 虚拟技师
  138. if(techType.equals(1)){
  139. //虚拟技师订单
  140. order.setVirtualOrderFlag(1);
  141. //虚拟技师订单未分配
  142. order.setVirtualOrderAllocation(1);
  143. }else{
  144. //真实订单
  145. order.setVirtualOrderFlag(0);
  146. order.setVirtualOrderAllocation(0);
  147. }
  148. // 2. 【新增】订单状态锁校验 - 检查技师是否可以接单
  149. // 确保技师没有进行中的订单,保证服务时间互斥
  150. log.info("开始校验技师 {} 是否可以接单,订单号:{}", order.getcJsId(), order.getOrderNo());
  151. orderValidationService.canAcceptOrder(order.getcJsId(), order);
  152. log.info("技师 {} 接单校验通过,继续创建订单", order.getcJsId());
  153. //优惠卷减免
  154. // List<CouponReceiveVo> coupons = couponReceiveService.getByOpenId(order.getcOpenId());
  155. // BigDecimal preferential = this.setCoupon(coupons);
  156. // order.setPreferential(preferential);
  157. // 生成订单号
  158. order.setOrderNo(generator.generateNextOrderNumber(OrderNumberGenerator.KEY_PREFIX_ORDER));
  159. //订单价格
  160. List<TXiangmu> list = JSONObject.parseArray(order.getcGoods().toJSONString(), TXiangmu.class);
  161. BigDecimal sum = list.stream().map(TXiangmu::getSum).reduce(BigDecimal.ZERO, BigDecimal::add);
  162. //订单总金额
  163. order.setdTotalMoney(sum);
  164. //获取用户默认地址
  165. TAddress address = addressService.getByOpenId(order.getcOpenId());
  166. if (address == null) {
  167. throw new ServiceException("请先添加地址");
  168. }
  169. //添加技师位置信息
  170. locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), Double.parseDouble(js.getLongitude().toString()), Double.parseDouble(js.getLatitude().toString()));
  171. //添加用户位置信息
  172. locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, order.getcOpenId() + order.getOrderNo(), Double.parseDouble(address.getLongitude().toString()), Double.parseDouble(address.getLatitude().toString()));
  173. //计算距离
  174. double distance = locationUtil.getDistance(js.getcOpenId() + order.getOrderNo(), order.getcOpenId() + order.getOrderNo());
  175. log.info("技师与用户之间的距离:{}", distance);
  176. locationUtil.remove(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), order.getcOpenId() + order.getOrderNo());
  177. order.setDistance(new BigDecimal(distance));
  178. //计算车费
  179. if (order.getDistance() != null && order.getDistance().compareTo(BigDecimal.ZERO) > 0 && StringUtils.isBlank(order.getParentNo())) {
  180. //判断是否可以免车费
  181. if (!this.isFree(js, order.getDistance())) {
  182. BigDecimal bigDecimal = massageUtil.calculateTaxiFare(order.getDistance(), js.getDeptId());
  183. order.setFare(bigDecimal.setScale(MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP));
  184. }
  185. }
  186. //总价 = 订单 + 车费
  187. order.setTotalPrice(sum.add(Optional.ofNullable(order.getFare()).orElse(BigDecimal.ZERO)));
  188. if (order.getParentNo() != null && order.getOrderType() == 2) {
  189. //升级订单 补差价
  190. TOrder partOrder = this.getByNo(order.getParentNo());
  191. order.setPriceDifference(order.getTotalPrice().subtract(partOrder.getTotalPrice()));
  192. }
  193. order.setAddress(address.getAddress());
  194. order.setName(address.getName());
  195. order.setLatitude(address.getLatitude());
  196. order.setLongitude(address.getLongitude());
  197. order.setcPhone(address.getPhone());
  198. //设置用户姓名
  199. order.setcName(address.getUserName());
  200. order.setAtlasAdd(address.getAtlasAdd());
  201. order.setDeptId(js.getDeptId());
  202. order.setDeptName(js.getCity());
  203. //设置订单状态:待支付
  204. order.setnStatus(OrderStatusEnum.WAIT_PAY.getCode());
  205. order.setDtCreateTime(LocalDateTime.now());
  206. Date date = DateTimeUtils.addMinute(new Date(), 10);
  207. order.setcTime(DateTimeUtils.formatDate(date, "yyyy-MM-dd HH:mm:ss"));
  208. save(order);
  209. return order;
  210. }
  211. private BigDecimal setCoupon(List<CouponReceiveVo> coupons) {
  212. //过滤过期的优惠券
  213. coupons = coupons.stream().filter(coupon -> coupon.getExpirationTime().after(new Date())).collect(Collectors.toList());
  214. //无门槛优惠券
  215. List<CouponReceiveVo> collect = coupons.stream().filter(coupon -> coupon.getDiscountType().equals(DiscountTypeEnum.NO_THRESHOLD.getCode())).collect(Collectors.toList());
  216. //支付成功 后 删除优惠卷
  217. // couponReceiveService.removeCoupons(collect);
  218. //计算优惠金额
  219. return collect.stream().map(CouponReceiveVo::getDiscountValue).reduce(BigDecimal.ZERO, BigDecimal::add);
  220. }
  221. @Override
  222. public void payNotifyOrder(String outTradeNo) {
  223. //查询未支付的订单
  224. LambdaQueryWrapper<TOrder> queryWrapper = new LambdaQueryWrapper<>();
  225. queryWrapper.eq(TOrder::getOrderNo, outTradeNo).eq(TOrder::getnStatus, OrderStatusEnum.WAIT_PAY.getCode());
  226. TOrder orderNew = this.getOne(queryWrapper);
  227. if (orderNew == null) {
  228. log.error("订单 {} 未支付状态不存在", outTradeNo);
  229. return;
  230. }
  231. // 设置微信支付
  232. orderNew.setPayType(1);
  233. TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId());
  234. orderPayManage(user, orderNew);
  235. }
  236. @Override
  237. public Object updateAddressById(TOrder borrow) {
  238. TOrder order = this.getById(borrow.getcId());
  239. if (borrow.getLatitude() != null && borrow.getLatitude() != 0 && borrow.getLongitude() != null && borrow.getLongitude() != 0) {
  240. order.setAtlasAdd(borrow.getAtlasAdd());
  241. order.setcName(borrow.getcName());
  242. order.setcPhone(borrow.getcPhone());
  243. order.setName(borrow.getName());
  244. order.setAddress(borrow.getAddress());
  245. order.setLatitude(borrow.getLatitude());
  246. order.setLongitude(borrow.getLongitude());
  247. TJs js = jsService.getById(order.getcJsId());
  248. //添加技师位置信息
  249. locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), Double.parseDouble(js.getLongitude().toString()), Double.parseDouble(js.getLatitude().toString()));
  250. //添加用户位置信息
  251. locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, order.getcOpenId() + order.getOrderNo(), Double.parseDouble(borrow.getLongitude().toString()), Double.parseDouble(borrow.getLatitude().toString()));
  252. double distance = locationUtil.getDistance(js.getcOpenId() + order.getOrderNo(), order.getcOpenId() + order.getOrderNo());
  253. locationUtil.remove(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), order.getcOpenId() + order.getOrderNo());
  254. order.setDistance(new BigDecimal(distance));
  255. //计算车费
  256. if (order.getDistance() != null && order.getDistance().compareTo(BigDecimal.ZERO) > 0) {
  257. //判断是否可以免车费
  258. if (!this.isFree(js, order.getDistance())) {
  259. BigDecimal bigDecimal = massageUtil.calculateTaxiFare(order.getDistance(), order.getDeptId());
  260. order.setFare(bigDecimal.setScale(MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP));
  261. }
  262. }
  263. order.setTotalPrice(order.getdTotalMoney().add(Optional.ofNullable(order.getFare()).orElse(BigDecimal.ZERO)));
  264. this.updateById(order);
  265. }
  266. return order;
  267. }
  268. @Override
  269. public Object depart(TOrder order) {
  270. LambdaQueryWrapper<TOrder> wrapper = new LambdaQueryWrapper<>();
  271. wrapper.eq(TOrder::getcId, order.getcId()).eq(TOrder::getnStatus, OrderStatusEnum.RECEIVED_ORDER.getCode());
  272. //设置订单状态:已出发
  273. order.setnStatus(OrderStatusEnum.DEPART.getCode());
  274. order.setDepartTime(new Date());
  275. order.setDepartLongitude(Optional.ofNullable(order.getDepartLongitude()).orElse(BigDecimal.ZERO));
  276. order.setDepartLatitude(Optional.ofNullable(order.getDepartLatitude()).orElse(BigDecimal.ZERO));
  277. return this.update(order, wrapper);
  278. }
  279. @Override
  280. public Integer getOrderNum(String jsid, Date startDate, Date endDate) {
  281. return orderMapper.getOrderNum(jsid, startDate, endDate);
  282. }
  283. @Override
  284. public Integer getAddNum(String jsid, Date startDate, Date endDate) {
  285. return orderMapper.getAddNum(jsid, startDate, endDate);
  286. }
  287. @Override
  288. public Integer getUpgradeNum(String jsid, Date startDate, Date endDate) {
  289. return orderMapper.getUpgradeNum(jsid, startDate, endDate);
  290. }
  291. @Override
  292. public BigDecimal getTurnover(String jsid, Date startDate, Date endDate) {
  293. return orderMapper.getTurnover(jsid, startDate, endDate);
  294. }
  295. @Override
  296. @Transactional(rollbackFor = Exception.class)
  297. public TOrder transferOrder(TOrder order) {
  298. // ========== 第1步:参数校验 ==========
  299. if (StringUtils.isBlank(order.getcId())) {
  300. throw new ServiceException("订单id不能为空");
  301. }
  302. if (StringUtils.isBlank(order.getcJsId())) {
  303. throw new ServiceException("转单技师ID不能为空");
  304. }
  305. // 定义操作结果(默认为失败)
  306. Integer operationResult = 1; // 1-失败
  307. // 定义操作记录所需的变量
  308. String orderId = null;
  309. String orderNo = null;
  310. String oldTechnicianId = null;
  311. String oldTechnicianName = null;
  312. Integer oldTechnicianStatusBefore = null;
  313. Integer oldTechnicianStatusAfter = null;
  314. String newTechnicianId = null;
  315. String newTechnicianName = null;
  316. Integer newTechnicianStatusBefore = null;
  317. Integer newTechnicianStatusAfter = null;
  318. Integer orderStatusBefore = null;
  319. Integer orderStatusAfter = null;
  320. String operatorId = null;
  321. String operatorName = null;
  322. String operationReason = "虚拟订单分配";
  323. try {
  324. // ========== 第2步:查询原订单信息 ==========
  325. TOrder oldOrder = this.getById(order.getcId());
  326. if (oldOrder == null) {
  327. throw new ServiceException("订单不存在");
  328. }
  329. //原技师ID
  330. oldTechnicianId = oldOrder.getcJsId();
  331. //新技师ID
  332. newTechnicianId = order.getcJsId();
  333. // 记录订单操作前状态
  334. orderStatusBefore = oldOrder.getnStatus();
  335. orderId = oldOrder.getcId();
  336. orderNo = oldOrder.getOrderNo();
  337. log.info("开始转单操作 - 订单号:{}, 原技师ID:{}, 新技师ID:{}, 订单状态:{}", oldOrder.getOrderNo(), oldTechnicianId, newTechnicianId, orderStatusBefore);
  338. // ========== 第3步:查询原技师信息 ==========
  339. TJs oldTechnician = jsService.getById(oldTechnicianId);
  340. if (oldTechnician == null) {
  341. throw new ServiceException("原技师不存在");
  342. }
  343. oldTechnicianName = oldTechnician.getcName();
  344. oldTechnicianStatusBefore = oldTechnician.getnStatus();
  345. // ========== 第4步:查询新技师信息 ==========
  346. TJs newTechnician = jsService.getById(newTechnicianId);
  347. if (newTechnician == null) {
  348. throw new ServiceException("新技师不存在");
  349. }
  350. newTechnicianName = newTechnician.getcName();
  351. newTechnicianStatusBefore = newTechnician.getnStatus();
  352. // ========== 第5步:更新订单技师信息 ==========
  353. oldOrder.setOldJsId(oldTechnicianId); // 保存原技师ID
  354. oldOrder.setcJsId(newTechnicianId); // 更新为新技师ID
  355. log.info("更新订单技师 - 订单号:{}, 原技师:[ID:{}, 姓名:{}], 新技师:[ID:{}, 姓名:{}]", oldOrder.getOrderNo(), oldTechnicianId, oldTechnicianName, newTechnicianId, newTechnicianName);
  356. if (!this.updateById(oldOrder)) {
  357. throw new ServiceException("转单失败:更新订单技师信息失败");
  358. }
  359. // 记录订单操作后状态(转单后状态通常保持不变)
  360. orderStatusAfter = oldOrder.getnStatus();
  361. // ========== 第6步:更新新技师状态(可服务 → 服务中)==========
  362. TJs newJsUpdate = new TJs();
  363. newJsUpdate.setId(newTechnicianId);
  364. newJsUpdate.setnStatus(JsStatusEnum.JS_SERVICE.getCode());
  365. if (!jsService.updateById(newJsUpdate)) {
  366. throw new ServiceException("转单失败:更新新技师状态失败");
  367. }
  368. newTechnicianStatusAfter = JsStatusEnum.JS_SERVICE.getCode();
  369. // ========== 第7步:更新原技师状态(服务中 → 可服务)==========
  370. TJs oldJsUpdate = new TJs();
  371. oldJsUpdate.setId(oldTechnicianId);
  372. oldJsUpdate.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
  373. if (!jsService.updateById(oldJsUpdate)) {
  374. throw new ServiceException("转单失败:更新原技师状态失败");
  375. }
  376. oldTechnicianStatusAfter = JsStatusEnum.JS_SERVICEABLE.getCode();
  377. log.info("更新技师状态完成 - 新技师:{} {}→{}, 原技师:{} {}→{}",
  378. newTechnicianName, getStatusName(newTechnicianStatusBefore), getStatusName(newTechnicianStatusAfter),
  379. oldTechnicianName, getStatusName(oldTechnicianStatusBefore), getStatusName(oldTechnicianStatusAfter));
  380. // ========== 第8步:获取操作人信息 ==========
  381. operatorId = SecurityUtils.getUserId() != null ? SecurityUtils.getUserId().toString() : "ADMIN";
  382. operatorName = SecurityUtils.getUsername() != null ? SecurityUtils.getUsername() : "系统管理员";
  383. // ========== 第9步:转单成功,设置操作结果为成功 ==========
  384. operationResult = 0; // 0-成功
  385. log.info("转单操作完成 - 订单号:{}", oldOrder.getOrderNo());
  386. return oldOrder;
  387. } catch (ServiceException e) {
  388. // 业务异常,操作失败
  389. log.error("转单操作失败 - 订单号:{}, 错误信息:{}", orderNo, e.getMessage());
  390. operationResult = 1; // 1-失败
  391. throw e;
  392. } catch (Exception e) {
  393. // 系统异常,操作失败
  394. log.error("转单操作异常 - 订单号:{}, 异常信息:{}", orderNo, e.getMessage(), e);
  395. operationResult = 1; // 1-失败
  396. throw new ServiceException("转单操作异常:" + e.getMessage());
  397. } finally {
  398. // ========== 第10步:记录转单操作日志(无论成功或失败都记录)==========
  399. try {
  400. // 只有在获取到基本信息后才记录日志
  401. if (orderId != null && orderNo != null && oldTechnicianId != null && newTechnicianId != null) {
  402. allocationLogService.recordTransferOrder(
  403. orderId, // orderId
  404. orderNo, // orderNo
  405. oldTechnicianId, // oldTechnicianId
  406. oldTechnicianName, // oldTechnicianName
  407. oldTechnicianStatusBefore, // oldTechnicianStatusBefore
  408. oldTechnicianStatusAfter, // oldTechnicianStatusAfter
  409. newTechnicianId, // newTechnicianId
  410. newTechnicianName, // newTechnicianName
  411. newTechnicianStatusBefore, // newTechnicianStatusBefore
  412. newTechnicianStatusAfter, // newTechnicianStatusAfter
  413. orderStatusBefore, // orderStatusBefore
  414. orderStatusAfter, // orderStatusAfter
  415. operatorId, // operatorId
  416. operatorName, // operatorName
  417. operationReason, // operationReason
  418. operationResult // operationResult(0-成功,1-失败)
  419. );
  420. String resultDesc = operationResult == 0 ? "成功" : "失败";
  421. log.info("转单操作记录已保存 - 订单号:{}, 操作结果:{}", orderNo, resultDesc);
  422. }
  423. } catch (Exception e) {
  424. // 记录日志失败不影响转单操作
  425. log.error("记录转单操作日志失败 - 订单号:{}, 错误信息:{}", orderNo, e.getMessage(), e);
  426. }
  427. }
  428. }
  429. /**
  430. * 获取技师状态名称
  431. *
  432. * @param status 状态码
  433. * @return String 状态名称
  434. */
  435. private String getStatusName(Integer status) {
  436. if (status == null) {
  437. return "未知";
  438. }
  439. switch (status) {
  440. case 0:
  441. return "可服务";
  442. case 1:
  443. return "服务中";
  444. case 2:
  445. return "不可服务";
  446. default:
  447. return "未知(" + status + ")";
  448. }
  449. }
  450. @Override
  451. public List<HomeBlock> getBlock(Date start, Date end, String deptId) {
  452. return orderMapper.getBlock(start, end, deptId);
  453. }
  454. @Override
  455. public OrderVerificationVo verification(TOrder order) {
  456. if (StringUtils.isBlank(order.getCouponReceiveId())) {
  457. throw new ServiceException("认领优惠券id为空");
  458. }
  459. if (StringUtils.isBlank(order.getcId())) {
  460. throw new ServiceException("订单id为空");
  461. }
  462. OrderVerificationVo orderVerificationVo = new OrderVerificationVo();
  463. TOrder tOrder = this.getById(order.getcId());
  464. orderVerificationVo.setCouponReceiveId(order.getCouponReceiveId());
  465. CouponReceive couponReceive = couponReceiveService.getById(order.getCouponReceiveId());
  466. Coupon coupon = couponService.getById(couponReceive.getCouponId());
  467. log.info("订单信息,{}", tOrder);
  468. log.info("优惠卷信息,{}", coupon);
  469. //折扣券
  470. if (coupon.getDiscountType() == 2) {
  471. //判断门槛金额
  472. if (tOrder.getTotalPrice().compareTo(coupon.getThresholdAmount()) >= 0) {
  473. //折扣值
  474. BigDecimal divide = coupon.getRebValue().divide(new BigDecimal(10));
  475. //优惠后的金额 = 订单总金额*折扣值
  476. BigDecimal bigDecimal = tOrder.getTotalPrice().multiply(divide).setScale(MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP);
  477. //优惠值
  478. orderVerificationVo.setPreferential(tOrder.getTotalPrice().subtract(bigDecimal));
  479. orderVerificationVo.setTotalPrice(bigDecimal);
  480. } else {
  481. throw new ServiceException("不满足优惠券门槛金额");
  482. }
  483. } else {
  484. if (tOrder.getTotalPrice().compareTo(coupon.getThresholdAmount()) >= 0) {
  485. //优惠值
  486. orderVerificationVo.setPreferential(coupon.getDiscountValue());
  487. orderVerificationVo.setTotalPrice(tOrder.getTotalPrice().subtract(coupon.getDiscountValue()));
  488. } else {
  489. throw new ServiceException("不满足优惠券门槛金额");
  490. }
  491. }
  492. if (orderVerificationVo.getTotalPrice().compareTo(BigDecimal.ZERO) < 0) {
  493. throw new ServiceException("当前项目不可用");
  494. }
  495. return orderVerificationVo;
  496. }
  497. private TOrder getOrder(TOrder tOrder) {
  498. if (updateById(tOrder)) {
  499. return tOrder;
  500. } else {
  501. throw new ServiceException("优惠券核销失败");
  502. }
  503. }
  504. /**
  505. * 支付订单
  506. *
  507. * @param order
  508. * @return R
  509. */
  510. @Override
  511. public R payOrder(TOrder order) throws Exception {
  512. // 根据订单ID查询订单信息
  513. TOrder orderNew = getById(order.getcId());
  514. if (!orderNew.getnStatus().equals(OrderStatusEnum.WAIT_PAY.getCode())) {
  515. throw new ServiceException("该订单已经支付或者超时被取消");
  516. }
  517. TJs js = jsService.getById(orderNew.getcJsId());
  518. if (StringUtils.isBlank(orderNew.getParentNo())) {
  519. if (null == js || js.getnStatus().equals(JsStatusEnum.JS_SERVICE.getCode())) {
  520. throw new ServiceException("该技师已在服务中请重新下单");
  521. }
  522. }
  523. orderNew.setPayType(order.getPayType());
  524. //优惠券核销
  525. if (StringUtils.isNotBlank(order.getCouponReceiveId())) {
  526. orderNew.setCouponReceiveId(order.getCouponReceiveId());
  527. orderNew.setPreferential(order.getPreferential());
  528. orderNew.setTotalPrice(order.getTotalPrice());
  529. if (!updateById(orderNew)) {
  530. throw new ServiceException("支付失败");
  531. }
  532. }
  533. //判断支付方式
  534. if (order.getPayType().equals(MassageConstants.INTEGER_ONE)) {
  535. //微信支付
  536. R resp = rechargeService.getPay(orderNew.getOrderNo(), orderNew.getTotalPrice(), orderNew.getcOpenId(), BillTypeEnum.WX_PAY.getInfo(), BillTypeEnum.WX_PAY.getCode().toString());
  537. //添加待接单消息通知(技师侧)这块的逻辑在回调接口中
  538. //orderNotificationService.sendPendingRemindNotification(orderNew);
  539. return resp;
  540. }
  541. TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId());
  542. if (null == user) {
  543. throw new ServiceException("用户不存在");
  544. }
  545. //现金支付
  546. if (order.getPayType().equals(MassageConstants.INTEGER_THREE)) {
  547. //现金支付
  548. orderPayManage(user, orderNew);
  549. //添加待接单消息通知(技师侧)
  550. orderNotificationService.sendPendingRemindNotification(orderNew);
  551. return R.ok();
  552. }
  553. if (user.getdBalance().compareTo(orderNew.getTotalPrice()) < MassageConstants.INTEGER_ZERO) {
  554. throw new ServiceException("账户金额不够请充值");
  555. } else {
  556. orderPayManage(user, orderNew);
  557. //添加待接单消息通知(技师侧)
  558. orderNotificationService.sendPendingRemindNotification(orderNew);
  559. return R.ok();
  560. }
  561. }
  562. /**
  563. * 新订单通知
  564. *
  565. * @param order
  566. */
  567. public void newOrderNotification(TOrder order) {
  568. cn.hutool.json.JSONObject param = JSONUtil.createObj();
  569. //订单号
  570. param.set("character_string9", JSONUtil.createObj().set("value", order.getOrderNo()));
  571. //电话
  572. param.set("phone_number14", JSONUtil.createObj().set("value", order.getcPhone()));
  573. param.set("thing18", JSONUtil.createObj().set("value", order.getcName()));
  574. param.set("time6", JSONUtil.createObj().set("value", DateTimeUtils.formatDate(new Date(), DateTimeUtils.DATE_NUMBER_YEAR_MONTH_FORMAT)));
  575. param.set("thing27", JSONUtil.createObj().set("value", order.getName()));
  576. TJs js = jsService.getById(order.getcJsId());
  577. weChatUtil.notification(js.getcOpenId(), wxPayProperties.getTemplateId1(), param);
  578. }
  579. /**
  580. * 订单支付管理
  581. *
  582. * @param user
  583. * @param orderNew
  584. */
  585. @Transactional(rollbackFor = Exception.class)
  586. public void orderPayManage(TWxUser user, TOrder orderNew) {
  587. //更新优惠卷状态
  588. if (StringUtils.isNotBlank(orderNew.getCouponReceiveId())) {
  589. CouponReceive couponReceive = new CouponReceive();
  590. couponReceive.setId(orderNew.getCouponReceiveId());
  591. couponReceive.setUseState(MassageConstants.INTEGER_TWO);
  592. if (!couponReceiveService.updateById(couponReceive)) {
  593. log.error("优惠券状态更新失败id:,{}", orderNew.getCouponReceiveId());
  594. }
  595. }
  596. // 更新用户金额 及下单此时
  597. TWxUser paramUser = new TWxUser();
  598. paramUser.setcOpenid(user.getcOpenid());
  599. // 余额支付
  600. if (orderNew.getPayType().equals(MassageConstants.INTEGER_TWO)) {
  601. paramUser.setdBalance(user.getdBalance().subtract(orderNew.getTotalPrice()));
  602. }
  603. paramUser.setdMoney(user.getdMoney().add(orderNew.getTotalPrice()));
  604. paramUser.setnNum(user.getnNum() + MassageConstants.INTEGER_ONE);
  605. paramUser.setId(user.getId());
  606. wxUserService.updateById(paramUser);
  607. //增加消费记录
  608. TConsumptionLog tConsumptionLog = new TConsumptionLog();
  609. tConsumptionLog.setAmount(orderNew.getTotalPrice().negate());
  610. tConsumptionLog.setBillNo(orderNew.getOrderNo());
  611. tConsumptionLog.setOpenId(orderNew.getcOpenId());
  612. if (orderNew.getPayType().equals(MassageConstants.INTEGER_TWO)) {
  613. tConsumptionLog.setBillType(BillTypeEnum.BALANCE_PAYMENT.getCode());
  614. tConsumptionLog.setNote("余额支付");
  615. } else if(orderNew.getPayType().equals(MassageConstants.INTEGER_ONE)){
  616. tConsumptionLog.setBillType(BillTypeEnum.WX_PAY.getCode());
  617. tConsumptionLog.setNote("微信支付");
  618. } else {
  619. tConsumptionLog.setBillType(BillTypeEnum.CASH_PAYMENT.getCode());
  620. tConsumptionLog.setNote("现金支付");
  621. }
  622. consumptionLogService.save(tConsumptionLog);
  623. // 更新项目数据
  624. JSONArray objects = orderNew.getcGoods();
  625. objects.forEach(item -> {
  626. UpdateWrapper<TXiangmu> wrapper = new UpdateWrapper<>();
  627. // 获取参数
  628. wrapper.lambda().eq(TXiangmu::getcId, ((JSONObject) item).getString("cId"));
  629. // 设置数量
  630. wrapper.setSql(" n_sale_number = n_sale_number + " + ((JSONObject) item).getInteger("number"));
  631. xiangmuService.update(wrapper);
  632. });
  633. TOrder orderParam = new TOrder();
  634. orderParam.setPayType(orderNew.getPayType());
  635. orderParam.setcId(orderNew.getcId());
  636. orderParam.setnStatus(OrderStatusEnum.WAIT_JD.getCode());
  637. orderParam.setPayTime(new Date());
  638. //加钟的订单支付完直接服务中
  639. if (StringUtils.isNotBlank(orderNew.getParentNo())) {
  640. orderParam.setnStatus(OrderStatusEnum.SERVICE.getCode());
  641. }
  642. // orderParam.setnStatus(OrderStatusEnum.SERVICE.getCode());
  643. //更新及技师状态
  644. updateJs(orderNew);
  645. updateById(orderParam);
  646. //
  647. //this.newOrderNotification(orderNew);
  648. //添加待接单消息通知(技师侧)
  649. orderNotificationService.sendPendingRemindNotification(orderNew);
  650. //电话通知
  651. TJs js = jsService.getById(orderNew.getcJsId());
  652. Sendvoice.sendPhone(js.getcPhone());
  653. }
  654. /**
  655. * 拒绝订单
  656. *
  657. * @param order
  658. */
  659. @Override
  660. public Boolean jujue(TOrder order) {
  661. TOrder orderNew = getById(order.getcId());
  662. TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId());
  663. // 更新用户金额 及下单此时
  664. TWxUser paramUser = new TWxUser();
  665. paramUser.setcOpenid(user.getcOpenid());
  666. paramUser.setId(user.getId());
  667. // 余额记录
  668. TConsumptionLog tConsumptionLog = new TConsumptionLog();
  669. tConsumptionLog.setAmount(orderNew.getTotalPrice());
  670. tConsumptionLog.setBillNo(orderNew.getOrderNo());
  671. tConsumptionLog.setOpenId(orderNew.getcOpenId());
  672. if (orderNew.getPayType() == 2) {
  673. // 金额归还对应账户
  674. paramUser.setdBalance(user.getdBalance().add(orderNew.getTotalPrice()));
  675. tConsumptionLog.setBillType(BillTypeEnum.REFUSE_ACCEPT_REFUND.getCode());
  676. tConsumptionLog.setNote("拒绝接单退款到余额");
  677. } else {
  678. // 微信支付
  679. // 生成退款单退款
  680. RefundVoucher refundVoucher = new RefundVoucher();
  681. refundVoucher.setRefundNo(generator.generateNextOrderNumber(OrderNumberGenerator.KEY_PREFIX_REFUND));
  682. refundVoucher.setOrderNo(orderNew.getOrderNo());
  683. refundVoucher.setMoney(orderNew.getTotalPrice());
  684. refundVoucher.setOpenId(orderNew.getcOpenId());
  685. refundVoucher.setReStatus(MassageConstants.INTEGER_ZERO);
  686. refundVoucher.setReason("技师拒绝接单");
  687. refundVoucherService.save(refundVoucher);
  688. tConsumptionLog.setBillType(BillTypeEnum.REFUSE_ACCEPT_REFUND.getCode());
  689. tConsumptionLog.setNote("拒绝接单退款到余额");
  690. // 微信退款原路返回
  691. rechargeService.refund(refundVoucher.getRefundNo(), null, orderNew.getOrderNo(), orderNew.getTotalPrice());
  692. }
  693. consumptionLogService.save(tConsumptionLog);
  694. //退优惠卷
  695. if (StringUtils.isNotBlank(orderNew.getCouponReceiveId())) {
  696. CouponReceive couponReceive = couponReceiveService.getById(orderNew.getCouponReceiveId());
  697. couponReceive.setUseState(MassageConstants.INTEGER_ZERO);
  698. couponReceiveService.updateById(couponReceive);
  699. }
  700. log.info("余额支付退款user:{}", user);
  701. // 消费金额对应减少
  702. paramUser.setdMoney(user.getdMoney().subtract(orderNew.getTotalPrice()));
  703. // 下单次数减一
  704. paramUser.setnNum(user.getnNum() - MassageConstants.INTEGER_ONE);
  705. wxUserService.updateById(paramUser);
  706. // 更新项目数据
  707. JSONArray objects = orderNew.getcGoods();
  708. objects.forEach(item -> {
  709. UpdateWrapper<TXiangmu> wrapper = new UpdateWrapper<>();
  710. // 获取参数
  711. wrapper.lambda().eq(TXiangmu::getcId, ((JSONObject) item).getString("cId"));
  712. // 设置数量
  713. wrapper.setSql(" n_sale_number = n_sale_number - " + ((JSONObject) item).getInteger("number"));
  714. xiangmuService.update(wrapper);
  715. });
  716. TOrder orderParam = new TOrder();
  717. orderParam.setcId(orderNew.getcId());
  718. orderParam.setnStatus(OrderStatusEnum.REFUSE.getCode());
  719. orderParam.setReasonRefusal(order.getReasonRefusal());
  720. // 订单结算状态:未结算 2026-03-27 lixl
  721. orderParam.setwStatus(OrderWStateEnum.NO_ACCOUNT.getCode());
  722. updateJs(orderNew);
  723. return updateById(orderParam);
  724. }
  725. /**
  726. * 确认服务完成
  727. *
  728. * @param order
  729. * @return Boolean
  730. */
  731. @Override
  732. @Transactional(rollbackFor = Exception.class)
  733. public Boolean confirm(TOrder order) {
  734. // 获取订单信息
  735. TOrder orderNew = getById(order.getcId());
  736. if (!orderNew.getnStatus().equals(OrderStatusEnum.SERVICE.getCode())) {
  737. throw new ServiceException("订单状态不是服务中");
  738. }
  739. // 更新技师信息
  740. TJs jsParam = new TJs();
  741. jsParam.setId(orderNew.getcJsId());
  742. jsParam.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
  743. //判断热度标识
  744. List<TOrder> list = list(new LambdaQueryWrapper<TOrder>().eq(TOrder::getcJsId, orderNew.getcJsId())
  745. .ge(TOrder::getDtCreateTime, DateTimeUtils.addDays(new Date(), -3))
  746. .ge(TOrder::getnStatus, OrderStatusEnum.WAIT_EVALUATE.getCode()));
  747. if (list.size() >= 2) {
  748. // 设置热度标识:1
  749. jsParam.setnB3(MassageConstants.INTEGER_ONE);
  750. }
  751. // 更新技师状态
  752. jsService.updateById(jsParam);
  753. // 更新技师钱包金额
  754. TJs jsById = jsService.getById(orderNew.getcJsId());
  755. // 获取技师抽成
  756. BigDecimal multiply = orderNew.getTotalPrice().multiply(new BigDecimal(jsById.getnBili()));
  757. multiply = multiply.divide(new BigDecimal(100), MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP);
  758. // 获取技师所对应的用户
  759. TWxUser jsUser = wxUserService.getByOpenId(jsById.getcOpenId());
  760. // 更新余额
  761. jsUser.setdBalance(jsUser.getdBalance().add(multiply));
  762. // 更新总钱数
  763. jsUser.setdAllMoney(jsUser.getdAllMoney().add(multiply));
  764. wxUserService.updateById(jsUser);
  765. //增加消费记录
  766. TConsumptionLog tConsumptionLog = new TConsumptionLog();
  767. tConsumptionLog.setAmount(multiply);
  768. tConsumptionLog.setBillNo(orderNew.getOrderNo());
  769. tConsumptionLog.setOpenId(jsUser.getcOpenid());
  770. tConsumptionLog.setBillType(BillTypeEnum.INCOME.getCode());
  771. tConsumptionLog.setNote("技师收益");
  772. consumptionLogService.save(tConsumptionLog);
  773. // 如果该技师有推荐人员 一级
  774. if (StringUtils.isNotBlank(jsUser.getcUpUser())) {
  775. // 获取技师上级对应的用户
  776. TWxUser jsUp = wxUserService.getByOpenId(jsUser.getcUpUser());
  777. extracted(orderNew, jsUp);
  778. //二级
  779. if (StringUtils.isNotBlank(jsUp.getcUpUser())) {
  780. TWxUser jsUpTwo = wxUserService.getByOpenId(jsUp.getcUpUser());
  781. extracted(orderNew, jsUpTwo);
  782. //三级
  783. if (StringUtils.isNotBlank(jsUpTwo.getcUpUser())) {
  784. TWxUser jsUpThree = wxUserService.getByOpenId(jsUpTwo.getcUpUser());
  785. extracted(orderNew, jsUpThree);
  786. }
  787. }
  788. }
  789. // 更新订单
  790. // 订单状态:待评价
  791. orderNew.setnStatus(OrderStatusEnum.WAIT_EVALUATE.getCode());
  792. orderNew.setEndTime(LocalDateTime.now());
  793. // 订单结算状态:待结算 2026-03-27 lixl
  794. orderNew.setwStatus(OrderWStateEnum.WAIT_ACCOUNT.getCode());
  795. updateById(orderNew);
  796. // 添加订单完成消息通知(用户侧)
  797. orderNotificationService.sendCompletedNotification(orderNew);
  798. return true;
  799. }
  800. private void extracted(TOrder orderNew, TWxUser jsUp) {
  801. log.info("TOrderServiceImpl->extracted->jsUp,{}", JSONUtil.toJsonStr(jsUp));
  802. log.info("TOrderServiceImpl->extracted->orderNew,{}",JSONUtil.toJsonStr(orderNew));
  803. BigDecimal up = orderNew.getdTotalMoney().multiply(new BigDecimal("10"));
  804. up = up.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
  805. // 更新余额
  806. jsUp.setdBalance(jsUp.getdBalance().add(up));
  807. // 更新总钱数
  808. jsUp.setdAllMoney(jsUp.getdAllMoney().add(up));
  809. jsUp.setDistributionAmount(up);
  810. wxUserService.updateById(jsUp);
  811. //记录分销收益
  812. TConsumptionLog tConsumptionLog = new TConsumptionLog();
  813. tConsumptionLog.setAmount(up);
  814. tConsumptionLog.setBillNo(orderNew.getOrderNo());
  815. tConsumptionLog.setOpenId(jsUp.getcOpenid());
  816. tConsumptionLog.setBillType(BillTypeEnum.DISTRIBUTION.getCode());
  817. tConsumptionLog.setNote("分销收益");
  818. consumptionLogService.save(tConsumptionLog);
  819. }
  820. /**
  821. * 获取技师当天可预约时间
  822. *
  823. * @param technicianId 技师ID
  824. * @param dateStr 查询日期(格式:yyyy-MM-dd),为null则查询当天
  825. * @return TechnicianAvailabilityVo 技师当天可预约时间VO
  826. */
  827. @Override
  828. public TechnicianAvailabilityVo getTechnicianAvailability(String technicianId, String dateStr) {
  829. // 1. 参数校验
  830. if (StringUtils.isBlank(technicianId)) {
  831. throw new ServiceException("技师ID不能为空");
  832. }
  833. // 2. 查询技师信息
  834. TJs js = jsService.getById(technicianId);
  835. if (js == null) {
  836. throw new ServiceException("技师不存在");
  837. }
  838. // 3. 解析日期,默认为当天
  839. LocalDate queryDate;
  840. if (StringUtils.isBlank(dateStr)) {
  841. queryDate = LocalDate.now();
  842. } else {
  843. try {
  844. queryDate = LocalDate.parse(dateStr);
  845. } catch (Exception e) {
  846. throw new ServiceException("日期格式错误,请使用 yyyy-MM-dd 格式");
  847. }
  848. }
  849. // 4. 定义当天的所有时间段(以30分钟为间隔,从00:00到23:30)
  850. List<TimeSlotVo> timeSlots = new ArrayList<>();
  851. for (int hour = 0; hour < 24; hour++) {
  852. // 每小时生成两个时间段:xx:00 和 xx:30
  853. timeSlots.add(TimeSlotVo.builder()
  854. .time(String.format("%02d:00", hour))
  855. .available(true)
  856. .build());
  857. timeSlots.add(TimeSlotVo.builder()
  858. .time(String.format("%02d:30", hour))
  859. .available(true)
  860. .build());
  861. }
  862. // 5. 查询技师当天所有进行中的订单
  863. // 开始时间
  864. LocalDateTime startOfDay = queryDate.atStartOfDay();
  865. // 结束时间
  866. LocalDateTime endOfDay = queryDate.plusDays(1).atStartOfDay();
  867. log.info("开始时间:{},结束时间:{}", startOfDay, endOfDay);
  868. LambdaQueryWrapper<TOrder> queryWrapper = new LambdaQueryWrapper<>();
  869. queryWrapper.eq(TOrder::getcJsId, technicianId)
  870. .in(TOrder::getnStatus, OrderStatusEnum.WAIT_JD.getCode(),
  871. OrderStatusEnum.RECEIVED_ORDER.getCode(),
  872. OrderStatusEnum.DEPART.getCode(),
  873. OrderStatusEnum.ARRIVED.getCode(),
  874. OrderStatusEnum.SERVICE.getCode())
  875. .ge(TOrder::getDtCreateTime, startOfDay)
  876. .lt(TOrder::getDtCreateTime, endOfDay)
  877. .eq(TOrder::getIsDelete, 0);
  878. List<TOrder> orders = this.list(queryWrapper);
  879. log.info("技师{},在{}天共有 {} 个进行中的订单", technicianId, queryDate, orders.size());
  880. // 6. 标记不可预约的时间段
  881. LocalDateTime now = LocalDateTime.now();
  882. for (TOrder order : orders) {
  883. // 6.1 计算订单的开始时间和结束时间
  884. LocalDateTime orderStart = OrderTimeRangeUtils.estimateStartTime(order);
  885. LocalDateTime orderEnd = OrderTimeRangeUtils.estimateEndTime(order);
  886. if (orderStart == null || orderEnd == null) {
  887. log.warn("订单 {} 的时间信息不完整,跳过", order.getOrderNo());
  888. continue;
  889. }
  890. // 6.2 限制在查询日期范围内
  891. LocalDateTime effectiveStart = orderStart.isBefore(startOfDay) ? startOfDay : orderStart;
  892. LocalDateTime effectiveEnd = orderEnd.isAfter(endOfDay) ? endOfDay : orderEnd;
  893. // 6.3 标记不可预约的时间段
  894. markTimeSlotsUnavailable(timeSlots, effectiveStart, effectiveEnd, order.getOrderNo());
  895. }
  896. // 7. 根据查询日期判断是否可预约
  897. LocalDate today = LocalDate.now();
  898. if (queryDate.isBefore(today)) {
  899. // 查询日期是过去的日期,所有时间段都不可预约
  900. markAllTimeSlotsUnavailable(timeSlots, "日期已过期");
  901. } else if (queryDate.equals(today)) {
  902. // 查询日期是今天,标记过去的时间为不可预约
  903. markPastTimeSlotsUnavailable(timeSlots, now);
  904. }
  905. // 查询日期是未来的日期,所有时间段默认可预约,无需处理
  906. // 8. 构建返回结果
  907. return TechnicianAvailabilityVo.builder()
  908. .date(queryDate.toString())
  909. .technicianId(technicianId)
  910. .technicianName(js.getcName())
  911. .timeSlots(timeSlots)
  912. .build();
  913. }
  914. /**
  915. * 标记指定时间范围内的时间段为不可预约
  916. *
  917. * @param timeSlots 时间段列表
  918. * @param start 开始时间
  919. * @param end 结束时间
  920. * @param orderNo 订单号
  921. */
  922. private void markTimeSlotsUnavailable(List<TimeSlotVo> timeSlots, LocalDateTime start, LocalDateTime end, String orderNo) {
  923. LocalTime startTime = start.toLocalTime();
  924. LocalTime endTime = end.toLocalTime();
  925. for (TimeSlotVo slot : timeSlots) {
  926. LocalTime slotTime = LocalTime.parse(slot.getTime());
  927. // 判断时间段是否在订单时间范围内
  928. boolean isInRange = !slotTime.isBefore(startTime) && slotTime.isBefore(endTime);
  929. if (isInRange) {
  930. slot.setAvailable(false);
  931. slot.setReason("已有订单");
  932. slot.setOrderNo(orderNo);
  933. }
  934. }
  935. }
  936. /**
  937. * 标记所有时间段为不可预约
  938. *
  939. * @param timeSlots 时间段列表
  940. * @param reason 不可预约原因
  941. */
  942. private void markAllTimeSlotsUnavailable(List<TimeSlotVo> timeSlots, String reason) {
  943. for (TimeSlotVo slot : timeSlots) {
  944. slot.setAvailable(false);
  945. slot.setReason(reason);
  946. slot.setOrderNo(null);
  947. }
  948. }
  949. /**
  950. * 标记过去的时间段为不可预约
  951. *
  952. * @param timeSlots 时间段列表
  953. * @param now 当前时间
  954. */
  955. private void markPastTimeSlotsUnavailable(List<TimeSlotVo> timeSlots, LocalDateTime now) {
  956. LocalTime currentTime = now.toLocalTime();
  957. for (TimeSlotVo slot : timeSlots) {
  958. LocalTime slotTime = LocalTime.parse(slot.getTime());
  959. // 如果当前时间已经过了这个时间段,标记为不可预约
  960. if (slotTime.isBefore(currentTime)) {
  961. slot.setAvailable(false);
  962. slot.setReason("已过期");
  963. slot.setOrderNo(null);
  964. }
  965. }
  966. }
  967. /**
  968. * 取消订单
  969. *
  970. * @param order
  971. * @return Boolean
  972. */
  973. @Override
  974. @Transactional(rollbackFor = Exception.class)
  975. public Boolean cancle(TOrder order) {
  976. // 获取订单信息
  977. // 根据orderid查询订单信息
  978. TOrder orderNew = getById(order.getcId());
  979. //待接单
  980. if (Objects.equals(orderNew.getnStatus(), OrderStatusEnum.WAIT_JD.getCode())) {
  981. TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId());
  982. // 更新用户金额 及下单此时
  983. TWxUser paramUser = new TWxUser();
  984. paramUser.setId(user.getId());
  985. paramUser.setcOpenid(user.getcOpenid());
  986. TConsumptionLog tConsumptionLog = new TConsumptionLog();
  987. tConsumptionLog.setAmount(orderNew.getTotalPrice());
  988. tConsumptionLog.setBillNo(orderNew.getOrderNo());
  989. tConsumptionLog.setOpenId(orderNew.getcOpenId());
  990. // 余额支付
  991. if (orderNew.getPayType() == 2) {
  992. // 金额归还对应账户
  993. paramUser.setdBalance(user.getdBalance().add(orderNew.getTotalPrice()));
  994. // 余额记录
  995. tConsumptionLog.setBillType(BillTypeEnum.CANCEL_ACCEPT_REFUND.getCode());
  996. tConsumptionLog.setNote("取消订单退款到余额");
  997. //自己取消的不退优惠卷
  998. } else {
  999. // 微信支付
  1000. // 生成退款单退款
  1001. RefundVoucher refundVoucher = new RefundVoucher();
  1002. refundVoucher.setRefundNo(generator.generateNextOrderNumber(OrderNumberGenerator.KEY_PREFIX_REFUND));
  1003. refundVoucher.setOrderNo(orderNew.getOrderNo());
  1004. refundVoucher.setMoney(orderNew.getTotalPrice());
  1005. refundVoucher.setOpenId(orderNew.getcOpenId());
  1006. refundVoucher.setReStatus(MassageConstants.INTEGER_ZERO);
  1007. refundVoucher.setReason("技师拒绝接单");
  1008. refundVoucherService.save(refundVoucher);
  1009. tConsumptionLog.setBillType(BillTypeEnum.CANCEL_WX_REFUND.getCode());
  1010. tConsumptionLog.setNote("取消订单退款到微信");
  1011. // 微信退款原路返回
  1012. rechargeService.refund(refundVoucher.getRefundNo(), null, orderNew.getOrderNo(), orderNew.getTotalPrice());
  1013. }
  1014. consumptionLogService.save(tConsumptionLog);
  1015. // 消费金额对应减少
  1016. paramUser.setdMoney(user.getdMoney().subtract(orderNew.getTotalPrice()));
  1017. // 下单次数减一
  1018. paramUser.setnNum(user.getnNum() - MassageConstants.INTEGER_ONE);
  1019. wxUserService.updateById(paramUser);
  1020. // 更新项目数据
  1021. JSONArray objects = orderNew.getcGoods();
  1022. objects.forEach(item -> {
  1023. UpdateWrapper<TXiangmu> wrapper = new UpdateWrapper<>();
  1024. // 获取参数
  1025. wrapper.lambda().eq(TXiangmu::getcId, ((JSONObject) item).getString("cId"));
  1026. // 设置数量
  1027. wrapper.setSql(" n_sale_number = n_sale_number - " + ((JSONObject) item).getInteger("number"));
  1028. xiangmuService.update(wrapper);
  1029. });
  1030. TOrder orderParam = new TOrder();
  1031. orderParam.setcId(orderNew.getcId());
  1032. orderParam.setnStatus(OrderStatusEnum.CANCEL.getCode());
  1033. // 订单结算状态:未结算 2026-03-27 lixl
  1034. orderParam.setwStatus(OrderWStateEnum.NO_ACCOUNT.getCode());
  1035. //更新技师状态
  1036. TJs tJs = new TJs();
  1037. tJs.setId(orderNew.getcJsId());
  1038. tJs.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
  1039. jsService.updateById(tJs);
  1040. updateById(orderParam);
  1041. // 添加取消订单通知(用户侧)
  1042. orderNotificationService.sendCancelledNotification(orderNew);
  1043. // 添加取消订单通知(技师侧)
  1044. orderNotificationService.sendTechnicianCancelledNotification(orderNew);
  1045. return true;
  1046. } else if (Objects.equals(orderNew.getnStatus(), OrderStatusEnum.WAIT_PAY.getCode())) {//待付款
  1047. TOrder orderParam = new TOrder();
  1048. orderParam.setcId(orderNew.getcId());
  1049. orderParam.setnStatus(OrderStatusEnum.CANCEL.getCode());
  1050. // 订单结算状态:未结算 2026-03-27 lixl
  1051. orderParam.setwStatus(OrderWStateEnum.NO_ACCOUNT.getCode());
  1052. updateById(orderParam);
  1053. // 添加取消订单通知
  1054. orderNotificationService.sendCancelledNotification(orderNew);
  1055. // 添加取消订单通知(技师侧)
  1056. orderNotificationService.sendTechnicianCancelledNotification(orderNew);
  1057. return true;
  1058. } else {
  1059. return false;
  1060. }
  1061. }
  1062. @Override
  1063. public TOrder getByNo(String orderNo) {
  1064. LambdaQueryWrapper<TOrder> objectLambdaQueryWrapper = new LambdaQueryWrapper<>();
  1065. return this.getOne(objectLambdaQueryWrapper.eq(TOrder::getOrderNo, orderNo));
  1066. }
  1067. // private TOrder gettOrder(TOrder order) {
  1068. // LambdaUpdateWrapper<TOrder> objectLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
  1069. // objectLambdaUpdateWrapper.eq(TOrder::getOrderNo, order.getOrderNo());
  1070. // return this.getOne(objectLambdaUpdateWrapper);
  1071. // }
  1072. @Override
  1073. public Page<TOrder> getAll(Page<TOrder> page, TOrder order) {
  1074. Page<TOrder> orderPage = orderMapper.getAll(page, order);
  1075. if (orderPage != null && CollectionUtil.isNotEmpty(orderPage.getRecords())) {
  1076. ArrayList<TOrder> ordersList = Lists.newArrayList();
  1077. orderPage.getRecords().forEach(orders -> {
  1078. orders.setStatusName(OrderStatusEnum.getDescByCode(orders.getnStatus()));
  1079. orders.setJsPhone(orders.getJs().getcPhone());
  1080. orders.setJsName(orders.getJs().getcName());
  1081. if (StringUtils.isEmpty(orders.getcTime())) {
  1082. orders.setRemainingTime(0L);
  1083. }
  1084. if (StringUtils.isNotBlank(orders.getcTime()) && DateTimeUtils.dateStringToStamp(orders.getcTime()) > DateTimeUtils.dateToStamp(new Date())) {
  1085. orders.setRemainingTime((DateTimeUtils.dateStringToStamp(orders.getcTime()) - DateTimeUtils.dateToStamp(new Date())) / 1000);
  1086. }
  1087. if (StringUtils.isNotBlank(orders.getcTime()) && DateTimeUtils.dateStringToStamp(orders.getcTime()) < DateTimeUtils.dateToStamp(new Date())) {
  1088. orders.setRemainingTime(0L);
  1089. }
  1090. if (StringUtils.isNotBlank(orders.getOldJsId())) {
  1091. orders.setOldJs(jsService.getById(orders.getOldJsId()));
  1092. }
  1093. // 计算已服务时长
  1094. Long serviceDuration = calculateServiceDuration(orders);
  1095. orders.setServiceDuration(String.valueOf(serviceDuration));
  1096. ordersList.add(orders);
  1097. });
  1098. orderPage.setRecords(ordersList);
  1099. }
  1100. return orderPage;
  1101. }
  1102. /**
  1103. * 计算已服务时长(分钟)
  1104. *
  1105. * @param order 订单对象
  1106. * @return Long 已服务时长(分钟)
  1107. */
  1108. private Long calculateServiceDuration(TOrder order) {
  1109. try {
  1110. LocalDateTime startTime = order.getStartTime();
  1111. LocalDateTime endTime = order.getEndTime();
  1112. if (startTime != null && endTime != null) {
  1113. long minutes = Duration.between(startTime, endTime).toMinutes();
  1114. if (minutes > 0) {
  1115. return minutes;
  1116. }
  1117. } else if (startTime != null) {
  1118. // 如果只有开始时间,计算到现在的时长
  1119. long minutes = Duration.between(startTime, LocalDateTime.now()).toMinutes();
  1120. if (minutes > 0) {
  1121. return minutes;
  1122. }
  1123. }
  1124. } catch (Exception e) {
  1125. log.warn("计算服务时长失败,订单ID:{}", order.getcId(), e);
  1126. }
  1127. return 0L;
  1128. }
  1129. @Override
  1130. @Transactional(rollbackFor = Exception.class)
  1131. public void takingOrders(TOrder order) {
  1132. String orderId = order.getcId();
  1133. if (orderId == null || StringUtils.isBlank(orderId)) {
  1134. throw new IllegalArgumentException("订单ID不能为空");
  1135. }
  1136. TOrder orderNew = this.getById(orderId);
  1137. // 【新增】订单状态锁校验 - 检查技师是否可以接单
  1138. log.info("开始校验技师 {} 是否可以接单,订单号:{}", orderNew.getcJsId(), orderNew.getOrderNo());
  1139. orderValidationService.canAcceptOrder(orderNew.getcJsId(), orderNew);
  1140. log.info("技师 {} 接单校验通过,继续接单流程", orderNew.getcJsId());
  1141. // 检查订单对应的技师是否存在
  1142. // updateJs (orderNew);
  1143. TOrder orderParam = new TOrder();
  1144. orderParam.setcId(orderId);
  1145. //设置订单状态:已接单
  1146. orderParam.setnStatus(OrderStatusEnum.RECEIVED_ORDER.getCode());
  1147. orderParam.setAcceptanceTime(LocalDateTime.now());
  1148. this.updateById(orderParam);
  1149. // 已接单消息通知(用户侧)
  1150. orderNotificationService.sendReceivedNotification(orderNew);
  1151. // 已接单消息通知(技师侧)
  1152. orderNotificationService.sendTechnicianReceivedNotification(orderNew);
  1153. }
  1154. private void updateJs(TOrder orderNew) {
  1155. TJs js = jsService.getById(orderNew.getcJsId());
  1156. if (js == null) {
  1157. throw new IllegalStateException("无法找到对应的技师");
  1158. }
  1159. if (Objects.equals(js.getnStatus(), JsStatusEnum.JS_SERVICEABLE.getCode())) {
  1160. // 更新技师状态
  1161. js.setnStatus(JsStatusEnum.JS_SERVICE.getCode());
  1162. // 确保js.getnNum()不为null,避免 NullPointerException
  1163. int num = js.getnNum() == null ? 0 : js.getnNum();
  1164. js.setnNum(num + MassageConstants.INTEGER_ONE);
  1165. } else {
  1166. // 更新技师状态
  1167. js.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
  1168. // 确保js.getnNum()不为null,避免 NullPointerException
  1169. int num = js.getnNum() == null ? 0 : js.getnNum();
  1170. js.setnNum(num - MassageConstants.INTEGER_ONE);
  1171. }
  1172. jsService.updateById(js);
  1173. }
  1174. /**
  1175. * 申请取消订单(退单申请)
  1176. * 业务流程:
  1177. * 1. 校验订单状态(仅进行中的订单可申请退单)
  1178. * 2. 创建退单申请记录
  1179. * 3. 更新订单状态为"退单待审核"
  1180. *
  1181. * @param cId 订单对象
  1182. * @param cancelReason 订单对象
  1183. * @return Boolean 申请结果
  1184. */
  1185. @Override
  1186. @Transactional(rollbackFor = Exception.class)
  1187. public Boolean applyCancle(String cId, String cancelReason) {
  1188. log.info("开始处理退单申请,订单ID:{}", cId);
  1189. // 1. 参数校验
  1190. if (StringUtils.isBlank(cId)) {
  1191. throw new ServiceException("订单ID不能为空");
  1192. }
  1193. if (StringUtils.isBlank(cancelReason)) {
  1194. throw new ServiceException("退单原因不能为空");
  1195. }
  1196. // 2. 根据订单ID查询订单信息
  1197. TOrder existingOrder = this.getById(cId);
  1198. if (existingOrder == null) {
  1199. throw new ServiceException("订单不存在");
  1200. }
  1201. // 3. 创建退单申请记录(内部会校验订单状态和其他业务规则)
  1202. String applicationId;
  1203. try {
  1204. applicationId = cancelOrderApplicationService.createApplication(existingOrder.getcId(), cancelReason);
  1205. log.info("退单申请记录创建成功,申请ID:{}", applicationId);
  1206. } catch (ServiceException e) {
  1207. log.error("创建退单申请失败:{}", e.getMessage());
  1208. throw e;
  1209. }
  1210. // 4. 更新订单状态为"退单待审核"
  1211. existingOrder.setnStatus(OrderStatusEnum.CANCEL_APPLICATION_PENDING.getCode());
  1212. this.updateById(existingOrder);
  1213. log.info("退单申请处理完成,订单ID:{},申请ID:{}", existingOrder.getcId(), applicationId);
  1214. return Boolean.TRUE;
  1215. }
  1216. /**
  1217. * 取消退单申请
  1218. * 用户主动取消退单申请,恢复订单状态
  1219. *
  1220. * 业务流程:
  1221. * 1. 参数校验(订单ID不能为空)
  1222. * 2. 查询订单和退单申请记录
  1223. * 3. 校验订单状态必须为"退单待审核"(6)
  1224. * 4. 校验退单审核状态必须为"待审核"(0)
  1225. * 5. 调用退单申请服务取消申请
  1226. * 6. 恢复订单状态到申请前的原始状态
  1227. *
  1228. * @param order 订单对象,需要包含cId(订单ID)
  1229. * @return Boolean 操作结果
  1230. */
  1231. @Override
  1232. @Transactional(rollbackFor = Exception.class)
  1233. public Boolean cancelApplyCancle(TOrder order) {
  1234. log.info("开始取消退单申请,订单ID:{}", order.getcId());
  1235. // 1. 参数校验
  1236. if (StringUtils.isBlank(order.getcId())) {
  1237. throw new ServiceException("订单ID不能为空");
  1238. }
  1239. // 2. 查询订单信息
  1240. TOrder existingOrder = this.getById(order.getcId());
  1241. if (existingOrder == null) {
  1242. throw new ServiceException("订单不存在");
  1243. }
  1244. // 3. 校验订单状态 - 只有"退单待审核"状态的订单才能取消申请
  1245. Integer currentStatus = existingOrder.getnStatus();
  1246. if (!OrderStatusEnum.CANCEL_APPLICATION_PENDING.getCode().equals(currentStatus)) {
  1247. throw new ServiceException("当前订单状态不允许取消退单申请");
  1248. }
  1249. // 4. 查询退单申请记录
  1250. com.ylx.massage.domain.CancelOrderApplication application = cancelOrderApplicationService.getInfoByOrderId(order.getcId());
  1251. if (application == null) {
  1252. throw new ServiceException("退单申请记录不存在");
  1253. }
  1254. // 5. 校验退单申请状态 - 只有"待审核"的申请才能取消
  1255. if (application.getAuditStatus() != 0) {
  1256. throw new ServiceException("当前退单申请状态不允许取消");
  1257. }
  1258. // 6. 获取订单原始状态(在申请退单时保存的状态)
  1259. Integer originalStatus = application.getOrderStatus();
  1260. if (originalStatus == null) {
  1261. throw new ServiceException("无法获取订单原始状态,取消申请失败");
  1262. }
  1263. // 7. 调用退单申请服务取消申请
  1264. try {
  1265. cancelOrderApplicationService.cancelApplication(order.getcId());
  1266. log.info("退单申请记录取消成功,订单ID:{}", order.getcId());
  1267. } catch (ServiceException e) {
  1268. log.error("取消退单申请失败:{}", e.getMessage());
  1269. throw e;
  1270. }
  1271. // 8. 恢复订单状态到原始状态
  1272. existingOrder.setnStatus(originalStatus);
  1273. boolean updated = this.updateById(existingOrder);
  1274. if (!updated) {
  1275. throw new ServiceException("恢复订单状态失败");
  1276. }
  1277. log.info("取消退单申请处理完成,订单ID:{},恢复到状态:{}", order.getcId(), originalStatus);
  1278. return Boolean.TRUE;
  1279. }
  1280. @Override
  1281. public Page<TOrder> getPcOrderIncome(Page<TOrder> page, TOrder o) {
  1282. Page<TOrder> orderPage = orderMapper.getAll(page, o);
  1283. if (orderPage != null && CollectionUtil.isNotEmpty(orderPage.getRecords())) {
  1284. ArrayList<TOrder> ordersList = Lists.newArrayList();
  1285. orderPage.getRecords().forEach(order -> {
  1286. order.setStatusName(OrderStatusEnum.getDescByCode(order.getnStatus()));
  1287. order.setJsPhone(order.getJs().getcPhone());
  1288. order.setJsName(order.getJs().getcName());
  1289. //项目名称
  1290. order.setProjectNames(getProjectNames(order));
  1291. //订单金额
  1292. order.setOrderPrice(String.valueOf(order.getTotalPrice()));
  1293. //优惠券补贴
  1294. order.setCouponPrice(String.valueOf(order.getPreferential()));
  1295. //实付金额
  1296. order.setRealPayPrice(String.valueOf(order.getTotalPrice()));
  1297. //平台佣金比例
  1298. order.setPercent(percent.substring(percent.length() - 1)+"%");
  1299. //商户应收金额 不进行四舍五入
  1300. BigDecimal rp = order.getTotalPrice().multiply(new BigDecimal(hPercent)).
  1301. setScale(2, RoundingMode.DOWN);
  1302. order.setrPrice(String.valueOf(rp));
  1303. //平台佣金
  1304. BigDecimal cp = order.getTotalPrice().subtract(rp).
  1305. setScale(2, RoundingMode.DOWN);
  1306. order.setyPrice(String.valueOf(cp));
  1307. ordersList.add(order);
  1308. });
  1309. orderPage.setRecords(ordersList);
  1310. }
  1311. return orderPage;
  1312. }
  1313. /**
  1314. * 累计营业收入 累计平台佣金收入 累计商家收入
  1315. * @return
  1316. */
  1317. @Override
  1318. public Map<String, String> totalData(TOrder order) {
  1319. //订单状态(-1:待付款 0:待接单 1:已接单 6:已出发 2:已到达 3:服务中 4:待评价
  1320. //5:已完成 7:退单待审核 8:退单审核通过 -2:已取消 -3:已拒绝)
  1321. // 过滤 待付款 退单审核通过 已取消 已拒绝
  1322. List<TOrder> listOrder = baseMapper.getAll(order);
  1323. Map<String, String> map = new HashMap<>();
  1324. final BigDecimal[] total1 = {BigDecimal.ZERO};
  1325. final BigDecimal[] total2 = {BigDecimal.ZERO};
  1326. BigDecimal total3 = BigDecimal.ZERO;
  1327. listOrder.forEach(o -> {
  1328. total1[0] = total1[0].add(null == o.getTotalPrice() ? BigDecimal.ZERO : o.getTotalPrice());
  1329. total2[0] = total2[0].add(null == o.getTotalPrice() ? BigDecimal.ZERO : o.getTotalPrice().
  1330. multiply(new BigDecimal(hPercent)).setScale(2, RoundingMode.DOWN));
  1331. });
  1332. total3 = total1[0].subtract(total2[0]).setScale(2, RoundingMode.DOWN);
  1333. map.put("totalPrice", total1[0].toString());
  1334. map.put("merchantPrice", total2[0].toString());
  1335. map.put("platformPrice", total3.toString());
  1336. return map;
  1337. }
  1338. @Override
  1339. public void callAutoAccount(int hCount, BigDecimal percent) {
  1340. baseMapper.callAutoAccount(hCount, percent);
  1341. }
  1342. /**
  1343. * 商户收益,总额,待结算,已结算,已提现
  1344. * @param user
  1345. * @return
  1346. */
  1347. public Map<String, String> myIncome(TWxUser user) {
  1348. List<TOrder> orderList = getTOrders(user, null);
  1349. Map<String, String> result = new HashMap<>();
  1350. // 我的收益 = 待结算 + 已结算
  1351. if(CollectionUtil.isNotEmpty(orderList)){
  1352. // 所有订单的总金额
  1353. BigDecimal tAmount = orderList.stream()
  1354. .filter(order -> (order.getTotalPrice()!=null && order.getTotalPrice().compareTo(BigDecimal.ZERO) > 0))
  1355. .map(TOrder::getTotalPrice)
  1356. .reduce(BigDecimal.ZERO, BigDecimal::add);
  1357. tAmount = tAmount.multiply(new BigDecimal(hPercent)).setScale(2, RoundingMode.DOWN);
  1358. // 已结算订单的总金额
  1359. BigDecimal yAmount = orderList.stream()
  1360. .filter(order -> OrderWStateEnum.OK_ACCOUNT.getCode().intValue() == order.getwStatus().intValue())
  1361. .map(TOrder::getTotalPrice)
  1362. .reduce(BigDecimal.ZERO, BigDecimal::add);
  1363. yAmount = yAmount.multiply(new BigDecimal(hPercent)).setScale(2, RoundingMode.DOWN);
  1364. // 待结算订单的总金额
  1365. BigDecimal wAmount = orderList.stream()
  1366. .filter(order -> OrderWStateEnum.WAIT_ACCOUNT.getCode().intValue() == order.getwStatus().intValue())
  1367. .map(TOrder::getTotalPrice)
  1368. .reduce(BigDecimal.ZERO, BigDecimal::add);
  1369. wAmount = wAmount.multiply(new BigDecimal(hPercent)).setScale(2, RoundingMode.DOWN);
  1370. result.put("tAmount", tAmount.toPlainString());
  1371. result.put("yAmount", yAmount.toPlainString());
  1372. result.put("wAmount", wAmount.toPlainString());
  1373. }
  1374. List<TTxRecord> tTxRecords = recordService.tTxRecordList(user.getcOpenid());
  1375. if(CollectionUtil.isNotEmpty(tTxRecords)){
  1376. // 已提现
  1377. BigDecimal gAmount = tTxRecords.stream()
  1378. .filter(order -> (order.getdPrice()!=null && order.getdPrice().compareTo(BigDecimal.ZERO) > 0))
  1379. .map(TTxRecord::getdPrice)
  1380. .reduce(BigDecimal.ZERO, BigDecimal::add);
  1381. result.put("gAmount", gAmount.toPlainString());
  1382. }
  1383. result.put("tAmount", StringUtils.isNotBlank(result.get("tAmount"))?result.get("tAmount"):"0.00");
  1384. result.put("yAmount", StringUtils.isNotBlank(result.get("yAmount"))?result.get("yAmount"):"0.00");
  1385. result.put("wAmount", StringUtils.isNotBlank(result.get("wAmount"))?result.get("wAmount"):"0.00");
  1386. result.put("gAmount", StringUtils.isNotBlank(result.get("gAmount"))?result.get("gAmount"):"0.00");
  1387. return result;
  1388. }
  1389. /**
  1390. * 商户收入明细
  1391. * @param user
  1392. * @param wStatus
  1393. * @return
  1394. */
  1395. public List<Map<String, Object>> myIncomeDetail(TWxUser user, Integer wStatus) {
  1396. List<TOrder> orderList = getTOrders(user, wStatus);
  1397. Map<YearMonth, List<TOrder>> sortedMonthlyGroupedOrders = new HashMap<>();
  1398. List<Map<String, Object>> listResult = new ArrayList<>();
  1399. if(CollectionUtil.isNotEmpty(orderList)){
  1400. orderList.forEach(item->{
  1401. //商户应收金额 不进行四舍五入
  1402. if(!Objects.isNull(item.getTotalPrice())){
  1403. item.setrPrice(String.valueOf(item.getTotalPrice().multiply(new BigDecimal(hPercent)).
  1404. setScale(2, RoundingMode.DOWN)));
  1405. }
  1406. item.setProjectNames(getProjectNames(item));
  1407. });
  1408. Map<YearMonth, List<TOrder>> monthlyGroupedOrders = orderList.stream()
  1409. .collect(Collectors.groupingBy(
  1410. order -> YearMonth.from(order.getDtCreateTime()),
  1411. Collectors.collectingAndThen(
  1412. Collectors.toList(),
  1413. list -> list.stream()
  1414. .sorted(Comparator.comparing(TOrder::getDtCreateTime).reversed())
  1415. .collect(Collectors.toList())
  1416. )
  1417. ));
  1418. sortedMonthlyGroupedOrders = new TreeMap<>(Comparator.reverseOrder());
  1419. sortedMonthlyGroupedOrders.putAll(monthlyGroupedOrders);
  1420. for (Map.Entry<YearMonth, List<TOrder>> entry : sortedMonthlyGroupedOrders.entrySet()) {
  1421. YearMonth yearMonth = entry.getKey();
  1422. List<TOrder> tTxRecordList = entry.getValue();
  1423. Map<String, Object> newMap = new HashMap<>();
  1424. // 将新键和对应的值放入新的Map中
  1425. newMap.put("date", yearMonth);
  1426. newMap.put("records", tTxRecordList);
  1427. listResult.add(newMap);
  1428. }
  1429. }
  1430. return listResult;
  1431. }
  1432. /**
  1433. * 查询待结算/已结算订单
  1434. * @param user
  1435. * @return
  1436. */
  1437. private List<TOrder> getTOrders(TWxUser user, Integer wStatus) {
  1438. LambdaQueryWrapper<TOrder> queryWrapper = new LambdaQueryWrapper<>();
  1439. queryWrapper.eq(TOrder::getIsDelete, 0).
  1440. eq(com.ylx.common.utils.StringUtils.isNotBlank(user.getcOpenid()),TOrder::getcOpenId, user.getcOpenid()).
  1441. eq(wStatus != null, TOrder::getwStatus, wStatus).
  1442. // 过滤 待付款 退单审核通过 已取消 已拒绝
  1443. ne(TOrder::getnStatus, OrderStatusEnum.WAIT_PAY).
  1444. ne(TOrder::getnStatus, OrderStatusEnum.CANCEL_APPLICATION_PASS).
  1445. ne(TOrder::getnStatus, OrderStatusEnum.CANCEL.getCode()).
  1446. ne(TOrder::getnStatus, OrderStatusEnum.REFUSE.getCode()).
  1447. orderByDesc(TOrder::getDtCreateTime);
  1448. return orderMapper.selectList(queryWrapper);
  1449. }
  1450. private String getProjectNames(TOrder order) {
  1451. List<TXiangmu> list = JSONObject.parseArray(order.getcGoods().toJSONString(), TXiangmu.class);
  1452. StringBuilder sb = new StringBuilder();
  1453. for (TXiangmu xiangmu : list) {
  1454. sb.append(xiangmu.getcTitle()).append(",");
  1455. }
  1456. // 移除最后一个逗号
  1457. if (sb.length() > 0) {
  1458. sb.setLength(sb.length() - 1);
  1459. }
  1460. return sb.toString();
  1461. }
  1462. }