Ver Fonte

开发商户端订单相关接口

wangzhijun há 1 semana atrás
pai
commit
1e189963e5

+ 4 - 0
nightFragrance-common/src/main/java/com/ylx/common/utils/DateUtils.java

@@ -42,6 +42,10 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
         return new Date();
     }
 
+    public static LocalDateTime getNowLocalDateTime() {
+        return LocalDateTime.now();
+    }
+
     /**
      * 获取当前日期, 默认格式为yyyy-MM-dd
      *

+ 37 - 0
nightFragrance-massage/src/main/java/com/ylx/order/controller/MerchantOrderController.java

@@ -1,11 +1,24 @@
 package com.ylx.order.controller;
 
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ylx.common.core.domain.R;
+import com.ylx.order.domain.dto.OrderAcceptDTO;
+import com.ylx.order.domain.dto.OrderDateQueryDTO;
+import com.ylx.order.domain.dto.OrderRejectDTO;
+import com.ylx.order.domain.vo.merchant.OrderPageVO;
+import com.ylx.order.service.TOrderService;
 import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.annotation.Resource;
+
 @RestController
 @RequestMapping("/merchant/order")
 @Api(tags = {"商户端订单模块"})
@@ -13,4 +26,28 @@ import org.springframework.web.bind.annotation.RestController;
 @PreAuthorize("@merchantAuth.isMerchant()")
 public class MerchantOrderController {
 
+    @Resource
+    private TOrderService orderService;
+
+    @ApiOperation("商户端查询订单分页列表")
+    @PostMapping("/queryByDate")
+    public R<Page<OrderPageVO>> queryOrderByDate(@Validated @RequestBody OrderDateQueryDTO dto) {
+        Page<OrderPageVO> page = orderService.queryMerchantOrderList(dto);
+        return R.ok(page);
+    }
+
+    @ApiOperation("商户端拒绝接单接口")
+    @PostMapping("/reject")
+    public R<Void> rejectOrder(@Validated @RequestBody OrderRejectDTO dto) {
+        this.orderService.rejectOrder(dto);
+        return R.ok();
+    }
+
+    @ApiOperation("商户立即接单接口")
+    @PostMapping("/accept")
+    public R<Void> acceptOrder(@Validated @RequestBody OrderAcceptDTO dto) {
+        this.orderService.acceptOrder(dto);
+        return R.ok();
+    }
+
 }

+ 17 - 0
nightFragrance-massage/src/main/java/com/ylx/order/domain/dto/OrderAcceptDTO.java

@@ -0,0 +1,17 @@
+package com.ylx.order.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@ApiModel("商户端接单DTO")
+public class OrderAcceptDTO {
+
+    @NotNull(message = "订单ID不能为空")
+    @ApiModelProperty("主键ID")
+    private Long id;
+
+}

+ 21 - 10
nightFragrance-massage/src/main/java/com/ylx/order/domain/dto/OrderDateQueryDTO.java

@@ -1,10 +1,10 @@
 package com.ylx.order.domain.dto;
 
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import org.springframework.format.annotation.DateTimeFormat;
 
-import javax.validation.constraints.Min;
 import java.time.LocalDate;
 
 /**
@@ -15,14 +15,7 @@ import java.time.LocalDate;
  * @date 2026/6/8 9:26
  */
 @Data
-public class OrderDateQueryDTO {
-    @ApiModelProperty("页码,默认1")
-    @Min(value = 1, message = "页码最小为1")
-    private Integer pageNum = 1;
-
-    @ApiModelProperty("每页条数,默认10")
-    @Min(value = 1, message = "每页条数最小为1")
-    private Integer pageSize = 10;
+public class OrderDateQueryDTO extends Page<OrderDateQueryDTO> {
     /**
      * 起始日期(可选),例如 2025-11-11
      */
@@ -31,7 +24,7 @@ public class OrderDateQueryDTO {
     private LocalDate startDate;
 
     /**
-     *  /终止日期(可选)
+     * /终止日期(可选)
      */
     @DateTimeFormat(pattern = "yyyy-MM-dd")
     @ApiModelProperty("终止日期(可选)")
@@ -47,4 +40,22 @@ public class OrderDateQueryDTO {
      */
     @ApiModelProperty("商户昵称")
     private String merchantNickName;
+
+    @ApiModelProperty("客户电话号码")
+    private String contactPhoneNumber;
+
+    @ApiModelProperty("是否免车费")
+    private Boolean isTrafficFree;
+
+    @ApiModelProperty("订单状态")
+    private Integer status;
+
+    @ApiModelProperty("商户ID")
+    private Long merchantId;
+
+    @ApiModelProperty("经度")
+    private Double longitude;
+
+    @ApiModelProperty("纬度")
+    private Double latitude;
 }

+ 20 - 0
nightFragrance-massage/src/main/java/com/ylx/order/domain/dto/OrderRejectDTO.java

@@ -0,0 +1,20 @@
+package com.ylx.order.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@ApiModel("商户端拒绝接单DTO")
+public class OrderRejectDTO {
+
+    @NotNull(message = "订单ID不能为空")
+    @ApiModelProperty("主键ID")
+    private Long id;
+
+    @ApiModelProperty("拒绝接单原因")
+    private String rejectReason;
+
+}

+ 71 - 0
nightFragrance-massage/src/main/java/com/ylx/order/domain/vo/merchant/OrderPageVO.java

@@ -0,0 +1,71 @@
+package com.ylx.order.domain.vo.merchant;
+
+import com.ylx.order.service.IAfterSaleDisplay;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel("商户端订单分页VO")
+public class OrderPageVO<T> implements IAfterSaleDisplay {
+
+    @ApiModelProperty("订单ID")
+    private Long id;
+
+    @ApiModelProperty("售后单ID")
+    private Long afterSalesServiceId;
+
+    @ApiModelProperty("售后状态文案")
+    private String afterSalesServiceStatus;
+
+    @ApiModelProperty("项目名称")
+    private String projectName;
+
+    @ApiModelProperty("项目封面图")
+    private String projectCover;
+
+    @ApiModelProperty("")
+    private Integer projectDuration;
+
+    @ApiModelProperty("项目标价/售价")
+    private BigDecimal basePrice;
+
+    @ApiModelProperty("交通费")
+    private BigDecimal trafficFee;
+
+    @ApiModelProperty("最终应付/实付金额")
+    private BigDecimal finalAmount;
+
+    @ApiModelProperty("预约开始时间")
+    private LocalDateTime appointmentStartTime;
+
+    @ApiModelProperty("预约结束时间")
+    private LocalDateTime appointmentEndTime;
+
+    @ApiModelProperty("联系人姓名")
+    private String contactPersonName;
+
+    @ApiModelProperty("联系人电话号码")
+    private String contactPhoneNumber;
+
+    @ApiModelProperty("详细服务地址")
+    private String contactAddressInfo;
+
+    @ApiModelProperty("距离")
+    private Double distanceKm;
+
+    @ApiModelProperty("用户下单时纬度")
+    private BigDecimal userLatitude;
+
+    @ApiModelProperty("用户下单时经度")
+    private BigDecimal userLongitude;
+
+    @ApiModelProperty("风险提示")
+    private Boolean isHighRiskArea;
+
+    @ApiModelProperty("订单状态")
+    private Integer status;
+}

+ 4 - 0
nightFragrance-massage/src/main/java/com/ylx/order/mapper/TOrderMapper.java

@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ylx.massage.domain.vo.HomeBlock;
 import com.ylx.order.domain.TOrder;
+import com.ylx.order.domain.dto.OrderDateQueryDTO;
+import com.ylx.order.domain.vo.merchant.OrderPageVO;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
@@ -46,4 +48,6 @@ public interface TOrderMapper extends BaseMapper<TOrder> {
             @Param("dayStart") LocalDateTime dayStart,
             @Param("dayEnd") LocalDateTime dayEnd
     );
+
+    Page<OrderPageVO> queryMerchantOrderList(Page<OrderPageVO> page, @Param("dto") OrderDateQueryDTO dto);
 }

+ 7 - 0
nightFragrance-massage/src/main/java/com/ylx/order/service/TOrderService.java

@@ -12,6 +12,7 @@ import com.ylx.massage.domain.vo.TechnicianAvailabilityVo;
 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.merchant.OrderPageVO;
 
 import java.math.BigDecimal;
 import java.util.Date;
@@ -191,4 +192,10 @@ public interface TOrderService extends IService<TOrder> {
     int cancelOrder(OrderCancleDTO dto);
 
     Boolean bookingCheck(BookingCheckDTO dto);
+
+    Page<OrderPageVO> queryMerchantOrderList(OrderDateQueryDTO dto);
+
+    void rejectOrder(OrderRejectDTO dto);
+
+    void acceptOrder(OrderAcceptDTO dto);
 }

+ 131 - 7
nightFragrance-massage/src/main/java/com/ylx/order/service/impl/TOrderServiceImpl.java

@@ -41,6 +41,7 @@ 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.OrderPageVO;
 import com.ylx.order.enums.AfterSaleServiceStatusEnum;
 import com.ylx.order.enums.OrderStatusEnum;
 import com.ylx.order.enums.PaymentMethodEnum;
@@ -118,6 +119,8 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
     @Resource
     private IMessageService messageService;
 
+    private static final int NOT_DELETE = 0;
+
     @Override
     public TOrder addOrder(TOrder order) {
         return null;
@@ -254,10 +257,7 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
         Map<String, Object> map = new HashMap<>();
 
         // 1. 获取并校验当前用户
-        WxLoginUser wxLoginUser = SecurityUtils.getWxLoginUser();
-        if (ObjectUtil.isNull(wxLoginUser)) {
-            throw new ServiceException("用户未登录");
-        }
+        WxLoginUser wxLoginUser = getCurrentWxLoginUser();
         Long userId = Long.parseLong(wxLoginUser.getId());
         String openId = wxLoginUser.getCOpenid();
 
@@ -348,7 +348,7 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
     @Override
     public Page<OrderDateQueryVo> queryOrderList(OrderDateQueryDTO dto) {
         // 1. 构造分页对象
-        Page<TOrder> page = new Page<>(dto.getPageNum(), dto.getPageSize());
+        Page<TOrder> page = new Page<>(dto.getCurrent(), dto.getSize());
 
         // 2. 构造查询条件
         LambdaQueryWrapper<TOrder> wrapper = new LambdaQueryWrapper<>();
@@ -888,6 +888,97 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
         return true;
     }
 
+    @Override
+    public Page<OrderPageVO> queryMerchantOrderList(OrderDateQueryDTO dto) {
+        // 1. 构造分页对象
+        Page<OrderPageVO> page = new Page<>(dto.getCurrent(), dto.getSize());
+
+        // 2.获取当前登录用户
+        WxLoginUser wxLoginUser = getCurrentWxLoginUser();
+        Long userId = Long.parseLong(wxLoginUser.getId());
+        dto.setMerchantId(userId);
+
+        // 3. 获取当前用户的默认地址
+        List<TAddress> merchantAddressList = this.addressService.list(new LambdaQueryWrapper<TAddress>()
+                .eq(TAddress::getMerchantId, userId)
+                .eq(TAddress::getUserType, 2)
+                .eq(TAddress::getIsDefault, 1)
+                .eq(TAddress::getIsDelete, NOT_DELETE));
+        if (CollUtil.isEmpty(merchantAddressList)) {
+            throw new ServiceException("商户地址不存在");
+        }
+
+        TAddress address = CollUtil.getFirst(merchantAddressList);
+        dto.setLongitude(address.getLongitude());
+        dto.setLatitude(address.getLatitude());
+
+        page = this.baseMapper.queryMerchantOrderList(page, dto);
+
+        if (ObjectUtil.isNotNull(page) && CollUtil.isNotEmpty(page.getRecords())) {
+            List<OrderPageVO> records = page.getRecords();
+            for (OrderPageVO record : records) {
+                this.fillCurrentAfterSaleInfo(record, record.getId());
+            }
+        }
+
+        return page;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void rejectOrder(OrderRejectDTO dto) {
+
+        // 1. 获取并校验当前用户
+        WxLoginUser wxLoginUser = getCurrentWxLoginUser();
+        Long userId = Long.parseLong(wxLoginUser.getId());
+
+        // 2. 校验订单状态
+        Long orderId = dto.getId();
+        TOrder order = validateAndGetOrder(dto.getId(), userId);
+
+        if (ObjectUtil.equals(order.getStatus(), OrderStatusEnum.REJECTED.getCode())) {
+            throw new ServiceException("无需重复拒绝接单");
+        }
+
+        // 3. 修改订单状态
+        TOrder updateOrder = new TOrder();
+        updateOrder.setId(orderId);
+        updateOrder.setStatus(OrderStatusEnum.REJECTED.getCode());
+        updateOrder.setRejectedTime(DateUtils.getNowLocalDateTime());
+        updateOrder.setRejectedReason(dto.getRejectReason());
+        updateOrder.setUpdateBy(wxLoginUser.getCNickName());
+        updateOrder.setUpdateTime(DateUtils.getNowDate());
+        this.baseMapper.updateById(updateOrder);
+
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void acceptOrder(OrderAcceptDTO dto) {
+
+        // 1. 获取并校验当前用户
+        WxLoginUser wxLoginUser = getCurrentWxLoginUser();
+        Long userId = Long.parseLong(wxLoginUser.getId());
+
+        // 2. 校验订单状态
+        Long orderId = dto.getId();
+        TOrder order = validateAndGetOrder(dto.getId(), userId);
+
+        if (ObjectUtil.equals(order.getStatus(), OrderStatusEnum.PENDING_SERVICE.getCode())) {
+            throw new ServiceException("无需重复接单");
+        }
+
+        // 3. 修改订单状态
+        TOrder updateOrder = new TOrder();
+        updateOrder.setId(orderId);
+        updateOrder.setStatus(OrderStatusEnum.PENDING_SERVICE.getCode());
+        updateOrder.setConfirmedTime(DateUtils.getNowLocalDateTime());
+        updateOrder.setUpdateBy(wxLoginUser.getCNickName());
+        updateOrder.setUpdateTime(DateUtils.getNowDate());
+        this.baseMapper.updateById(updateOrder);
+
+    }
+
     private void fillCurrentAfterSaleInfo(IAfterSaleDisplay vo, Long orderId) {
         LambdaQueryWrapper<AfterSalesService> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(AfterSalesService::getOrderId, orderId)
@@ -951,8 +1042,8 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
      * 计算订单分佣金额
      * 核心财务原则:先算出其中一方收益,另一方用总金额相减,避免精度丢失导致 1 分钱误差
      *
-     * @param order           订单对象
-     * @param finalAmount     最终实付金额
+     * @param order              订单对象
+     * @param finalAmount        最终实付金额
      * @param merchantShareRatio 商户分佣比例(%)
      */
     private void calculateOrderIncome(TOrder order, BigDecimal finalAmount, BigDecimal merchantShareRatio) {
@@ -978,4 +1069,37 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
         order.setPlatformIncome(platformIncome);
     }
 
+
+    private WxLoginUser getCurrentWxLoginUser() {
+        WxLoginUser loginUser = SecurityUtils.getWxLoginUser();
+        if (ObjectUtil.isNull(loginUser)) {
+            throw new ServiceException("用户未登录或登录已过期");
+        }
+        return loginUser;
+    }
+
+    /**
+     * 抽取公共校验逻辑:校验订单是否存在、是否被删除、是否属于当前商户
+     */
+    private TOrder validateAndGetOrder(Long orderId, Long merchantId) {
+        if (ObjectUtil.isNull(orderId)) {
+            throw new IllegalArgumentException("订单ID不能为空");
+        }
+
+        TOrder order = this.baseMapper.selectById(orderId);
+        if (ObjectUtil.isNull(order)) {
+            throw new ServiceException("订单不存在");
+        }
+
+        if (ObjectUtil.notEqual(order.getIsDelete(), NOT_DELETE)) {
+            throw new ServiceException("订单已被删除");
+        }
+
+        if (ObjectUtil.notEqual(order.getMerchantId(), merchantId)) {
+            throw new ServiceException("您无权操作此订单");
+        }
+
+        return order;
+    }
+
 }

+ 64 - 0
nightFragrance-massage/src/main/resources/mapper/order/TOrderMapper.xml

@@ -306,4 +306,68 @@
           AND appointment_start_time >= #{dayStart}
           AND appointment_start_time &lt;= #{dayEnd}
     </select>
+
+    <select id="queryMerchantOrderList" resultType="com.ylx.order.domain.vo.merchant.OrderPageVO">
+        SELECT
+        o.id,
+        p.project_name AS projectName,
+        p.cover_url AS projectCover,
+        p.duration AS projectDuration,
+        p.base_price AS basePrice,
+        o.traffic_fee AS trafficFee,
+        o.final_amount AS finalAmount,
+        o.appointment_start_time AS appointmentStartTime,
+        o.appointment_end_time AS appointmentEndTime,
+        o.contact_person_name AS contactPersonName,
+        o.contact_phone_number AS contactPhoneNumber,
+        o.service_address_detail AS contactAddressInfo,
+        o.user_latitude AS userLatitude,
+        o.user_longitude AS userLongitude,
+        o.status,
+        -- 计算距离 (如果需要,根据你的数据库类型调整,这里是 MySQL 示例)
+        ST_Distance_Sphere(point(o.user_longitude, o.user_latitude), point(dto.longitude, dto.latitude)) AS distanceKm
+        FROM t_order o
+        LEFT JOIN t_project p ON o.project_id = p.id
+        <where>
+            o.is_delete = 0
+            <!-- 商户ID过滤 -->
+            AND o.merchant_id = #{dto.merchantId}
+
+            <!-- 日期范围过滤 -->
+            <if test="dto.startDate != null">
+                AND o.create_time &gt;= #{dto.startDate}
+            </if>
+            <if test="dto.endDate != null">
+                AND o.create_time &lt;= #{dto.endDate}
+            </if>
+
+            <!-- 项目名称模糊搜索 -->
+            <if test="dto.projectName != null and dto.projectName != ''">
+                AND p.project_name LIKE CONCAT('%', #{dto.projectName}, '%')
+            </if>
+
+            <!-- 联系人电话号码模糊搜索 -->
+            <if test="dto.contactPhoneNumber != null and dto.contactPhoneNumber != ''">
+                AND p.contact_phone_number LIKE CONCAT('%', #{dto.contactPhoneNumber}, '%')
+            </if>
+
+            <!-- 状态过滤 -->
+            <if test="dto.status != null">
+                AND o.status = #{dto.status}
+            </if>
+
+            <if test="dto.isTrafficFree != null">
+                <choose>
+                    <when test="dto.isTrafficFree == 0">
+                        AND traffic_fee = 0
+                    </when>
+                    <when test="dto.isTrafficFree == 1">
+                        AND traffic_fee > 0
+                    </when>
+                </choose>
+            </if>
+        </where>
+        ORDER BY o.create_time DESC
+    </select>
+
 </mapper>