!315 秒杀活动相关

Merge pull request !315 from halfninety/feature/1.8.0-uniapp
pull/2/head
芋道源码 2022-11-29 15:34:48 +00:00 committed by Gitee
commit aee5f39a77
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
38 changed files with 2666 additions and 2 deletions

View File

@ -25,6 +25,8 @@ public class DateUtils {
public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";
public static final String FORMAT_HOUR_MINUTE_SECOND = "HH:mm:ss";
/**
* LocalDateTime Date
*

View File

@ -1,8 +1,9 @@
package cn.iocoder.yudao.framework.jackson.config;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.jackson.core.databind.LocalDateTimeDeserializer;
import cn.iocoder.yudao.framework.jackson.core.databind.LocalDateTimeSerializer;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.jackson.core.databind.LocalTimeJson;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import lombok.extern.slf4j.Slf4j;
@ -12,6 +13,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
import java.time.LocalTime;
@Configuration(proxyBeanMethods = false)
@Slf4j
@ -30,12 +32,15 @@ public class YudaoJacksonAutoConfiguration {
/*
* 1. Long2^53-1JSLong
* 2. LocalDateTime
* 3. LocalTime
*/
simpleModule
// .addSerializer(Long.class, ToStringSerializer.instance)
// .addSerializer(Long.TYPE, ToStringSerializer.instance)
.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE)
.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE)
.addSerializer(LocalTime.class, LocalTimeJson.SERIALIZER)
.addDeserializer(LocalTime.class, LocalTimeJson.DESERIALIZABLE);
objectMapper.registerModules(simpleModule);

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.framework.jackson.core.databind;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_HOUR_MINUTE_SECOND;
public class LocalTimeJson {
public static final LocalTimeSerializer SERIALIZER = new LocalTimeSerializer(DateTimeFormatter
.ofPattern(FORMAT_HOUR_MINUTE_SECOND)
.withZone(ZoneId.systemDefault()));
public static final LocalTimeDeserializer DESERIALIZABLE = new LocalTimeDeserializer(DateTimeFormatter
.ofPattern(FORMAT_HOUR_MINUTE_SECOND)
.withZone(ZoneId.systemDefault()));
}

View File

@ -45,4 +45,16 @@ public interface ErrorCodeConstants {
// ========== Price 相关 1003007000 ============
ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1003007000, "支付价格计算异常,原因:价格小于等于 0");
// ========== 秒杀活动 1003008000 ==========
ErrorCode SECKILL_ACTIVITY_NOT_EXISTS = new ErrorCode(1003008000, "秒杀活动不存在");
ErrorCode SECKILL_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1003008002, "存在商品参加了其它秒杀活动");
ErrorCode SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1003008003, "秒杀活动已关闭,不能修改");
ErrorCode SECKILL_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1003008004, "秒杀活动未关闭或未结束,不能删除");
ErrorCode SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1003008005, "秒杀活动已关闭,不能重复关闭");
ErrorCode SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1003008006, "秒杀活动已结束,不能关闭");
// ========== 秒杀时段 1003009000 ==========
ErrorCode SECKILL_TIME_NOT_EXISTS = new ErrorCode(1003009000, "秒杀时段不存在");
ErrorCode SECKILL_TIME_CONFLICTS = new ErrorCode(1003009001, "秒杀时段冲突");
}

View File

@ -0,0 +1,96 @@
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo.*;
import cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity.SeckillActivityConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillProductDO;
import cn.iocoder.yudao.module.promotion.service.seckill.seckillactivity.SeckillActivityService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Api(tags = "管理后台 - 秒杀活动")
@RestController
@RequestMapping("/promotion/seckill-activity")
@Validated
public class SeckillActivityController {
@Resource
private SeckillActivityService seckillActivityService;
@PostMapping("/create")
@ApiOperation("创建秒杀活动")
@PreAuthorize("@ss.hasPermission('promotion:seckill-activity:create')")
public CommonResult<Long> createSeckillActivity(@Valid @RequestBody SeckillActivityCreateReqVO createReqVO) {
return success(seckillActivityService.createSeckillActivity(createReqVO));
}
@PutMapping("/update")
@ApiOperation("更新秒杀活动")
@PreAuthorize("@ss.hasPermission('promotion:seckill-activity:update')")
public CommonResult<Boolean> updateSeckillActivity(@Valid @RequestBody SeckillActivityUpdateReqVO updateReqVO) {
seckillActivityService.updateSeckillActivity(updateReqVO);
return success(true);
}
@PutMapping("/close")
@ApiOperation("关闭秒杀活动")
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('promotion:seckill-activity:close')")
public CommonResult<Boolean> closeSeckillActivity(@RequestParam("id") Long id) {
seckillActivityService.closeSeckillActivity(id);
return success(true);
}
@DeleteMapping("/delete")
@ApiOperation("删除秒杀活动")
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('promotion:seckill-activity:delete')")
public CommonResult<Boolean> deleteSeckillActivity(@RequestParam("id") Long id) {
seckillActivityService.deleteSeckillActivity(id);
return success(true);
}
@GetMapping("/get")
@ApiOperation("获得秒杀活动")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('promotion:seckill-activity:query')")
public CommonResult<SeckillActivityDetailRespVO> getSeckillActivity(@RequestParam("id") Long id) {
SeckillActivityDO seckillActivity = seckillActivityService.getSeckillActivity(id);
if (seckillActivity == null) {
return success(null);
}
List<SeckillProductDO> seckillProducts = seckillActivityService.getSeckillProductListByActivityId(id);
return success(SeckillActivityConvert.INSTANCE.convert(seckillActivity,seckillProducts));
}
@GetMapping("/list")
@ApiOperation("获得秒杀活动列表")
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)
@PreAuthorize("@ss.hasPermission('promotion:seckill-activity:query')")
public CommonResult<List<SeckillActivityRespVO>> getSeckillActivityList(@RequestParam("ids") Collection<Long> ids) {
List<SeckillActivityDO> list = seckillActivityService.getSeckillActivityList(ids);
return success(SeckillActivityConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@ApiOperation("获得秒杀活动分页")
@PreAuthorize("@ss.hasPermission('promotion:seckill-activity:query')")
public CommonResult<PageResult<SeckillActivityRespVO>> getSeckillActivityPage(@Valid SeckillActivityPageReqVO pageVO) {
PageResult<SeckillActivityDO> pageResult = seckillActivityService.getSeckillActivityPage(pageVO);
return success(SeckillActivityConvert.INSTANCE.convertPage(pageResult));
}
}

View File

@ -0,0 +1,66 @@
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
/**
* Base VO VO 使
* VO Swagger
*/
@Data
public class SeckillActivityBaseVO {
@ApiModelProperty(value = "秒杀活动名称", required = true, example = "晚九点限时秒杀")
@NotNull(message = "秒杀活动名称不能为空")
private String name;
@ApiModelProperty(value = "活动开始时间", required = true)
@NotNull(message = "活动开始时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private LocalDateTime startTime;
@ApiModelProperty(value = "活动结束时间", required = true)
@NotNull(message = "活动结束时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private LocalDateTime endTime;
@ApiModel("商品")
@Data
public static class Product {
@ApiModelProperty(value = "商品 SPU 编号", required = true, example = "1")
@NotNull(message = "商品 SPU 编号不能为空")
private Long spuId;
@ApiModelProperty(value = "商品 SKU 编号", required = true, example = "1")
@NotNull(message = "商品 SKU 编号不能为空")
private Long skuId;
@ApiModelProperty(value = "秒杀金额", required = true, example = "12.00")
@NotNull(message = "秒杀金额不能为空")
private Integer seckillPrice;
@ApiModelProperty(value = "秒杀库存", example = "80")
@Min(value = 0, message = "秒杀库存需要大于等于 0")
private Integer stock;
@ApiModelProperty(value = "每人限购", example = "10", notes = "如果为0则不限购")
@Min(value = 0, message = "每人限购需要大于等于 0")
private Integer limitBuyCount;
}
}

View File

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
@ApiModel("管理后台 - 秒杀活动创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillActivityCreateReqVO extends SeckillActivityBaseVO {
@ApiModelProperty(value = "备注", example = "限时秒杀活动")
private String remark;
@ApiModelProperty(value = "排序", required = true)
@NotNull(message = "排序不能为空")
private Integer sort;
@ApiModelProperty(value = "秒杀时段id", required = true)
@NotBlank(message = "参与场次不能为空")
private String timeId;
/**
*
*/
@NotEmpty(message = "商品列表不能为空")
@Valid
private List<Product> products;
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.List;
@ApiModel("管理后台 - 秒杀活动的详细 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillActivityDetailRespVO extends SeckillActivityRespVO{
/**
*
*/
private List<Product> products;
}

View File

@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
@ApiModel("管理后台 - 秒杀活动分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillActivityPageReqVO extends PageParam {
@ApiModelProperty(value = "秒杀活动名称", example = "晚九点限时秒杀")
private String name;
@ApiModelProperty(value = "活动状态", example = "进行中")
private Integer status;
@ApiModelProperty(value = "秒杀时段id")
private String timeId;
@ApiModelProperty(value = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.time.LocalDateTime;
@ApiModel("管理后台 - 秒杀活动 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillActivityRespVO extends SeckillActivityBaseVO {
@ApiModelProperty(value = "秒杀活动id", required = true, example = "1")
private Long id;
@ApiModelProperty(value = "付款订单数", required = true)
private Integer orderCount;
@ApiModelProperty(value = "付款人数", required = true)
private Integer userCount;
@ApiModelProperty(value = "创建时间", required = true)
private LocalDateTime createTime;
@ApiModelProperty(value = "秒杀时段id", required = true)
private String timeId;
@ApiModelProperty(value = "排序", required = true)
private Integer sort;
@ApiModelProperty(value = "备注", example = "限时秒杀活动")
private String remark;
@ApiModelProperty(value = "活动状态", example = "进行中")
private Integer status;
}

View File

@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
@ApiModel("管理后台 - 秒杀活动更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillActivityUpdateReqVO extends SeckillActivityBaseVO {
@ApiModelProperty(value = "秒杀活动编号", required = true, example = "224")
@NotNull(message = "秒杀活动编号不能为空")
private Long id;
@ApiModelProperty(value = "备注", example = "限时秒杀活动")
private String remark;
@ApiModelProperty(value = "排序", required = true)
@NotNull(message = "排序不能为空")
private Integer sort;
@ApiModelProperty(value = "秒杀时段id", required = true)
@NotNull(message = "秒杀时段id不能为空")
private String timeId;
/**
*
*/
@NotEmpty(message = "商品列表不能为空")
@Valid
private List<Product> products;
}

View File

@ -0,0 +1,81 @@
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo.SeckillTimeCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo.SeckillTimeRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo.SeckillTimeUpdateReqVO;
import cn.iocoder.yudao.module.promotion.convert.seckill.seckilltime.SeckillTimeConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckilltime.SeckillTimeDO;
import cn.iocoder.yudao.module.promotion.service.seckill.seckilltime.SeckillTimeService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Api(tags = "管理后台 - 秒杀时段")
@RestController
@RequestMapping("/promotion/seckill-time")
@Validated
public class SeckillTimeController {
@Resource
private SeckillTimeService seckillTimeService;
@PostMapping("/create")
@ApiOperation("创建秒杀时段")
@PreAuthorize("@ss.hasPermission('promotion:seckill-time:create')")
public CommonResult<Long> createSeckillTime(@Valid @RequestBody SeckillTimeCreateReqVO createReqVO) {
return success(seckillTimeService.createSeckillTime(createReqVO));
}
@PutMapping("/update")
@ApiOperation("更新秒杀时段")
@PreAuthorize("@ss.hasPermission('promotion:seckill-time:update')")
public CommonResult<Boolean> updateSeckillTime(@Valid @RequestBody SeckillTimeUpdateReqVO updateReqVO) {
seckillTimeService.updateSeckillTime(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@ApiOperation("删除秒杀时段")
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('promotion:seckill-time:delete')")
public CommonResult<Boolean> deleteSeckillTime(@RequestParam("id") Long id) {
seckillTimeService.deleteSeckillTime(id);
return success(true);
}
@GetMapping("/get")
@ApiOperation("获得秒杀时段")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('promotion:seckill-time:query')")
public CommonResult<SeckillTimeRespVO> getSeckillTime(@RequestParam("id") Long id) {
SeckillTimeDO seckillTime = seckillTimeService.getSeckillTime(id);
return success(SeckillTimeConvert.INSTANCE.convert(seckillTime));
}
@GetMapping("/list")
@ApiOperation("获得所有秒杀时段列表")
// @PreAuthorize("@ss.hasPermission('promotion:seckill-time:query')")
public CommonResult<List<SeckillTimeRespVO>> getSeckillTimeList() {
List<SeckillTimeDO> list = seckillTimeService.getSeckillTimeList();
return success(SeckillTimeConvert.INSTANCE.convertList(list));
}
// @GetMapping("/page")
// @ApiOperation("获得秒杀时段分页")
// @PreAuthorize("@ss.hasPermission('promotion:seckill-time:query')")
// public CommonResult<PageResult<SeckillTimeRespVO>> getSeckillTimePage(@Valid SeckillTimePageReqVO pageVO) {
// PageResult<SeckillTimeDO> pageResult = seckillTimeService.getSeckillTimePage(pageVO);
// return success(SeckillTimeConvert.INSTANCE.convertPage(pageResult));
// }
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo;
import lombok.*;
import java.time.LocalTime;
import java.util.*;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
import org.springframework.format.annotation.DateTimeFormat;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* Base VO VO 使
* VO Swagger
*/
@Data
public class SeckillTimeBaseVO {
@ApiModelProperty(value = "秒杀时段名称", required = true)
@NotNull(message = "秒杀时段名称不能为空")
private String name;
@ApiModelProperty(value = "开始时间点", required = true)
@NotNull(message = "开始时间点不能为空")
private LocalTime startTime;
@ApiModelProperty(value = "结束时间点", required = true)
@NotNull(message = "结束时间点不能为空")
private LocalTime endTime;
}

View File

@ -0,0 +1,12 @@
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo;
import lombok.*;
import io.swagger.annotations.*;
@ApiModel("管理后台 - 秒杀时段创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillTimeCreateReqVO extends SeckillTimeBaseVO {
}

View File

@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalTime;
@ApiModel("管理后台 - 秒杀时段分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillTimePageReqVO extends PageParam {
@ApiModelProperty(value = "秒杀时段名称")
private String name;
@ApiModelProperty(value = "开始时间点")
@DateTimeFormat(pattern = "HH:mm:ss")
private LocalTime startTime;
@ApiModelProperty(value = "结束时间点")
@DateTimeFormat(pattern = "HH:mm:ss")
private LocalTime endTime;
// @ApiModelProperty(value = "创建时间")
// @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
// private Date[] createTime;
}

View File

@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
@ApiModel("管理后台 - 秒杀时段 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillTimeRespVO extends SeckillTimeBaseVO {
@ApiModelProperty(value = "编号", required = true)
private Long id;
@ApiModelProperty(value = "秒杀活动数量", required = true)
private Integer seckillActivityCount;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
}

View File

@ -0,0 +1,4 @@
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo;
public class SeckillTimeSimpleRespVO {
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
@ApiModel("管理后台 - 秒杀时段更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SeckillTimeUpdateReqVO extends SeckillTimeBaseVO {
@ApiModelProperty(value = "编号", required = true)
@NotNull(message = "编号不能为空")
private Long id;
}

View File

@ -0,0 +1,77 @@
package cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo.*;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillProductDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* Convert
*
* @author
*/
@Mapper
public interface SeckillActivityConvert {
SeckillActivityConvert INSTANCE = Mappers.getMapper(SeckillActivityConvert.class);
SeckillProductDO convert(SeckillActivityBaseVO.Product product);
SeckillActivityDO convert(SeckillActivityCreateReqVO bean);
default String map(Long[] value){
return value.toString();
}
SeckillActivityDO convert(SeckillActivityUpdateReqVO bean);
SeckillActivityRespVO convert(SeckillActivityDO bean);
List<SeckillActivityRespVO> convertList(List<SeckillActivityDO> list);
PageResult<SeckillActivityRespVO> convertPage(PageResult<SeckillActivityDO> page);
@Mappings({@Mapping(target = "products",source = "seckillProducts")})
SeckillActivityDetailRespVO convert(SeckillActivityDO seckillActivity, List<SeckillProductDO> seckillProducts);
/**
*
*
* @param productDO
* @param productVO
* @return
*/
default boolean isEquals(SeckillProductDO productDO, SeckillActivityBaseVO.Product productVO) {
return ObjectUtil.equals(productDO.getSpuId(), productVO.getSpuId())
&& ObjectUtil.equals(productDO.getSkuId(), productVO.getSkuId())
&& ObjectUtil.equals(productDO.getSeckillPrice(), productVO.getSeckillPrice())
&& ObjectUtil.equals(productDO.getStock(), productVO.getStock())
&& ObjectUtil.equals(productDO.getLimitBuyCount(), productVO.getLimitBuyCount());
}
/**
*
*
* @param productDO 1
* @param productVO 2
* @return
*/
default boolean isEquals(SeckillProductDO productDO, SeckillProductDO productVO) {
return ObjectUtil.equals(productDO.getSpuId(), productVO.getSpuId())
&& ObjectUtil.equals(productDO.getSkuId(), productVO.getSkuId())
&& ObjectUtil.equals(productDO.getSeckillPrice(), productVO.getSeckillPrice())
&& ObjectUtil.equals(productDO.getStock(), productVO.getStock())
&& ObjectUtil.equals(productDO.getLimitBuyCount(), productVO.getLimitBuyCount());
}
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.promotion.convert.seckill.seckilltime;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo.SeckillTimeCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo.SeckillTimeRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo.SeckillTimeUpdateReqVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckilltime.SeckillTimeDO;
/**
* Convert
*
* @author
*/
@Mapper
public interface SeckillTimeConvert {
SeckillTimeConvert INSTANCE = Mappers.getMapper(SeckillTimeConvert.class);
SeckillTimeDO convert(SeckillTimeCreateReqVO bean);
SeckillTimeDO convert(SeckillTimeUpdateReqVO bean);
SeckillTimeRespVO convert(SeckillTimeDO bean);
List<SeckillTimeRespVO> convertList(List<SeckillTimeDO> list);
PageResult<SeckillTimeRespVO> convertPage(PageResult<SeckillTimeDO> page);
}

View File

@ -0,0 +1,76 @@
package cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* DO
*
* @author
*/
@TableName("promotion_seckill_activity")
@KeySequence("promotion_seckill_activity_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SeckillActivityDO extends BaseDO {
/**
*
*/
@TableId
private Long id;
/**
*
*/
private String name;
/**
*
*
* {@link PromotionActivityStatusEnum }
*/
private Integer status;
/**
*
*/
private String remark;
/**
*
*/
private LocalDateTime startTime;
/**
*
*/
private LocalDateTime endTime;
/**
*
*/
private Integer sort;
/**
* id
*/
private String timeId;
/**
*
*/
private Integer orderCount;
/**
*
*/
private Integer userCount;
/**
*
*/
private BigDecimal totalPrice;
}

View File

@ -0,0 +1,60 @@
package cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import lombok.Data;
/**
*
* @TableName promotion_seckill_product
*/
@TableName(value ="promotion_seckill_product")
@Data
public class SeckillProductDO extends BaseDO {
/**
*
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* id
*/
private Long activityId;
/**
* id
*/
private Long timeId;
/**
* id
*/
private Long spuId;
/**
* sku_id
*/
private Long skuId;
/**
*
*/
private Integer seckillPrice;
/**
*
*/
private Integer stock;
/**
*
*/
private Integer limitBuyCount;
}

View File

@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckilltime;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalTime;
/**
* DO
*
* @author
*/
@TableName("promotion_seckill_time")
@KeySequence("promotion_seckill_time_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SeckillTimeDO extends BaseDO {
/**
*
*/
@TableId
private Long id;
/**
*
*/
private String name;
/**
*
*/
private LocalTime startTime;
/**
*
*/
private LocalTime endTime;
/**
*
*/
private Integer seckillActivityCount;
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo.SeckillActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO;
import org.apache.ibatis.annotations.Mapper;
/**
* Mapper
*
* @author
*/
@Mapper
public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> {
// TODO: 2022/11/28 halfninety 秒杀活动通过场次查询使用like会出现问题查询活动场次编号为1则活动场次编号为 111......的都会被查出来
default PageResult<SeckillActivityDO> selectPage(SeckillActivityPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<SeckillActivityDO>()
.likeIfPresent(SeckillActivityDO::getName, reqVO.getName())
.eqIfPresent(SeckillActivityDO::getStatus, reqVO.getStatus())
.likeIfPresent(SeckillActivityDO::getTimeId, reqVO.getTimeId())
// .like(StringUtils.hasText(reqVO.getTimeId()),SeckillActivityDO::getTimeId, reqVO.getTimeId() + ",")
.betweenIfPresent(SeckillActivityDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(SeckillActivityDO::getId));
}
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillProductDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
@Mapper
public interface SeckillProductMapper extends BaseMapperX<SeckillProductDO> {
default List<SeckillProductDO> selectListByActivityId(Long id){
return selectList(SeckillProductDO::getActivityId,id);
}
default List<SeckillProductDO> selectListBySkuIds(Collection<Long> skuIds){
return selectList(SeckillProductDO::getSkuId,skuIds);
}
}

View File

@ -0,0 +1,58 @@
package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckilltime;
import java.time.LocalTime;
import java.util.*;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckilltime.SeckillTimeDO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import org.apache.ibatis.annotations.Mapper;
/**
* Mapper
*
* @author
*/
@Mapper
public interface SeckillTimeMapper extends BaseMapperX<SeckillTimeDO> {
default List<SeckillTimeDO> selectListWithTime(LocalTime time){
if (time == null) {
return Collections.emptyList();
}
return selectList(new LambdaQueryWrapper<SeckillTimeDO>()
.le(SeckillTimeDO::getStartTime,time)
.ge(SeckillTimeDO::getEndTime,time));
}
default List<SeckillTimeDO> selectListWithTime(LocalTime startTime, LocalTime endTime){
if (startTime == null && endTime == null) {
return Collections.emptyList();
}
return selectList(new LambdaQueryWrapper<SeckillTimeDO>()
.ge(SeckillTimeDO::getStartTime,startTime)
.le(SeckillTimeDO::getEndTime,endTime));
}
default void sekillActivityCountAdd(List<Long> ids){
if (CollUtil.isEmpty(ids)){
return;
}
new LambdaUpdateChainWrapper<SeckillTimeDO>(this)
.in(SeckillTimeDO::getId,ids)
.setSql("`seckill_activity_count` = `seckill_activity_count` + 1 ")
.update();
}
default void sekillActivityCountReduce(List<Long> ids){
if (CollUtil.isEmpty(ids)){
return;
}
new LambdaUpdateChainWrapper<SeckillTimeDO>(this)
.in(SeckillTimeDO::getId,ids)
.setSql("`seckill_activity_count` = `seckill_activity_count` - 1 ")
.update();
}
}

View File

@ -0,0 +1,78 @@
package cn.iocoder.yudao.module.promotion.service.seckill.seckillactivity;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo.SeckillActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo.SeckillActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo.SeckillActivityUpdateReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillProductDO;
/**
* Service
*
* @author
*/
public interface SeckillActivityService {
/**
*
*
* @param createReqVO
* @return
*/
Long createSeckillActivity(@Valid SeckillActivityCreateReqVO createReqVO);
/**
*
*
* @param updateReqVO
*/
void updateSeckillActivity(@Valid SeckillActivityUpdateReqVO updateReqVO);
/**
*
* @param id
*/
void closeSeckillActivity(Long id);
/**
*
*
* @param id
*/
void deleteSeckillActivity(Long id);
/**
*
*
* @param id
* @return
*/
SeckillActivityDO getSeckillActivity(Long id);
/**
*
*
* @param ids
* @return
*/
List<SeckillActivityDO> getSeckillActivityList(Collection<Long> ids);
/**
*
*
* @param pageReqVO
* @return
*/
PageResult<SeckillActivityDO> getSeckillActivityPage(SeckillActivityPageReqVO pageReqVO);
/**
*
* @param id
* @return
*/
List<SeckillProductDO> getSeckillProductListByActivityId(Long id);
}

View File

@ -0,0 +1,221 @@
package cn.iocoder.yudao.module.promotion.service.seckill.seckillactivity;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo.SeckillActivityBaseVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo.SeckillActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo.SeckillActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo.SeckillActivityUpdateReqVO;
import cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity.SeckillActivityConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillProductDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillActivityMapper;
import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillProductMapper;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
import cn.iocoder.yudao.module.promotion.service.seckill.seckilltime.SeckillTimeService;
import cn.iocoder.yudao.module.promotion.util.PromotionUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
import static java.util.Arrays.asList;
/**
* Service
*
* @author
*/
@Service
@Validated
public class SeckillActivityServiceImpl implements SeckillActivityService {
@Resource
private SeckillActivityMapper seckillActivityMapper;
@Resource
private SeckillProductMapper seckillProductMapper;
@Resource
private SeckillTimeService seckillTimeService;
@Override
public Long createSeckillActivity(SeckillActivityCreateReqVO createReqVO) {
List<Integer> statuses = asList(PromotionActivityStatusEnum.WAIT.getStatus(), PromotionActivityStatusEnum.RUN.getStatus());
// 校验商品是否冲突
validateSeckillActivityProductConflicts(null, createReqVO.getProducts());
// 更新秒杀时段的秒杀活动数量
seckillTimeService.sekillActivityCountAdd(StrUtils.splitToLong(createReqVO.getTimeId(),","));
// 插入秒杀活动
SeckillActivityDO seckillActivity = SeckillActivityConvert.INSTANCE.convert(createReqVO)
.setStatus(PromotionUtils.calculateActivityStatus(createReqVO.getStartTime(), createReqVO.getEndTime()));
seckillActivityMapper.insert(seckillActivity);
// 插入商品
List<SeckillProductDO> productDOS = CollectionUtils.convertList(createReqVO.getProducts(),
product -> SeckillActivityConvert.INSTANCE.convert(product).setActivityId(seckillActivity.getId()));
seckillProductMapper.insertBatch(productDOS);
return seckillActivity.getId();
}
@Override
public void updateSeckillActivity(SeckillActivityUpdateReqVO updateReqVO) {
// 校验存在
SeckillActivityDO seckillActivity = this.validateSeckillActivityExists(updateReqVO.getId());
if (PromotionActivityStatusEnum.CLOSE.getStatus().equals(seckillActivity.getStatus())) {
throw exception(SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED);
}
// 校验商品是否冲突
validateSeckillActivityProductConflicts(updateReqVO.getId(), updateReqVO.getProducts());
// 更新秒杀时段的秒杀活动数量
updateSeckillTimeActivityCount(updateReqVO.getId(), updateReqVO.getTimeId());
// 更新活动
SeckillActivityDO updateObj = SeckillActivityConvert.INSTANCE.convert(updateReqVO)
.setStatus(PromotionUtils.calculateActivityStatus(updateReqVO.getStartTime(), updateReqVO.getEndTime()));
seckillActivityMapper.updateById(updateObj);
// 更新商品
updateSeckillProduct(updateReqVO);
}
/**
*
*
* @param id
* @param timeId
*/
private void updateSeckillTimeActivityCount(Long id, String timeId) {
List<Long> updateTimeIds = StrUtils.splitToLong(timeId, ",");
// 查出自己的timeIds
SeckillActivityDO seckillActivityDO = seckillActivityMapper.selectById(id);
List<Long> existsTimeIds = StrUtils.splitToLong(seckillActivityDO.getTimeId(), ",");
//需要减少的时间段
List<Long> reduceIds = existsTimeIds.stream()
.filter(existsTimeId -> !updateTimeIds.contains(existsTimeId))
.collect(Collectors.toList());
//需要添加的时间段
updateTimeIds.removeIf(updateTimeId -> existsTimeIds.contains(updateTimeId));
//更新减少时间段和增加时间段
seckillTimeService.sekillActivityCountAdd(updateTimeIds);
seckillTimeService.sekillActivityCountReduce(reduceIds);
}
/**
*
*/
private void updateSeckillProduct(SeckillActivityUpdateReqVO updateReqVO) {
List<SeckillProductDO> seckillProductDOS = seckillProductMapper.selectListByActivityId(updateReqVO.getId());
List<SeckillActivityBaseVO.Product> products = updateReqVO.getProducts();
//对后台查出的数据和前台查出的数据进行遍历,
//1.对前台数据进行遍历如果不存在于后台的sku中需要新增
//2.对后台数据进行遍历如果不存在于前台的sku中需要删除
//计算需要删除的数据
List<Long> deleteIds = CollectionUtils.convertList(seckillProductDOS, SeckillProductDO::getId,
seckillProductDO -> products.stream()
.noneMatch(product -> SeckillActivityConvert.INSTANCE.isEquals(seckillProductDO, product)));
if (CollUtil.isNotEmpty(deleteIds)) {
seckillProductMapper.deleteBatchIds(deleteIds);
}
//计算需要新增的数据
List<SeckillProductDO> newSeckillProductDOs = CollectionUtils.convertList(products,
product -> SeckillActivityConvert.INSTANCE.convert(product).setActivityId(updateReqVO.getId()));
newSeckillProductDOs.removeIf(product -> seckillProductDOS.stream()
.anyMatch(seckillProduct -> SeckillActivityConvert.INSTANCE.isEquals(seckillProduct, product)));
if (CollUtil.isNotEmpty(newSeckillProductDOs)) {
seckillProductMapper.insertBatch(newSeckillProductDOs);
}
}
/**
*
*
* @param id
* @param products
*/
private void validateSeckillActivityProductConflicts(Long id, List<SeckillActivityBaseVO.Product> products) {
if (CollUtil.isEmpty(products)) {
return;
}
List<SeckillProductDO> seckillProductDOS = seckillProductMapper
.selectListBySkuIds(CollectionUtils.convertSet(products, SeckillActivityBaseVO.Product::getSkuId));
if (CollUtil.isEmpty(seckillProductDOS)) {
return;
}
List<SeckillActivityDO> seckillActivityDOS = seckillActivityMapper
.selectBatchIds(CollectionUtils.convertSet(seckillProductDOS, SeckillProductDO::getActivityId));
if (id != null) {// 排除自己这个活动
seckillActivityDOS.removeIf(item -> id.equals(item.getId()));
}
// 排除不满足status的活动
List<Integer> statuses = asList(PromotionActivityStatusEnum.WAIT.getStatus(), PromotionActivityStatusEnum.RUN.getStatus());
seckillActivityDOS.removeIf(item -> !statuses.contains(item.getStatus()));
//如果非空,则说明冲突
if (CollUtil.isNotEmpty(seckillActivityDOS)) {
throw exception(SECKILL_ACTIVITY_SPU_CONFLICTS);
}
}
@Override
public void closeSeckillActivity(Long id) {
// 校验存在
SeckillActivityDO seckillActivity = this.validateSeckillActivityExists(id);
if (PromotionActivityStatusEnum.CLOSE.getStatus().equals(seckillActivity.getStatus())) {
throw exception(SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED);
}
if (PromotionActivityStatusEnum.END.getStatus().equals(seckillActivity.getStatus())) {
throw exception(SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_END);
}
// 更新
SeckillActivityDO updateObj = new SeckillActivityDO().setId(id).setStatus(PromotionActivityStatusEnum.CLOSE.getStatus());
seckillActivityMapper.updateById(updateObj);
}
@Override
public void deleteSeckillActivity(Long id) {
// 校验存在
SeckillActivityDO seckillActivity = this.validateSeckillActivityExists(id);
List<Integer> statuses = asList(PromotionActivityStatusEnum.CLOSE.getStatus(), PromotionActivityStatusEnum.END.getStatus());
if (!statuses.contains(seckillActivity.getStatus())) {
throw exception(SECKILL_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END);
}
// 更新秒杀时段的秒杀活动数量
seckillTimeService.sekillActivityCountReduce(StrUtils.splitToLong(seckillActivity.getTimeId(),","));
// 删除
seckillActivityMapper.deleteById(id);
}
private SeckillActivityDO validateSeckillActivityExists(Long id) {
SeckillActivityDO seckillActivity = seckillActivityMapper.selectById(id);
if (seckillActivity == null) {
throw exception(SECKILL_ACTIVITY_NOT_EXISTS);
}
return seckillActivity;
}
@Override
public SeckillActivityDO getSeckillActivity(Long id) {
return seckillActivityMapper.selectById(id);
}
@Override
public List<SeckillActivityDO> getSeckillActivityList(Collection<Long> ids) {
return seckillActivityMapper.selectBatchIds(ids);
}
@Override
public PageResult<SeckillActivityDO> getSeckillActivityPage(SeckillActivityPageReqVO pageReqVO) {
return seckillActivityMapper.selectPage(pageReqVO);
}
@Override
public List<SeckillProductDO> getSeckillProductListByActivityId(Long id) {
return seckillProductMapper.selectListByActivityId(id);
}
}

View File

@ -0,0 +1,68 @@
package cn.iocoder.yudao.module.promotion.service.seckill.seckilltime;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo.SeckillTimeCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo.SeckillTimeUpdateReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckilltime.SeckillTimeDO;
import javax.validation.Valid;
import java.util.List;
/**
* Service
*
* @author
*/
public interface SeckillTimeService {
/**
*
*
* @param createReqVO
* @return
*/
Long createSeckillTime(@Valid SeckillTimeCreateReqVO createReqVO);
/**
*
*
* @param updateReqVO
*/
void updateSeckillTime(@Valid SeckillTimeUpdateReqVO updateReqVO);
/**
*
*
* @param id
*/
void deleteSeckillTime(Long id);
/**
*
*
* @param id
* @return
*/
SeckillTimeDO getSeckillTime(Long id);
/**
*
*
* @return
*/
List<SeckillTimeDO> getSeckillTimeList();
/**
* 1
*
* @param ids id
*/
void sekillActivityCountAdd(List<Long> ids);
/**
* 1
*
* @param ids id
*/
void sekillActivityCountReduce(List<Long> ids);
}

View File

@ -0,0 +1,117 @@
package cn.iocoder.yudao.module.promotion.service.seckill.seckilltime;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo.SeckillTimeCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo.SeckillTimeUpdateReqVO;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import java.time.LocalTime;
import java.util.*;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckilltime.SeckillTimeDO;
import cn.iocoder.yudao.module.promotion.convert.seckill.seckilltime.SeckillTimeConvert;
import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckilltime.SeckillTimeMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
/**
* Service
*
* @author
*/
@Service
@Validated
public class SeckillTimeServiceImpl implements SeckillTimeService {
@Resource
private SeckillTimeMapper seckillTimeMapper;
@Override
public Long createSeckillTime(SeckillTimeCreateReqVO createReqVO) {
// 校验时间段是否冲突
validateSeckillTimeConflict(null,createReqVO.getStartTime(), createReqVO.getEndTime());
// 插入
SeckillTimeDO seckillTime = SeckillTimeConvert.INSTANCE.convert(createReqVO);
seckillTimeMapper.insert(seckillTime);
// 返回
return seckillTime.getId();
}
@Override
public void updateSeckillTime(SeckillTimeUpdateReqVO updateReqVO) {
// 校验存在
this.validateSeckillTimeExists(updateReqVO.getId());
// 校验时间段是否冲突
validateSeckillTimeConflict(updateReqVO.getId(), updateReqVO.getStartTime(), updateReqVO.getEndTime());
// 更新
SeckillTimeDO updateObj = SeckillTimeConvert.INSTANCE.convert(updateReqVO);
seckillTimeMapper.updateById(updateObj);
}
@Override
public void deleteSeckillTime(Long id) {
// 校验存在
this.validateSeckillTimeExists(id);
// 删除
seckillTimeMapper.deleteById(id);
}
private void validateSeckillTimeExists(Long id) {
if (seckillTimeMapper.selectById(id) == null) {
throw exception(SECKILL_TIME_NOT_EXISTS);
}
}
/**
*
*
* @param startTime
* @param endTime
*/
private void validateSeckillTimeConflict(Long id, LocalTime startTime, LocalTime endTime) {
//查询开始时间,结束时间,是否在别人的时间段内
List<SeckillTimeDO> startTimeList = seckillTimeMapper.selectListWithTime(startTime);
List<SeckillTimeDO> endTimeList = seckillTimeMapper.selectListWithTime(endTime);
//查询自己时间段内是否有时间段
List<SeckillTimeDO> startEndTimeList = seckillTimeMapper.selectListWithTime(startTime, endTime);
if (id != null) {
//移除自己
startTimeList.removeIf(seckillTime -> Objects.equals(seckillTime.getId(), id));
endTimeList.removeIf(seckillTime -> Objects.equals(seckillTime.getId(), id));
startEndTimeList.removeIf(seckillTime -> Objects.equals(seckillTime.getId(), id));
}
if (CollUtil.isNotEmpty(startTimeList) || CollUtil.isNotEmpty(endTimeList)
|| CollUtil.isNotEmpty(startEndTimeList)) {
throw exception(SECKILL_TIME_CONFLICTS);
}
}
@Override
public SeckillTimeDO getSeckillTime(Long id) {
return seckillTimeMapper.selectById(id);
}
@Override
public List<SeckillTimeDO> getSeckillTimeList() {
return seckillTimeMapper.selectList();
}
@Override
public void sekillActivityCountAdd(List<Long> ids) {
seckillTimeMapper.sekillActivityCountAdd(ids);
}
@Override
public void sekillActivityCountReduce(List<Long> ids) {
seckillTimeMapper.sekillActivityCountReduce(ids);
}
}

View File

@ -0,0 +1,12 @@
<?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="cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillActivityMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -0,0 +1,12 @@
<?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="cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckilltime.SeckillTimeMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -0,0 +1,170 @@
package cn.iocoder.yudao.module.promotion.service.seckillactivity;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo.SeckillActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo.SeckillActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckillactivity.vo.SeckillActivityUpdateReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckillactivity.SeckillActivityDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillActivityMapper;
import cn.iocoder.yudao.module.promotion.service.seckill.seckillactivity.SeckillActivityServiceImpl;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.SECKILL_ACTIVITY_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link SeckillActivityServiceImpl}
*
* @author
*/
@Import(SeckillActivityServiceImpl.class)
public class SeckillActivityServiceImplTest extends BaseDbUnitTest {
@Resource
private SeckillActivityServiceImpl seckillActivityService;
@Resource
private SeckillActivityMapper seckillActivityMapper;
@Test
public void testCreateSeckillActivity_success() {
// 准备参数
SeckillActivityCreateReqVO reqVO = randomPojo(SeckillActivityCreateReqVO.class);
// 调用
Long seckillActivityId = seckillActivityService.createSeckillActivity(reqVO);
// 断言
assertNotNull(seckillActivityId);
// 校验记录的属性是否正确
SeckillActivityDO seckillActivity = seckillActivityMapper.selectById(seckillActivityId);
assertPojoEquals(reqVO, seckillActivity);
}
@Test
public void testUpdateSeckillActivity_success() {
// mock 数据
SeckillActivityDO dbSeckillActivity = randomPojo(SeckillActivityDO.class);
seckillActivityMapper.insert(dbSeckillActivity);// @Sql: 先插入出一条存在的数据
// 准备参数
SeckillActivityUpdateReqVO reqVO = randomPojo(SeckillActivityUpdateReqVO.class, o -> {
o.setId(dbSeckillActivity.getId()); // 设置更新的 ID
});
// 调用
seckillActivityService.updateSeckillActivity(reqVO);
// 校验是否更新正确
SeckillActivityDO seckillActivity = seckillActivityMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, seckillActivity);
}
@Test
public void testUpdateSeckillActivity_notExists() {
// 准备参数
SeckillActivityUpdateReqVO reqVO = randomPojo(SeckillActivityUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> seckillActivityService.updateSeckillActivity(reqVO), SECKILL_ACTIVITY_NOT_EXISTS);
}
@Test
public void testDeleteSeckillActivity_success() {
// mock 数据
SeckillActivityDO dbSeckillActivity = randomPojo(SeckillActivityDO.class);
seckillActivityMapper.insert(dbSeckillActivity);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbSeckillActivity.getId();
// 调用
seckillActivityService.deleteSeckillActivity(id);
// 校验数据不存在了
assertNull(seckillActivityMapper.selectById(id));
}
@Test
public void testDeleteSeckillActivity_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> seckillActivityService.deleteSeckillActivity(id), SECKILL_ACTIVITY_NOT_EXISTS);
}
@Test
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
public void testGetSeckillActivityPage() {
// mock 数据
SeckillActivityDO dbSeckillActivity = randomPojo(SeckillActivityDO.class, o -> { // 等会查询到
o.setName(null);
o.setStatus(null);
o.setTimeId(null);
o.setCreateTime(null);
});
seckillActivityMapper.insert(dbSeckillActivity);
// 测试 name 不匹配
seckillActivityMapper.insert(cloneIgnoreId(dbSeckillActivity, o -> o.setName(null)));
// 测试 status 不匹配
seckillActivityMapper.insert(cloneIgnoreId(dbSeckillActivity, o -> o.setStatus(null)));
// 测试 timeId 不匹配
seckillActivityMapper.insert(cloneIgnoreId(dbSeckillActivity, o -> o.setTimeId(null)));
// 测试 createTime 不匹配
seckillActivityMapper.insert(cloneIgnoreId(dbSeckillActivity, o -> o.setCreateTime(null)));
// 准备参数
SeckillActivityPageReqVO reqVO = new SeckillActivityPageReqVO();
reqVO.setName(null);
reqVO.setStatus(null);
reqVO.setTimeId(null);
reqVO.setCreateTime((new LocalDateTime[]{}));
// 调用
PageResult<SeckillActivityDO> pageResult = seckillActivityService.getSeckillActivityPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbSeckillActivity, pageResult.getList().get(0));
}
@Test
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
public void testGetSeckillActivityList() {
// mock 数据
SeckillActivityDO dbSeckillActivity = randomPojo(SeckillActivityDO.class, o -> { // 等会查询到
o.setName(null);
o.setStatus(null);
o.setTimeId(null);
o.setCreateTime(null);
});
seckillActivityMapper.insert(dbSeckillActivity);
// 测试 name 不匹配
seckillActivityMapper.insert(cloneIgnoreId(dbSeckillActivity, o -> o.setName(null)));
// 测试 status 不匹配
seckillActivityMapper.insert(cloneIgnoreId(dbSeckillActivity, o -> o.setStatus(null)));
// 测试 timeId 不匹配
seckillActivityMapper.insert(cloneIgnoreId(dbSeckillActivity, o -> o.setTimeId(null)));
// 测试 createTime 不匹配
seckillActivityMapper.insert(cloneIgnoreId(dbSeckillActivity, o -> o.setCreateTime(null)));
// 准备参数
// SeckillActivityExportReqVO reqVO = new SeckillActivityExportReqVO();
// reqVO.setName(null);
// reqVO.setStatus(null);
// reqVO.setTimeId(null);
// reqVO.setCreateTime((new Date[]{}));
//
// // 调用
// List<SeckillActivityDO> list = seckillActivityService.getSeckillActivityList(reqVO);
// // 断言
// assertEquals(1, list.size());
// assertPojoEquals(dbSeckillActivity, list.get(0));
}
}

View File

@ -0,0 +1,189 @@
package cn.iocoder.yudao.module.promotion.service.seckilltime;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo.SeckillTimeCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.seckilltime.vo.SeckillTimeUpdateReqVO;
import cn.iocoder.yudao.module.promotion.service.seckill.seckilltime.SeckillTimeServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import javax.annotation.Resource;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.seckilltime.SeckillTimeDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckilltime.SeckillTimeMapper;
import org.springframework.context.annotation.Import;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link SeckillTimeServiceImpl}
*
* @author
*/
@Import(SeckillTimeServiceImpl.class)
public class SeckillTimeServiceImplTest extends BaseDbUnitTest {
@Resource
private SeckillTimeServiceImpl seckillTimeService;
@Resource
private SeckillTimeMapper seckillTimeMapper;
@Resource
private ObjectMapper objectMapper;
@Test
public void testJacksonSerializ(){
// 准备参数
SeckillTimeCreateReqVO reqVO = randomPojo(SeckillTimeCreateReqVO.class);
// ObjectMapper objectMapper = new ObjectMapper();
try {
String string = objectMapper.writeValueAsString(reqVO);
System.out.println(string);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
@Test
public void testCreateSeckillTime_success() {
// 准备参数
SeckillTimeCreateReqVO reqVO = randomPojo(SeckillTimeCreateReqVO.class);
// 调用
Long seckillTimeId = seckillTimeService.createSeckillTime(reqVO);
// 断言
assertNotNull(seckillTimeId);
// 校验记录的属性是否正确
SeckillTimeDO seckillTime = seckillTimeMapper.selectById(seckillTimeId);
assertPojoEquals(reqVO, seckillTime);
}
@Test
public void testUpdateSeckillTime_success() {
// mock 数据
SeckillTimeDO dbSeckillTime = randomPojo(SeckillTimeDO.class);
seckillTimeMapper.insert(dbSeckillTime);// @Sql: 先插入出一条存在的数据
// 准备参数
SeckillTimeUpdateReqVO reqVO = randomPojo(SeckillTimeUpdateReqVO.class, o -> {
o.setId(dbSeckillTime.getId()); // 设置更新的 ID
});
// 调用
seckillTimeService.updateSeckillTime(reqVO);
// 校验是否更新正确
SeckillTimeDO seckillTime = seckillTimeMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, seckillTime);
}
@Test
public void testUpdateSeckillTime_notExists() {
// 准备参数
SeckillTimeUpdateReqVO reqVO = randomPojo(SeckillTimeUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> seckillTimeService.updateSeckillTime(reqVO), SECKILL_TIME_NOT_EXISTS);
}
@Test
public void testDeleteSeckillTime_success() {
// mock 数据
SeckillTimeDO dbSeckillTime = randomPojo(SeckillTimeDO.class);
seckillTimeMapper.insert(dbSeckillTime);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbSeckillTime.getId();
// 调用
seckillTimeService.deleteSeckillTime(id);
// 校验数据不存在了
assertNull(seckillTimeMapper.selectById(id));
}
@Test
public void testDeleteSeckillTime_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> seckillTimeService.deleteSeckillTime(id), SECKILL_TIME_NOT_EXISTS);
}
@Test
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
public void testGetSeckillTimePage() {
// mock 数据
// SeckillTimeDO dbSeckillTime = randomPojo(SeckillTimeDO.class, o -> { // 等会查询到
// o.setName(null);
// o.setStartTime(null);
// o.setEndTime(null);
// o.setCreateTime(null);
// });
// seckillTimeMapper.insert(dbSeckillTime);
// // 测试 name 不匹配
// seckillTimeMapper.insert(cloneIgnoreId(dbSeckillTime, o -> o.setName(null)));
// // 测试 startTime 不匹配
// seckillTimeMapper.insert(cloneIgnoreId(dbSeckillTime, o -> o.setStartTime(null)));
// // 测试 endTime 不匹配
// seckillTimeMapper.insert(cloneIgnoreId(dbSeckillTime, o -> o.setEndTime(null)));
// // 测试 createTime 不匹配
// seckillTimeMapper.insert(cloneIgnoreId(dbSeckillTime, o -> o.setCreateTime(null)));
// // 准备参数
// SeckillTimePageReqVO reqVO = new SeckillTimePageReqVO();
// reqVO.setName(null);
//// reqVO.setStartTime((new LocalTime()));
//// reqVO.setEndTime((new LocalTime[]{}));
//// reqVO.setCreateTime((new Date[]{}));
//
// // 调用
// PageResult<SeckillTimeDO> pageResult = seckillTimeService.getSeckillTimePage(reqVO);
// // 断言
// assertEquals(1, pageResult.getTotal());
// assertEquals(1, pageResult.getList().size());
// assertPojoEquals(dbSeckillTime, pageResult.getList().get(0));
}
@Test
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
public void testGetSeckillTimeList() {
// mock 数据
SeckillTimeDO dbSeckillTime = randomPojo(SeckillTimeDO.class, o -> { // 等会查询到
o.setName(null);
o.setStartTime(null);
o.setEndTime(null);
o.setCreateTime(null);
});
seckillTimeMapper.insert(dbSeckillTime);
// 测试 name 不匹配
seckillTimeMapper.insert(cloneIgnoreId(dbSeckillTime, o -> o.setName(null)));
// 测试 startTime 不匹配
seckillTimeMapper.insert(cloneIgnoreId(dbSeckillTime, o -> o.setStartTime(null)));
// 测试 endTime 不匹配
seckillTimeMapper.insert(cloneIgnoreId(dbSeckillTime, o -> o.setEndTime(null)));
// 测试 createTime 不匹配
seckillTimeMapper.insert(cloneIgnoreId(dbSeckillTime, o -> o.setCreateTime(null)));
// 准备参数
// SeckillTimeExportReqVO reqVO = new SeckillTimeExportReqVO();
// reqVO.setName(null);
// reqVO.setStartTime((new LocalTime[]{}));
// reqVO.setEndTime((new LocalTime[]{}));
// reqVO.setCreateTime((new Date[]{}));
//
// // 调用
// List<SeckillTimeDO> list = seckillTimeService.getSeckillTimeList(reqVO);
// // 断言
// assertEquals(1, list.size());
// assertPojoEquals(dbSeckillTime, list.get(0));
}
}

View File

@ -0,0 +1,52 @@
import request from '@/utils/request'
// 创建秒杀活动
export function createSeckillActivity(data) {
return request({
url: '/promotion/seckill-activity/create',
method: 'post',
data: data
})
}
// 更新秒杀活动
export function updateSeckillActivity(data) {
return request({
url: '/promotion/seckill-activity/update',
method: 'put',
data: data
})
}
// 关闭限时折扣活动
export function closeSeckillActivity(id) {
return request({
url: '/promotion/seckill-activity/close?id=' + id,
method: 'put'
})
}
// 删除秒杀活动
export function deleteSeckillActivity(id) {
return request({
url: '/promotion/seckill-activity/delete?id=' + id,
method: 'delete'
})
}
// 获得秒杀活动
export function getSeckillActivity(id) {
return request({
url: '/promotion/seckill-activity/get?id=' + id,
method: 'get'
})
}
// 获得秒杀活动分页
export function getSeckillActivityPage(query) {
return request({
url: '/promotion/seckill-activity/page',
method: 'get',
params: query
})
}

View File

@ -0,0 +1,62 @@
import request from '@/utils/request'
// 创建秒杀时段
export function createSeckillTime(data) {
return request({
url: '/promotion/seckill-time/create',
method: 'post',
data: data
})
}
// 更新秒杀时段
export function updateSeckillTime(data) {
return request({
url: '/promotion/seckill-time/update',
method: 'put',
data: data
})
}
// 删除秒杀时段
export function deleteSeckillTime(id) {
return request({
url: '/promotion/seckill-time/delete?id=' + id,
method: 'delete'
})
}
// 获得秒杀时段
export function getSeckillTime(id) {
return request({
url: '/promotion/seckill-time/get?id=' + id,
method: 'get'
})
}
// 获得秒杀时段分页
export function getSeckillTimePage(query) {
return request({
url: '/promotion/seckill-time/page',
method: 'get',
params: query
})
}
// 获取所有的秒杀时段
export function getSeckillTimeList() {
return request({
url: '/promotion/seckill-time/list',
method: 'get'
})
}
// 导出秒杀时段 Excel
export function exportSeckillTimeExcel(query) {
return request({
url: '/promotion/seckill-time/export-excel',
method: 'get',
params: query,
responseType: 'blob'
})
}

View File

@ -0,0 +1,490 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch"
label-width="68px">
<el-form-item label="活动名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入秒杀活动名称" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="活动状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择活动状态" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_ACTIVITY_STATUS)" :key="dict.value"
:label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="参与场次" prop="timeId">
<el-select v-model="queryParams.timeId" placeholder="请选择参与场次" clearable size="small">
<el-option v-for="item in seckillTimeList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['promotion:seckill-activity:create']">新增秒杀活动</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-menu" size="mini" @click="openSeckillTime"
v-hasPermi="['promotion:seckill-activity:create']">管理参与场次</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="活动名称" align="center" prop="name" />
<el-table-column label="活动状态" align="center" prop="status">
<template slot-scope="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_ACTIVITY_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="参与场次" prop="timeId" width="250">
<template slot-scope="scope">
<span v-for="item in seckillTimeList" :key="item.id"
v-if="scope.row.timeId.indexOf(item.id) !== -1">
<el-tag style="margin:4px;" size="small">{{ item.name }}</el-tag>
</span>
</template>
</el-table-column>
<el-table-column label="活动开始时间" align="center" prop="startTime" width="190">
<template slot-scope="scope">
<span>{{ "开始: " + parseTime(scope.row.startTime) }}</span>
<span>{{ "结束: " + parseTime(scope.row.endTime) }}</span>
</template>
</el-table-column>
<el-table-column label="付款订单数" align="center" prop="orderCount" />
<el-table-column label="付款人数" align="center" prop="userCount" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['promotion:seckill-activity:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-close" @click="handleClose(scope.row)"
v-hasPermi="['promotion:seckill-activity:delete']">关闭</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['promotion:seckill-activity:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList" />
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="1200px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="活动名称" prop="name">
<el-input v-model="form.name" placeholder="请输入秒杀活动名称" />
</el-form-item>
<el-form-item label="活动时间" prop="startAndEndTime">
<el-date-picker clearable v-model="form.startAndEndTime" type="datetimerange"
value-format="timestamp" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"
style="width: 1080px" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" controls-position="right" :min="0" :max="10000">
</el-input-number>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="场次选择">
<el-select v-model="form.timeId" placeholder="请选择参与场次" clearable size="small" multiple filterable
style="width: 880px">
<el-option v-for="item in seckillTimeList" :key="item.id" :label="item.name" :value="item.id">
<span style="float: left">{{ item.name + ': { ' }} {{ item.startTime }} -- {{ item.endTime +
' }'
}}</span>
<span style="float: right; color: #8492a6; font-size: 13px"></span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="商品选择">
<el-select v-model="form.skuIds" placeholder="请选择活动商品" clearable size="small" multiple filterable
style="width: 880px" @change="changeFormSku">
<el-option v-for="item in productSkus" :key="item.id" :label="item.spuName + ' ' + item.name"
:value="item.id">
<span style="float: left">{{ item.spuName }} &nbsp; {{ item.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ (item.price /
100.0).toFixed(2)
}}</span>
</el-option>
</el-select>
<el-row>
<el-button type="primary" size="mini" @click="batchEditProduct('limitBuyCount')"></el-button>
<el-button type="primary" size="mini" @click="batchEditProduct('seckillPrice')"></el-button>
<el-button type="primary" size="mini" @click="batchEditProduct('seckillStock')"></el-button>
</el-row>
<el-table v-loading="loading" ref="productsTable" :data="form.products">
<el-table-column type="selection" width="55">
</el-table-column>
<el-table-column label="商品名称" align="center" width="200">
<template slot-scope="scope">
{{ scope.row.spuName }} &nbsp; {{ scope.row.name }}
</template>
</el-table-column>
<el-table-column label="商品价格" align="center" prop="price">
<template slot-scope="scope">
{{ (scope.row.price / 100.0).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="库存" align="center" prop="productStock" />
<el-table-column label="限购(0为不限购)" align="center" width="150">
<template slot-scope="scope">
<el-input-number v-model="scope.row.limitBuyCount" size="mini" :min="0" :max="10000">
</el-input-number>
</template>
</el-table-column>
<el-table-column label="秒杀价(元)" align="center" width="150">
<template slot-scope="scope">
<el-input-number v-model="scope.row.seckillPrice" size="mini" :precision="2" :min="0"
:max="10000">
</el-input-number>
</template>
</el-table-column>
<el-table-column label="秒杀库存" align="center" width="150" prop="seckillStock">
<template slot-scope="scope">
<el-input-number v-model="scope.row.seckillStock" size="mini" :min="0" :max="10000">
</el-input-number>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-delete"
@click="removeFormSku(scope.row.skuId)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getSkuOptionList } from "@/api/mall/product/sku";
import { createSeckillActivity, updateSeckillActivity, closeSeckillActivity,deleteSeckillActivity, getSeckillActivity, getSeckillActivityPage, exportSeckillActivityExcel } from "@/api/mall/promotion/seckillActivity";
import { getSeckillTimeList } from "@/api/mall/promotion/seckillTime";
import { deepClone } from "@/utils";
export default {
name: "SeckillActivity",
components: {
},
data() {
return {
//
loading: true,
//
showSearch: true,
//
total: 0,
//
list: [],
//
seckillTimeList: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
status: null,
timeId: null,
createTime: [],
},
//
form: {
skuIds: [], // SKU
products: [], //
timeId: [], //id
},
// SKU
productSkus: [],
//
rules: {
name: [{ required: true, message: "秒杀活动名称不能为空", trigger: "blur" }],
status: [{ required: true, message: "活动状态不能为空", trigger: "blur" }],
startAndEndTime: [{ required: true, message: "活动时间不能为空", trigger: "blur" }],
sort: [{ required: true, message: "排序不能为空", trigger: "blur" }],
timeId: [{ required: true, message: "秒杀场次不能为空", trigger: "blur" }],
totalPrice: [{ required: true, message: "订单实付金额,单位:分不能为空", trigger: "blur" }],
}
};
},
created() {
this.getList();
},
watch:{
$route: 'getList'
},
methods: {
/** 查询列表 */
getList() {
const timeId = this.$route.params && this.$route.params.timeId;
if (timeId) {
this.queryParams.timeId = timeId
}
this.loading = true;
//
getSeckillActivityPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
if (timeId) {
this.$route.params.timeId = undefined
}
// SKU
getSkuOptionList().then(response => {
this.productSkus = response.data;
});
//
getSeckillTimeList().then(response => {
this.seckillTimeList = response.data;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
status: undefined,
remark: undefined,
startTime: undefined,
endTime: undefined,
sort: undefined,
timeId: [],
totalPrice: undefined,
skuIds: [],
products: [],
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/**打开秒杀场次管理页面 */
openSeckillTime() {
this.$tab.openPage("秒杀场次管理", "/promotion/seckill-time");
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加秒杀活动";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getSeckillActivity(id).then(response => {
var timeIdList = response.data.timeId.split(',')
this.form = response.data;
//
this.form.startAndEndTime = [response.data.startTime, response.data.endTime];
this.form.timeId = timeIdList.map(item => parseInt(item))
this.form.skuIds = response.data.products.map(item => item.skuId);
this.form.products.forEach(product => {
// SKU
const sku = this.productSkus.find(item => item.id === product.skuId);
if (!sku) {
return;
}
//
product.name = sku.name;
product.spuName = sku.spuName;
product.price = sku.price;
product.productStock = sku.stock;
this.$set(product,'seckillStock',product.stock);
product.seckillPrice = product.seckillPrice !== undefined ? product.seckillPrice / 100 : undefined;
});
//
this.open = true;
this.title = "修改限时折扣活动";
})
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
//
const data = deepClone(this.form);
data.startTime = this.form.startAndEndTime[0];
data.endTime = this.form.startAndEndTime[1];
data.timeId = data.timeId.toString();
data.products.forEach(product => {
product.stock = product.seckillStock;
product.seckillPrice = product.seckillPrice !== undefined ? product.seckillPrice * 100 : undefined;
});
//
if (this.form.id != null) {
updateSeckillActivity(data).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
//
createSeckillActivity(data).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 关闭按钮操作 */
handleClose(row) {
const id = row.id;
this.$modal.confirm('是否确认关闭秒杀活动编号为"' + id + '"的数据项?').then(function () {
return closeSeckillActivity(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("关闭成功");
}).catch(() => { });
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除秒杀活动编号为"' + id + '"的数据项?').then(function () {
return deleteSeckillActivity(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => { });
},
/** 批量修改商品秒杀价,秒杀库存,每人限购数量 */
batchEditProduct(editType) {
const selectProducts = this.$refs.productsTable.selection;
if (selectProducts.length === 0) {
this.$modal.msgError("请选择需要修改的商品");
return;
}
let promptTitle = '请输入';
let regularPattern = /^[\s\S]*.*[^\s][\s\S]*$/; //
//
if (editType === 'limitBuyCount') {
promptTitle = '限购数';
regularPattern = /^[0-9]*$/; //
}
//
if (editType === 'seckillPrice') {
promptTitle = '秒杀价(元)';
regularPattern = /^[0-9]+(\.[0-9]{1,2})?$/; //
}
//
if (editType === 'seckillStock') {
promptTitle = '秒杀库存';
regularPattern = /^[0-9]*$/; //
}
this.$prompt(promptTitle, '提示', {
confirmButtonText: '保存',
cancelButtonText: '取消',
inputPattern: regularPattern,
inputErrorMessage: promptTitle + '格式不正确'
}).then(({ value }) => {
if (editType === 'limitBuyCount') {
selectProducts.forEach((item) => {
item.limitBuyCount = value;
})
}
if (editType === 'seckillPrice') {
selectProducts.forEach((item) => {
item.seckillPrice = value;
})
}
if (editType === 'seckillStock') {
selectProducts.forEach((item) => {
item.seckillStock = value;
})
}
}).catch();
},
/** 当 Form 的 SKU 发生变化时 */
changeFormSku(skuIds) {
//
skuIds.forEach(skuId => {
// SKU
const sku = this.productSkus.find(item => item.id === skuId);
if (!sku) {
return;
}
//
const product = this.form.products.find(item => item.skuId === skuId);
if (product) {
return;
}
this.form.products.push({
skuId: sku.id,
name: sku.name,
price: sku.price,
productStock: sku.stock,
spuId: sku.spuId,
spuName: sku.spuName,
limitBuyCount: 1,
seckillStock: sku.stock,
seckillPrice: sku.price,
});
});
//
this.form.products.map((product, index) => {
if (!skuIds.includes(product.skuId)) {
this.form.products.splice(index, 1);
}
});
},
/** 移除 Form 的 SKU */
removeFormSku(skuId) {
this.form.skuIds.map((id, index) => {
if (skuId === id) {
this.form.skuIds.splice(index, 1);
}
});
this.changeFormSku(this.form.skuIds);
},
}
};
</script>

View File

@ -0,0 +1,197 @@
<template>
<div class="app-container">
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['promotion:seckill-time:create']">新增秒杀时段</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="秒杀时段名称" align="center" prop="name" />
<el-table-column label="开始时间点" align="center" prop="startTime" width="180">
<template slot-scope="scope">
<span>{{ scope.row.startTime }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间点" align="center" prop="endTime" width="180">
<template slot-scope="scope">
<span>{{ scope.row.endTime }}</span>
</template>
</el-table-column>
<el-table-column label="秒杀活动数量" align="center" prop="seckillActivityCount" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleOpenSeckillActivity(scope.row)">
查看秒杀活动</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['promotion:seckill-time:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['promotion:seckill-time:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="600px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="140px">
<el-form-item label="秒杀场次名称" prop="name">
<el-input v-model="form.name" placeholder="请输入秒杀时段名称" clearable />
</el-form-item>
<el-form-item label="秒杀时间段" prop="startAndEndTime">
<el-time-picker is-range v-model="form.startAndEndTime" range-separator="" start-placeholder=""
end-placeholder="结束时间" placeholder="选择时间范围" value-format="HH:mm:ss">
</el-time-picker>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { createSeckillTime, updateSeckillTime, deleteSeckillTime, getSeckillTime, getSeckillTimePage, exportSeckillTimeExcel, getSeckillTimeList } from "@/api/mall/promotion/seckillTime";
import router from "@/router";
import { deepClone } from "@/utils";
export default {
name: "SeckillTime",
components: {
},
data() {
return {
//
loading: true,
//
exportLoading: false,
//
showSearch: true,
//
// total: 0,
//
list: [],
//
title: "",
//
open: false,
//
form: {},
//
rules: {
name: [{ required: true, message: "秒杀时段名称不能为空", trigger: "blur" }],
startAndEndTime: [{ required: true, message: "秒杀时间段不能为空", trigger: "blur" }],
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
//
getSeckillTimeList().then(response => {
this.list = response.data;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
startAndEndTime: undefined,
startTime: undefined,
endTime: undefined,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/**查看当前秒杀时段的秒杀活动 */
handleOpenSeckillActivity(row) {
router.push({ name: 'SeckillActivity', params: { timeId: row.id } })
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加秒杀时段";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getSeckillTime(id).then(response => {
response.data.startAndEndTime = [response.data.startTime, response.data.endTime]
this.form = response.data;
this.open = true;
this.title = "修改秒杀时段";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
console.log(valid, "是否通过");
if (!valid) {
return;
}
//
const data = deepClone(this.form);
data.startTime = this.form.startAndEndTime[0];
data.endTime = this.form.startAndEndTime[1];
//
if (this.form.id != null) {
updateSeckillTime(data).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
//
createSeckillTime(data).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除秒杀时段编号为"' + id + '"的数据项?').then(function () {
return deleteSeckillTime(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => { });
},
}
};
</script>