feat(market):新增运营端的活动管理的相关功能

1. 查询活动信息 2. 新增/更新活动信息 3. 撤销活动信息
This commit is contained in:
JIAN
2024-09-23 21:18:47 +08:00
parent d4c244701b
commit f6238fbca7
10 changed files with 262 additions and 96 deletions

View File

@@ -0,0 +1,43 @@
package com.jzo2o.market.controller.operation;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.market.model.dto.request.ActivityPageQueryDTO;
import com.jzo2o.market.model.dto.request.ActivitySaveReqDTO;
import com.jzo2o.market.model.dto.response.ActivityInfoResDTO;
import com.jzo2o.market.service.IActivityService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* 运营端 - 活动管理控制器
* @author JIAN
*/
@Slf4j
@RestController("operationActivityController")
@RequestMapping("/operation/activity")
public class ActivityController {
@Resource
private IActivityService activityService;
@GetMapping("/page")
public PageResult<ActivityInfoResDTO> pageActivity(ActivityPageQueryDTO activityPageQueryDTO) {
return activityService.page(activityPageQueryDTO);
}
@GetMapping("/{id}")
public ActivityInfoResDTO getActivity(@PathVariable Long id) {
return activityService.getDetailById(id);
}
@PostMapping("/save")
public void saveOrUpdateActivity(@RequestBody ActivitySaveReqDTO activitySaveReqDTO) {
activityService.saveOrUpdate(activitySaveReqDTO);
}
@PostMapping("/revoke/{id}")
public void revokeActivity(@PathVariable Long id) {
activityService.revoke(id);
}
}

View File

@@ -1,16 +1,28 @@
package com.jzo2o.market.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
/**
* 活动状态枚举类
* @author JIAN
*/
@Getter
@AllArgsConstructor
public enum ActivityStatusEnum {
NO_DISTRIBUTE(1, "待生效"), DISTRIBUTING(2, "进行中"), LOSE_EFFICACY(3, "已失效"),VOIDED(4, "作废");
NO_DISTRIBUTE(1, "待生效"),
DISTRIBUTING(2, "进行中"),
LOSE_EFFICACY(3, "已失效"),
VOIDED(4, "作废");
@EnumValue
@JsonValue
private int status;
private String name;
public boolean equals(Integer status) {
return status != null && this.status == status;
}
}
}

View File

@@ -1,17 +0,0 @@
package com.jzo2o.market.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum ActivityTypeEnum {
AMOUNT_DISCOUNT(1, "满减"), RATE_DISCOUNT(2, "打折");
private int type;
private String name;
public boolean equals(Integer type) {
return type != null && type.equals(this.type);
}
}

View File

@@ -0,0 +1,41 @@
package com.jzo2o.market.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 优惠券类型枚举类
* @author JIAN
*/
@Getter
@AllArgsConstructor
public enum CouponTypeEnum {
AMOUNT_DISCOUNT(1, "满减"),
RATE_DISCOUNT(2, "打折");
@EnumValue
@JsonValue
private int type;
private String name;
public boolean equals(Integer type) {
return type != null && type.equals(this.type);
}
public static CouponTypeEnum typeOf(Integer type) {
if (type == null) {
return null;
}
for (CouponTypeEnum couponTypeEnum : CouponTypeEnum.values()) {
if (couponTypeEnum.getType() == type) {
return couponTypeEnum;
}
}
// not found
return null;
}
}

View File

@@ -1,23 +1,18 @@
package com.jzo2o.market.model.domain;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.*;
import com.jzo2o.market.enums.ActivityStatusEnum;
import com.jzo2o.market.enums.CouponTypeEnum;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* <p>
*
* </p>
*
* 活动实体类
* @author itcast
* @since 2023-09-16
*/
@@ -31,7 +26,7 @@ public class Activity implements Serializable {
/**
* 优惠券配置id
*/
@TableId(value = "id", type = IdType.NONE)
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id;
/**
@@ -42,7 +37,7 @@ public class Activity implements Serializable {
/**
* 使用类型1满减2折扣
*/
private Integer type;
private CouponTypeEnum type;
/**
* 使用条件0表示无门槛其他值最低消费金额
@@ -77,7 +72,7 @@ public class Activity implements Serializable {
/**
* 优惠券配置状态1待生效2进行中3已失效
*/
private Integer status;
private ActivityStatusEnum status;
/**
* 发放数量0表示无限量其他正数表示最大发放量
@@ -88,6 +83,7 @@ public class Activity implements Serializable {
* 库存数量
*/
private Integer stockNum;
/**
* 创建时间
*/
@@ -114,6 +110,4 @@ public class Activity implements Serializable {
* 逻辑删除
*/
private Integer isDeleted;
}
}

View File

@@ -4,10 +4,12 @@ import com.jzo2o.common.model.dto.PageQueryDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@ApiModel("活动分页查询模型")
public class ActivityQueryForPageReqDTO extends PageQueryDTO {
@EqualsAndHashCode(callSuper = true)
public class ActivityPageQueryDTO extends PageQueryDTO {
@ApiModelProperty("活动id")
private Long id;
@ApiModelProperty("活动名称")
@@ -16,4 +18,4 @@ public class ActivityQueryForPageReqDTO extends PageQueryDTO {
private Integer type;
@ApiModelProperty("优惠券配置状态1待生效2进行中3已失效")
private Integer status;
}
}

View File

@@ -3,14 +3,12 @@ package com.jzo2o.market.model.dto.request;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.market.enums.ActivityTypeEnum;
import com.jzo2o.market.enums.CouponTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.checkerframework.checker.units.qual.Length;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Null;
import javax.validation.constraints.Size;
@@ -21,68 +19,67 @@ import java.time.LocalDateTime;
@ApiModel("活动保存请求模型")
@Validated
public class ActivitySaveReqDTO {
@ApiModelProperty(value = "活动id,新增时不填,修改时必填",required = false)
@ApiModelProperty(value = "活动id,新增时不填,修改时必填")
private Long id;
@ApiModelProperty(value = "活动名称",required = true)
@ApiModelProperty(value = "活动名称", required = true)
@Size(max = 20, message = "活动名称超出20个字符无法输入")
@Null(message = "活动名称为空,请输入活动名称")
private String name;
@ApiModelProperty(value = "优惠券类型1满减2折扣",required = true)
@ApiModelProperty(value = "优惠券类型1满减2折扣", required = true)
private Integer type;
@ApiModelProperty(value = "满减限额0表示无门槛其他值最低消费金额")
@Min(value = 0, message = "满额限制请输入大于/等于 0的整数")
@Null(message = "满额限制为空,请输入满额限制")
private BigDecimal amountCondition;
@ApiModelProperty(value = "折扣率折扣类型的折扣率例如8,打8折, type为2时必填",required = false)
@ApiModelProperty(value = "折扣率折扣类型的折扣率例如8,打8折, type为2时必填")
private Integer discountRate;
@ApiModelProperty(value = "优惠金额,满减或无门槛的优惠金额",required = true)
@ApiModelProperty(value = "优惠金额,满减或无门槛的优惠金额", required = true)
private BigDecimal discountAmount;
@ApiModelProperty(value = "发放开始时间",required = true)
@ApiModelProperty(value = "发放开始时间", required = true)
@Null(message = "发放时间为空,请输入发放时间")
private LocalDateTime distributeStartTime;
@Null(message = "发放时间为空,请输入发放时间")
@ApiModelProperty(value = "发放结束时间",required = true)
@ApiModelProperty(value = "发放结束时间", required = true)
private LocalDateTime distributeEndTime;
@ApiModelProperty(value = "发放数量0表示无限量其他正数表示最大发放量",required = false)
@ApiModelProperty(value = "发放数量0表示无限量其他正数表示最大发放量")
private Integer totalNum = 0;
@ApiModelProperty(value = "有效期天数",required = true)
@ApiModelProperty(value = "有效期天数", required = true)
@Null(message = "使用期限请输入大于0的整数")
@Min(value = 0, message = "使用期限请输入大于0的整数")
private Integer validityDays;
public void check() {
if(ActivityTypeEnum.AMOUNT_DISCOUNT.equals(type)) {
if (CouponTypeEnum.AMOUNT_DISCOUNT.equals(type)) {
// 满减
//discountAmount字段不能为空且值为正数
if(ObjectUtils.isNull(discountAmount)) {
if (ObjectUtils.isNull(discountAmount)) {
throw new BadRequestException("折扣金额为空,请输入折扣金额");
}else if(discountAmount.compareTo(BigDecimal.ZERO) <0){
} else if (discountAmount.compareTo(BigDecimal.ZERO) < 0) {
throw new BadRequestException("折扣金额请输入大于0的整数");
}
}else if(ActivityTypeEnum.RATE_DISCOUNT.equals(type)) {
} else if (CouponTypeEnum.RATE_DISCOUNT.equals(type)) {
// 折扣
if(ObjectUtils.isNull(discountRate)) {
if (ObjectUtils.isNull(discountRate)) {
throw new BadRequestException("折扣比例为空,请输入折扣比例");
}else if(discountRate.compareTo(0) < 0 || discountRate.compareTo(100) > 0) {
} else if (discountRate.compareTo(0) < 0 || discountRate.compareTo(100) > 0) {
throw new BadRequestException("折扣比例请输入大于0小于10的整数");
}
}else {
} else {
throw new BadRequestException("优惠券类型不存在");
}
// 发放时间
if(distributeStartTime.isAfter(distributeEndTime)){
if (distributeStartTime.isAfter(distributeEndTime)) {
throw new BadRequestException("结束时间不能早于开始时间");
}
if(distributeEndTime.isBefore(DateUtils.now())) {
if (distributeEndTime.isBefore(DateUtils.now())) {
throw new BadRequestException("发放时间已过期");
}
}
}
}

View File

@@ -1,21 +1,25 @@
package com.jzo2o.market.model.dto.response;
import com.jzo2o.market.enums.ActivityStatusEnum;
import com.jzo2o.market.enums.CouponTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@ApiModel("活动分页字段模型")
@Accessors(chain = true)
public class ActivityInfoResDTO {
@ApiModelProperty("活动id")
private Long id;
@ApiModelProperty("活动名称")
private String name;
@ApiModelProperty("优惠券类型1满减2折扣")
private Integer type;
private CouponTypeEnum type;
@ApiModelProperty("满减限额0表示无门槛其他值最低消费金额")
private BigDecimal amountCondition;
@ApiModelProperty("折扣率折扣类型的折扣率例如8,打8折")
@@ -27,7 +31,7 @@ public class ActivityInfoResDTO {
@ApiModelProperty("发放结束时间")
private LocalDateTime distributeEndTime;
@ApiModelProperty("优惠券配置状态1待生效2进行中3已失效")
private Integer status;
private ActivityStatusEnum status;
@ApiModelProperty("发放数量0表示无限量其他正数表示最大发放量")
private Integer totalNum;
@ApiModelProperty("领取数量")
@@ -40,4 +44,4 @@ public class ActivityInfoResDTO {
private LocalDateTime createTime;
@ApiModelProperty("更新时间")
private LocalDateTime updateTime;
}
}

View File

@@ -3,12 +3,9 @@ package com.jzo2o.market.service;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.market.model.domain.Activity;
import com.baomidou.mybatisplus.extension.service.IService;
import com.jzo2o.market.model.dto.request.ActivityQueryForPageReqDTO;
import com.jzo2o.market.model.dto.request.ActivityPageQueryDTO;
import com.jzo2o.market.model.dto.request.ActivitySaveReqDTO;
import com.jzo2o.market.model.dto.response.ActivityInfoResDTO;
import com.jzo2o.market.model.dto.response.SeizeCouponInfoResDTO;
import java.util.List;
/**
* <p>
@@ -19,6 +16,23 @@ import java.util.List;
* @since 2023-09-16
*/
public interface IActivityService extends IService<Activity> {
/**
* 分页查询活动数据
*/
PageResult<ActivityInfoResDTO> page(ActivityPageQueryDTO activityPageQueryDTO);
/**
* 查询活动详细数据
*/
ActivityInfoResDTO getDetailById(Long id);
}
/**
* 新增/插入活动信息
*/
void saveOrUpdate(ActivitySaveReqDTO activitySaveReqDTO);
/**
* 撤销活动
*/
void revoke(Long id);
}

View File

@@ -1,58 +1,134 @@
package com.jzo2o.market.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.common.expcetions.BadRequestException;
import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.*;
import com.jzo2o.market.constants.TabTypeConstants;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.market.enums.ActivityStatusEnum;
import com.jzo2o.market.enums.CouponStatusEnum;
import com.jzo2o.market.enums.CouponTypeEnum;
import com.jzo2o.market.mapper.ActivityMapper;
import com.jzo2o.market.model.domain.Activity;
import com.jzo2o.market.model.dto.request.ActivityQueryForPageReqDTO;
import com.jzo2o.market.model.domain.Coupon;
import com.jzo2o.market.model.dto.request.ActivityPageQueryDTO;
import com.jzo2o.market.model.dto.request.ActivitySaveReqDTO;
import com.jzo2o.market.model.dto.response.ActivityInfoResDTO;
import com.jzo2o.market.model.dto.response.SeizeCouponInfoResDTO;
import com.jzo2o.market.service.IActivityService;
import com.jzo2o.market.service.ICouponService;
import com.jzo2o.market.service.ICouponWriteOffService;
import com.jzo2o.mysql.utils.PageUtils;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static com.jzo2o.market.constants.RedisConstants.RedisKey.*;
import static com.jzo2o.market.enums.ActivityStatusEnum.*;
/**
* <p>
* 服务实现类
* </p>
*
* @author itcast
* @since 2023-09-16
*/
@Service
public class ActivityServiceImpl extends ServiceImpl<ActivityMapper, Activity> implements IActivityService {
private static final int MILLION = 1000000;
@Resource
private RedisTemplate redisTemplate;
@Resource
private ICouponService couponService;
@Resource
private ICouponWriteOffService couponWriteOffService;
@Override
@SuppressWarnings("unchecked")
public PageResult<ActivityInfoResDTO> page(ActivityPageQueryDTO activityPageQueryDTO) {
// 分页查询
Page<Activity> activityPage = PageUtils.parsePageQuery(activityPageQueryDTO, Activity.class);
LambdaQueryWrapper<Activity> queryWrapper = Wrappers.<Activity>lambdaQuery()
.eq(ObjectUtils.isNotEmpty(activityPageQueryDTO.getId()), Activity::getId, activityPageQueryDTO.getId())
.like(ObjectUtils.isNotEmpty(activityPageQueryDTO.getName()), Activity::getName, activityPageQueryDTO.getName())
.eq(ObjectUtils.isNotEmpty(activityPageQueryDTO.getType()), Activity::getType, activityPageQueryDTO.getType())
.eq(ObjectUtils.isNotEmpty(activityPageQueryDTO.getStatus()), Activity::getStatus, activityPageQueryDTO.getStatus())
// 新创建的活动显示在列表前面
.orderByDesc(Activity::getCreateTime);
Page<Activity> pageAns = baseMapper.selectPage(activityPage, queryWrapper);
return PageUtils.toPage(pageAns, ActivityInfoResDTO.class);
}
}
@Override
public ActivityInfoResDTO getDetailById(Long id) {
Activity activity = baseMapper.selectById(id);
if (ObjectUtils.isEmpty(activity)) {
return new ActivityInfoResDTO();
}
ActivityInfoResDTO activityInfoResDTO = BeanUtils.toBean(activity, ActivityInfoResDTO.class);
List<Coupon> coupons = couponService.lambdaQuery()
.eq(Coupon::getActivityId, id)
.select(Coupon::getStatus)
.list();
activityInfoResDTO.setReceiveNum(coupons.size());
activityInfoResDTO.setWriteOffNum((int) coupons.stream()
.filter(coupon -> coupon.getStatus() == CouponStatusEnum.USED)
.count());
return activityInfoResDTO;
}
@Override
@Transactional
public void saveOrUpdate(ActivitySaveReqDTO activitySaveReqDTO) {
// 参数检验
activitySaveReqDTO.check();
Activity activity = BeanUtils.toBean(activitySaveReqDTO, Activity.class);
activity.setType(CouponTypeEnum.typeOf(activitySaveReqDTO.getType()));
if (ObjectUtils.isEmpty(activity.getId())) {
// 新增记录初始化相关状态
activity.setStatus(ActivityStatusEnum.NO_DISTRIBUTE)
.setStockNum(activity.getTotalNum());
} else {
// 更新需要检查活动状态
Activity activityInDb = baseMapper.selectById(activity.getId());
if (ObjectUtils.isEmpty(activityInDb)) {
throw new ForbiddenOperationException("活动不存在无法修改");
} else if (activityInDb.getStatus() != ActivityStatusEnum.NO_DISTRIBUTE) {
throw new ForbiddenOperationException("活动状态错误无法修改");
}
}
if (!this.saveOrUpdate(activity)) {
throw new DBException("新增/更新活动信息失败");
}
}
@Override
@Transactional
public void revoke(Long id) {
Activity activityInDb = baseMapper.selectById(id);
if (ObjectUtils.isEmpty(activityInDb)) {
throw new ForbiddenOperationException("活动不存在无法撤销");
} else if (activityInDb.getStatus() != ActivityStatusEnum.NO_DISTRIBUTE
&& activityInDb.getStatus() != ActivityStatusEnum.DISTRIBUTING) {
throw new ForbiddenOperationException("活动状态错误无法撤销");
}
if (!lambdaUpdate()
.eq(Activity::getId, id)
.set(Activity::getStatus, ActivityStatusEnum.VOIDED)
.update()) {
throw new DBException("更新活动表失败");
}
// 优惠卷可能没有发放 -> 更新0行
ChainWrappers.lambdaUpdateChain(couponService.getBaseMapper())
.eq(Coupon::getActivityId, id)
.set(Coupon::getStatus, CouponStatusEnum.VOIDED)
.update();
}
}