|
|
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
|
|
|
import cn.hutool.core.util.ObjectUtil;
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
@@ -32,6 +33,8 @@ import com.ylx.order.domain.dto.*;
|
|
|
import com.ylx.order.domain.vo.OrderDateQueryVo;
|
|
|
import com.ylx.order.domain.vo.OrderDetailVO;
|
|
|
import com.ylx.order.domain.vo.OrderStatusFlowVO;
|
|
|
+import com.ylx.order.domain.vo.merchant.MerchantCancelOrderDTO;
|
|
|
+import com.ylx.order.domain.vo.merchant.OrderCustomerPhoneVO;
|
|
|
import com.ylx.order.domain.vo.merchant.OrderPageVO;
|
|
|
import com.ylx.order.enums.AfterSaleServiceStatusEnum;
|
|
|
import com.ylx.order.enums.OrderStatusEnum;
|
|
|
@@ -48,6 +51,7 @@ import com.ylx.shopingfundsdetail.enums.ShoppingFundsExpenseTypeEnum;
|
|
|
import com.ylx.shopingfundsdetail.service.ShoppingFundsDetailService;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.BeanUtils;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
@@ -114,6 +118,12 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
|
|
|
@Resource
|
|
|
private TGeoFenceService geoFenceService;
|
|
|
|
|
|
+ @Value("${ylx.sendSmsEnabled:false}")
|
|
|
+ private boolean sendSmsEnabled;
|
|
|
+
|
|
|
+ @Value("${ylx.fixedVerifyCode:123456}")
|
|
|
+ private String fixedVerifyCode;
|
|
|
+
|
|
|
@Override
|
|
|
public TOrder addOrder(TOrder order) {
|
|
|
return null;
|
|
|
@@ -1041,6 +1051,241 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
|
|
|
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public OrderCustomerPhoneVO getCustomerPhoneByOrderId(OrderPhoneQueryDTO dto) {
|
|
|
+ // 1. 获取当前登录商户
|
|
|
+ WxLoginUser wxLoginUser = getCurrentWxLoginUser();
|
|
|
+ Long merchantId = Long.parseLong(wxLoginUser.getId());
|
|
|
+ Long orderId = dto.getOrderId();
|
|
|
+
|
|
|
+ // 2. 查询订单:只查归属商户、未删除、手机号字段
|
|
|
+ TOrder order = baseMapper.selectOne(Wrappers.lambdaQuery(TOrder.class)
|
|
|
+ .select(TOrder::getId, TOrder::getMerchantId, TOrder::getContactPhoneNumber)
|
|
|
+ .eq(TOrder::getId, orderId)
|
|
|
+ .eq(TOrder::getMerchantId, merchantId)
|
|
|
+ .eq(TOrder::getIsDelete, 0));
|
|
|
+
|
|
|
+ // 3. 校验订单
|
|
|
+ if (ObjectUtil.isNull(order)) {
|
|
|
+ throw new ServiceException("订单不存在或不属于当前商户");
|
|
|
+ }
|
|
|
+ String phone = order.getContactPhoneNumber();
|
|
|
+ if (StrUtil.isBlank(phone)) {
|
|
|
+ throw new ServiceException("该订单未预留客户联系电话");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 封装返回明文手机号
|
|
|
+ OrderCustomerPhoneVO vo = new OrderCustomerPhoneVO();
|
|
|
+ vo.setContactPhoneNumber(phone);
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void merchantCancelOrderToAfterSale(MerchantCancelOrderDTO dto) {
|
|
|
+ // 1. 获取当前登录商户
|
|
|
+ WxLoginUser loginUser = getCurrentWxLoginUser();
|
|
|
+ Long merchantId = Long.parseLong(loginUser.getId());
|
|
|
+ Long orderId = dto.getOrderId();
|
|
|
+ String cancelReason = dto.getCancelReason();
|
|
|
+
|
|
|
+ // 2. 查询订单基础信息
|
|
|
+ TOrder order = baseMapper.selectOne(Wrappers.lambdaQuery(TOrder.class)
|
|
|
+ .select(TOrder::getId, TOrder::getMerchantId, TOrder::getStatus,
|
|
|
+ TOrder::getUserId, TOrder::getFinalAmount, TOrder::getProjectId)
|
|
|
+ .eq(TOrder::getId, orderId)
|
|
|
+ .eq(TOrder::getMerchantId, merchantId)
|
|
|
+ .eq(TOrder::getIsDelete, NOT_DELETE));
|
|
|
+
|
|
|
+ // 订单不存在/不属于当前商户
|
|
|
+ if (ObjectUtil.isNull(order)) {
|
|
|
+ throw new ServiceException("订单不存在,无法操作");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 状态校验:仅【商户已接单】允许取消转售后
|
|
|
+ Integer statusCode = order.getStatus();
|
|
|
+ OrderStatusEnum orderStatus = OrderStatusEnum.fromCode(statusCode);
|
|
|
+ if (orderStatus == null
|
|
|
+ || (!OrderStatusEnum.PENDING_SERVICE.getCode().equals(statusCode)
|
|
|
+ && !OrderStatusEnum.IN_SERVICE.getCode().equals(statusCode))) {
|
|
|
+ String currentText = OrderStatusEnum.getInfoByCode(statusCode);
|
|
|
+ throw new ServiceException("仅【待服务、服务中】订单可操作,当前订单状态:" + currentText);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 更新订单为售后状态,并发乐观锁校验
|
|
|
+ if (OrderStatusEnum.PENDING_SERVICE.getCode().equals(statusCode)) {
|
|
|
+ // ========== 分支1:待服务 → 退回待派单,无售后、无退款 ==========
|
|
|
+ LambdaUpdateWrapper<TOrder> updateWrapper = Wrappers.lambdaUpdate(TOrder.class)
|
|
|
+ .set(TOrder::getStatus, OrderStatusEnum.PENDING_DISPATCH.getCode())
|
|
|
+ .set(TOrder::getCancelledTime,LocalDateTime.now())
|
|
|
+ .set(TOrder::getCancelledReason, cancelReason)
|
|
|
+ .set(TOrder::getUpdateTime, LocalDateTime.now())
|
|
|
+ .set(TOrder::getUpdateBy,loginUser.getCNickName())
|
|
|
+ .eq(TOrder::getId, orderId)
|
|
|
+ .eq(TOrder::getStatus, statusCode);
|
|
|
+ int row = baseMapper.update(null, updateWrapper);
|
|
|
+ if (row <= 0) {
|
|
|
+ throw new ServiceException("订单状态已变更,请刷新重试");
|
|
|
+ }
|
|
|
+ } else if (OrderStatusEnum.IN_SERVICE.getCode().equals(statusCode)) {
|
|
|
+ // ========== 分支2:服务中 → 转售后、生成售后单、发起退款 ==========
|
|
|
+ // 1. 更新订单信息
|
|
|
+ LambdaUpdateWrapper<TOrder> updateWrapper = Wrappers.lambdaUpdate(TOrder.class)
|
|
|
+ .set(TOrder::getCancelledTime,LocalDateTime.now())
|
|
|
+ .set(TOrder::getCancelledReason, cancelReason)
|
|
|
+ .set(TOrder::getUpdateTime, LocalDateTime.now())
|
|
|
+ .set(TOrder::getUpdateBy,loginUser.getCNickName())
|
|
|
+ .eq(TOrder::getId, orderId)
|
|
|
+ .eq(TOrder::getStatus, statusCode);
|
|
|
+ int row = baseMapper.update(null, updateWrapper);
|
|
|
+ if (row <= 0) {
|
|
|
+ throw new ServiceException("订单状态已变更,请刷新重试");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 创建售后单
|
|
|
+ AfterSalesServiceDTO afterSalesServiceDTO = new AfterSalesServiceDTO();
|
|
|
+ afterSalesServiceDTO.setOrderId(orderId);
|
|
|
+ afterSalesServiceDTO.setReason(cancelReason);
|
|
|
+ this.afterSalesServiceService.applyAfterSale(afterSalesServiceDTO);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void startService(MerchantOrderOperateDTO dto) {
|
|
|
+
|
|
|
+ // 1. 获取当前登录商户
|
|
|
+ WxLoginUser loginUser = getCurrentWxLoginUser();
|
|
|
+ Long merchantId = Long.parseLong(loginUser.getId());
|
|
|
+ Long orderId = dto.getOrderId();
|
|
|
+
|
|
|
+ TOrder order = baseMapper.selectOne(Wrappers.lambdaQuery(TOrder.class)
|
|
|
+ .select(TOrder::getId, TOrder::getMerchantId, TOrder::getStatus,
|
|
|
+ TOrder::getArrivedTime)
|
|
|
+ .eq(TOrder::getId, orderId)
|
|
|
+ .eq(TOrder::getMerchantId, merchantId)
|
|
|
+ .eq(TOrder::getIsDelete, NOT_DELETE));
|
|
|
+ if (ObjectUtil.isNull(order)) {
|
|
|
+ throw new ServiceException("订单不存在或不属于当前商户");
|
|
|
+ }
|
|
|
+
|
|
|
+ Integer status = order.getStatus();
|
|
|
+ if (!OrderStatusEnum.PENDING_SERVICE.getCode().equals(status)) {
|
|
|
+ throw new ServiceException("仅【待服务】订单可开始服务,当前状态:" + OrderStatusEnum.getInfoByCode(status));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 可选强校验:必须先签到到达才能开始服务
|
|
|
+ if (ObjectUtil.isNull(order.getArrivedTime())) {
|
|
|
+ throw new ServiceException("请先完成到达签到,再开始服务");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新状态+服务开始时间
|
|
|
+ LambdaUpdateWrapper<TOrder> update = Wrappers.lambdaUpdate(TOrder.class)
|
|
|
+ .set(TOrder::getStatus, OrderStatusEnum.IN_SERVICE.getCode())
|
|
|
+ .set(TOrder::getStartTime, LocalDateTime.now())
|
|
|
+ .set(TOrder::getUpdateTime, LocalDateTime.now())
|
|
|
+ .eq(TOrder::getId, orderId)
|
|
|
+ .set(TOrder::getUpdateBy,loginUser.getCNickName())
|
|
|
+ .eq(TOrder::getStatus, status);
|
|
|
+ int count = baseMapper.update(null, update);
|
|
|
+ if (count <= 0) {
|
|
|
+ throw new ServiceException("订单状态已变更,请刷新重试");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void finishService(MerchantOrderOperateDTO dto) {
|
|
|
+
|
|
|
+ // 1. 获取当前登录商户
|
|
|
+ WxLoginUser loginUser = getCurrentWxLoginUser();
|
|
|
+ Long merchantId = Long.parseLong(loginUser.getId());
|
|
|
+ Long orderId = dto.getOrderId();
|
|
|
+
|
|
|
+ TOrder order = baseMapper.selectOne(Wrappers.lambdaQuery(TOrder.class)
|
|
|
+ .select(TOrder::getId, TOrder::getMerchantId, TOrder::getStatus,
|
|
|
+ TOrder::getStartTime)
|
|
|
+ .eq(TOrder::getId, orderId)
|
|
|
+ .eq(TOrder::getMerchantId, merchantId)
|
|
|
+ .eq(TOrder::getIsDelete, NOT_DELETE));
|
|
|
+ if (ObjectUtil.isNull(order)) {
|
|
|
+ throw new ServiceException("订单不存在或不属于当前商户");
|
|
|
+ }
|
|
|
+
|
|
|
+ Integer status = order.getStatus();
|
|
|
+ if (!OrderStatusEnum.IN_SERVICE.getCode().equals(status)) {
|
|
|
+ throw new ServiceException("仅【服务中】订单可完成服务,当前状态:" + OrderStatusEnum.getInfoByCode(status));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 必须存在服务开始时间才能结束
|
|
|
+ if (ObjectUtil.isNull(order.getStartTime())) {
|
|
|
+ throw new ServiceException("未开始服务,无法完成");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新状态、结束时间
|
|
|
+ LambdaUpdateWrapper<TOrder> update = Wrappers.lambdaUpdate(TOrder.class)
|
|
|
+ .set(TOrder::getStatus, OrderStatusEnum.COMPLETED.getCode())
|
|
|
+ .set(TOrder::getCompletedTime, LocalDateTime.now())
|
|
|
+ .set(TOrder::getUpdateTime, LocalDateTime.now())
|
|
|
+ .eq(TOrder::getId, orderId)
|
|
|
+ .set(TOrder::getUpdateBy,loginUser.getCNickName())
|
|
|
+ .eq(TOrder::getStatus, status);
|
|
|
+ int count = baseMapper.update(null, update);
|
|
|
+ if (count <= 0) {
|
|
|
+ throw new ServiceException("订单状态已变更,请刷新重试");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void merchantArriveSign(MerchantOrderOperateDTO dto) {
|
|
|
+
|
|
|
+ // 1. 获取当前登录商户
|
|
|
+ WxLoginUser loginUser = getCurrentWxLoginUser();
|
|
|
+ Long merchantId = Long.parseLong(loginUser.getId());
|
|
|
+ Long orderId = dto.getOrderId();
|
|
|
+ String code = dto.getCode();
|
|
|
+ String phone = dto.getPhone();
|
|
|
+
|
|
|
+ // 手机号或者验证码 校验
|
|
|
+ if (ObjectUtil.hasNull(phone, code)) {
|
|
|
+ throw new ServiceException("手机号或者验证码不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!validateVerifyCode(phone, code)) {
|
|
|
+ throw new ServiceException("验证码错误");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询订单
|
|
|
+ TOrder order = baseMapper.selectOne(Wrappers.lambdaQuery(TOrder.class)
|
|
|
+ .select(TOrder::getId, TOrder::getMerchantId, TOrder::getStatus)
|
|
|
+ .eq(TOrder::getId, orderId)
|
|
|
+ .eq(TOrder::getMerchantId, merchantId)
|
|
|
+ .eq(TOrder::getIsDelete, NOT_DELETE));
|
|
|
+ if (ObjectUtil.isNull(order)) {
|
|
|
+ throw new ServiceException("订单不存在或不属于当前商户");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 状态仅允许待服务
|
|
|
+ Integer status = order.getStatus();
|
|
|
+ if (!OrderStatusEnum.PENDING_SERVICE.getCode().equals(status)) {
|
|
|
+ throw new ServiceException("仅【待服务】订单可签到到达,当前状态:" + OrderStatusEnum.getInfoByCode(status));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新到达信息
|
|
|
+ LambdaUpdateWrapper<TOrder> update = Wrappers.lambdaUpdate(TOrder.class)
|
|
|
+ .set(TOrder::getArrivedTime, LocalDateTime.now())
|
|
|
+ .set(TOrder::getUpdateTime, LocalDateTime.now())
|
|
|
+ .set(TOrder::getUpdateBy,loginUser.getCNickName())
|
|
|
+ .eq(TOrder::getId, orderId)
|
|
|
+ .eq(TOrder::getStatus, status);
|
|
|
+ int count = baseMapper.update(null, update);
|
|
|
+ if (count <= 0) {
|
|
|
+ throw new ServiceException("订单状态已变更,请刷新重试");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private void fillCurrentAfterSaleInfo(IAfterSaleDisplay vo, Long orderId) {
|
|
|
LambdaQueryWrapper<AfterSalesService> wrapper = new LambdaQueryWrapper<>();
|
|
|
wrapper.eq(AfterSalesService::getOrderId, orderId)
|
|
|
@@ -1164,4 +1409,23 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
|
|
|
return order;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 校验验证码
|
|
|
+ *
|
|
|
+ * @param phone 手机号
|
|
|
+ * @param code 用户输入的验证码
|
|
|
+ * @return boolean 校验是否通过
|
|
|
+ */
|
|
|
+ private boolean validateVerifyCode(String phone, String code) {
|
|
|
+ if (sendSmsEnabled) {
|
|
|
+ // TODO: 从Redis或其他存储中获取真实发送的验证码进行校验
|
|
|
+ // String realCode = redisCache.getCacheObject("sms:bindPhone:" + phone);
|
|
|
+ // return code.equals(realCode);
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ // 使用写死的验证码进行校验
|
|
|
+ return fixedVerifyCode.equals(code);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
}
|