3 次代碼提交 fa845a4509 ... 7f71909d03

作者 SHA1 備註 提交日期
  wangzhijun 7f71909d03 订单提交,代码完善 2 天之前
  wangzhijun a763e2c445 添加ShoppingFundsExpenseTypeEnum枚举类,并调整代码 2 天之前
  wangzhijun 27080283b7 提交优惠券想过代码,用于优惠券核销和退回 2 天之前

+ 2 - 3
nightFragrance-massage/src/main/java/com/ylx/giftCard/service/impl/GiftCardOrderServiceImpl.java

@@ -18,6 +18,7 @@ import com.ylx.massage.domain.TWxUser;
 import com.ylx.massage.service.TJsService;
 import com.ylx.massage.service.TWxUserService;
 import com.ylx.shopingfundsdetail.domain.vo.ShoppingFundsDetailAddDto;
+import com.ylx.shopingfundsdetail.enums.ShoppingFundsExpenseTypeEnum;
 import com.ylx.shopingfundsdetail.service.ShoppingFundsDetailService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
@@ -37,8 +38,6 @@ public class GiftCardOrderServiceImpl extends ServiceImpl<GiftCardOrderMapper, G
     private TWxUserService wxUserService;
     @Resource
     private ShoppingFundsDetailService shoppingFundsDetailService;
-    // 充值
-    public static final int EXPENSE_TYPE_RECHARGE = 0;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -133,7 +132,7 @@ public class GiftCardOrderServiceImpl extends ServiceImpl<GiftCardOrderMapper, G
         dto.setUserId(wxUser.getId());
         dto.setAmount(payAmount);
         dto.setOrderNo(result.getOutTradeNo());
-        dto.setExpenseType(EXPENSE_TYPE_RECHARGE);
+        dto.setExpenseType(ShoppingFundsExpenseTypeEnum.RECHARGE.getCode());
         dto.setBalance(newBalance);
         dto.setGiftCardId(cardOrder.getGiftCardId());
         shoppingFundsDetailService.addShoppingFundsDetail(dto);

+ 25 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/mapper/CouponReceiveMapper.java

@@ -1,11 +1,14 @@
 package com.ylx.massage.mapper;
 
+import java.time.LocalDateTime;
 import java.util.List;
+import java.util.Map;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.ylx.massage.domain.vo.CouponReceiveVo;
 import org.apache.ibatis.annotations.Param;
 import com.ylx.massage.domain.CouponReceive;
+import org.apache.ibatis.annotations.Select;
 
 /**
  * 优惠券领取表(CouponReceive)表数据库访问层
@@ -34,5 +37,27 @@ public interface CouponReceiveMapper extends BaseMapper<CouponReceive> {
 
 
     List<CouponReceiveVo> getByOpenId(String openid);
+
+    Map<String, Object> selectCouponDetailForCalc(@Param("couponId") String couponId,
+                                                  @Param("openId") String openId);
+
+    // 乐观锁核销
+    int useCouponOptimisticLock(@Param("couponId") String couponId,
+                                @Param("openId") String openId,
+                                @Param("orderId") Long orderId,
+                                @Param("orderType") Integer orderType,
+                                @Param("useTime") LocalDateTime useTime);
+
+    // 使用数量 +1
+    int incrementUsedNum(@Param("couponId") String couponId);
+
+    // 乐观锁退还
+    int returnCouponOptimisticLock(@Param("couponId") String couponId,
+                                   @Param("openId") String openId,
+                                   @Param("orderId") Long orderId);
+
+    // 使用数量 -1
+    int decrementUsedNum(@Param("couponId") String couponId);
+
 }
 

+ 11 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/CouponReceiveService.java

@@ -8,7 +8,9 @@ import com.ylx.massage.domain.vo.ClaimCouponRequestVO;
 import com.ylx.massage.domain.vo.CouponReceiveVo;
 import com.ylx.massage.domain.vo.CouponReceivesVO;
 
+import java.time.LocalDateTime;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 优惠券领取表(CouponReceive)表服务接口
@@ -48,5 +50,14 @@ public interface CouponReceiveService extends IService<CouponReceive> {
     List<Coupon> couponWindows(CouponReceive couponReceive);
 
 
+    Map<String, Object> selectCouponDetailForCalc(String couponId, String openId);
+
+    int useCouponOptimisticLock(String couponId, String openId, Long orderId, Integer orderType, LocalDateTime now);
+
+    int incrementUsedNum(String couponId);
+
+    int returnCouponOptimisticLock(String couponId, String openId, Long orderId);
+
+    void decrementUsedNum(String couponId);
 }
 

+ 8 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/CouponService.java

@@ -4,6 +4,8 @@ package com.ylx.massage.service;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.ylx.massage.domain.Coupon;
 
+import java.math.BigDecimal;
+
 /**
  * 优惠券的规则信息(Coupon)表服务接口
  *
@@ -12,5 +14,11 @@ import com.ylx.massage.domain.Coupon;
  */
 public interface CouponService extends IService<Coupon> {
 
+    BigDecimal calculateDiscountAmount(String couponId, String openId,BigDecimal orderAmount);
+
+    void useCoupon(String couponId, String openId, Long orderId, Integer orderType);
+
+    void returnCoupon(String couponId, String openId, Long orderId);
+
 }
 

+ 26 - 4
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/CouponReceiveServiceImpl.java

@@ -29,10 +29,7 @@ import javax.annotation.Resource;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
@@ -266,5 +263,30 @@ public class CouponReceiveServiceImpl extends ServiceImpl<CouponReceiveMapper, C
             return Collections.emptyList();
         }
     }
+
+    @Override
+    public Map<String, Object> selectCouponDetailForCalc(String couponId, String openId) {
+        return this.baseMapper.selectCouponDetailForCalc(couponId, openId);
+    }
+
+    @Override
+    public int useCouponOptimisticLock(String couponId, String openId, Long orderId, Integer orderType, LocalDateTime now) {
+        return this.baseMapper.useCouponOptimisticLock(couponId, openId, orderId, orderType, now);
+    }
+
+    @Override
+    public int incrementUsedNum(String couponId) {
+        return this.baseMapper.incrementUsedNum(couponId);
+    }
+
+    @Override
+    public int returnCouponOptimisticLock(String couponId, String openId, Long orderId) {
+        return this.baseMapper.returnCouponOptimisticLock(couponId,openId,orderId);
+    }
+
+    @Override
+    public void decrementUsedNum(String couponId) {
+        this.baseMapper.decrementUsedNum(couponId);
+    }
 }
 

+ 109 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/CouponServiceImpl.java

@@ -1,11 +1,26 @@
 package com.ylx.massage.service.impl;
 
 
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ylx.common.exception.ServiceException;
 import com.ylx.massage.domain.Coupon;
+import com.ylx.massage.domain.CouponReceive;
 import com.ylx.massage.mapper.CouponMapper;
+import com.ylx.massage.service.CouponReceiveService;
 import com.ylx.massage.service.CouponService;
+import lombok.extern.slf4j.Slf4j;
 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.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
 
 /**
  * 优惠券的规则信息(Coupon)表服务实现类
@@ -13,8 +28,102 @@ import org.springframework.stereotype.Service;
  * @author makejava
  * @since 2024-05-13 16:32:59
  */
+@Slf4j
 @Service("couponService")
 public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements CouponService {
 
+    @Resource
+    private CouponReceiveService couponReceiveService;
+
+    @Override
+    public BigDecimal calculateDiscountAmount(String couponId, String openId, BigDecimal orderAmount) {
+
+        // 1. 查询优惠券领取详情
+        Map<String, Object> detail = couponReceiveService.selectCouponDetailForCalc(couponId, openId);
+
+        if (CollUtil.isEmpty(detail)) {
+            throw new ServiceException("未查询到该用户的优惠券信息");
+        }
+
+        // 3. 校验优惠券状态(必须是 0:待使用)
+        Integer couponStatus = (Integer) detail.get("coupon_status");
+        if (couponStatus == null || couponStatus != 0) {
+            throw new ServiceException("优惠券状态异常,不可使用");
+        }
+
+        // 4. 校验有效期
+        LocalDate validStart = ((java.sql.Date) detail.get("valid_start_time")).toLocalDate();
+        LocalDate expiration = ((java.sql.Date) detail.get("expiration_time")).toLocalDate();
+        LocalDate today = LocalDate.now();
+        if (today.isBefore(validStart) || today.isAfter(expiration)) {
+            throw new ServiceException("优惠券不在有效期内");
+        }
+
+        // 5. 提取优惠规则字段
+        Integer discountType = (Integer) detail.get("discount_type");
+        BigDecimal discountValue = (BigDecimal) detail.get("discount_value");
+        BigDecimal rebValue = (BigDecimal) detail.get("reb_value");
+        BigDecimal thresholdAmount = (BigDecimal) detail.get("threshold_amount");
+
+        // 6. 根据类型计算抵扣金额
+        BigDecimal discountAmount = BigDecimal.ZERO;
+        switch (discountType) {
+            case 1: // 无门槛
+                discountAmount = discountValue;
+                break;
+            case 2: // 折扣 (reb_value 例如 0.8 代表8折)
+                // 抵扣金额 = 订单金额 * (1 - 折扣值)
+                discountAmount = orderAmount.multiply(BigDecimal.ONE.subtract(rebValue));
+                break;
+            case 3: // 满减
+                if (orderAmount.compareTo(thresholdAmount) < 0) {
+                    throw new ServiceException("订单金额未达到满减门槛");
+                }
+                discountAmount = discountValue;
+                break;
+            default:
+                throw new ServiceException("未知的优惠券类型: " + discountType);
+        }
+
+        // 7. 兜底逻辑:抵扣金额不能大于订单实际金额
+        if (discountAmount.compareTo(orderAmount) > 0) {
+            discountAmount = orderAmount;
+        }
+
+        // 8. 保留两位小数,四舍五入
+        return discountAmount.setScale(2, RoundingMode.HALF_UP);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void useCoupon(String couponId, String openId, Long orderId, Integer orderType) {
+        // 1. 乐观锁核销用户领取记录
+        int updatedRows = couponReceiveService.useCouponOptimisticLock(couponId, openId, orderId, orderType, LocalDateTime.now());
+        if (updatedRows == 0) {
+            throw new ServiceException("优惠券核销失败:状态异常或已被使用");
+        }
+
+        // 2. 规则表使用数量 +1
+        int ruleUpdatedRows = couponReceiveService.incrementUsedNum(couponId);
+        if (ruleUpdatedRows == 0) {
+            throw new ServiceException("优惠券规则状态异常,核销中止");
+        }
+        log.info("优惠券核销成功: couponId={}, openId={}, orderId={}", couponId, openId, orderId);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void returnCoupon(String couponId, String openId, Long orderId) {
+        // 1. 将已使用的券退回到待使用状态
+        int rows = couponReceiveService.returnCouponOptimisticLock(couponId, openId, orderId);
+        if (rows == 0) {
+            log.warn("退还优惠券失败,可能券已过期或不属于该订单: couponId={}, orderId={}", couponId, orderId);
+            return;
+        }
+
+        // 2. 规则表使用数量 -1
+        this.couponReceiveService.decrementUsedNum(couponId);
+        log.info("优惠券退还成功: couponId={}, orderId={}", couponId, orderId);
+    }
 }
 

+ 3 - 0
nightFragrance-massage/src/main/java/com/ylx/order/domain/TOrder.java

@@ -186,4 +186,7 @@ public class TOrder extends BaseEntity {
 
     @ApiModelProperty("派单情况:0=未派单 1=已派单")
     private Integer dispatchedStatus;
+
+    @ApiModelProperty("优惠券id")
+    private String couponId;
 }

+ 2 - 2
nightFragrance-massage/src/main/java/com/ylx/order/domain/dto/OrderSubmitDTO.java

@@ -33,8 +33,8 @@ public class OrderSubmitDTO implements Serializable {
     @ApiModelProperty("联系人地址ID")
     private Long addressId;
 
-    @ApiModelProperty("优惠券抵扣金额")
-    private BigDecimal couponDiscount;
+    @ApiModelProperty("优惠券id")
+    private String couponId;
 
     @ApiModelProperty("交通费")
     private BigDecimal trafficFee;

+ 62 - 4
nightFragrance-massage/src/main/java/com/ylx/order/service/impl/TOrderServiceImpl.java

@@ -17,18 +17,24 @@ import com.ylx.massage.domain.TWxUser;
 import com.ylx.massage.domain.vo.HomeBlock;
 import com.ylx.massage.domain.vo.OrderVerificationVo;
 import com.ylx.massage.domain.vo.TechnicianAvailabilityVo;
+import com.ylx.massage.service.CouponService;
 import com.ylx.massage.service.IMaTechnicianService;
 import com.ylx.massage.service.TAddressService;
+import com.ylx.massage.service.TWxUserService;
 import com.ylx.massage.utils.OrderNumberGenerator;
 import com.ylx.order.domain.TOrder;
 import com.ylx.order.domain.dto.OrderDateQueryDTO;
 import com.ylx.order.domain.dto.OrderSubmitDTO;
 import com.ylx.order.domain.vo.OrderDateQueryVo;
 import com.ylx.order.enums.OrderStatusEnum;
+import com.ylx.order.enums.PaymentMethodEnum;
 import com.ylx.order.mapper.TOrderMapper;
 import com.ylx.order.service.TOrderService;
 import com.ylx.project.domain.Project;
 import com.ylx.project.service.ProjectService;
+import com.ylx.shopingfundsdetail.domain.vo.ShoppingFundsDetailAddDto;
+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.stereotype.Service;
@@ -60,6 +66,12 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
     private IMaTechnicianService maTechnicianService;
     @Resource
     private OrderNumberGenerator orderNumberGenerator;
+    @Resource
+    private TWxUserService wxUserService;
+    @Resource
+    private ShoppingFundsDetailService shoppingFundsDetailService;
+    @Resource
+    private CouponService couponService;
 
     @Override
     public TOrder addOrder(TOrder order) {
@@ -205,7 +217,9 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
             throw new ServiceException("用户未登录");
         }
 
-        order.setUserId(Long.parseLong(wxLoginUser.getId()));
+        Long userId = Long.parseLong(wxLoginUser.getId());
+
+        order.setUserId(userId);
 
         // 2. 获取项目信息
         Project project = this.projectService.getById(dto.getProjectId());
@@ -253,9 +267,12 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
         // 5. 设置价格信息
         LocalDateTime appointmentStartTime = dto.getAppointmentStartTime();
 
-        Integer paymentMethod = dto.getPaymentMethod();
         // 优惠券优惠
-        BigDecimal couponDiscount = Optional.ofNullable(dto.getCouponDiscount()).orElse(BigDecimal.ZERO);
+        BigDecimal couponDiscount = BigDecimal.ZERO;
+        order.setCouponId(dto.getCouponId());
+        if (ObjectUtil.isNotNull(dto.getCouponId())) {
+            couponDiscount = this.couponService.calculateDiscountAmount(dto.getCouponId(), wxLoginUser.getCOpenid(), project.getPrice());
+        }
         // 车费
         BigDecimal trafficFee = Optional.ofNullable(dto.getTrafficFee()).orElse(BigDecimal.ZERO);
         // 商品原价
@@ -268,7 +285,6 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
         order.setCouponDiscount(couponDiscount);
         order.setTrafficFee(trafficFee);
         order.setFinalAmount(finalAmount);
-        order.setPaymentMethod(paymentMethod);
 
         order.setCreateTime(DateUtils.getNowDate());
         order.setAppointmentStartTime(appointmentStartTime);
@@ -297,14 +313,53 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
             order.setVirtualLatitude(new BigDecimal(addr.getLatitude()));
         });
 
+        // 7. 判断支付方式
+        Integer paymentMethod = dto.getPaymentMethod();
+        order.setPaymentMethod(paymentMethod);
+        // 余额支付
+        if (ObjectUtil.equals(PaymentMethodEnum.BALANCE.getCode(), paymentMethod)) {
+            order.setStatus(OrderStatusEnum.PENDING_DISPATCH.getCode());
+            order.setPaidTime(LocalDateTime.now());
+        } else {
+            order.setStatus(OrderStatusEnum.PENDING_PAYMENT.getCode());
+        }
+
         boolean saveResult = this.save(order);
         if (!saveResult) {
             throw new ServiceException("添加订单失败");
         }
+
+        // 8. 处理用户余额数据
+        if (ObjectUtil.equals(PaymentMethodEnum.BALANCE.getCode(), paymentMethod)) {
+
+            // 处理用户余额
+            TWxUser user = this.wxUserService.getById(userId);
+            BigDecimal newBalance = user.getdBalance().add(finalAmount);
+            this.wxUserService.lambdaUpdate()
+                    .set(TWxUser::getdBalance, newBalance)
+                    .eq(TWxUser::getId, user.getId())
+                    .update();
+
+            // 记录购物金明细
+            ShoppingFundsDetailAddDto shoppingFundsDetailAddDto = new ShoppingFundsDetailAddDto();
+            shoppingFundsDetailAddDto.setUserId(userId.toString());
+            shoppingFundsDetailAddDto.setAmount(finalAmount);
+            shoppingFundsDetailAddDto.setOrderNo(orderNo);
+            shoppingFundsDetailAddDto.setExpenseType(ShoppingFundsExpenseTypeEnum.CONSUMPTION.getCode());
+            shoppingFundsDetailAddDto.setBalance(newBalance);
+            shoppingFundsDetailService.addShoppingFundsDetail(shoppingFundsDetailAddDto);
+
+        }
+        // 9. 判断有没有使用优惠券
+        if (couponDiscount.compareTo(BigDecimal.ZERO) > 0) {
+            this.couponService.useCoupon(dto.getCouponId(), wxLoginUser.getCOpenid(), order.getId(), 1);
+        }
+
     }
 
     /**
      * 用户端订单列表
+     *
      * @param dto
      * @return
      */
@@ -351,6 +406,7 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
 
     /**
      * 用户端逻辑删除
+     *
      * @param orderId
      */
     @Override
@@ -394,12 +450,14 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
 
         return vo;
     }
+
     /**
      * 订单状态码 -> 中文描述
      */
     private String getOrderStatusName(Integer status) {
         return OrderStatusEnum.getInfoByCode(status);
     }
+
     /**
      * 拼接服务时间字符串,例如 “2026-06-08 14:30 (90分钟)”
      */

+ 0 - 1
nightFragrance-massage/src/main/java/com/ylx/shopingfundsdetail/domain/vo/ShoppingFundsDetailAddDto.java

@@ -32,7 +32,6 @@ public class ShoppingFundsDetailAddDto {
     /**
      * 购物卡id
      */
-    @NotNull(message = "购物卡id不能为空")
     @ApiModelProperty("购物卡id")
     private Long giftCardId;
     /**

+ 19 - 0
nightFragrance-massage/src/main/java/com/ylx/shopingfundsdetail/enums/ShoppingFundsExpenseTypeEnum.java

@@ -0,0 +1,19 @@
+package com.ylx.shopingfundsdetail.enums;
+
+import lombok.Getter;
+
+@Getter
+public enum ShoppingFundsExpenseTypeEnum {
+
+    RECHARGE(0, "充值"),
+    CONSUMPTION(1, "消费");
+
+    private final Integer code;
+    private final String info;
+
+    ShoppingFundsExpenseTypeEnum(Integer code, String info) {
+        this.code = code;
+        this.info = info;
+    }
+
+}

+ 74 - 0
nightFragrance-massage/src/main/resources/mapper/massage/CouponReceiveMapper.xml

@@ -80,5 +80,79 @@
         and a.receive_open_id = #{openid}
     </select>
 
+    <!-- 1. 核销优惠券 (乐观锁) -->
+    <update id="useCouponOptimisticLock">
+        UPDATE coupon_receive
+        SET coupon_status = 2,
+        use_time = #{useTime},
+        order_id = #{orderId},
+        order_type = #{orderType},
+        update_time = NOW()
+        WHERE coupon_id = #{couponId}
+        AND receive_open_id = #{openId}
+        AND coupon_status = 0  <!-- 核心:只有状态为 0(待使用) 才能被更新 -->
+        AND is_delete = 0
+    </update>
+
+    <!-- 2. 优惠券规则表使用数量 +1 -->
+    <update id="incrementUsedNum">
+        UPDATE coupon
+        SET used_num = used_num + 1,
+            update_time = NOW()
+        WHERE id = #{couponId}
+          AND is_delete = 0
+    </update>
+
+    <!-- 3. 退还优惠券 (乐观锁) -->
+    <update id="returnCouponOptimisticLock">
+        UPDATE coupon_receive
+        SET coupon_status = 0,
+        use_time = NULL,
+        order_id = NULL,
+        order_type = NULL,
+        update_time = NOW()
+        WHERE coupon_id = #{couponId}
+        AND receive_open_id = #{openId}
+        AND order_id = #{orderId} <!-- 核心:必须匹配原订单,防止退错 -->
+        AND coupon_status = 2     <!-- 核心:只有状态为 2(已使用) 才能退回 -->
+        AND is_delete = 0
+    </update>
+
+    <!-- 4. 优惠券规则表使用数量 -1 -->
+    <update id="decrementUsedNum">
+        UPDATE coupon
+        SET used_num = used_num - 1,
+        update_time = NOW()
+        WHERE id = #{couponId}
+        AND used_num > 0  <!-- 核心防御:防止减为负数 -->
+        AND is_delete = 0
+    </update>
+
+    <!-- 查询优惠券领取详情 -->
+    <select id="selectCouponDetailForCalc" resultType="java.util.Map">
+        SELECT
+        cr.id,
+        cr.coupon_status,
+        cr.valid_start_time,
+        cr.expiration_time,
+        c.discount_type,
+        c.discount_value,
+        c.reb_value,
+        c.threshold_amount
+        FROM coupon_receive cr
+        JOIN coupon c ON cr.coupon_id = c.id
+        <where>
+            cr.is_delete = 0
+            AND c.is_delete = 0
+            <if test="couponId != null and couponId != ''">
+                AND cr.coupon_id = #{couponId}
+            </if>
+            <if test="openId != null and openId != ''">
+                AND cr.receive_open_id = #{openId}
+            </if>
+        </where>
+        LIMIT 1
+    </select>
+
 </mapper>