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();
}