25 changed files with 543 additions and 26 deletions
@ -0,0 +1,52 @@ |
|||||
|
package cc.hiver.mall.controller; |
||||
|
|
||||
|
import cc.hiver.core.common.utils.ResultUtil; |
||||
|
import cc.hiver.core.common.vo.Result; |
||||
|
import cc.hiver.mall.entity.MallCoupon; |
||||
|
import cc.hiver.mall.entity.MallUserCoupon; |
||||
|
import cc.hiver.mall.service.mybatis.MallCouponService; |
||||
|
import cc.hiver.mall.service.mybatis.MallUserCouponService; |
||||
|
import io.swagger.annotations.Api; |
||||
|
import io.swagger.annotations.ApiOperation; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.web.bind.annotation.*; |
||||
|
|
||||
|
import java.math.BigDecimal; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@RestController |
||||
|
@RequestMapping("/hiver/mall/coupon") |
||||
|
@Api(tags = "优惠券接口") |
||||
|
public class MallCouponController { |
||||
|
|
||||
|
@Autowired |
||||
|
private MallCouponService mallCouponService; |
||||
|
|
||||
|
@Autowired |
||||
|
private MallUserCouponService mallUserCouponService; |
||||
|
|
||||
|
@PostMapping("/add") |
||||
|
@ApiOperation(value = "添加/发行优惠券") |
||||
|
public Result<Object> addCoupon(@RequestBody MallCoupon coupon) { |
||||
|
mallCouponService.save(coupon); |
||||
|
return ResultUtil.success("发行成功"); |
||||
|
} |
||||
|
|
||||
|
@PostMapping("/receive") |
||||
|
@ApiOperation(value = "用户领取优惠券") |
||||
|
public Result<Object> receiveCoupon(@RequestParam String userId, @RequestParam String couponId) { |
||||
|
mallUserCouponService.receiveCoupon(userId, couponId); |
||||
|
return ResultUtil.success("领取成功"); |
||||
|
} |
||||
|
|
||||
|
@GetMapping("/available") |
||||
|
@ApiOperation(value = "获取可用优惠券列表(下单时调用)") |
||||
|
public Result<List<MallUserCoupon>> getAvailableCoupons( |
||||
|
@RequestParam String userId, |
||||
|
@RequestParam Integer applyScene, |
||||
|
@RequestParam BigDecimal amount, |
||||
|
@RequestParam(required = false, defaultValue = "0") String merchantId, |
||||
|
@RequestParam(required = true) String regionId) { |
||||
|
return new ResultUtil<List<MallUserCoupon>>().setData(mallUserCouponService.getAvailableCoupons(userId, applyScene, amount, merchantId, regionId)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
package cc.hiver.mall.dao.mapper; |
||||
|
|
||||
|
import cc.hiver.mall.entity.MallCoupon; |
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
import org.apache.ibatis.annotations.Param; |
||||
|
|
||||
|
public interface MallCouponMapper extends BaseMapper<MallCoupon> { |
||||
|
int deductStock(@Param("id") String id); |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
package cc.hiver.mall.dao.mapper; |
||||
|
|
||||
|
import cc.hiver.mall.entity.MallUserCoupon; |
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
|
||||
|
public interface MallUserCouponMapper extends BaseMapper<MallUserCoupon> { |
||||
|
} |
||||
@ -0,0 +1,67 @@ |
|||||
|
package cc.hiver.mall.entity; |
||||
|
|
||||
|
import cc.hiver.core.base.HiverBaseEntity; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import io.swagger.annotations.ApiModel; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
|
||||
|
import java.math.BigDecimal; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
@Data |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
@ApiModel(value = "优惠券模板配置表") |
||||
|
@TableName(value = "t_mall_coupon", autoResultMap = true) |
||||
|
public class MallCoupon extends HiverBaseEntity { |
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@ApiModelProperty(value = "优惠券名称") |
||||
|
private String name; |
||||
|
|
||||
|
@ApiModelProperty(value = "发放方:1-平台,2-商家") |
||||
|
private Integer issuerType; |
||||
|
|
||||
|
@ApiModelProperty(value = "发放方ID") |
||||
|
private String issuerId; |
||||
|
|
||||
|
@ApiModelProperty(value = "适用场景:0-通用,1-外卖/买饭,2-快递/跑腿,3-二手物品交易") |
||||
|
private Integer applyScene; |
||||
|
|
||||
|
@ApiModelProperty(value = "优惠券类型:1-满减券,2-无门槛直减券") |
||||
|
private Integer type; |
||||
|
|
||||
|
@ApiModelProperty(value = "使用门槛金额") |
||||
|
private BigDecimal minAmount; |
||||
|
|
||||
|
@ApiModelProperty(value = "抵扣面额") |
||||
|
private BigDecimal discountAmount; |
||||
|
|
||||
|
@ApiModelProperty(value = "发行总数") |
||||
|
private Integer totalCount; |
||||
|
|
||||
|
@ApiModelProperty(value = "剩余领取数量") |
||||
|
private Integer remainCount; |
||||
|
|
||||
|
@ApiModelProperty(value = "每人最多限领张数") |
||||
|
private Integer limitPerUser; |
||||
|
|
||||
|
@ApiModelProperty(value = "有效期类型:1-绝对时间段有效,2-领取后相对天数有效") |
||||
|
private Integer validType; |
||||
|
|
||||
|
@ApiModelProperty(value = "有效期开始时间") |
||||
|
private Date validStartTime; |
||||
|
|
||||
|
@ApiModelProperty(value = "有效期结束时间") |
||||
|
private Date validEndTime; |
||||
|
|
||||
|
@ApiModelProperty(value = "自领取之日起有效天数") |
||||
|
private Integer validDays; |
||||
|
|
||||
|
@ApiModelProperty(value = "状态:0-已下架/停发,1-发放中") |
||||
|
private Integer status; |
||||
|
|
||||
|
@ApiModelProperty(value = "学校id") |
||||
|
private String regionId; |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
package cc.hiver.mall.entity; |
||||
|
|
||||
|
import cc.hiver.core.base.HiverBaseEntity; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import io.swagger.annotations.ApiModel; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
|
||||
|
import java.math.BigDecimal; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
@Data |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
@ApiModel(value = "用户领取的优惠券明细表") |
||||
|
@TableName(value = "t_mall_user_coupon", autoResultMap = true) |
||||
|
public class MallUserCoupon extends HiverBaseEntity { |
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@ApiModelProperty(value = "领取用户ID") |
||||
|
private String userId; |
||||
|
|
||||
|
@ApiModelProperty(value = "关联的优惠券模板ID") |
||||
|
private String couponId; |
||||
|
|
||||
|
@ApiModelProperty(value = "发放方(1平台/2商家)") |
||||
|
private Integer issuerType; |
||||
|
|
||||
|
@ApiModelProperty(value = "发放方ID") |
||||
|
private String issuerId; |
||||
|
|
||||
|
@ApiModelProperty(value = "适用场景") |
||||
|
private Integer applyScene; |
||||
|
|
||||
|
@ApiModelProperty(value = "使用门槛") |
||||
|
private BigDecimal minAmount; |
||||
|
|
||||
|
@ApiModelProperty(value = "抵扣金额") |
||||
|
private BigDecimal discountAmount; |
||||
|
|
||||
|
@ApiModelProperty(value = "状态:0-未使用,1-已挂起,2-已使用,3-已过期") |
||||
|
private Integer status; |
||||
|
|
||||
|
@ApiModelProperty(value = "领取时间") |
||||
|
private Date receiveTime; |
||||
|
|
||||
|
@ApiModelProperty(value = "实际可使用生效时间") |
||||
|
private Date validStartTime; |
||||
|
|
||||
|
@ApiModelProperty(value = "实际可使用失效时间") |
||||
|
private Date validEndTime; |
||||
|
|
||||
|
@ApiModelProperty(value = "真实核销时间") |
||||
|
private Date useTime; |
||||
|
|
||||
|
@ApiModelProperty(value = "核销时使用的订单ID") |
||||
|
private String orderId; |
||||
|
|
||||
|
@ApiModelProperty(value = "学校id") |
||||
|
private String regionId; |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
package cc.hiver.mall.service.mybatis; |
||||
|
|
||||
|
import cc.hiver.mall.entity.MallCoupon; |
||||
|
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
|
||||
|
public interface MallCouponService extends IService<MallCoupon> { |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
package cc.hiver.mall.service.mybatis; |
||||
|
|
||||
|
import cc.hiver.mall.entity.MallUserCoupon; |
||||
|
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
|
||||
|
import java.math.BigDecimal; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public interface MallUserCouponService extends IService<MallUserCoupon> { |
||||
|
void receiveCoupon(String userId, String couponId); |
||||
|
List<MallUserCoupon> getAvailableCoupons(String userId, Integer applyScene, BigDecimal amount, String merchantId,String regionId); |
||||
|
boolean lockCoupon(String userCouponId, String orderId); |
||||
|
boolean useCoupon(String orderId); |
||||
|
boolean refundCoupon(String orderId); |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
package cc.hiver.mall.serviceimpl.mybatis; |
||||
|
|
||||
|
import cc.hiver.mall.dao.mapper.MallCouponMapper; |
||||
|
import cc.hiver.mall.entity.MallCoupon; |
||||
|
import cc.hiver.mall.service.mybatis.MallCouponService; |
||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
@Service |
||||
|
public class MallCouponServiceImpl extends ServiceImpl<MallCouponMapper, MallCoupon> implements MallCouponService { |
||||
|
} |
||||
@ -0,0 +1,121 @@ |
|||||
|
package cc.hiver.mall.serviceimpl.mybatis; |
||||
|
|
||||
|
import cc.hiver.core.common.exception.HiverException; |
||||
|
import cc.hiver.mall.dao.mapper.MallCouponMapper; |
||||
|
import cc.hiver.mall.dao.mapper.MallUserCouponMapper; |
||||
|
import cc.hiver.mall.entity.MallCoupon; |
||||
|
import cc.hiver.mall.entity.MallUserCoupon; |
||||
|
import cc.hiver.mall.service.mybatis.MallUserCouponService; |
||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
|
||||
|
import java.math.BigDecimal; |
||||
|
import java.util.Date; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Service |
||||
|
public class MallUserCouponServiceImpl extends ServiceImpl<MallUserCouponMapper, MallUserCoupon> implements MallUserCouponService { |
||||
|
|
||||
|
@Autowired |
||||
|
private MallCouponMapper mallCouponMapper; |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public void receiveCoupon(String userId, String couponId) { |
||||
|
MallCoupon coupon = mallCouponMapper.selectById(couponId); |
||||
|
if (coupon == null || coupon.getStatus() != 1) { |
||||
|
throw new HiverException("优惠券不存在或已下架"); |
||||
|
} |
||||
|
|
||||
|
// 检查限领
|
||||
|
QueryWrapper<MallUserCoupon> checkQr = new QueryWrapper<>(); |
||||
|
checkQr.eq("user_id", userId).eq("coupon_id", couponId); |
||||
|
if (this.count(checkQr) >= coupon.getLimitPerUser()) { |
||||
|
throw new HiverException("您已达到该优惠券领取上限"); |
||||
|
} |
||||
|
|
||||
|
// 扣库存
|
||||
|
if (coupon.getTotalCount() != -1) { |
||||
|
int row = mallCouponMapper.deductStock(couponId); |
||||
|
if (row <= 0) { |
||||
|
throw new HiverException("优惠券已被抢光"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 发放
|
||||
|
MallUserCoupon userCoupon = new MallUserCoupon(); |
||||
|
userCoupon.setUserId(userId); |
||||
|
userCoupon.setCouponId(couponId); |
||||
|
userCoupon.setIssuerType(coupon.getIssuerType()); |
||||
|
userCoupon.setIssuerId(coupon.getIssuerId()); |
||||
|
userCoupon.setApplyScene(coupon.getApplyScene()); |
||||
|
userCoupon.setMinAmount(coupon.getMinAmount()); |
||||
|
userCoupon.setDiscountAmount(coupon.getDiscountAmount()); |
||||
|
userCoupon.setStatus(0); |
||||
|
userCoupon.setReceiveTime(new Date()); |
||||
|
|
||||
|
// 计算有效期
|
||||
|
if (coupon.getValidType() == 1) { |
||||
|
userCoupon.setValidStartTime(coupon.getValidStartTime()); |
||||
|
userCoupon.setValidEndTime(coupon.getValidEndTime()); |
||||
|
} else { |
||||
|
userCoupon.setValidStartTime(new Date()); |
||||
|
long end = System.currentTimeMillis() + (long) coupon.getValidDays() * 24 * 3600 * 1000; |
||||
|
userCoupon.setValidEndTime(new Date(end)); |
||||
|
} |
||||
|
|
||||
|
this.save(userCoupon); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public List<MallUserCoupon> getAvailableCoupons(String userId, Integer applyScene, BigDecimal amount, String merchantId,String regionId) { |
||||
|
QueryWrapper<MallUserCoupon> qw = new QueryWrapper<>(); |
||||
|
qw.eq("user_id", userId).eq("region_id", regionId) |
||||
|
.eq("status", 0) |
||||
|
.le("valid_start_time", new Date()) |
||||
|
.ge("valid_end_time", new Date()) |
||||
|
.le("min_amount", amount); |
||||
|
|
||||
|
// 场景兼容
|
||||
|
qw.and(wrapper -> wrapper.eq("apply_scene", 0).or().eq("apply_scene", applyScene)); |
||||
|
|
||||
|
// 商家兼容
|
||||
|
if (merchantId != null && !merchantId.equals("0")) { |
||||
|
qw.and(wrapper -> wrapper.eq("issuer_type", 1).or().eq("issuer_id", merchantId)); |
||||
|
} |
||||
|
|
||||
|
return this.list(qw); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean lockCoupon(String userCouponId, String orderId) { |
||||
|
UpdateWrapper<MallUserCoupon> uw = new UpdateWrapper<>(); |
||||
|
uw.eq("id", userCouponId).eq("status", 0) |
||||
|
.set("status", 1) |
||||
|
.set("order_id", orderId); |
||||
|
return this.update(uw); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean useCoupon(String orderId) { |
||||
|
UpdateWrapper<MallUserCoupon> uw = new UpdateWrapper<>(); |
||||
|
uw.eq("order_id", orderId).eq("status", 1) |
||||
|
.set("status", 2) |
||||
|
.set("use_time", new Date()); |
||||
|
return this.update(uw); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean refundCoupon(String orderId) { |
||||
|
UpdateWrapper<MallUserCoupon> uw = new UpdateWrapper<>(); |
||||
|
uw.eq("order_id", orderId).in("status", 1, 2) |
||||
|
.set("status", 0) |
||||
|
.set("order_id", null) |
||||
|
.set("use_time", null); |
||||
|
return this.update(uw); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="cc.hiver.mall.dao.mapper.MallCouponMapper"> |
||||
|
|
||||
|
<update id="deductStock"> |
||||
|
UPDATE t_mall_coupon |
||||
|
SET remain_count = remain_count - 1 |
||||
|
WHERE id = #{id} AND remain_count > 0 AND del_flag = 0 |
||||
|
</update> |
||||
|
|
||||
|
</mapper> |
||||
@ -0,0 +1,5 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="cc.hiver.mall.dao.mapper.MallUserCouponMapper"> |
||||
|
|
||||
|
</mapper> |
||||
Loading…
Reference in new issue