| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424 |
- package com.ylx.massage.service.impl;
- import cn.hutool.core.collection.CollectionUtil;
- import cn.hutool.json.JSONUtil;
- import com.alibaba.fastjson.JSONArray;
- import com.alibaba.fastjson.JSONObject;
- import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
- import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
- import com.baomidou.mybatisplus.core.toolkit.StringUtils;
- import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.ylx.common.config.WechatAccountConfig;
- import com.ylx.common.constant.MassageConstants;
- import com.ylx.common.core.domain.R;
- import com.ylx.common.exception.ServiceException;
- import com.ylx.common.utils.SecurityUtils;
- import com.ylx.massage.domain.*;
- import com.ylx.massage.domain.vo.*;
- import com.ylx.massage.enums.BillTypeEnum;
- import com.ylx.massage.enums.DiscountTypeEnum;
- import com.ylx.massage.enums.JsStatusEnum;
- import com.ylx.massage.enums.OrderStatusEnum;
- import com.ylx.massage.mapper.TOrderMapper;
- import com.ylx.massage.service.*;
- import com.ylx.massage.utils.*;
- import lombok.extern.slf4j.Slf4j;
- import org.apache.commons.compress.utils.Lists;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
- import javax.annotation.Resource;
- import java.math.BigDecimal;
- import java.math.RoundingMode;
- import java.time.Duration;
- import java.time.LocalDate;
- import java.time.LocalDateTime;
- import java.time.LocalTime;
- import java.util.*;
- import java.util.stream.Collectors;
- /**
- * 订单表 服务实现类
- */
- @Service
- @Slf4j
- public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> implements TOrderService {
- @Resource
- private TOrderMapper orderMapper;
- @Resource
- private WechatAccountConfig wxPayProperties;
- @Resource
- private LocationUtil locationUtil;
- @Resource
- private TWxUserService wxUserService;
- @Resource
- private TRechargeService rechargeService;
- @Resource
- private TXiangmuService xiangmuService;
- @Resource
- private OrderNumberGenerator generator;
- @Resource
- private TJsService jsService;
- @Resource
- private TAddressService addressService;
- @Resource
- private TConsumptionLogService consumptionLogService;
- @Resource
- private MassageUtil massageUtil;
- @Resource
- private CouponReceiveService couponReceiveService;
- @Resource
- private CouponService couponService;
- @Resource
- private WeChatUtil weChatUtil;
- @Resource
- private RefundVoucherService refundVoucherService;
- @Resource
- private OrderValidationService orderValidationService;
- @Resource
- private OrderNotificationService orderNotificationService;
- @Resource
- private OrderAllocationLogService allocationLogService;
- @Resource
- private CancelOrderApplicationService cancelOrderApplicationService;
- /**
- * 判断是否免车费
- * 时间段判断:
- * - 白天时段(7:30-20:00):距离 ≤ 技师白天免车费里程 → 免费
- * - 夜间时段(20:00-7:30):距离 ≤ 技师夜间免车费里程 → 免费
- *
- * @param js
- * @param distance
- * @return Boolean
- */
- public Boolean isFree(TJs js, BigDecimal distance) {
- Date date = new Date();
- //白天免车费(07.30-20.00)
- long current = Long.parseLong(DateTimeUtils.numTime(date));
- if (current >= MassageConstants.START_FREE && current <= MassageConstants.END_FREE) {
- if (js.getDaytimeMileage().compareTo(distance) >= 0) {
- //免车费
- return Boolean.TRUE;
- } else {
- return Boolean.FALSE;
- }
- } else {
- //夜间免车费(20.00-07.30)
- if (js.getNigthMileage().compareTo(distance) >= 0) {
- //免车费
- return Boolean.TRUE;
- } else {
- return Boolean.FALSE;
- }
- }
- }
- /**
- * 添加订单
- *
- * @param order
- * @return TOrder
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public TOrder addOrder(TOrder order) {
- String jsId = order.getcJsId();
- // 1. 基础参数校验
- if (StringUtils.isBlank(jsId)) {
- throw new ServiceException("请选择技师");
- }
- if (order.getcGoods().isEmpty()) {
- throw new ServiceException("请选择项目");
- }
- TJs js = jsService.getById(jsId);
- if (js == null) {
- throw new ServiceException("技师不存在");
- }
- Integer techType = js.getTechType();
- // 虚拟技师
- if(techType.equals(1)){
- //虚拟技师订单
- order.setVirtualOrderFlag(1);
- //虚拟技师订单未分配
- order.setVirtualOrderAllocation(1);
- }else{
- //真实订单
- order.setVirtualOrderFlag(0);
- order.setVirtualOrderAllocation(0);
- }
- // 2. 【新增】订单状态锁校验 - 检查技师是否可以接单
- // 确保技师没有进行中的订单,保证服务时间互斥
- log.info("开始校验技师 {} 是否可以接单,订单号:{}", order.getcJsId(), order.getOrderNo());
- orderValidationService.canAcceptOrder(order.getcJsId(), order);
- log.info("技师 {} 接单校验通过,继续创建订单", order.getcJsId());
- //优惠卷减免
- // List<CouponReceiveVo> coupons = couponReceiveService.getByOpenId(order.getcOpenId());
- // BigDecimal preferential = this.setCoupon(coupons);
- // order.setPreferential(preferential);
- // 生成订单号
- order.setOrderNo(generator.generateNextOrderNumber(OrderNumberGenerator.KEY_PREFIX_ORDER));
- //订单价格
- List<TXiangmu> list = JSONObject.parseArray(order.getcGoods().toJSONString(), TXiangmu.class);
- BigDecimal sum = list.stream().map(TXiangmu::getSum).reduce(BigDecimal.ZERO, BigDecimal::add);
- //订单总金额
- order.setdTotalMoney(sum);
- //获取用户默认地址
- TAddress address = addressService.getByOpenId(order.getcOpenId());
- if (address == null) {
- throw new ServiceException("请先添加地址");
- }
- //添加技师位置信息
- locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), Double.parseDouble(js.getLongitude().toString()), Double.parseDouble(js.getLatitude().toString()));
- //添加用户位置信息
- locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, order.getcOpenId() + order.getOrderNo(), Double.parseDouble(address.getLongitude().toString()), Double.parseDouble(address.getLatitude().toString()));
- //计算距离
- double distance = locationUtil.getDistance(js.getcOpenId() + order.getOrderNo(), order.getcOpenId() + order.getOrderNo());
- log.info("技师与用户之间的距离:{}", distance);
- locationUtil.remove(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), order.getcOpenId() + order.getOrderNo());
- order.setDistance(new BigDecimal(distance));
- //计算车费
- if (order.getDistance() != null && order.getDistance().compareTo(BigDecimal.ZERO) > 0 && StringUtils.isBlank(order.getParentNo())) {
- //判断是否可以免车费
- if (!this.isFree(js, order.getDistance())) {
- BigDecimal bigDecimal = massageUtil.calculateTaxiFare(order.getDistance(), js.getDeptId());
- order.setFare(bigDecimal.setScale(MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP));
- }
- }
- //总价 = 订单 + 车费
- order.setTotalPrice(sum.add(Optional.ofNullable(order.getFare()).orElse(BigDecimal.ZERO)));
- if (order.getParentNo() != null && order.getOrderType() == 2) {
- //升级订单 补差价
- TOrder partOrder = this.getByNo(order.getParentNo());
- order.setPriceDifference(order.getTotalPrice().subtract(partOrder.getTotalPrice()));
- }
- order.setAddress(address.getAddress());
- order.setName(address.getName());
- order.setLatitude(address.getLatitude());
- order.setLongitude(address.getLongitude());
- order.setcPhone(address.getPhone());
- //设置用户姓名
- order.setcName(address.getUserName());
- order.setAtlasAdd(address.getAtlasAdd());
- order.setDeptId(js.getDeptId());
- order.setDeptName(js.getCity());
- //设置订单状态:待支付
- order.setnStatus(OrderStatusEnum.WAIT_PAY.getCode());
- order.setDtCreateTime(LocalDateTime.now());
- Date date = DateTimeUtils.addMinute(new Date(), 10);
- order.setcTime(DateTimeUtils.formatDate(date, "yyyy-MM-dd HH:mm:ss"));
- save(order);
- return order;
- }
- private BigDecimal setCoupon(List<CouponReceiveVo> coupons) {
- //过滤过期的优惠券
- coupons = coupons.stream().filter(coupon -> coupon.getExpirationTime().after(new Date())).collect(Collectors.toList());
- //无门槛优惠券
- List<CouponReceiveVo> collect = coupons.stream().filter(coupon -> coupon.getDiscountType().equals(DiscountTypeEnum.NO_THRESHOLD.getCode())).collect(Collectors.toList());
- //支付成功 后 删除优惠卷
- // couponReceiveService.removeCoupons(collect);
- //计算优惠金额
- return collect.stream().map(CouponReceiveVo::getDiscountValue).reduce(BigDecimal.ZERO, BigDecimal::add);
- }
- @Override
- public void payNotifyOrder(String outTradeNo) {
- //查询未支付的订单
- LambdaQueryWrapper<TOrder> queryWrapper = new LambdaQueryWrapper<>();
- queryWrapper.eq(TOrder::getOrderNo, outTradeNo).eq(TOrder::getnStatus, OrderStatusEnum.WAIT_PAY.getCode());
- TOrder orderNew = this.getOne(queryWrapper);
- if (orderNew == null) {
- log.error("订单 {} 未支付状态不存在", outTradeNo);
- return;
- }
- // 设置微信支付
- orderNew.setPayType(1);
- TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId());
- orderPayManage(user, orderNew);
- }
- @Override
- public Object updateAddressById(TOrder borrow) {
- TOrder order = this.getById(borrow.getcId());
- if (borrow.getLatitude() != null && borrow.getLatitude() != 0 && borrow.getLongitude() != null && borrow.getLongitude() != 0) {
- order.setAtlasAdd(borrow.getAtlasAdd());
- order.setcName(borrow.getcName());
- order.setcPhone(borrow.getcPhone());
- order.setName(borrow.getName());
- order.setAddress(borrow.getAddress());
- order.setLatitude(borrow.getLatitude());
- order.setLongitude(borrow.getLongitude());
- TJs js = jsService.getById(order.getcJsId());
- //添加技师位置信息
- locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), Double.parseDouble(js.getLongitude().toString()), Double.parseDouble(js.getLatitude().toString()));
- //添加用户位置信息
- locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, order.getcOpenId() + order.getOrderNo(), Double.parseDouble(borrow.getLongitude().toString()), Double.parseDouble(borrow.getLatitude().toString()));
- double distance = locationUtil.getDistance(js.getcOpenId() + order.getOrderNo(), order.getcOpenId() + order.getOrderNo());
- locationUtil.remove(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), order.getcOpenId() + order.getOrderNo());
- order.setDistance(new BigDecimal(distance));
- //计算车费
- if (order.getDistance() != null && order.getDistance().compareTo(BigDecimal.ZERO) > 0) {
- //判断是否可以免车费
- if (!this.isFree(js, order.getDistance())) {
- BigDecimal bigDecimal = massageUtil.calculateTaxiFare(order.getDistance(), order.getDeptId());
- order.setFare(bigDecimal.setScale(MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP));
- }
- }
- order.setTotalPrice(order.getdTotalMoney().add(Optional.ofNullable(order.getFare()).orElse(BigDecimal.ZERO)));
- this.updateById(order);
- }
- return order;
- }
- @Override
- public Object depart(TOrder order) {
- LambdaQueryWrapper<TOrder> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(TOrder::getcId, order.getcId()).eq(TOrder::getnStatus, OrderStatusEnum.RECEIVED_ORDER.getCode());
- //设置订单状态:已出发
- order.setnStatus(OrderStatusEnum.DEPART.getCode());
- order.setDepartTime(new Date());
- order.setDepartLongitude(Optional.ofNullable(order.getDepartLongitude()).orElse(BigDecimal.ZERO));
- order.setDepartLatitude(Optional.ofNullable(order.getDepartLatitude()).orElse(BigDecimal.ZERO));
- return this.update(order, wrapper);
- }
- @Override
- public Integer getOrderNum(String jsid, Date startDate, Date endDate) {
- return orderMapper.getOrderNum(jsid, startDate, endDate);
- }
- @Override
- public Integer getAddNum(String jsid, Date startDate, Date endDate) {
- return orderMapper.getAddNum(jsid, startDate, endDate);
- }
- @Override
- public Integer getUpgradeNum(String jsid, Date startDate, Date endDate) {
- return orderMapper.getUpgradeNum(jsid, startDate, endDate);
- }
- @Override
- public BigDecimal getTurnover(String jsid, Date startDate, Date endDate) {
- return orderMapper.getTurnover(jsid, startDate, endDate);
- }
- @Override
- @Transactional(rollbackFor = Exception.class)
- public TOrder transferOrder(TOrder order) {
- // ========== 第1步:参数校验 ==========
- if (StringUtils.isBlank(order.getcId())) {
- throw new ServiceException("订单id不能为空");
- }
- if (StringUtils.isBlank(order.getcJsId())) {
- throw new ServiceException("转单技师ID不能为空");
- }
- // 定义操作结果(默认为失败)
- Integer operationResult = 1; // 1-失败
- // 定义操作记录所需的变量
- String orderId = null;
- String orderNo = null;
- String oldTechnicianId = null;
- String oldTechnicianName = null;
- Integer oldTechnicianStatusBefore = null;
- Integer oldTechnicianStatusAfter = null;
- String newTechnicianId = null;
- String newTechnicianName = null;
- Integer newTechnicianStatusBefore = null;
- Integer newTechnicianStatusAfter = null;
- Integer orderStatusBefore = null;
- Integer orderStatusAfter = null;
- String operatorId = null;
- String operatorName = null;
- String operationReason = "虚拟订单分配";
- try {
- // ========== 第2步:查询原订单信息 ==========
- TOrder oldOrder = this.getById(order.getcId());
- if (oldOrder == null) {
- throw new ServiceException("订单不存在");
- }
- //原技师ID
- oldTechnicianId = oldOrder.getcJsId();
- //新技师ID
- newTechnicianId = order.getcJsId();
- // 记录订单操作前状态
- orderStatusBefore = oldOrder.getnStatus();
- orderId = oldOrder.getcId();
- orderNo = oldOrder.getOrderNo();
- log.info("开始转单操作 - 订单号:{}, 原技师ID:{}, 新技师ID:{}, 订单状态:{}", oldOrder.getOrderNo(), oldTechnicianId, newTechnicianId, orderStatusBefore);
- // ========== 第3步:查询原技师信息 ==========
- TJs oldTechnician = jsService.getById(oldTechnicianId);
- if (oldTechnician == null) {
- throw new ServiceException("原技师不存在");
- }
- oldTechnicianName = oldTechnician.getcName();
- oldTechnicianStatusBefore = oldTechnician.getnStatus();
- // ========== 第4步:查询新技师信息 ==========
- TJs newTechnician = jsService.getById(newTechnicianId);
- if (newTechnician == null) {
- throw new ServiceException("新技师不存在");
- }
- newTechnicianName = newTechnician.getcName();
- newTechnicianStatusBefore = newTechnician.getnStatus();
- // ========== 第5步:更新订单技师信息 ==========
- oldOrder.setOldJsId(oldTechnicianId); // 保存原技师ID
- oldOrder.setcJsId(newTechnicianId); // 更新为新技师ID
- log.info("更新订单技师 - 订单号:{}, 原技师:[ID:{}, 姓名:{}], 新技师:[ID:{}, 姓名:{}]", oldOrder.getOrderNo(), oldTechnicianId, oldTechnicianName, newTechnicianId, newTechnicianName);
- if (!this.updateById(oldOrder)) {
- throw new ServiceException("转单失败:更新订单技师信息失败");
- }
- // 记录订单操作后状态(转单后状态通常保持不变)
- orderStatusAfter = oldOrder.getnStatus();
- // ========== 第6步:更新新技师状态(可服务 → 服务中)==========
- TJs newJsUpdate = new TJs();
- newJsUpdate.setId(newTechnicianId);
- newJsUpdate.setnStatus(JsStatusEnum.JS_SERVICE.getCode());
- if (!jsService.updateById(newJsUpdate)) {
- throw new ServiceException("转单失败:更新新技师状态失败");
- }
- newTechnicianStatusAfter = JsStatusEnum.JS_SERVICE.getCode();
- // ========== 第7步:更新原技师状态(服务中 → 可服务)==========
- TJs oldJsUpdate = new TJs();
- oldJsUpdate.setId(oldTechnicianId);
- oldJsUpdate.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
- if (!jsService.updateById(oldJsUpdate)) {
- throw new ServiceException("转单失败:更新原技师状态失败");
- }
- oldTechnicianStatusAfter = JsStatusEnum.JS_SERVICEABLE.getCode();
- log.info("更新技师状态完成 - 新技师:{} {}→{}, 原技师:{} {}→{}",
- newTechnicianName, getStatusName(newTechnicianStatusBefore), getStatusName(newTechnicianStatusAfter),
- oldTechnicianName, getStatusName(oldTechnicianStatusBefore), getStatusName(oldTechnicianStatusAfter));
- // ========== 第8步:获取操作人信息 ==========
- operatorId = SecurityUtils.getUserId() != null ? SecurityUtils.getUserId().toString() : "ADMIN";
- operatorName = SecurityUtils.getUsername() != null ? SecurityUtils.getUsername() : "系统管理员";
- // ========== 第9步:转单成功,设置操作结果为成功 ==========
- operationResult = 0; // 0-成功
- log.info("转单操作完成 - 订单号:{}", oldOrder.getOrderNo());
- return oldOrder;
- } catch (ServiceException e) {
- // 业务异常,操作失败
- log.error("转单操作失败 - 订单号:{}, 错误信息:{}", orderNo, e.getMessage());
- operationResult = 1; // 1-失败
- throw e;
- } catch (Exception e) {
- // 系统异常,操作失败
- log.error("转单操作异常 - 订单号:{}, 异常信息:{}", orderNo, e.getMessage(), e);
- operationResult = 1; // 1-失败
- throw new ServiceException("转单操作异常:" + e.getMessage());
- } finally {
- // ========== 第10步:记录转单操作日志(无论成功或失败都记录)==========
- try {
- // 只有在获取到基本信息后才记录日志
- if (orderId != null && orderNo != null && oldTechnicianId != null && newTechnicianId != null) {
- allocationLogService.recordTransferOrder(
- orderId, // orderId
- orderNo, // orderNo
- oldTechnicianId, // oldTechnicianId
- oldTechnicianName, // oldTechnicianName
- oldTechnicianStatusBefore, // oldTechnicianStatusBefore
- oldTechnicianStatusAfter, // oldTechnicianStatusAfter
- newTechnicianId, // newTechnicianId
- newTechnicianName, // newTechnicianName
- newTechnicianStatusBefore, // newTechnicianStatusBefore
- newTechnicianStatusAfter, // newTechnicianStatusAfter
- orderStatusBefore, // orderStatusBefore
- orderStatusAfter, // orderStatusAfter
- operatorId, // operatorId
- operatorName, // operatorName
- operationReason, // operationReason
- operationResult // operationResult(0-成功,1-失败)
- );
- String resultDesc = operationResult == 0 ? "成功" : "失败";
- log.info("转单操作记录已保存 - 订单号:{}, 操作结果:{}", orderNo, resultDesc);
- }
- } catch (Exception e) {
- // 记录日志失败不影响转单操作
- log.error("记录转单操作日志失败 - 订单号:{}, 错误信息:{}", orderNo, e.getMessage(), e);
- }
- }
- }
- /**
- * 获取技师状态名称
- *
- * @param status 状态码
- * @return String 状态名称
- */
- private String getStatusName(Integer status) {
- if (status == null) {
- return "未知";
- }
- switch (status) {
- case 0:
- return "可服务";
- case 1:
- return "服务中";
- case 2:
- return "不可服务";
- default:
- return "未知(" + status + ")";
- }
- }
- @Override
- public List<HomeBlock> getBlock(Date start, Date end, String deptId) {
- return orderMapper.getBlock(start, end, deptId);
- }
- @Override
- public OrderVerificationVo verification(TOrder order) {
- if (StringUtils.isBlank(order.getCouponReceiveId())) {
- throw new ServiceException("认领优惠券id为空");
- }
- if (StringUtils.isBlank(order.getcId())) {
- throw new ServiceException("订单id为空");
- }
- OrderVerificationVo orderVerificationVo = new OrderVerificationVo();
- TOrder tOrder = this.getById(order.getcId());
- orderVerificationVo.setCouponReceiveId(order.getCouponReceiveId());
- CouponReceive couponReceive = couponReceiveService.getById(order.getCouponReceiveId());
- Coupon coupon = couponService.getById(couponReceive.getCouponId());
- log.info("订单信息,{}", tOrder);
- log.info("优惠卷信息,{}", coupon);
- //折扣券
- if (coupon.getDiscountType() == 2) {
- //判断门槛金额
- if (tOrder.getTotalPrice().compareTo(coupon.getThresholdAmount()) >= 0) {
- //折扣值
- BigDecimal divide = coupon.getRebValue().divide(new BigDecimal(10));
- //优惠后的金额 = 订单总金额*折扣值
- BigDecimal bigDecimal = tOrder.getTotalPrice().multiply(divide).setScale(MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP);
- //优惠值
- orderVerificationVo.setPreferential(tOrder.getTotalPrice().subtract(bigDecimal));
- orderVerificationVo.setTotalPrice(bigDecimal);
- } else {
- throw new ServiceException("不满足优惠券门槛金额");
- }
- } else {
- if (tOrder.getTotalPrice().compareTo(coupon.getThresholdAmount()) >= 0) {
- //优惠值
- orderVerificationVo.setPreferential(coupon.getDiscountValue());
- orderVerificationVo.setTotalPrice(tOrder.getTotalPrice().subtract(coupon.getDiscountValue()));
- } else {
- throw new ServiceException("不满足优惠券门槛金额");
- }
- }
- if (orderVerificationVo.getTotalPrice().compareTo(BigDecimal.ZERO) < 0) {
- throw new ServiceException("当前项目不可用");
- }
- return orderVerificationVo;
- }
- private TOrder getOrder(TOrder tOrder) {
- if (updateById(tOrder)) {
- return tOrder;
- } else {
- throw new ServiceException("优惠券核销失败");
- }
- }
- /**
- * 支付订单
- *
- * @param order
- * @return R
- */
- @Override
- public R payOrder(TOrder order) throws Exception {
- // 根据订单ID查询订单信息
- TOrder orderNew = getById(order.getcId());
- if (!orderNew.getnStatus().equals(OrderStatusEnum.WAIT_PAY.getCode())) {
- throw new ServiceException("该订单已经支付或者超时被取消");
- }
- TJs js = jsService.getById(orderNew.getcJsId());
- if (StringUtils.isBlank(orderNew.getParentNo())) {
- if (null == js || js.getnStatus().equals(JsStatusEnum.JS_SERVICE.getCode())) {
- throw new ServiceException("该技师已在服务中请重新下单");
- }
- }
- orderNew.setPayType(order.getPayType());
- //优惠券核销
- if (StringUtils.isNotBlank(order.getCouponReceiveId())) {
- orderNew.setCouponReceiveId(order.getCouponReceiveId());
- orderNew.setPreferential(order.getPreferential());
- orderNew.setTotalPrice(order.getTotalPrice());
- if (!updateById(orderNew)) {
- throw new ServiceException("支付失败");
- }
- }
- //判断支付方式
- if (order.getPayType().equals(MassageConstants.INTEGER_ONE)) {
- //微信支付
- R resp = rechargeService.getPay(orderNew.getOrderNo(), orderNew.getTotalPrice(), orderNew.getcOpenId(), BillTypeEnum.WX_PAY.getInfo(), BillTypeEnum.WX_PAY.getCode().toString());
- //添加待接单消息通知(技师侧)这块的逻辑在回调接口中
- //orderNotificationService.sendPendingRemindNotification(orderNew);
- return resp;
- }
- TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId());
- if (null == user) {
- throw new ServiceException("用户不存在");
- }
- //现金支付
- if (order.getPayType().equals(MassageConstants.INTEGER_THREE)) {
- //现金支付
- orderPayManage(user, orderNew);
- //添加待接单消息通知(技师侧)
- orderNotificationService.sendPendingRemindNotification(orderNew);
- return R.ok();
- }
- if (user.getdBalance().compareTo(orderNew.getTotalPrice()) < MassageConstants.INTEGER_ZERO) {
- throw new ServiceException("账户金额不够请充值");
- } else {
- orderPayManage(user, orderNew);
- //添加待接单消息通知(技师侧)
- orderNotificationService.sendPendingRemindNotification(orderNew);
- return R.ok();
- }
- }
- /**
- * 新订单通知
- *
- * @param order
- */
- public void newOrderNotification(TOrder order) {
- cn.hutool.json.JSONObject param = JSONUtil.createObj();
- //订单号
- param.set("character_string9", JSONUtil.createObj().set("value", order.getOrderNo()));
- //电话
- param.set("phone_number14", JSONUtil.createObj().set("value", order.getcPhone()));
- param.set("thing18", JSONUtil.createObj().set("value", order.getcName()));
- param.set("time6", JSONUtil.createObj().set("value", DateTimeUtils.formatDate(new Date(), DateTimeUtils.DATE_NUMBER_YEAR_MONTH_FORMAT)));
- param.set("thing27", JSONUtil.createObj().set("value", order.getName()));
- TJs js = jsService.getById(order.getcJsId());
- weChatUtil.notification(js.getcOpenId(), wxPayProperties.getTemplateId1(), param);
- }
- /**
- * 订单支付管理
- *
- * @param user
- * @param orderNew
- */
- @Transactional(rollbackFor = Exception.class)
- public void orderPayManage(TWxUser user, TOrder orderNew) {
- //更新优惠卷状态
- if (StringUtils.isNotBlank(orderNew.getCouponReceiveId())) {
- CouponReceive couponReceive = new CouponReceive();
- couponReceive.setId(orderNew.getCouponReceiveId());
- couponReceive.setUseState(MassageConstants.INTEGER_TWO);
- if (!couponReceiveService.updateById(couponReceive)) {
- log.error("优惠券状态更新失败id:,{}", orderNew.getCouponReceiveId());
- }
- }
- // 更新用户金额 及下单此时
- TWxUser paramUser = new TWxUser();
- paramUser.setcOpenid(user.getcOpenid());
- // 余额支付
- if (orderNew.getPayType().equals(MassageConstants.INTEGER_TWO)) {
- paramUser.setdBalance(user.getdBalance().subtract(orderNew.getTotalPrice()));
- }
- paramUser.setdMoney(user.getdMoney().add(orderNew.getTotalPrice()));
- paramUser.setnNum(user.getnNum() + MassageConstants.INTEGER_ONE);
- paramUser.setId(user.getId());
- wxUserService.updateById(paramUser);
- //增加消费记录
- TConsumptionLog tConsumptionLog = new TConsumptionLog();
- tConsumptionLog.setAmount(orderNew.getTotalPrice().negate());
- tConsumptionLog.setBillNo(orderNew.getOrderNo());
- tConsumptionLog.setOpenId(orderNew.getcOpenId());
- if (orderNew.getPayType().equals(MassageConstants.INTEGER_TWO)) {
- tConsumptionLog.setBillType(BillTypeEnum.BALANCE_PAYMENT.getCode());
- tConsumptionLog.setNote("余额支付");
- } else if(orderNew.getPayType().equals(MassageConstants.INTEGER_ONE)){
- tConsumptionLog.setBillType(BillTypeEnum.WX_PAY.getCode());
- tConsumptionLog.setNote("微信支付");
- } else {
- tConsumptionLog.setBillType(BillTypeEnum.CASH_PAYMENT.getCode());
- tConsumptionLog.setNote("现金支付");
- }
- consumptionLogService.save(tConsumptionLog);
- // 更新项目数据
- JSONArray objects = orderNew.getcGoods();
- objects.forEach(item -> {
- UpdateWrapper<TXiangmu> wrapper = new UpdateWrapper<>();
- // 获取参数
- wrapper.lambda().eq(TXiangmu::getcId, ((JSONObject) item).getString("cId"));
- // 设置数量
- wrapper.setSql(" n_sale_number = n_sale_number + " + ((JSONObject) item).getInteger("number"));
- xiangmuService.update(wrapper);
- });
- TOrder orderParam = new TOrder();
- orderParam.setPayType(orderNew.getPayType());
- orderParam.setcId(orderNew.getcId());
- orderParam.setnStatus(OrderStatusEnum.WAIT_JD.getCode());
- orderParam.setPayTime(new Date());
- //加钟的订单支付完直接服务中
- if (StringUtils.isNotBlank(orderNew.getParentNo())) {
- orderParam.setnStatus(OrderStatusEnum.SERVICE.getCode());
- }
- // orderParam.setnStatus(OrderStatusEnum.SERVICE.getCode());
- //更新及技师状态
- updateJs(orderNew);
- updateById(orderParam);
- //
- //this.newOrderNotification(orderNew);
- //添加待接单消息通知(技师侧)
- orderNotificationService.sendPendingRemindNotification(orderNew);
- //电话通知
- TJs js = jsService.getById(orderNew.getcJsId());
- Sendvoice.sendPhone(js.getcPhone());
- }
- /**
- * 拒绝订单
- *
- * @param order
- */
- @Override
- public Boolean jujue(TOrder order) {
- TOrder orderNew = getById(order.getcId());
- TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId());
- // 更新用户金额 及下单此时
- TWxUser paramUser = new TWxUser();
- paramUser.setcOpenid(user.getcOpenid());
- paramUser.setId(user.getId());
- // 余额记录
- TConsumptionLog tConsumptionLog = new TConsumptionLog();
- tConsumptionLog.setAmount(orderNew.getTotalPrice());
- tConsumptionLog.setBillNo(orderNew.getOrderNo());
- tConsumptionLog.setOpenId(orderNew.getcOpenId());
- if (orderNew.getPayType() == 2) {
- // 金额归还对应账户
- paramUser.setdBalance(user.getdBalance().add(orderNew.getTotalPrice()));
- tConsumptionLog.setBillType(BillTypeEnum.REFUSE_ACCEPT_REFUND.getCode());
- tConsumptionLog.setNote("拒绝接单退款到余额");
- } else {
- // 微信支付
- // 生成退款单退款
- RefundVoucher refundVoucher = new RefundVoucher();
- refundVoucher.setRefundNo(generator.generateNextOrderNumber(OrderNumberGenerator.KEY_PREFIX_REFUND));
- refundVoucher.setOrderNo(orderNew.getOrderNo());
- refundVoucher.setMoney(orderNew.getTotalPrice());
- refundVoucher.setOpenId(orderNew.getcOpenId());
- refundVoucher.setReStatus(MassageConstants.INTEGER_ZERO);
- refundVoucher.setReason("技师拒绝接单");
- refundVoucherService.save(refundVoucher);
- tConsumptionLog.setBillType(BillTypeEnum.REFUSE_ACCEPT_REFUND.getCode());
- tConsumptionLog.setNote("拒绝接单退款到余额");
- // 微信退款原路返回
- rechargeService.refund(refundVoucher.getRefundNo(), null, orderNew.getOrderNo(), orderNew.getTotalPrice());
- }
- consumptionLogService.save(tConsumptionLog);
- //退优惠卷
- if (StringUtils.isNotBlank(orderNew.getCouponReceiveId())) {
- CouponReceive couponReceive = couponReceiveService.getById(orderNew.getCouponReceiveId());
- couponReceive.setUseState(MassageConstants.INTEGER_ZERO);
- couponReceiveService.updateById(couponReceive);
- }
- log.info("余额支付退款user:{}", user);
- // 消费金额对应减少
- paramUser.setdMoney(user.getdMoney().subtract(orderNew.getTotalPrice()));
- // 下单次数减一
- paramUser.setnNum(user.getnNum() - MassageConstants.INTEGER_ONE);
- wxUserService.updateById(paramUser);
- // 更新项目数据
- JSONArray objects = orderNew.getcGoods();
- objects.forEach(item -> {
- UpdateWrapper<TXiangmu> wrapper = new UpdateWrapper<>();
- // 获取参数
- wrapper.lambda().eq(TXiangmu::getcId, ((JSONObject) item).getString("cId"));
- // 设置数量
- wrapper.setSql(" n_sale_number = n_sale_number - " + ((JSONObject) item).getInteger("number"));
- xiangmuService.update(wrapper);
- });
- TOrder orderParam = new TOrder();
- orderParam.setcId(orderNew.getcId());
- orderParam.setnStatus(OrderStatusEnum.REFUSE.getCode());
- orderParam.setReasonRefusal(order.getReasonRefusal());
- updateJs(orderNew);
- return updateById(orderParam);
- }
- /**
- * 确认服务完成
- *
- * @param order
- * @return Boolean
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public Boolean confirm(TOrder order) {
- // 获取订单信息
- TOrder orderNew = getById(order.getcId());
- if (!orderNew.getnStatus().equals(OrderStatusEnum.SERVICE.getCode())) {
- throw new ServiceException("订单状态不是服务中");
- }
- // 更新技师信息
- TJs jsParam = new TJs();
- jsParam.setId(orderNew.getcJsId());
- jsParam.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
- //判断热度标识
- List<TOrder> list = list(new LambdaQueryWrapper<TOrder>().eq(TOrder::getcJsId, orderNew.getcJsId())
- .ge(TOrder::getDtCreateTime, DateTimeUtils.addDays(new Date(), -3))
- .ge(TOrder::getnStatus, OrderStatusEnum.WAIT_EVALUATE.getCode()));
- if (list.size() >= 2) {
- // 设置热度标识:1
- jsParam.setnB3(MassageConstants.INTEGER_ONE);
- }
- // 更新技师状态
- jsService.updateById(jsParam);
- // 更新技师钱包金额
- TJs jsById = jsService.getById(orderNew.getcJsId());
- // 获取技师抽成
- BigDecimal multiply = orderNew.getTotalPrice().multiply(new BigDecimal(jsById.getnBili()));
- multiply = multiply.divide(new BigDecimal(100), MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP);
- // 获取技师所对应的用户
- TWxUser jsUser = wxUserService.getByOpenId(jsById.getcOpenId());
- // 更新余额
- jsUser.setdBalance(jsUser.getdBalance().add(multiply));
- // 更新总钱数
- jsUser.setdAllMoney(jsUser.getdAllMoney().add(multiply));
- wxUserService.updateById(jsUser);
- //增加消费记录
- TConsumptionLog tConsumptionLog = new TConsumptionLog();
- tConsumptionLog.setAmount(multiply);
- tConsumptionLog.setBillNo(orderNew.getOrderNo());
- tConsumptionLog.setOpenId(jsUser.getcOpenid());
- tConsumptionLog.setBillType(BillTypeEnum.INCOME.getCode());
- tConsumptionLog.setNote("技师收益");
- consumptionLogService.save(tConsumptionLog);
- // 如果该技师有推荐人员 一级
- if (StringUtils.isNotBlank(jsUser.getcUpUser())) {
- // 获取技师上级对应的用户
- TWxUser jsUp = wxUserService.getByOpenId(jsUser.getcUpUser());
- extracted(orderNew, jsUp);
- //二级
- if (StringUtils.isNotBlank(jsUp.getcUpUser())) {
- TWxUser jsUpTwo = wxUserService.getByOpenId(jsUp.getcUpUser());
- extracted(orderNew, jsUpTwo);
- //三级
- if (StringUtils.isNotBlank(jsUpTwo.getcUpUser())) {
- TWxUser jsUpThree = wxUserService.getByOpenId(jsUpTwo.getcUpUser());
- extracted(orderNew, jsUpThree);
- }
- }
- }
- // 更新订单
- // 订单状态:待评价
- orderNew.setnStatus(OrderStatusEnum.WAIT_EVALUATE.getCode());
- orderNew.setEndTime(LocalDateTime.now());
- updateById(orderNew);
- // 添加订单完成消息通知(用户侧)
- orderNotificationService.sendCompletedNotification(orderNew);
- return true;
- }
- private void extracted(TOrder orderNew, TWxUser jsUp) {
- log.info("TOrderServiceImpl->extracted->jsUp,{}", JSONUtil.toJsonStr(jsUp));
- log.info("TOrderServiceImpl->extracted->orderNew,{}",JSONUtil.toJsonStr(orderNew));
- BigDecimal up = orderNew.getdTotalMoney().multiply(new BigDecimal("10"));
- up = up.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
- // 更新余额
- jsUp.setdBalance(jsUp.getdBalance().add(up));
- // 更新总钱数
- jsUp.setdAllMoney(jsUp.getdAllMoney().add(up));
- jsUp.setDistributionAmount(up);
- wxUserService.updateById(jsUp);
- //记录分销收益
- TConsumptionLog tConsumptionLog = new TConsumptionLog();
- tConsumptionLog.setAmount(up);
- tConsumptionLog.setBillNo(orderNew.getOrderNo());
- tConsumptionLog.setOpenId(jsUp.getcOpenid());
- tConsumptionLog.setBillType(BillTypeEnum.DISTRIBUTION.getCode());
- tConsumptionLog.setNote("分销收益");
- consumptionLogService.save(tConsumptionLog);
- }
- /**
- * 获取技师当天可预约时间
- *
- * @param technicianId 技师ID
- * @param dateStr 查询日期(格式:yyyy-MM-dd),为null则查询当天
- * @return TechnicianAvailabilityVo 技师当天可预约时间VO
- */
- @Override
- public TechnicianAvailabilityVo getTechnicianAvailability(String technicianId, String dateStr) {
- // 1. 参数校验
- if (StringUtils.isBlank(technicianId)) {
- throw new ServiceException("技师ID不能为空");
- }
- // 2. 查询技师信息
- TJs js = jsService.getById(technicianId);
- if (js == null) {
- throw new ServiceException("技师不存在");
- }
- // 3. 解析日期,默认为当天
- LocalDate queryDate;
- if (StringUtils.isBlank(dateStr)) {
- queryDate = LocalDate.now();
- } else {
- try {
- queryDate = LocalDate.parse(dateStr);
- } catch (Exception e) {
- throw new ServiceException("日期格式错误,请使用 yyyy-MM-dd 格式");
- }
- }
- // 4. 定义当天的所有时间段(以30分钟为间隔,从00:00到23:30)
- List<TimeSlotVo> timeSlots = new ArrayList<>();
- for (int hour = 0; hour < 24; hour++) {
- // 每小时生成两个时间段:xx:00 和 xx:30
- timeSlots.add(TimeSlotVo.builder()
- .time(String.format("%02d:00", hour))
- .available(true)
- .build());
- timeSlots.add(TimeSlotVo.builder()
- .time(String.format("%02d:30", hour))
- .available(true)
- .build());
- }
- // 5. 查询技师当天所有进行中的订单
- // 开始时间
- LocalDateTime startOfDay = queryDate.atStartOfDay();
- // 结束时间
- LocalDateTime endOfDay = queryDate.plusDays(1).atStartOfDay();
- log.info("开始时间:{},结束时间:{}", startOfDay, endOfDay);
- LambdaQueryWrapper<TOrder> queryWrapper = new LambdaQueryWrapper<>();
- queryWrapper.eq(TOrder::getcJsId, technicianId)
- .in(TOrder::getnStatus, OrderStatusEnum.WAIT_JD.getCode(),
- OrderStatusEnum.RECEIVED_ORDER.getCode(),
- OrderStatusEnum.DEPART.getCode(),
- OrderStatusEnum.ARRIVED.getCode(),
- OrderStatusEnum.SERVICE.getCode())
- .ge(TOrder::getDtCreateTime, startOfDay)
- .lt(TOrder::getDtCreateTime, endOfDay)
- .eq(TOrder::getIsDelete, 0);
- List<TOrder> orders = this.list(queryWrapper);
- log.info("技师{},在{}天共有 {} 个进行中的订单", technicianId, queryDate, orders.size());
- // 6. 标记不可预约的时间段
- LocalDateTime now = LocalDateTime.now();
- for (TOrder order : orders) {
- // 6.1 计算订单的开始时间和结束时间
- LocalDateTime orderStart = OrderTimeRangeUtils.estimateStartTime(order);
- LocalDateTime orderEnd = OrderTimeRangeUtils.estimateEndTime(order);
- if (orderStart == null || orderEnd == null) {
- log.warn("订单 {} 的时间信息不完整,跳过", order.getOrderNo());
- continue;
- }
- // 6.2 限制在查询日期范围内
- LocalDateTime effectiveStart = orderStart.isBefore(startOfDay) ? startOfDay : orderStart;
- LocalDateTime effectiveEnd = orderEnd.isAfter(endOfDay) ? endOfDay : orderEnd;
- // 6.3 标记不可预约的时间段
- markTimeSlotsUnavailable(timeSlots, effectiveStart, effectiveEnd, order.getOrderNo());
- }
- // 7. 根据查询日期判断是否可预约
- LocalDate today = LocalDate.now();
- if (queryDate.isBefore(today)) {
- // 查询日期是过去的日期,所有时间段都不可预约
- markAllTimeSlotsUnavailable(timeSlots, "日期已过期");
- } else if (queryDate.equals(today)) {
- // 查询日期是今天,标记过去的时间为不可预约
- markPastTimeSlotsUnavailable(timeSlots, now);
- }
- // 查询日期是未来的日期,所有时间段默认可预约,无需处理
- // 8. 构建返回结果
- return TechnicianAvailabilityVo.builder()
- .date(queryDate.toString())
- .technicianId(technicianId)
- .technicianName(js.getcName())
- .timeSlots(timeSlots)
- .build();
- }
- /**
- * 标记指定时间范围内的时间段为不可预约
- *
- * @param timeSlots 时间段列表
- * @param start 开始时间
- * @param end 结束时间
- * @param orderNo 订单号
- */
- private void markTimeSlotsUnavailable(List<TimeSlotVo> timeSlots, LocalDateTime start, LocalDateTime end, String orderNo) {
- LocalTime startTime = start.toLocalTime();
- LocalTime endTime = end.toLocalTime();
- for (TimeSlotVo slot : timeSlots) {
- LocalTime slotTime = LocalTime.parse(slot.getTime());
- // 判断时间段是否在订单时间范围内
- boolean isInRange = !slotTime.isBefore(startTime) && slotTime.isBefore(endTime);
- if (isInRange) {
- slot.setAvailable(false);
- slot.setReason("已有订单");
- slot.setOrderNo(orderNo);
- }
- }
- }
- /**
- * 标记所有时间段为不可预约
- *
- * @param timeSlots 时间段列表
- * @param reason 不可预约原因
- */
- private void markAllTimeSlotsUnavailable(List<TimeSlotVo> timeSlots, String reason) {
- for (TimeSlotVo slot : timeSlots) {
- slot.setAvailable(false);
- slot.setReason(reason);
- slot.setOrderNo(null);
- }
- }
- /**
- * 标记过去的时间段为不可预约
- *
- * @param timeSlots 时间段列表
- * @param now 当前时间
- */
- private void markPastTimeSlotsUnavailable(List<TimeSlotVo> timeSlots, LocalDateTime now) {
- LocalTime currentTime = now.toLocalTime();
- for (TimeSlotVo slot : timeSlots) {
- LocalTime slotTime = LocalTime.parse(slot.getTime());
- // 如果当前时间已经过了这个时间段,标记为不可预约
- if (slotTime.isBefore(currentTime)) {
- slot.setAvailable(false);
- slot.setReason("已过期");
- slot.setOrderNo(null);
- }
- }
- }
- /**
- * 取消订单
- *
- * @param order
- * @return Boolean
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public Boolean cancle(TOrder order) {
- // 获取订单信息
- // 根据orderid查询订单信息
- TOrder orderNew = getById(order.getcId());
- //待接单
- if (Objects.equals(orderNew.getnStatus(), OrderStatusEnum.WAIT_JD.getCode())) {
- TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId());
- // 更新用户金额 及下单此时
- TWxUser paramUser = new TWxUser();
- paramUser.setId(user.getId());
- paramUser.setcOpenid(user.getcOpenid());
- TConsumptionLog tConsumptionLog = new TConsumptionLog();
- tConsumptionLog.setAmount(orderNew.getTotalPrice());
- tConsumptionLog.setBillNo(orderNew.getOrderNo());
- tConsumptionLog.setOpenId(orderNew.getcOpenId());
- // 余额支付
- if (orderNew.getPayType() == 2) {
- // 金额归还对应账户
- paramUser.setdBalance(user.getdBalance().add(orderNew.getTotalPrice()));
- // 余额记录
- tConsumptionLog.setBillType(BillTypeEnum.CANCEL_ACCEPT_REFUND.getCode());
- tConsumptionLog.setNote("取消订单退款到余额");
- //自己取消的不退优惠卷
- } else {
- // 微信支付
- // 生成退款单退款
- RefundVoucher refundVoucher = new RefundVoucher();
- refundVoucher.setRefundNo(generator.generateNextOrderNumber(OrderNumberGenerator.KEY_PREFIX_REFUND));
- refundVoucher.setOrderNo(orderNew.getOrderNo());
- refundVoucher.setMoney(orderNew.getTotalPrice());
- refundVoucher.setOpenId(orderNew.getcOpenId());
- refundVoucher.setReStatus(MassageConstants.INTEGER_ZERO);
- refundVoucher.setReason("技师拒绝接单");
- refundVoucherService.save(refundVoucher);
- tConsumptionLog.setBillType(BillTypeEnum.CANCEL_WX_REFUND.getCode());
- tConsumptionLog.setNote("取消订单退款到微信");
- // 微信退款原路返回
- rechargeService.refund(refundVoucher.getRefundNo(), null, orderNew.getOrderNo(), orderNew.getTotalPrice());
- }
- consumptionLogService.save(tConsumptionLog);
- // 消费金额对应减少
- paramUser.setdMoney(user.getdMoney().subtract(orderNew.getTotalPrice()));
- // 下单次数减一
- paramUser.setnNum(user.getnNum() - MassageConstants.INTEGER_ONE);
- wxUserService.updateById(paramUser);
- // 更新项目数据
- JSONArray objects = orderNew.getcGoods();
- objects.forEach(item -> {
- UpdateWrapper<TXiangmu> wrapper = new UpdateWrapper<>();
- // 获取参数
- wrapper.lambda().eq(TXiangmu::getcId, ((JSONObject) item).getString("cId"));
- // 设置数量
- wrapper.setSql(" n_sale_number = n_sale_number - " + ((JSONObject) item).getInteger("number"));
- xiangmuService.update(wrapper);
- });
- TOrder orderParam = new TOrder();
- orderParam.setcId(orderNew.getcId());
- orderParam.setnStatus(OrderStatusEnum.CANCEL.getCode());
- //更新技师状态
- TJs tJs = new TJs();
- tJs.setId(orderNew.getcJsId());
- tJs.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
- jsService.updateById(tJs);
- updateById(orderParam);
- // 添加取消订单通知(用户侧)
- orderNotificationService.sendCancelledNotification(orderNew);
- // 添加取消订单通知(技师侧)
- orderNotificationService.sendTechnicianCancelledNotification(orderNew);
- return true;
- } else if (Objects.equals(orderNew.getnStatus(), OrderStatusEnum.WAIT_PAY.getCode())) {//待付款
- TOrder orderParam = new TOrder();
- orderParam.setcId(orderNew.getcId());
- orderParam.setnStatus(OrderStatusEnum.CANCEL.getCode());
- updateById(orderParam);
- // 添加取消订单通知
- orderNotificationService.sendCancelledNotification(orderNew);
- // 添加取消订单通知(技师侧)
- orderNotificationService.sendTechnicianCancelledNotification(orderNew);
- return true;
- } else {
- return false;
- }
- }
- @Override
- public TOrder getByNo(String orderNo) {
- LambdaQueryWrapper<TOrder> objectLambdaQueryWrapper = new LambdaQueryWrapper<>();
- return this.getOne(objectLambdaQueryWrapper.eq(TOrder::getOrderNo, orderNo));
- }
- // private TOrder gettOrder(TOrder order) {
- // LambdaUpdateWrapper<TOrder> objectLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
- // objectLambdaUpdateWrapper.eq(TOrder::getOrderNo, order.getOrderNo());
- // return this.getOne(objectLambdaUpdateWrapper);
- // }
- @Override
- public Page<TOrder> getAll(Page<TOrder> page, TOrder order) {
- Page<TOrder> orderPage = orderMapper.getAll(page, order);
- if (orderPage != null && CollectionUtil.isNotEmpty(orderPage.getRecords())) {
- ArrayList<TOrder> ordersList = Lists.newArrayList();
- orderPage.getRecords().forEach(orders -> {
- orders.setStatusName(OrderStatusEnum.getDescByCode(orders.getnStatus()));
- orders.setJsPhone(orders.getJs().getcPhone());
- orders.setJsName(orders.getJs().getcName());
- if (StringUtils.isEmpty(orders.getcTime())) {
- orders.setRemainingTime(0L);
- }
- if (StringUtils.isNotBlank(orders.getcTime()) && DateTimeUtils.dateStringToStamp(orders.getcTime()) > DateTimeUtils.dateToStamp(new Date())) {
- orders.setRemainingTime((DateTimeUtils.dateStringToStamp(orders.getcTime()) - DateTimeUtils.dateToStamp(new Date())) / 1000);
- }
- if (StringUtils.isNotBlank(orders.getcTime()) && DateTimeUtils.dateStringToStamp(orders.getcTime()) < DateTimeUtils.dateToStamp(new Date())) {
- orders.setRemainingTime(0L);
- }
- if (StringUtils.isNotBlank(orders.getOldJsId())) {
- orders.setOldJs(jsService.getById(orders.getOldJsId()));
- }
- // 计算已服务时长
- Long serviceDuration = calculateServiceDuration(orders);
- orders.setServiceDuration(String.valueOf(serviceDuration));
- ordersList.add(orders);
- });
- orderPage.setRecords(ordersList);
- }
- return orderPage;
- }
- /**
- * 计算已服务时长(分钟)
- *
- * @param order 订单对象
- * @return Long 已服务时长(分钟)
- */
- private Long calculateServiceDuration(TOrder order) {
- try {
- LocalDateTime startTime = order.getStartTime();
- LocalDateTime endTime = order.getEndTime();
- if (startTime != null && endTime != null) {
- long minutes = Duration.between(startTime, endTime).toMinutes();
- if (minutes > 0) {
- return minutes;
- }
- } else if (startTime != null) {
- // 如果只有开始时间,计算到现在的时长
- long minutes = Duration.between(startTime, LocalDateTime.now()).toMinutes();
- if (minutes > 0) {
- return minutes;
- }
- }
- } catch (Exception e) {
- log.warn("计算服务时长失败,订单ID:{}", order.getcId(), e);
- }
- return 0L;
- }
- @Override
- @Transactional(rollbackFor = Exception.class)
- public void takingOrders(TOrder order) {
- String orderId = order.getcId();
- if (orderId == null || StringUtils.isBlank(orderId)) {
- throw new IllegalArgumentException("订单ID不能为空");
- }
- TOrder orderNew = this.getById(orderId);
- // 【新增】订单状态锁校验 - 检查技师是否可以接单
- log.info("开始校验技师 {} 是否可以接单,订单号:{}", orderNew.getcJsId(), orderNew.getOrderNo());
- orderValidationService.canAcceptOrder(orderNew.getcJsId(), orderNew);
- log.info("技师 {} 接单校验通过,继续接单流程", orderNew.getcJsId());
- // 检查订单对应的技师是否存在
- // updateJs (orderNew);
- TOrder orderParam = new TOrder();
- orderParam.setcId(orderId);
- //设置订单状态:已接单
- orderParam.setnStatus(OrderStatusEnum.RECEIVED_ORDER.getCode());
- orderParam.setAcceptanceTime(LocalDateTime.now());
- this.updateById(orderParam);
- // 已接单消息通知(用户侧)
- orderNotificationService.sendReceivedNotification(orderNew);
- // 已接单消息通知(技师侧)
- orderNotificationService.sendTechnicianReceivedNotification(orderNew);
- }
- /**
- * 更新技师状态
- * @param orderNew
- */
- private void updateJs(TOrder orderNew) {
- TJs js = jsService.getById(orderNew.getcJsId());
- if (js == null) {
- throw new IllegalStateException("无法找到对应的技师");
- }
- if (Objects.equals(js.getnStatus(), JsStatusEnum.JS_SERVICEABLE.getCode())) {
- // 更新技师状态
- js.setnStatus(JsStatusEnum.JS_SERVICE.getCode());
- // 确保js.getnNum()不为null,避免 NullPointerException
- int num = js.getnNum() == null ? 0 : js.getnNum();
- js.setnNum(num + MassageConstants.INTEGER_ONE);
- } else {
- // 更新技师状态
- js.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
- // 确保js.getnNum()不为null,避免 NullPointerException
- int num = js.getnNum() == null ? 0 : js.getnNum();
- js.setnNum(num - MassageConstants.INTEGER_ONE);
- }
- jsService.updateById(js);
- }
- /**
- * 申请取消订单(退单申请)
- *
- * 业务流程:
- * 1. 校验订单状态(仅进行中的订单可申请退单)
- * 2. 创建退单申请记录
- * 3. 更新订单状态为"退单待审核"
- *
- * @param order 订单对象
- * @return Boolean 申请结果
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public Boolean applyCancle(String cId, String cancelReason) {
- log.info("开始处理退单申请,订单ID:{}", cId);
- // 1. 参数校验
- if (StringUtils.isBlank(cId)) {
- throw new ServiceException("订单ID不能为空");
- }
- if (StringUtils.isBlank(cancelReason)) {
- throw new ServiceException("退单原因不能为空");
- }
- // 2. 根据订单ID查询订单信息
- TOrder existingOrder = this.getById(cId);
- if (existingOrder == null) {
- throw new ServiceException("订单不存在");
- }
- // 3. 创建退单申请记录(内部会校验订单状态和其他业务规则)
- String applicationId;
- try {
- applicationId = cancelOrderApplicationService.createApplication(existingOrder.getcId(), cancelReason);
- log.info("退单申请记录创建成功,申请ID:{}", applicationId);
- } catch (ServiceException e) {
- log.error("创建退单申请失败:{}", e.getMessage());
- throw e;
- }
- // 4. 更新订单状态为"退单待审核"
- existingOrder.setnStatus(OrderStatusEnum.CANCEL_APPLICATION_PENDING.getCode());
- this.updateById(existingOrder);
- log.info("退单申请处理完成,订单ID:{},申请ID:{}", existingOrder.getcId(), applicationId);
- return Boolean.TRUE;
- }
- /**
- * 取消退单申请
- * 用户主动取消退单申请,恢复订单状态
- *
- * 业务流程:
- * 1. 参数校验(订单ID不能为空)
- * 2. 查询订单和退单申请记录
- * 3. 校验订单状态必须为"退单待审核"(6)
- * 4. 校验退单审核状态必须为"待审核"(0)
- * 5. 调用退单申请服务取消申请
- * 6. 恢复订单状态到申请前的原始状态
- *
- * @param order 订单对象,需要包含cId(订单ID)
- * @return Boolean 操作结果
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public Boolean cancelApplyCancle(TOrder order) {
- log.info("开始取消退单申请,订单ID:{}", order.getcId());
- // 1. 参数校验
- if (StringUtils.isBlank(order.getcId())) {
- throw new ServiceException("订单ID不能为空");
- }
- // 2. 查询订单信息
- TOrder existingOrder = this.getById(order.getcId());
- if (existingOrder == null) {
- throw new ServiceException("订单不存在");
- }
- // 3. 校验订单状态 - 只有"退单待审核"状态的订单才能取消申请
- Integer currentStatus = existingOrder.getnStatus();
- if (!OrderStatusEnum.CANCEL_APPLICATION_PENDING.getCode().equals(currentStatus)) {
- throw new ServiceException("当前订单状态不允许取消退单申请");
- }
- // 4. 查询退单申请记录
- com.ylx.massage.domain.CancelOrderApplication application = cancelOrderApplicationService.getInfoByOrderId(order.getcId());
- if (application == null) {
- throw new ServiceException("退单申请记录不存在");
- }
- // 5. 校验退单申请状态 - 只有"待审核"的申请才能取消
- if (application.getAuditStatus() != 0) {
- throw new ServiceException("当前退单申请状态不允许取消");
- }
- // 6. 获取订单原始状态(在申请退单时保存的状态)
- Integer originalStatus = application.getOrderStatus();
- if (originalStatus == null) {
- throw new ServiceException("无法获取订单原始状态,取消申请失败");
- }
- // 7. 调用退单申请服务取消申请
- try {
- cancelOrderApplicationService.cancelApplication(order.getcId());
- log.info("退单申请记录取消成功,订单ID:{}", order.getcId());
- } catch (ServiceException e) {
- log.error("取消退单申请失败:{}", e.getMessage());
- throw e;
- }
- // 8. 恢复订单状态到原始状态
- existingOrder.setnStatus(originalStatus);
- boolean updated = this.updateById(existingOrder);
- if (!updated) {
- throw new ServiceException("恢复订单状态失败");
- }
- log.info("取消退单申请处理完成,订单ID:{},恢复到状态:{}", order.getcId(), originalStatus);
- return Boolean.TRUE;
- }
- }
|