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.LambdaUpdateWrapper; 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.constant.MassageConstants; import com.ylx.common.core.domain.R; import com.ylx.common.exception.ServiceException; import com.ylx.massage.domain.*; import com.ylx.massage.domain.vo.CouponReceiveVo; 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.DateTimeUtils; import com.ylx.massage.utils.MassageUtil; import com.ylx.massage.utils.OrderNumberGenerator; import com.ylx.massage.utils.WeChatUtil; 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.LocalDateTime; import java.util.*; import java.util.stream.Collectors; /** * 订单表 服务实现类 */ @Service @Slf4j public class TOrderServiceImpl extends ServiceImpl implements TOrderService { @Resource private TOrderMapper orderMapper; @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; public Boolean isFree(String jsId, BigDecimal distance) { TJs js = jsService.getById(jsId); 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 */ @Override @Transactional(rollbackFor = Exception.class) public TOrder addOrder(TOrder order) { if (StringUtils.isBlank(order.getcJsId())) { throw new ServiceException("请选择技师"); } if (order.getcGoods().isEmpty()) { throw new ServiceException("请选择项目"); } //计算车费 if (order.getDistance() != null) { //判断是否可以免车费 if (!this.isFree(order.getcJsId(), order.getDistance())) { BigDecimal bigDecimal = massageUtil.calculateTaxiFare(order.getDistance()); order.setFare(bigDecimal.setScale(MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP)); } } //优惠卷减免 // List coupons = couponReceiveService.getByOpenId(order.getcOpenId()); // BigDecimal preferential = this.setCoupon(coupons); // order.setPreferential(preferential); //订单价格 List list = JSONObject.parseArray(order.getcGoods().toJSONString(), TXiangmu.class); BigDecimal sum = list.stream().map(TXiangmu::getSum).reduce(BigDecimal.ZERO, BigDecimal::add); order.setdTotalMoney(sum); //总价 = 订单 + 车费 - 优惠 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())); } //获取用户默认地址 TAddress address = addressService.getByOpenId(order.getcOpenId()); if (address == null) { throw new ServiceException("请先添加地址"); } 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.setOrderNo(generator.generateNextOrderNumber(OrderNumberGenerator.KEY_PREFIX_ORDER)); 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 coupons) { //过滤过期的优惠券 coupons = coupons.stream().filter(coupon -> coupon.getExpirationTime().after(new Date())).collect(Collectors.toList()); //无门槛优惠券 List 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 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); } /** * 支付订单 * * @param order */ @Override public R payOrder(TOrder order) throws Exception { // 根据orderid查询订单信息 TOrder orderNew = getById(order.getcId()); if (!orderNew.getnStatus().equals(OrderStatusEnum.WAIT_PAY.getCode())) { throw new ServiceException("该订单已经支付或者超时被取消"); } if (StringUtils.isBlank(orderNew.getParentNo())) { TJs js = jsService.getById(orderNew.getcJsId()); 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()); CouponReceive couponReceive = couponReceiveService.getById(order.getCouponReceiveId()); Coupon coupon = couponService.getById(couponReceive.getCouponId()); //无门槛优惠券 orderNew.setPreferential(coupon.getDiscountValue()); //todo 其他优惠卷.... orderNew.setTotalPrice(orderNew.getTotalPrice().subtract(coupon.getDiscountValue())); } //判断支付方式 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()); return resp; } // 从对应账户扣款 TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId()); if (null == user) { throw new ServiceException("用户不存在"); } if (user.getdBalance().compareTo(orderNew.getTotalPrice()) < MassageConstants.INTEGER_ZERO) { throw new ServiceException("账户金额不够请充值"); } else { orderPayManage(user, orderNew); return R.ok(); } } 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() + " " + order.getAddress())); TJs js = jsService.getById(order.getcJsId()); weChatUtil.notification(js.getcOpenId(), param); } @Transactional(rollbackFor = Exception.class) public void orderPayManage(TWxUser user, TOrder orderNew) { //删除优惠卷 // 更新用户金额 及下单此时 TWxUser paramUser = new TWxUser(); paramUser.setcOpenid(user.getcOpenid()); if (!orderNew.getPayType().equals(MassageConstants.INTEGER_ONE)) { paramUser.setdBalance(user.getdBalance().subtract(orderNew.getTotalPrice())); } paramUser.setdMoney(user.getdMoney().add(orderNew.getTotalPrice())); paramUser.setnNum(user.getnNum() + MassageConstants.INTEGER_ZERO); 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_ONE)) { tConsumptionLog.setBillType(BillTypeEnum.BALANCE_PAYMENT.getCode()); tConsumptionLog.setNote("余额支付"); } else { tConsumptionLog.setBillType(BillTypeEnum.WX_PAY.getCode()); tConsumptionLog.setNote("微信支付"); } consumptionLogService.save(tConsumptionLog); // 更新项目数据 JSONArray objects = orderNew.getcGoods(); objects.forEach(item -> { UpdateWrapper 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()); //加钟的订单支付完直接服务中 if (StringUtils.isNotBlank(orderNew.getParentNo())) { orderParam.setnStatus(OrderStatusEnum.SERVICE.getCode()); } // orderParam.setnStatus(OrderStatusEnum.SERVICE.getCode()); //更新及技师状态 updateJs(orderNew); updateById(orderParam); this.newOrderNotification(orderNew); } /** * 拒绝订单 * * @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()); if (orderNew.getPayType() == 2) { // 金额归还对应账户 paramUser.setdBalance(user.getdBalance().add(orderNew.getTotalPrice())); } 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); // 微信退款原路返回 rechargeService.refund(refundVoucher.getRefundNo(), null, orderNew.getOrderNo(), orderNew.getTotalPrice()); } 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 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()); updateJs(orderNew); return updateById(orderParam); } /** * 确认服务完成 * * @param order * @return */ @Override @Transactional(rollbackFor = Exception.class) public Boolean confirm(TOrder order) { // 获取订单信息 TOrder orderNew = getById(order); if (!orderNew.getnStatus().equals(OrderStatusEnum.SERVICE.getCode())) { throw new ServiceException("订单状态不是服务中"); } // 更新技师信息 TJs jsParam = new TJs(); jsParam.setId(orderNew.getcJsId()); jsParam.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode()); // 更新技师状态 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.getById(jsUser.getcUpUser()); extracted(orderNew, jsUp); //二级 if (jsUp.getcUpUser() != null) { TWxUser jsUpTwo = wxUserService.getById(jsUp.getcUpUser()); extracted(orderNew, jsUpTwo); //三级 if (jsUpTwo.getcUpUser() != null) { TWxUser jsUpThree = wxUserService.getById(jsUpTwo.getcUpUser()); extracted(orderNew, jsUpThree); } } } // 更新订单 orderNew.setnStatus(OrderStatusEnum.WAIT_EVALUATE.getCode()); return updateById(orderNew); } private void extracted(TOrder orderNew, TWxUser jsUp) { 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)); 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 order * @return */ @Override public Boolean cancle(TOrder order) { // 获取订单信息 // 根据orderid查询订单信息 TOrder orderNew = getById(order.getcId()); //待接单 // if (Objects.equals(orderNew.getnStatus(), OrderStatusEnum.WAIT_JD.getCode())) { // TWxUser user = wxUserService.getById(orderNew.getcOpenId()); // // 更新用户金额 及下单此时 // TWxUser paramUser = new TWxUser(); // paramUser.setcOpenid(user.getcOpenid()); // // 金额归还对应账户 // paramUser.setdBalance(user.getdBalance().add(orderNew.getdTotalMoney())); // // 消费金额对应减少 // paramUser.setdMoney(user.getdMoney().subtract(orderNew.getdTotalMoney())); // // 下单次数减一 // paramUser.setnNum(user.getnNum() - MassageConstants.INTEGER_ONE); // wxUserService.updateById(paramUser); // // // 更新项目数据 // JSONArray objects = orderNew.getcGoods(); // objects.forEach(item -> { // UpdateWrapper 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()); // return updateById(orderParam); // // } else if (Objects.equals(orderNew.getnStatus(), OrderStatusEnum.WAIT_PAY.getCode())) { //待付款 TOrder orderParam = new TOrder(); orderParam.setcId(orderNew.getcId()); orderParam.setnStatus(OrderStatusEnum.CANCEL.getCode()); return updateById(orderParam); } else { return false; } } @Override public TOrder getByNo(String orderNo) { LambdaQueryWrapper objectLambdaQueryWrapper = new LambdaQueryWrapper<>(); return this.getOne(objectLambdaQueryWrapper.eq(TOrder::getOrderNo, orderNo)); } private TOrder gettOrder(TOrder order) { LambdaUpdateWrapper objectLambdaUpdateWrapper = new LambdaUpdateWrapper<>(); objectLambdaUpdateWrapper.eq(TOrder::getOrderNo, order.getOrderNo()); return this.getOne(objectLambdaUpdateWrapper); } @Override public Page getAll(Page page, TOrder param) { Page orderPage = orderMapper.getAll(page, param); if (orderPage != null && CollectionUtil.isNotEmpty(orderPage.getRecords())) { ArrayList orders = Lists.newArrayList(); orderPage.getRecords().forEach(order -> { order.setStatusName(OrderStatusEnum.getDescByCode(order.getnStatus())); if (StringUtils.isEmpty(order.getcTime())) { order.setRemainingTime(0L); } if (StringUtils.isNotBlank(order.getcTime()) && DateTimeUtils.dateStringToStamp(order.getcTime()) > DateTimeUtils.dateToStamp(new Date())) { order.setRemainingTime((DateTimeUtils.dateStringToStamp(order.getcTime()) - DateTimeUtils.dateToStamp(new Date())) / 1000); } if (StringUtils.isNotBlank(order.getcTime()) && DateTimeUtils.dateStringToStamp(order.getcTime()) < DateTimeUtils.dateToStamp(new Date())) { order.setRemainingTime(0L); } orders.add(order); }); orderPage.setRecords(orders); } return orderPage; } @Override @Transactional(rollbackFor = Exception.class) public void takingOrders(TOrder order) { if (order == null || StringUtils.isBlank(order.getcId())) { throw new IllegalArgumentException("订单对象不能为空"); } TOrder orderNew = this.getById(order); // 检查订单对应的技师是否存在 // updateJs (orderNew); // 更新订单状态 TOrder orderParam = new TOrder(); orderParam.setcId(order.getcId()); orderParam.setnStatus(OrderStatusEnum.RECEIVED_ORDER.getCode()); this.updateById(orderParam); } 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); } }