天机学堂-优惠券功能-day09(七)

day09 接口

一 优惠券管理

1.新增优惠券

接口说明 新增优惠券功能,如果优惠券有限定使用范围,注意保持范围数据
请求方式 POST
请求路径 /coupons
请求参数 { "name" : "新年大促", // 优惠券名称 "specific" : true, // 是否限定使用范围 "scopes": ["2001", "2004", "2007"], // 限定的分类id集合 "discountType" : 1, // 折扣类型 "thresholdAmount" : 100, // 折扣阈值,0代表无门槛 "discountValue" : 15, // 优惠值。满减填优惠金额,折扣填折扣值 "maxDiscountAmount" : 0, // 最大优惠金额 "obtainWay" : 1, // 领取方式,1-手动领取,2-兑换码 "totalNum" : 200, // 优惠券总发放数量 "userLimit" : 1, // 每个人的限领数量 }
返回值
CouponController.java
java 复制代码
    /**
     * 新增优惠券
     *
     * @param coupon
     * @return
     */
    @ApiOperation("新增优惠券")
    @PostMapping
    public void saveCoupon(@RequestBody @Valid CouponFormDTO coupon) {
        couponService.saveCoupon(coupon);
    }
ICouponService.java
java 复制代码
    void saveCoupon(@Valid CouponFormDTO coupon);
CouponServiceImpl.java

测试时注意:添加优惠券时优惠券名称需要不少于4

java 复制代码
    @Override
    @Transactional
    public void saveCoupon(CouponFormDTO coupon) {
        // 1.拷贝优惠券相关信息
        Coupon coupon1 = BeanUtils.copyProperties(coupon, Coupon.class);
        // 2.保存优惠券
        save(coupon1);
        // 3.保存限定范围(指定的课程或者分类生效)
        if (!coupon.getSpecific()) {
            //没有范围限定
            return;
        }
        //4.有的话就需要去存储该获取优惠券的使用范围
        List<Long> scopes = coupon.getScopes();
        List<CouponScope> list = new ArrayList<>();
        for (Long scope : scopes) {
            //保存优惠券和课程或者分类的关系
            // 1.创建对象(三个属性:范围限定的类型,业务的id,优惠券的id)
            CouponScope scope1 = new CouponScope();
            // 2.设置属性
            scope1.setCouponId(coupon1.getId());
            scope1.setBizId(scope);
            scope1.setType(1);
            list.add(scope1);
        }
        couponScopeService.saveBatch(list);

    }

2.修改优惠券

  • 请求方式:PUT
  • 请求路径:/coupons/{id}
  • 请求参数:与新增类似,参考新增接口。
  • 返回值:无
CouponController.java
java 复制代码
    /**
     * 修改优惠券
     *
     * @param coupon
     * @return
     */
    @ApiOperation("修改优惠券")
    @PutMapping("{id}")
    public void updateCoupon(@RequestBody CouponFormDTO coupon) {
        couponService.updateById(BeanUtils.copyProperties(coupon, Coupon.class));
    }

分页查询优惠券

接口说明 分页查询优惠券,默认按照创建时间排序
请求方式 GET
请求路径 /coupons/page
请求参数 { "pageNo" : 1, // 页码 "pageSize" : 10, // 每页大小 "type" : 1, // 折扣类型 "status" : 1, // 优惠券状态 "name" : "大促", // 优惠券名称关键字 }
返回值 { "list": [ { "id": "110", // 优惠券id "name": "年中大促", // 优惠券名称 "discountType": 1, // 优惠券折扣类型 "thresholdAmount": 100, // 优惠门槛 "discountValue": 10, // 优惠值 "maxDiscountAmount": 0, // 优惠上限 "specific": true, // 是否限定范围 "obtainWay": 1, // 领取方式 "totalNum": 1000, // 总发放数量 "issueNum": 800, // 已领取数量 "usedNum": 100 // 已使用数量 "createTime": "2023-05-01", // 创建时间 "issueBeginTime": "2023-06-01", // 发放开始时间 "issueEndTime": "2023-06-20", // 发放结束时间 "termBeginTime": "2023-06-10", // 使用有效期开始时间 "termEndTime": "2023-06-30", // 使用有效期结束时间 "termDays": 0, // 有效天数 "status": 1, // 状态 } ], "pages": 0, "total": 0 }
CouponController.java
java 复制代码
    /**
     * 分页查询优惠券
     *
     * @param query
     * @return
     */
    @ApiOperation("分页查询优惠券")
    @GetMapping("/page")
    public PageDTO<CouponPageVO> queryCoupons(CouponQuery query) {
        return couponService.queryCoupons(query);
    }
ICouponService.java
java 复制代码
    PageDTO<CouponPageVO> queryCoupons(CouponQuery query);
CouponServiceImpl.java
java 复制代码
    @Override
    public PageDTO<CouponPageVO> queryCoupons(CouponQuery query) {
        Integer status = query.getStatus();
        String name = query.getName();
        Integer type = query.getType();
        Page<Coupon> page = lambdaQuery()
                .eq(status != null, Coupon::getDiscountType, type)
                .eq(status != null, Coupon::getStatus, status)
                .like(name != null, Coupon::getName, name)
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        List<Coupon> records = page.getRecords();
        if (CollUtils.isEmpty(records)) {
            return PageDTO.empty(page);
        }
        ArrayList<CouponPageVO> couponPageVOS = new ArrayList<>();
        for (Coupon record : records) {
            CouponPageVO vo = BeanUtils.copyProperties(record, CouponPageVO.class);
            couponPageVOS.add(vo);
        }
        return PageDTO.of(page, couponPageVOS);
    }

3.根据id查询优惠券

接口说明 都需要根据id查询优惠券的详细信息
请求方式 GET
请求路径 /coupons/{id}
请求参数 路径占位符id
返回值 { "id": "110", // 优惠券id "name": "年中大促", // 优惠券名称 "discountType": 1, // 优惠券折扣类型 "thresholdAmount": 100, // 优惠门槛 "discountValue": 10, // 优惠值 "maxDiscountAmount": 0, // 优惠上限 "specific": true, // 是否限定范围 "scopes": [ // 限定的分类 {"id": "2001", "name": "IT互联网"} ] "obtainWay": 1, // 领取方式 "totalNum": 1000, // 总发放数量 "useLimit": 1, // 限领数量 "issueBeginTime": "2023-06-01", // 发放开始时间 "issueEndTime": "2023-06-20", // 发放结束时间 "termBeginTime": "2023-06-10", // 使用有效期开始时间 "termEndTime": "2023-06-30", // 使用有效期结束时间 "termDays": 0, // 有效天数 }
CouponController.java
java 复制代码
    /**
     * 根据id查询优惠券
     *
     * @param id
     * @return
     */
    @ApiOperation("根据id查询优惠券")
    @GetMapping("{id}")
    public CouponDetailVO getById(@PathVariable("id") Long id) {
        return couponService.queryById(id);
    }
ICouponService.java
java 复制代码
    CouponDetailVO queryById(Long id);
CouponServiceImpl.java
复制代码
    @Override
    public CouponDetailVO queryById(Long id) {
        Coupon coupon = getById(id);
        CouponDetailVO couponDetailVO = BeanUtils.copyBean(coupon, CouponDetailVO.class);
        if (couponDetailVO == null || !coupon.getSpecific()) {
            // 数据不存在,或者没有限定范围,直接结束
            return couponDetailVO;
        }
        LambdaQueryWrapper<CouponScope> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CouponScope::getCouponId, id);
        List<CouponScope> couponScopes = couponScopeMapper.selectList(queryWrapper);
        if (CollUtils.isEmpty(couponScopes)) {
            return couponDetailVO;
        }
        List<CouponScopeVO> scopeVOS = couponScopes.stream()
                .map(CouponScope::getBizId)
                .map(cateId -> new CouponScopeVO(cateId, categoryCache.getNameByLv3Id(cateId)))
                .collect(Collectors.toList());
        couponDetailVO.setScopes(scopeVOS);
        return couponDetailVO;

    }

4.删除优惠券

  • 请求方式:DELETE
  • 请求路径:/coupons/{id}
  • 请求参数:与新增类似,参考新增接口。
  • 返回值:无
CouponController.java
java 复制代码
    /**
     * 删除优惠券
     *
     * @param id
     * @return
     */
    @ApiOperation("删除优惠券")
    @DeleteMapping("{id}")
    public void deleteById(@PathVariable("id") Long id) {
        couponService.deleteById(id);
    }
ICouponService.java
java 复制代码
    void deleteById(Long id);
CouponServiceImpl.java
复制代码
    @Override
    public void deleteById(Long id) {
        // 1.查询
        Coupon coupon = getById(id);
        if (coupon == null || coupon.getStatus() != DRAFT) {
            throw new BadRequestException("优惠券不存在或者优惠券正在使用中");
        }
        // 2.删除优惠券
        boolean success = remove(new LambdaQueryWrapper<Coupon>()
                .eq(Coupon::getId, id)
                .eq(Coupon::getStatus, DRAFT)
        );
        if (!success) {
            throw new BadRequestException("优惠券不存在或者优惠券正在使用中");
        }
        // 3.删除优惠券对应限定范围
        if (!coupon.getSpecific()) {
            return;
        }
        couponScopeService.remove(new LambdaQueryWrapper<CouponScope>().eq(CouponScope::getCouponId, id));
    }

二 优惠券发放

1.发放优惠券(版本1)

接口说明 发放优惠券
请求方式 PUT
请求路径 /coupons/{id}/issue
请求参数 { "issueBeginTime": "2023-06-01", // 发放开始时间 "issueEndTime": "2023-06-20", // 发放结束时间 "termBeginTime": "2023-06-10", // 使用有效期开始时间 "termEndTime": "2023-06-30", // 使用有效期结束时间 "termDays": 0, // 有效天数 }
返回值
CouponController.java
java 复制代码
    /**
     * 发放优惠券
     *
     * @param dto
     * @return
     */
    @ApiOperation("发放优惠券")
    @PutMapping("{id}/issue")
    public void beginIssue(@RequestBody @Valid CouponIssueFormDTO dto) {
        couponService.beginIssue(dto);
    }
ICouponService.java
java 复制代码
    void beginIssue(@Valid CouponIssueFormDTO dto);
CouponServiceImpl.java
java 复制代码
    @Override
    public void beginIssue(CouponIssueFormDTO dto) {
        //1.查询优惠券
        Coupon byId = this.getById(dto.getId());
        //2.判断优惠券的状态,是否是暂停或者待发放
        if (byId.getStatus() != DRAFT && byId.getStatus() != PAUSE) {
            throw new BadRequestException("优惠券状态错误");
        }
        //3.判断是否是立刻发放(当前时间为null或者发放时间已经过了,都代表立即发放)
        LocalDateTime issueBeginTime = dto.getIssueBeginTime();
        boolean isBegin = issueBeginTime == null || !issueBeginTime.isAfter(LocalDateTime.now());
        //4.更新优惠券
        Coupon coupon = BeanUtils.copyProperties(dto, Coupon.class);
        if(isBegin){
            coupon.setStatus(CouponStatus.ISSUING);
            coupon.setIssueBeginTime(issueBeginTime);
        }else {
            coupon.setStatus(CouponStatus.UN_ISSUE);
        }
        this.updateById(coupon);
    }

2.发放优惠券(版本2)

兑换码算法

java 复制代码
    @Override
    public void beginIssue(CouponIssueFormDTO dto) {
        //1.查询优惠券
        Coupon byId = this.getById(dto.getId());
        //2.判断优惠券的状态,是否是暂停或者待发放
        if (byId.getStatus() != DRAFT && byId.getStatus() != PAUSE) {
            throw new BadRequestException("优惠券状态错误");
        }
        //3.判断是否是立刻发放(当前时间为null或者发放时间已经过了,都代表立即发放)
        LocalDateTime issueBeginTime = dto.getIssueBeginTime();
        boolean isBegin = issueBeginTime == null || !issueBeginTime.isAfter(LocalDateTime.now());
        //4.更新优惠券
        Coupon coupon = BeanUtils.copyProperties(dto, Coupon.class);
        if (isBegin) {
            coupon.setStatus(CouponStatus.ISSUING);
            coupon.setIssueBeginTime(issueBeginTime);
        } else {
            coupon.setStatus(CouponStatus.UN_ISSUE);
        }
        this.updateById(coupon);
        //5.判断是否需要生成兑换码,优惠券状态必须是待发放,发放类型必须是发放兑换码类型
        if (byId.getObtainWay() == ISSUE && byId.getStatus() == DRAFT) {
            //生成兑换码
            byId.setIssueEndTime(coupon.getIssueEndTime());
            exchangeCodeService.asyncGenerateCode(byId);
        }
    }
java 复制代码
@Service
@RequiredArgsConstructor
public class ExchangeCodeServiceImpl extends ServiceImpl<ExchangeCodeMapper, ExchangeCode> implements IExchangeCodeService {


    private final StringRedisTemplate stringRedisTemplate;
    private BoundValueOperations<String, String> serialOps;

    @PostConstruct
    public void init() {
        this.serialOps = stringRedisTemplate.boundValueOps(PromotionConstants.COUPON_CODE_SERIAL_KEY);
    }

    @Override
    @Async("generateExchangeExecutor")//指定线程池
    public void asyncGenerateCode(Coupon byId) {
        //0.发放数量
        int totalNum = byId.getTotalNum();
        ArrayList<ExchangeCode> exchangeCodes = new ArrayList<>();
        for (int i = 0; i < totalNum; i++) {

            //1.Redis序列号(自增数量)
            Long incrementNum = serialOps.increment(1);
            //2.生成兑换码
            if (incrementNum == null) {
                return;
            }
            String code = CodeUtil.generateCode(incrementNum, byId.getId());
            ExchangeCode exchangeCode = new ExchangeCode();
            //3.兑换码
            exchangeCode.setCode(code);
            //4.优惠券id
            exchangeCode.setExchangeTargetId(byId.getId());
            //5.借助自增号来填充兑换码id
            exchangeCode.setId(incrementNum.intValue());
            //6.时间
            exchangeCode.setExpiredTime(byId.getIssueEndTime());
            exchangeCodes.add(exchangeCode);
        }
        //7.保存数据库
        this.saveBatch(exchangeCodes);
    }```

### 3.暂停发放

- **请求方式**:PUT
- **请求路径**:/coupons/{id}/pause
- **请求参数**:路径占位符id
- **返回值**:无

#### CouponController.java

```java
@ApiOperation("停发优惠券")
@PutMapping("/{id}/pause")
public void pauseIssue(@ApiParam("优惠券id") @PathVariable("id") long id) {
    couponService.pauseIssue(id);
}
ICouponService.java
java 复制代码
void pauseIssue(long id);
CouponServiceImpl.java
java 复制代码
@Override
public void pauseIssue(long id) {
    this.lambdaUpdate()
            .eq(Coupon::getId, id)
            .set(Coupon::getStatus, CouponStatus.PAUSE)
            .update();
}
相关推荐
it_czz4 小时前
MCP调用流程图
java
爱学习的小可爱卢5 小时前
JavaEE进阶——SpringBoot统一功能处理实战指南
java·spring boot·java-ee
小单于PRO5 小时前
Spring Boot 实现构建一个轻量级地图瓦片服务
java·spring boot·后端
Selegant5 小时前
Spring Boot 3 + Java 21 全新特性实战:虚拟线程、结构化并发与 Record 类型
java·spring boot·后端
管理大亨5 小时前
ELK + Redis Docker 企业级部署落地方案
大数据·运维·elk·elasticsearch·docker·jenkins
Jinkxs5 小时前
Java 架构 02:DDD 领域模型设计实战(限界上下文划分)
java·开发语言·架构
百锦再5 小时前
国产数据库的平替亮点——关系型数据库架构适配
android·java·前端·数据库·sql·算法·数据库架构
爱笑的眼睛115 小时前
文本分类的范式演进:从统计概率到语言模型提示工程
java·人工智能·python·ai
周杰伦_Jay6 小时前
【Go/Python/Java】基础语法+核心特性对比
java·python·golang