feat(mall): tenant-scope product, promotion and trade modules
This commit is contained in:
@@ -25,6 +25,10 @@
|
||||
</dependency>
|
||||
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.module.product.dal.dataobject.brand;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
@@ -20,7 +20,7 @@ import lombok.*;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ProductBrandDO extends BaseDO {
|
||||
public class ProductBrandDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 品牌编号
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.module.product.dal.dataobject.category;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
@@ -20,7 +20,7 @@ import lombok.*;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ProductCategoryDO extends BaseDO {
|
||||
public class ProductCategoryDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 父分类编号 - 根分类
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.product.dal.dataobject.comment;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
@@ -26,7 +26,7 @@ import java.util.List;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ProductCommentDO extends BaseDO {
|
||||
public class ProductCommentDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 默认匿名昵称
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.product.dal.dataobject.favorite;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@@ -20,7 +20,7 @@ import lombok.*;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ProductFavoriteDO extends BaseDO {
|
||||
public class ProductFavoriteDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 编号,主键自增
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.product.dal.dataobject.history;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
@@ -19,7 +19,7 @@ import lombok.*;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ProductBrowseHistoryDO extends BaseDO {
|
||||
public class ProductBrowseHistoryDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 记录编号
|
||||
@@ -39,4 +39,4 @@ public class ProductBrowseHistoryDO extends BaseDO {
|
||||
*/
|
||||
private Boolean userDeleted;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.product.dal.dataobject.property;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
@@ -19,7 +19,7 @@ import lombok.*;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ProductPropertyDO extends BaseDO {
|
||||
public class ProductPropertyDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* SPU 单规格时,默认属性 id
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.product.dal.dataobject.property;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
@@ -20,7 +20,7 @@ import lombok.*;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ProductPropertyValueDO extends BaseDO {
|
||||
public class ProductPropertyValueDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* SPU 单规格时,默认属性值 id
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.product.dal.dataobject.sku;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
|
||||
@@ -26,7 +26,7 @@ import java.util.List;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ProductSkuDO extends BaseDO {
|
||||
public class ProductSkuDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 商品 SKU 编号,自增
|
||||
@@ -131,4 +131,3 @@ public class ProductSkuDO extends BaseDO {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.product.dal.dataobject.spu;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.type.IntegerListTypeHandler;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;
|
||||
@@ -28,7 +28,7 @@ import java.util.List;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ProductSpuDO extends BaseDO {
|
||||
public class ProductSpuDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 商品 SPU 编号,自增
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package cn.iocoder.yudao.module.product.dal.dataobject;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.brand.ProductBrandDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.history.ProductBrowseHistoryDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.property.ProductPropertyValueDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
|
||||
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class ProductTenantContractTest {
|
||||
|
||||
@Test
|
||||
void allNativeProductRecordsParticipateInFrameworkTenantIsolation() {
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(ProductBrandDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(ProductCategoryDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(ProductPropertyDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(ProductPropertyValueDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(ProductSpuDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(ProductSkuDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(ProductCommentDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(ProductFavoriteDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(ProductBrowseHistoryDO.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.promotion.api.combination.dto;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import jakarta.validation.constraints.PositiveOrZero;
|
||||
|
||||
/**
|
||||
* 拼团记录的创建 Request DTO
|
||||
@@ -16,40 +18,48 @@ public class CombinationRecordCreateReqDTO {
|
||||
* 拼团活动编号
|
||||
*/
|
||||
@NotNull(message = "拼团活动编号不能为空")
|
||||
@Positive(message = "拼团活动编号必须大于 0")
|
||||
private Long activityId;
|
||||
/**
|
||||
* spu 编号
|
||||
*/
|
||||
@NotNull(message = "spu 编号不能为空")
|
||||
@Positive(message = "spu 编号必须大于 0")
|
||||
private Long spuId;
|
||||
/**
|
||||
* sku 编号
|
||||
*/
|
||||
@NotNull(message = "sku 编号不能为空")
|
||||
@Positive(message = "sku 编号必须大于 0")
|
||||
private Long skuId;
|
||||
/**
|
||||
* 购买的商品数量
|
||||
*/
|
||||
@NotNull(message = "购买数量不能为空")
|
||||
@Positive(message = "购买数量必须大于 0")
|
||||
private Integer count;
|
||||
/**
|
||||
* 订单编号
|
||||
*/
|
||||
@NotNull(message = "订单编号不能为空")
|
||||
@Positive(message = "订单编号必须大于 0")
|
||||
private Long orderId;
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
@Positive(message = "用户编号必须大于 0")
|
||||
private Long userId;
|
||||
/**
|
||||
* 团长编号
|
||||
*/
|
||||
@PositiveOrZero(message = "团长编号不能小于 0")
|
||||
private Long headId;
|
||||
/**
|
||||
* 拼团商品单价
|
||||
*/
|
||||
@NotNull(message = "拼团商品单价不能为空")
|
||||
@Positive(message = "拼团商品单价必须大于 0")
|
||||
private Integer combinationPrice;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
@@ -19,31 +24,38 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
|
||||
public class BargainActivityBaseVO {
|
||||
|
||||
@Schema(description = "砍价活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "砍得越多省得越多,是兄弟就来砍我")
|
||||
@NotNull(message = "砍价名称不能为空")
|
||||
@NotBlank(message = "砍价名称不能为空")
|
||||
@Size(max = 255, message = "砍价名称不能超过 255 个字符")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "商品 SPU 编号", example = "1")
|
||||
@NotNull(message = "砍价商品不能为空")
|
||||
@Positive(message = "砍价商品必须大于 0")
|
||||
private Long spuId;
|
||||
|
||||
@Schema(description = "商品 skuId", requiredMode = Schema.RequiredMode.REQUIRED, example = "23")
|
||||
@NotNull(message = "商品 skuId 不能为空")
|
||||
@Positive(message = "商品 skuId 必须大于 0")
|
||||
private Long skuId;
|
||||
|
||||
@Schema(description = "砍价起始价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "23")
|
||||
@NotNull(message = "砍价起始价格不能为空")
|
||||
@Positive(message = "砍价起始价格必须大于 0")
|
||||
private Integer bargainFirstPrice;
|
||||
|
||||
@Schema(description = "砍价底价", requiredMode = Schema.RequiredMode.REQUIRED, example = "23")
|
||||
@NotNull(message = "砍价底价不能为空")
|
||||
@Positive(message = "砍价底价必须大于 0")
|
||||
private Integer bargainMinPrice;
|
||||
|
||||
@Schema(description = "活动库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "23")
|
||||
@NotNull(message = "活动库存不能为空")
|
||||
@Positive(message = "活动库存必须大于 0")
|
||||
private Integer stock;
|
||||
|
||||
@Schema(description = "总限购数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "16218")
|
||||
@NotNull(message = "总限购数量不能为空")
|
||||
@Positive(message = "总限购数量必须大于 0")
|
||||
private Integer totalLimitCount;
|
||||
|
||||
@Schema(description = "活动开始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2022-07-01 23:59:59]")
|
||||
@@ -58,18 +70,40 @@ public class BargainActivityBaseVO {
|
||||
|
||||
@Schema(description = "最大助力次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
|
||||
@NotNull(message = "最大助力次数不能为空")
|
||||
@Positive(message = "最大助力次数必须大于 0")
|
||||
private Integer helpMaxCount;
|
||||
|
||||
@Schema(description = "最大帮砍次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
|
||||
@NotNull(message = "最大帮砍次数不能为空")
|
||||
@Positive(message = "最大帮砍次数必须大于 0")
|
||||
private Integer bargainCount;
|
||||
|
||||
@Schema(description = "用户每次砍价的最小金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
|
||||
@NotNull(message = "用户每次砍价的最小金额不能为空")
|
||||
@Positive(message = "用户每次砍价的最小金额必须大于 0")
|
||||
private Integer randomMinPrice;
|
||||
|
||||
@Schema(description = "用户每次砍价的最大金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
|
||||
@NotNull(message = "用户每次砍价的最大金额不能为空")
|
||||
@Positive(message = "用户每次砍价的最大金额必须大于 0")
|
||||
private Integer randomMaxPrice;
|
||||
|
||||
@AssertTrue(message = "活动结束时间必须晚于开始时间")
|
||||
@JsonIgnore
|
||||
public boolean isActivityTimeValid() {
|
||||
return startTime == null || endTime == null || endTime.isAfter(startTime);
|
||||
}
|
||||
|
||||
@AssertTrue(message = "砍价底价不能高于砍价起始价格")
|
||||
@JsonIgnore
|
||||
public boolean isBargainPriceValid() {
|
||||
return bargainFirstPrice == null || bargainMinPrice == null || bargainMinPrice <= bargainFirstPrice;
|
||||
}
|
||||
|
||||
@AssertTrue(message = "单次砍价最大金额不能小于最小金额")
|
||||
@JsonIgnore
|
||||
public boolean isRandomPriceValid() {
|
||||
return randomMinPrice == null || randomMaxPrice == null || randomMinPrice <= randomMaxPrice;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
|
||||
@Schema(description = "管理后台 - 砍价活动更新 Request VO")
|
||||
@Data
|
||||
@@ -15,6 +16,7 @@ public class BargainActivityUpdateReqVO extends BargainActivityBaseVO {
|
||||
|
||||
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
|
||||
@NotNull(message = "活动编号不能为空")
|
||||
@Positive(message = "活动编号必须大于 0")
|
||||
private Long id;
|
||||
|
||||
}
|
||||
|
||||
@@ -93,6 +93,8 @@ public class CombinationActivityController {
|
||||
@GetMapping("/list-by-ids")
|
||||
@Operation(summary = "获得拼团活动列表,基于活动编号数组")
|
||||
@Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]")
|
||||
@PreAuthorize("@ss.hasAnyPermissions('promotion:combination-activity:query', " +
|
||||
"'promotion:diy-page:query', 'promotion:diy-template:query')")
|
||||
public CommonResult<List<CombinationActivityRespVO>> getCombinationActivityListByIds(@RequestParam("ids") List<Long> ids) {
|
||||
// 1. 获得开启的活动列表
|
||||
List<CombinationActivityDO> activityList = combinationActivityService.getCombinationActivityListByIds(ids);
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
@@ -19,19 +25,23 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
|
||||
public class CombinationActivityBaseVO {
|
||||
|
||||
@Schema(description = "拼团名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "越拼越省钱")
|
||||
@NotNull(message = "拼团名称不能为空")
|
||||
@NotBlank(message = "拼团名称不能为空")
|
||||
@Size(max = 255, message = "拼团名称不能超过 255 个字符")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "拼团商品不能为空")
|
||||
@Positive(message = "拼团商品必须大于 0")
|
||||
private Long spuId;
|
||||
|
||||
@Schema(description = "总限购数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "16218")
|
||||
@NotNull(message = "总限购数量不能为空")
|
||||
@Positive(message = "总限购数量必须大于 0")
|
||||
private Integer totalLimitCount;
|
||||
|
||||
@Schema(description = "单次限购数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "28265")
|
||||
@NotNull(message = "单次限购数量不能为空")
|
||||
@Positive(message = "单次限购数量必须大于 0")
|
||||
private Integer singleLimitCount;
|
||||
|
||||
@Schema(description = "活动时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2022-07-01 23:59:59]")
|
||||
@@ -46,6 +56,7 @@ public class CombinationActivityBaseVO {
|
||||
|
||||
@Schema(description = "开团人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "25222")
|
||||
@NotNull(message = "开团人数不能为空")
|
||||
@Min(value = 2, message = "开团人数不能小于 2")
|
||||
private Integer userSize;
|
||||
|
||||
@Schema(description = "虚拟成团", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
|
||||
@@ -54,6 +65,20 @@ public class CombinationActivityBaseVO {
|
||||
|
||||
@Schema(description = "限制时长(小时)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
@NotNull(message = "限制时长不能为空")
|
||||
@Positive(message = "限制时长必须大于 0")
|
||||
private Integer limitDuration;
|
||||
|
||||
@AssertTrue(message = "活动结束时间必须晚于开始时间")
|
||||
@JsonIgnore
|
||||
public boolean isActivityTimeValid() {
|
||||
return startTime == null || endTime == null || endTime.isAfter(startTime);
|
||||
}
|
||||
|
||||
@AssertTrue(message = "单次限购数量不能超过总限购数量")
|
||||
@JsonIgnore
|
||||
public boolean isLimitCountValid() {
|
||||
return totalLimitCount == null || singleLimitCount == null
|
||||
|| singleLimitCount <= totalLimitCount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 拼团活动创建 Request VO")
|
||||
@@ -16,7 +17,7 @@ import java.util.List;
|
||||
public class CombinationActivityCreateReqVO extends CombinationActivityBaseVO {
|
||||
|
||||
@Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@Valid
|
||||
private List<CombinationProductBaseVO> products;
|
||||
@NotEmpty(message = "拼团商品不能为空")
|
||||
private List<@Valid CombinationProductBaseVO> products;
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 拼团活动更新 Request VO")
|
||||
@@ -18,10 +20,11 @@ public class CombinationActivityUpdateReqVO extends CombinationActivityBaseVO {
|
||||
|
||||
@Schema(description = "活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22901")
|
||||
@NotNull(message = "活动编号不能为空")
|
||||
@Positive(message = "活动编号必须大于 0")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "拼团商品", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@Valid
|
||||
private List<CombinationProductBaseVO> products;
|
||||
@NotEmpty(message = "拼团商品不能为空")
|
||||
private List<@Valid CombinationProductBaseVO> products;
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
|
||||
/**
|
||||
* 拼团商品 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
@@ -14,14 +15,17 @@ public class CombinationProductBaseVO {
|
||||
|
||||
@Schema(description = "商品 spuId", requiredMode = Schema.RequiredMode.REQUIRED, example = "30563")
|
||||
@NotNull(message = "商品 spuId 不能为空")
|
||||
@Positive(message = "商品 spuId 必须大于 0")
|
||||
private Long spuId;
|
||||
|
||||
@Schema(description = "商品 skuId", requiredMode = Schema.RequiredMode.REQUIRED, example = "30563")
|
||||
@NotNull(message = "商品 skuId 不能为空")
|
||||
@Positive(message = "商品 skuId 必须大于 0")
|
||||
private Long skuId;
|
||||
|
||||
@Schema(description = "拼团价格,单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "27682")
|
||||
@NotNull(message = "拼团价格不能为空")
|
||||
@Positive(message = "拼团价格必须大于 0")
|
||||
private Integer combinationPrice;
|
||||
|
||||
}
|
||||
|
||||
@@ -102,6 +102,8 @@ public class SeckillActivityController {
|
||||
@GetMapping("/list-by-ids")
|
||||
@Operation(summary = "获得秒杀活动列表,基于活动编号数组")
|
||||
@Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]")
|
||||
@PreAuthorize("@ss.hasAnyPermissions('promotion:seckill-activity:query', " +
|
||||
"'promotion:diy-page:query', 'promotion:diy-template:query')")
|
||||
public CommonResult<List<SeckillActivityRespVO>> getSeckillActivityListByIds(@RequestParam("ids") List<Long> ids) {
|
||||
// 1. 获得开启的活动列表
|
||||
List<SeckillActivityDO> activityList = seckillActivityService.getSeckillActivityListByIds(ids);
|
||||
|
||||
@@ -80,6 +80,7 @@ public class SeckillConfigController {
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得所有开启状态的秒杀时段精简列表", description = "主要用于前端的下拉选项")
|
||||
@PreAuthorize("@ss.hasPermission('promotion:seckill-activity:query')")
|
||||
public CommonResult<List<SeckillConfigSimpleRespVO>> getSeckillConfigSimpleList() {
|
||||
List<SeckillConfigDO> list = seckillConfigService.getSeckillConfigListByStatus(
|
||||
CommonStatusEnum.ENABLE.getStatus());
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import jakarta.validation.constraints.PositiveOrZero;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@@ -22,10 +29,12 @@ public class SeckillActivityBaseVO {
|
||||
|
||||
@Schema(description = "秒杀活动商品 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "[121,1212]")
|
||||
@NotNull(message = "秒杀活动商品不能为空")
|
||||
@Positive(message = "秒杀活动商品必须大于 0")
|
||||
private Long spuId;
|
||||
|
||||
@Schema(description = "秒杀活动名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "618大促")
|
||||
@NotNull(message = "秒杀活动名称不能为空")
|
||||
@NotBlank(message = "秒杀活动名称不能为空")
|
||||
@Size(max = 255, message = "秒杀活动名称不能超过 255 个字符")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "备注", example = "清仓大甩卖割韭菜")
|
||||
@@ -43,16 +52,34 @@ public class SeckillActivityBaseVO {
|
||||
|
||||
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "排序不能为空")
|
||||
@PositiveOrZero(message = "排序不能小于 0")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "秒杀时段 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2,3]")
|
||||
@NotNull(message = "秒杀时段不能为空")
|
||||
private List<Long> configIds;
|
||||
@NotEmpty(message = "秒杀时段不能为空")
|
||||
private List<@Positive(message = "秒杀时段编号必须大于 0") Long> configIds;
|
||||
|
||||
@Schema(description = "总限购数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "12877")
|
||||
@NotNull(message = "总限购数量不能为空")
|
||||
@Positive(message = "总限购数量必须大于 0")
|
||||
private Integer totalLimitCount;
|
||||
|
||||
@Schema(description = "单次限够数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "31683")
|
||||
@NotNull(message = "单次限购数量不能为空")
|
||||
@Positive(message = "单次限购数量必须大于 0")
|
||||
private Integer singleLimitCount;
|
||||
|
||||
@AssertTrue(message = "活动结束时间必须晚于开始时间")
|
||||
@JsonIgnore
|
||||
public boolean isActivityTimeValid() {
|
||||
return startTime == null || endTime == null || endTime.isAfter(startTime);
|
||||
}
|
||||
|
||||
@AssertTrue(message = "单次限购数量不能超过总限购数量")
|
||||
@JsonIgnore
|
||||
public boolean isLimitCountValid() {
|
||||
return totalLimitCount == null || singleLimitCount == null
|
||||
|| singleLimitCount <= totalLimitCount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity;
|
||||
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductBaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
@@ -16,6 +18,7 @@ import java.util.List;
|
||||
public class SeckillActivityCreateReqVO extends SeckillActivityBaseVO {
|
||||
|
||||
@Schema(description = "秒杀商品", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<SeckillProductBaseVO> products;
|
||||
@NotEmpty(message = "秒杀商品不能为空")
|
||||
private List<@Valid SeckillProductBaseVO> products;
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@ package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity;
|
||||
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductBaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
@@ -15,9 +19,12 @@ import java.util.List;
|
||||
public class SeckillActivityUpdateReqVO extends SeckillActivityBaseVO {
|
||||
|
||||
@Schema(description = "秒杀活动id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "秒杀活动编号不能为空")
|
||||
@Positive(message = "秒杀活动编号必须大于 0")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "秒杀商品", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<SeckillProductBaseVO> products;
|
||||
@NotEmpty(message = "秒杀商品不能为空")
|
||||
private List<@Valid SeckillProductBaseVO> products;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.time.LocalTime;
|
||||
import java.util.List;
|
||||
|
||||
@@ -19,35 +25,52 @@ import java.util.List;
|
||||
public class SeckillConfigBaseVO {
|
||||
|
||||
@Schema(description = "秒杀时段名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "早上场")
|
||||
@NotNull(message = "秒杀时段名称不能为空")
|
||||
@NotBlank(message = "秒杀时段名称不能为空")
|
||||
@Size(max = 128, message = "秒杀时段名称不能超过 128 个字符")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "开始时间点", requiredMode = Schema.RequiredMode.REQUIRED, example = "09:00:00")
|
||||
@NotNull(message = "开始时间点不能为空")
|
||||
@Pattern(regexp = "^(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d)?$", message = "开始时间点格式必须为 HH:mm 或 HH:mm:ss")
|
||||
private String startTime;
|
||||
|
||||
@Schema(description = "结束时间点", requiredMode = Schema.RequiredMode.REQUIRED, example = "16:00:00")
|
||||
@NotNull(message = "结束时间点不能为空")
|
||||
@Pattern(regexp = "^(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d)?$", message = "结束时间点格式必须为 HH:mm 或 HH:mm:ss")
|
||||
private String endTime;
|
||||
|
||||
@Schema(description = "秒杀轮播图", requiredMode = Schema.RequiredMode.REQUIRED, example = "[https://www.iocoder.cn/xx.png]")
|
||||
@NotNull(message = "秒杀轮播图不能为空")
|
||||
private List<String> sliderPicUrls;
|
||||
@NotEmpty(message = "秒杀轮播图不能为空")
|
||||
@Size(max = 5, message = "秒杀轮播图不能超过 5 张")
|
||||
private List<@NotBlank(message = "秒杀轮播图地址不能为空") String> sliderPicUrls;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "状态不能为空")
|
||||
@InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
@AssertTrue(message = "秒杀时段开始时间和结束时间不能相等")
|
||||
@JsonIgnore
|
||||
public boolean isValidStartTimeValid() {
|
||||
return !LocalTime.parse(startTime).equals(LocalTime.parse(endTime));
|
||||
LocalTime start = parseTime(startTime);
|
||||
LocalTime end = parseTime(endTime);
|
||||
return start == null || end == null || !start.equals(end);
|
||||
}
|
||||
|
||||
@AssertTrue(message = "秒杀时段开始时间不能在结束时间之后")
|
||||
@JsonIgnore
|
||||
public boolean isValidEndTimeValid() {
|
||||
return !LocalTime.parse(startTime).isAfter(LocalTime.parse(endTime));
|
||||
LocalTime start = parseTime(startTime);
|
||||
LocalTime end = parseTime(endTime);
|
||||
return start == null || end == null || start.isBefore(end);
|
||||
}
|
||||
|
||||
private static LocalTime parseTime(String value) {
|
||||
try {
|
||||
return value == null ? null : LocalTime.parse(value);
|
||||
} catch (RuntimeException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
|
||||
@Schema(description = "管理后台 - 秒杀时段更新 Request VO")
|
||||
@Data
|
||||
@@ -15,6 +16,7 @@ public class SeckillConfigUpdateReqVO extends SeckillConfigBaseVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "编号不能为空")
|
||||
@Positive(message = "编号必须大于 0")
|
||||
private Long id;
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
|
||||
@Schema(description = "管理后台 - 修改时段配置状态 Request VO")
|
||||
@Data
|
||||
@@ -13,6 +14,7 @@ public class SeckillConfigUpdateStatusReqVo {
|
||||
|
||||
@Schema(description = "时段配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "时段配置编号不能为空")
|
||||
@Positive(message = "时段配置编号必须大于 0")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
|
||||
/**
|
||||
* 秒杀参与商品 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
@@ -16,14 +17,17 @@ public class SeckillProductBaseVO {
|
||||
|
||||
@Schema(description = "商品sku_id", requiredMode = Schema.RequiredMode.REQUIRED, example = "30563")
|
||||
@NotNull(message = "商品sku_id不能为空")
|
||||
@Positive(message = "商品sku_id必须大于 0")
|
||||
private Long skuId;
|
||||
|
||||
@Schema(description = "秒杀金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "6689")
|
||||
@NotNull(message = "秒杀金额,单位:分不能为空")
|
||||
@Positive(message = "秒杀金额必须大于 0")
|
||||
private Integer seckillPrice;
|
||||
|
||||
@Schema(description = "秒杀库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
|
||||
@NotNull(message = "秒杀库存不能为空")
|
||||
@Positive(message = "秒杀库存必须大于 0")
|
||||
private Integer stock;
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
|
||||
@Schema(description = "用户 App - 砍价助力的创建 Request VO")
|
||||
@Data
|
||||
@@ -11,6 +12,7 @@ public class AppBargainHelpCreateReqVO {
|
||||
|
||||
@Schema(description = "砍价记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "砍价记录编号不能为空")
|
||||
@Positive(message = "砍价记录编号必须大于 0")
|
||||
private Long recordId;
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
|
||||
@Schema(description = "用户 App - 砍价记录的创建 Request VO")
|
||||
@Data
|
||||
@@ -11,6 +12,7 @@ public class AppBargainRecordCreateReqVO {
|
||||
|
||||
@Schema(description = "砍价活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "砍价活动编号不能为空")
|
||||
@Positive(message = "砍价活动编号必须大于 0")
|
||||
private Long activityId;
|
||||
|
||||
}
|
||||
|
||||
@@ -112,8 +112,10 @@ public interface CombinationActivityConvert {
|
||||
CombinationRecordDO convert(CombinationRecordCreateReqDTO reqDTO);
|
||||
|
||||
default CombinationRecordCreateRespDTO convert4(CombinationRecordDO combinationRecord) {
|
||||
Long headId = ObjectUtil.equal(combinationRecord.getHeadId(), CombinationRecordDO.HEAD_ID_GROUP)
|
||||
? combinationRecord.getId() : combinationRecord.getHeadId();
|
||||
return new CombinationRecordCreateRespDTO().setCombinationActivityId(combinationRecord.getActivityId())
|
||||
.setCombinationRecordId(combinationRecord.getId()).setCombinationHeadId(combinationRecord.getHeadId());
|
||||
.setCombinationRecordId(combinationRecord.getId()).setCombinationHeadId(headId);
|
||||
}
|
||||
|
||||
default CombinationRecordDO convert(CombinationRecordCreateReqDTO reqDTO,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.bargain;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
@@ -22,7 +22,7 @@ import java.time.LocalDateTime;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BargainActivityDO extends BaseDO {
|
||||
public class BargainActivityDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 砍价活动编号
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.bargain;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
@@ -19,7 +19,7 @@ import lombok.*;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BargainHelpDO extends BaseDO {
|
||||
public class BargainHelpDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.bargain;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.bargain.BargainRecordStatusEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@@ -22,7 +22,7 @@ import java.time.LocalDateTime;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BargainRecordDO extends BaseDO {
|
||||
public class BargainRecordDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.combination;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
@@ -22,7 +22,7 @@ import java.time.LocalDateTime;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CombinationActivityDO extends BaseDO {
|
||||
public class CombinationActivityDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 活动编号
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.combination;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
@@ -21,7 +21,7 @@ import java.time.LocalDateTime;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CombinationProductDO extends BaseDO {
|
||||
public class CombinationProductDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.combination;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@@ -26,7 +26,7 @@ import java.time.LocalDateTime;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CombinationRecordDO extends BaseDO {
|
||||
public class CombinationRecordDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 团长编号 - 团长
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.coupon;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum;
|
||||
@@ -25,7 +25,7 @@ import java.util.List;
|
||||
@KeySequence("promotion_coupon_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CouponDO extends BaseDO {
|
||||
public class CouponDO extends TenantBaseDO {
|
||||
|
||||
// ========== 基本信息 BEGIN ==========
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.coupon;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
|
||||
@@ -28,7 +28,7 @@ import java.util.List;
|
||||
@KeySequence("promotion_coupon_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CouponTemplateDO extends BaseDO {
|
||||
public class CouponTemplateDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 领取数量 - 不限制
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.discount;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
@@ -22,7 +22,7 @@ import java.time.LocalDateTime;
|
||||
@KeySequence("promotion_discount_activity_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DiscountActivityDO extends BaseDO {
|
||||
public class DiscountActivityDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 活动编号,主键自增
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.discount;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@@ -19,7 +19,7 @@ import java.time.LocalDateTime;
|
||||
@KeySequence("promotion_discount_product_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DiscountProductDO extends BaseDO {
|
||||
public class DiscountProductDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 编号,主键自增
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.reward;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
|
||||
@@ -27,7 +27,7 @@ import java.util.Map;
|
||||
@KeySequence("promotion_reward_activity_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class RewardActivityDO extends BaseDO {
|
||||
public class RewardActivityDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 活动编号,主键自增
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.seckill;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@@ -24,7 +24,7 @@ import java.util.List;
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class SeckillActivityDO extends BaseDO {
|
||||
public class SeckillActivityDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 秒杀活动编号
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.seckill;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@@ -24,7 +24,7 @@ import java.util.List;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SeckillConfigDO extends BaseDO {
|
||||
public class SeckillConfigDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.seckill;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@@ -17,7 +17,7 @@ import java.util.List;
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@TableName("promotion_seckill_product")
|
||||
@TableName(value = "promotion_seckill_product", autoResultMap = true)
|
||||
@KeySequence("promotion_seckill_product_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@@ -25,7 +25,7 @@ import java.util.List;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SeckillProductDO extends BaseDO {
|
||||
public class SeckillProductDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 秒杀参与商品编号
|
||||
|
||||
@@ -43,6 +43,7 @@ public interface BargainActivityMapper extends BaseMapperX<BargainActivityDO> {
|
||||
if (count > 0) {
|
||||
return update(null, new LambdaUpdateWrapper<BargainActivityDO>()
|
||||
.eq(BargainActivityDO::getId, id)
|
||||
.apply("stock + {0} <= total_stock", count)
|
||||
.setSql("stock = stock + " + count));
|
||||
}
|
||||
// 情况二:扣减库存
|
||||
|
||||
@@ -5,6 +5,7 @@ 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.combination.vo.product.CombinationProductPageReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -35,4 +36,14 @@ public interface CombinationProductMapper extends BaseMapperX<CombinationProduct
|
||||
return selectList(CombinationProductDO::getActivityId, ids);
|
||||
}
|
||||
|
||||
default int updateActivityStatusByActivityId(Long activityId, Integer status) {
|
||||
return update(null, new LambdaUpdateWrapper<CombinationProductDO>()
|
||||
.eq(CombinationProductDO::getActivityId, activityId)
|
||||
.set(CombinationProductDO::getActivityStatus, status));
|
||||
}
|
||||
|
||||
default void deleteByActivityId(Long activityId) {
|
||||
delete(CombinationProductDO::getActivityId, activityId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -43,6 +43,13 @@ public interface CombinationRecordMapper extends BaseMapperX<CombinationRecordDO
|
||||
.eq(CombinationRecordDO::getStatus, status));
|
||||
}
|
||||
|
||||
default CombinationRecordDO selectByHeadIdForUpdate(Long headId, Integer status) {
|
||||
return selectOne(new LambdaQueryWrapperX<CombinationRecordDO>()
|
||||
.eq(CombinationRecordDO::getId, headId)
|
||||
.eq(CombinationRecordDO::getStatus, status)
|
||||
.last("FOR UPDATE"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询拼团记录
|
||||
*
|
||||
|
||||
@@ -37,6 +37,11 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> {
|
||||
SeckillActivityDO::getStatus, status);
|
||||
}
|
||||
|
||||
default Long selectCountByConfigId(Long configId) {
|
||||
return selectCount(new LambdaQueryWrapperX<SeckillActivityDO>()
|
||||
.apply(MyBatisUtils.findInSet("config_ids"), configId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新活动库存(减少)
|
||||
*
|
||||
@@ -63,6 +68,7 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> {
|
||||
Assert.isTrue(count > 0);
|
||||
return update(null, new LambdaUpdateWrapper<SeckillActivityDO>()
|
||||
.eq(SeckillActivityDO::getId, id)
|
||||
.apply("stock + {0} <= total_stock", count)
|
||||
.setSql("stock = stock + " + count));
|
||||
}
|
||||
|
||||
|
||||
@@ -59,4 +59,10 @@ public interface SeckillProductMapper extends BaseMapperX<SeckillProductDO> {
|
||||
.setSql("stock = stock + " + count));
|
||||
}
|
||||
|
||||
default int updateActivityStatusByActivityId(Long activityId, Integer status) {
|
||||
return update(null, new LambdaUpdateWrapper<SeckillProductDO>()
|
||||
.eq(SeckillProductDO::getActivityId, activityId)
|
||||
.set(SeckillProductDO::getActivityStatus, status));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ public interface ErrorCodeConstants {
|
||||
|
||||
// ========== 秒杀活动 1-013-008-000 ==========
|
||||
ErrorCode SECKILL_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_008_000, "秒杀活动不存在");
|
||||
ErrorCode SECKILL_ACTIVITY_PRODUCT_DUPLICATE = new ErrorCode(1_013_008_001, "秒杀商品 SKU 不能重复");
|
||||
ErrorCode SECKILL_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1_013_008_002, "存在商品参加了其它秒杀活动,秒杀时段冲突");
|
||||
ErrorCode SECKILL_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1_013_008_003, "秒杀活动已关闭,不能修改");
|
||||
ErrorCode SECKILL_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1_013_008_004, "秒杀活动未关闭或未结束,不能删除");
|
||||
@@ -66,10 +67,13 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode SECKILL_JOIN_ACTIVITY_STATUS_CLOSED = new ErrorCode(1_013_008_008, "秒杀失败,原因:秒杀活动已关闭");
|
||||
ErrorCode SECKILL_JOIN_ACTIVITY_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1_013_008_009, "秒杀失败,原因:单次限购超出");
|
||||
ErrorCode SECKILL_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS = new ErrorCode(1_013_008_010, "秒杀失败,原因:商品不存在");
|
||||
ErrorCode SECKILL_ACTIVITY_PRODUCT_PRICE_INVALID = new ErrorCode(1_013_008_011, "秒杀价格不能高于商品价格");
|
||||
ErrorCode SECKILL_ACTIVITY_PRODUCT_STOCK_EXCEED = new ErrorCode(1_013_008_012, "秒杀库存不能超过商品库存");
|
||||
|
||||
// ========== 秒杀时段 1-013-009-000 ==========
|
||||
ErrorCode SECKILL_CONFIG_NOT_EXISTS = new ErrorCode(1_013_009_000, "秒杀时段不存在");
|
||||
ErrorCode SECKILL_CONFIG_TIME_CONFLICTS = new ErrorCode(1_013_009_001, "秒杀时段冲突");
|
||||
ErrorCode SECKILL_CONFIG_DELETE_FAIL_USED = new ErrorCode(1_013_009_002, "秒杀时段已被活动使用,不能删除");
|
||||
ErrorCode SECKILL_CONFIG_DISABLE = new ErrorCode(1_013_009_004, "秒杀时段已关闭");
|
||||
|
||||
// ========== 拼团活动 1-013-010-000 ==========
|
||||
@@ -80,6 +84,9 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE = new ErrorCode(1_013_010_004, "拼团失败,原因:拼团活动已关闭");
|
||||
ErrorCode COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS = new ErrorCode(1_013_010_005, "拼团失败,原因:拼团活动商品不存在");
|
||||
ErrorCode COMBINATION_ACTIVITY_UPDATE_STOCK_FAIL = new ErrorCode(1_013_010_006, "拼团失败,原因:拼团活动商品库存不足");
|
||||
ErrorCode COMBINATION_ACTIVITY_PRODUCT_DUPLICATE = new ErrorCode(1_013_010_007, "拼团商品 SKU 不能重复");
|
||||
ErrorCode COMBINATION_ACTIVITY_PRODUCT_PRICE_INVALID = new ErrorCode(1_013_010_008, "拼团价格不能高于商品价格");
|
||||
ErrorCode COMBINATION_ACTIVITY_PRODUCT_SPU_MISMATCH = new ErrorCode(1_013_010_009, "拼团商品必须属于活动 SPU");
|
||||
|
||||
// ========== 拼团记录 1-013-011-000 ==========
|
||||
ErrorCode COMBINATION_RECORD_NOT_EXISTS = new ErrorCode(1_013_011_000, "拼团不存在");
|
||||
@@ -92,6 +99,7 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED = new ErrorCode(1_013_011_007, "拼团失败,原因:单次限购超出");
|
||||
ErrorCode COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED = new ErrorCode(1_013_011_008, "拼团失败,原因:超出总购买次数");
|
||||
ErrorCode COMBINATION_RECORD_FAILED_ORDER_STATUS_UNPAID = new ErrorCode(1_013_011_009, "拼团失败,原因:存在未支付订单,请先支付");
|
||||
ErrorCode COMBINATION_RECORD_HEAD_ACTIVITY_MISMATCH = new ErrorCode(1_013_011_010, "拼团失败,父拼团不属于当前活动");
|
||||
|
||||
// ========== 砍价活动 1-013-012-000 ==========
|
||||
ErrorCode BARGAIN_ACTIVITY_NOT_EXISTS = new ErrorCode(1_013_012_000, "砍价活动不存在");
|
||||
@@ -101,6 +109,9 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode BARGAIN_ACTIVITY_STOCK_NOT_ENOUGH = new ErrorCode(1_013_012_004, "砍价活动库存不足");
|
||||
ErrorCode BARGAIN_ACTIVITY_STATUS_CLOSED = new ErrorCode(1_013_012_005, "砍价活动已关闭");
|
||||
ErrorCode BARGAIN_ACTIVITY_TIME_END = new ErrorCode(1_013_012_006, "砍价活动已经结束");
|
||||
ErrorCode BARGAIN_ACTIVITY_SKU_SPU_MISMATCH = new ErrorCode(1_013_012_007, "砍价商品 SKU 不属于活动 SPU");
|
||||
ErrorCode BARGAIN_ACTIVITY_PRICE_INVALID = new ErrorCode(1_013_012_008, "砍价起始价格不能高于商品价格");
|
||||
ErrorCode BARGAIN_ACTIVITY_STOCK_RESTORE_EXCEED = new ErrorCode(1_013_012_009, "砍价活动库存回补不能超过总库存");
|
||||
|
||||
// ========== 砍价记录 1-013-013-000 ==========
|
||||
ErrorCode BARGAIN_RECORD_NOT_EXISTS = new ErrorCode(1_013_013_000, "砍价记录不存在");
|
||||
|
||||
@@ -48,7 +48,7 @@ public class BargainActivityServiceImpl implements BargainActivityService {
|
||||
// 校验商品 SPU 是否存在是否参加的别的活动
|
||||
validateBargainConflict(createReqVO.getSpuId(), null);
|
||||
// 校验商品 sku 是否存在
|
||||
validateSku(createReqVO.getSkuId());
|
||||
validateSku(createReqVO.getSpuId(), createReqVO.getSkuId(), createReqVO.getBargainFirstPrice());
|
||||
|
||||
// 插入砍价活动
|
||||
BargainActivityDO activityDO = BargainActivityConvert.INSTANCE.convert(createReqVO)
|
||||
@@ -70,7 +70,7 @@ public class BargainActivityServiceImpl implements BargainActivityService {
|
||||
// 校验商品冲突
|
||||
validateBargainConflict(updateReqVO.getSpuId(), updateReqVO.getId());
|
||||
// 校验商品 sku 是否存在
|
||||
validateSku(updateReqVO.getSkuId());
|
||||
validateSku(updateReqVO.getSpuId(), updateReqVO.getSkuId(), updateReqVO.getBargainFirstPrice());
|
||||
|
||||
// 更新
|
||||
BargainActivityDO updateObj = BargainActivityConvert.INSTANCE.convert(updateReqVO);
|
||||
@@ -89,7 +89,9 @@ public class BargainActivityServiceImpl implements BargainActivityService {
|
||||
throw exception(BARGAIN_ACTIVITY_STOCK_NOT_ENOUGH);
|
||||
}
|
||||
} else if (count > 0) {
|
||||
bargainActivityMapper.updateStock(id, count);
|
||||
if (bargainActivityMapper.updateStock(id, count) == 0) {
|
||||
throw exception(BARGAIN_ACTIVITY_STOCK_RESTORE_EXCEED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,11 +120,17 @@ public class BargainActivityServiceImpl implements BargainActivityService {
|
||||
}
|
||||
}
|
||||
|
||||
private void validateSku(Long skuId) {
|
||||
private void validateSku(Long spuId, Long skuId, Integer bargainFirstPrice) {
|
||||
ProductSkuRespDTO sku = productSkuApi.getSku(skuId);
|
||||
if (sku == null) {
|
||||
throw exception(SKU_NOT_EXISTS);
|
||||
}
|
||||
if (!ObjectUtil.equal(sku.getSpuId(), spuId)) {
|
||||
throw exception(BARGAIN_ACTIVITY_SKU_SPU_MISMATCH);
|
||||
}
|
||||
if (bargainFirstPrice > sku.getPrice()) {
|
||||
throw exception(BARGAIN_ACTIVITY_PRICE_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -96,7 +96,7 @@ public class BargainHelpServiceImpl implements BargainHelpService {
|
||||
// TODO 管理员:优化点:实现一个更随机的逻辑,可以按照你自己的业务;
|
||||
private Integer calculateReducePrice(BargainActivityDO activity, BargainRecordDO record) {
|
||||
// 1. 随机金额
|
||||
Integer reducePrice = MathUtil.randomInt(activity.getBargainMinPrice(),
|
||||
Integer reducePrice = MathUtil.randomInt(activity.getRandomMinPrice(),
|
||||
activity.getRandomMaxPrice() + 1); // + 1 的原因是,randomInt 默认不包含第二个参数
|
||||
// 2. 校验是否超过砍价上限
|
||||
if (record.getBargainPrice() - reducePrice < activity.getBargainMinPrice()) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -71,7 +72,7 @@ public class BargainRecordServiceImpl implements BargainRecordService {
|
||||
Integer reducePrice, Boolean success) {
|
||||
BargainRecordDO updateObj = new BargainRecordDO().setBargainPrice(whereBargainPrice - reducePrice);
|
||||
if (success) {
|
||||
updateObj.setStatus(BargainRecordStatusEnum.SUCCESS.getStatus());
|
||||
updateObj.setStatus(BargainRecordStatusEnum.SUCCESS.getStatus()).setEndTime(LocalDateTime.now());
|
||||
}
|
||||
return bargainRecordMapper.updateByIdAndBargainPrice(id, whereBargainPrice, updateObj) > 0;
|
||||
}
|
||||
|
||||
@@ -25,8 +25,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
@@ -108,10 +110,21 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
|
||||
// 2. 校验商品 sku 都存在
|
||||
List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(singletonList(spuId));
|
||||
Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);
|
||||
Set<Long> skuIds = new HashSet<>();
|
||||
products.forEach(product -> {
|
||||
if (!skuMap.containsKey(product.getSkuId())) {
|
||||
if (!skuIds.add(product.getSkuId())) {
|
||||
throw exception(COMBINATION_ACTIVITY_PRODUCT_DUPLICATE);
|
||||
}
|
||||
if (!ObjectUtil.equal(product.getSpuId(), spuId)) {
|
||||
throw exception(COMBINATION_ACTIVITY_PRODUCT_SPU_MISMATCH);
|
||||
}
|
||||
ProductSkuRespDTO sku = skuMap.get(product.getSkuId());
|
||||
if (sku == null) {
|
||||
throw exception(SKU_NOT_EXISTS);
|
||||
}
|
||||
if (product.getCombinationPrice() > sku.getPrice()) {
|
||||
throw exception(COMBINATION_ACTIVITY_PRODUCT_PRICE_INVALID);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -130,7 +143,8 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
|
||||
validateProductExists(updateReqVO.getSpuId(), updateReqVO.getProducts());
|
||||
|
||||
// 更新活动
|
||||
CombinationActivityDO updateObj = CombinationActivityConvert.INSTANCE.convert(updateReqVO);
|
||||
CombinationActivityDO updateObj = CombinationActivityConvert.INSTANCE.convert(updateReqVO)
|
||||
.setStatus(activityDO.getStatus());
|
||||
combinationActivityMapper.updateById(updateObj);
|
||||
// 更新商品
|
||||
updateCombinationProduct(updateObj, updateReqVO.getProducts());
|
||||
@@ -148,6 +162,7 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
|
||||
// 关闭活动
|
||||
combinationActivityMapper.updateById(new CombinationActivityDO().setId(id)
|
||||
.setStatus(CommonStatusEnum.DISABLE.getStatus()));
|
||||
combinationProductMapper.updateActivityStatusByActivityId(id, CommonStatusEnum.DISABLE.getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,6 +206,7 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
|
||||
}
|
||||
|
||||
// 删除
|
||||
combinationProductMapper.deleteByActivityId(id);
|
||||
combinationActivityMapper.deleteById(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,9 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
||||
@Override
|
||||
public KeyValue<CombinationActivityDO, CombinationProductDO> validateCombinationRecord(
|
||||
Long userId, Long activityId, Long headId, Long skuId, Integer count) {
|
||||
if (count == null || count <= 0) {
|
||||
throw exception(COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED);
|
||||
}
|
||||
// 1. 校验拼团活动是否存在
|
||||
CombinationActivityDO activity = combinationActivityService.validateCombinationActivityExists(activityId);
|
||||
// 1.1 校验活动是否开启
|
||||
@@ -99,9 +102,12 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
||||
if (isJoinCombination(headId)) {
|
||||
// 2.1. 查询进行中的父拼团
|
||||
CombinationRecordDO record = combinationRecordMapper.selectByHeadId(headId, CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
|
||||
if (record == null) {
|
||||
if (record == null || ObjUtil.notEqual(record.getHeadId(), CombinationRecordDO.HEAD_ID_GROUP)) {
|
||||
throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS);
|
||||
}
|
||||
if (ObjUtil.notEqual(record.getActivityId(), activityId)) {
|
||||
throw exception(COMBINATION_RECORD_HEAD_ACTIVITY_MISMATCH);
|
||||
}
|
||||
// 2.2. 校验拼团是否已满
|
||||
if (ObjUtil.equal(record.getUserCount(), record.getUserSize())) {
|
||||
throw exception(COMBINATION_RECORD_USER_FULL);
|
||||
@@ -122,6 +128,9 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
||||
if (product == null) {
|
||||
throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);
|
||||
}
|
||||
if (ObjUtil.notEqual(product.getSpuId(), activity.getSpuId())) {
|
||||
throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);
|
||||
}
|
||||
// 4.2 校验 sku 是否存在
|
||||
ProductSkuRespDTO sku = productSkuApi.getSku(skuId);
|
||||
if (sku == null) {
|
||||
@@ -165,14 +174,29 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public CombinationRecordDO createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
|
||||
if (isJoinCombination(reqDTO.getHeadId())) {
|
||||
CombinationRecordDO headRecord = combinationRecordMapper.selectByHeadIdForUpdate(reqDTO.getHeadId(),
|
||||
CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
|
||||
if (headRecord == null
|
||||
|| ObjUtil.notEqual(headRecord.getHeadId(), CombinationRecordDO.HEAD_ID_GROUP)) {
|
||||
throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
// 1. 校验拼团活动
|
||||
KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(reqDTO.getUserId(),
|
||||
reqDTO.getActivityId(), reqDTO.getHeadId(), reqDTO.getSkuId(), reqDTO.getCount());
|
||||
if (ObjUtil.notEqual(reqDTO.getSpuId(), keyValue.getKey().getSpuId())
|
||||
|| ObjUtil.notEqual(reqDTO.getCombinationPrice(), keyValue.getValue().getCombinationPrice())) {
|
||||
throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);
|
||||
}
|
||||
|
||||
// 2. 组合数据创建拼团记录
|
||||
MemberUserRespDTO user = memberUserApi.getUser(reqDTO.getUserId());
|
||||
ProductSpuRespDTO spu = productSpuApi.getSpu(reqDTO.getSpuId());
|
||||
ProductSkuRespDTO sku = productSkuApi.getSku(reqDTO.getSkuId());
|
||||
if (user == null || spu == null || sku == null) {
|
||||
throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);
|
||||
}
|
||||
CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO, keyValue.getKey(), user, spu, sku);
|
||||
// 2.1. 如果是团长需要设置 headId 为 CombinationRecordDO#HEAD_ID_GROUP
|
||||
if (!isJoinCombination(record.getHeadId())) {
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashSet;
|
||||
|
||||
import static cn.hutool.core.collection.CollUtil.isNotEmpty;
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
@@ -124,10 +125,20 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
|
||||
// 2. 校验商品 sku 都存在
|
||||
List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(singletonList(spuId));
|
||||
Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);
|
||||
if (new HashSet<>(convertList(products, SeckillProductBaseVO::getSkuId)).size() != products.size()) {
|
||||
throw exception(SECKILL_ACTIVITY_PRODUCT_DUPLICATE);
|
||||
}
|
||||
products.forEach(product -> {
|
||||
if (!skuMap.containsKey(product.getSkuId())) {
|
||||
ProductSkuRespDTO sku = skuMap.get(product.getSkuId());
|
||||
if (sku == null) {
|
||||
throw exception(SKU_NOT_EXISTS);
|
||||
}
|
||||
if (product.getSeckillPrice() > sku.getPrice()) {
|
||||
throw exception(SECKILL_ACTIVITY_PRODUCT_PRICE_INVALID);
|
||||
}
|
||||
if (product.getStock() > sku.getStock()) {
|
||||
throw exception(SECKILL_ACTIVITY_PRODUCT_STOCK_EXCEED);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -185,11 +196,21 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateSeckillStockIncr(Long id, Long skuId, Integer count) {
|
||||
validateSeckillActivityExists(id);
|
||||
SeckillProductDO product = seckillProductMapper.selectByActivityIdAndSkuId(id, skuId);
|
||||
if (product == null) {
|
||||
throw exception(SECKILL_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);
|
||||
}
|
||||
// 更新活动商品库存
|
||||
seckillProductMapper.updateStockIncr(product.getId(), count);
|
||||
int updateCount = seckillProductMapper.updateStockIncr(product.getId(), count);
|
||||
if (updateCount == 0) {
|
||||
throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
|
||||
}
|
||||
// 更新活动库存
|
||||
seckillActivityMapper.updateStockIncr(id, count);
|
||||
updateCount = seckillActivityMapper.updateStockIncr(id, count);
|
||||
if (updateCount == 0) {
|
||||
throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,6 +244,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void closeSeckillActivity(Long id) {
|
||||
// 校验存在
|
||||
SeckillActivityDO activity = validateSeckillActivityExists(id);
|
||||
@@ -233,6 +255,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
|
||||
// 更新
|
||||
SeckillActivityDO updateObj = new SeckillActivityDO().setId(id).setStatus(CommonStatusEnum.DISABLE.getStatus());
|
||||
seckillActivityMapper.updateById(updateObj);
|
||||
seckillProductMapper.updateActivityStatusByActivityId(id, CommonStatusEnum.DISABLE.getStatus());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.Seck
|
||||
import cn.iocoder.yudao.module.promotion.convert.seckill.SeckillConfigConvert;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillconfig.SeckillConfigMapper;
|
||||
import cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity.SeckillActivityMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@@ -36,6 +37,8 @@ public class SeckillConfigServiceImpl implements SeckillConfigService {
|
||||
|
||||
@Resource
|
||||
private SeckillConfigMapper seckillConfigMapper;
|
||||
@Resource
|
||||
private SeckillActivityMapper seckillActivityMapper;
|
||||
|
||||
@Override
|
||||
public Long createSeckillConfig(SeckillConfigCreateReqVO createReqVO) {
|
||||
@@ -80,6 +83,9 @@ public class SeckillConfigServiceImpl implements SeckillConfigService {
|
||||
public void deleteSeckillConfig(Long id) {
|
||||
// 校验存在
|
||||
validateSeckillConfigExists(id);
|
||||
if (seckillActivityMapper.selectCountByConfigId(id) > 0) {
|
||||
throw exception(SECKILL_CONFIG_DELETE_FAIL_USED);
|
||||
}
|
||||
|
||||
// 删除
|
||||
seckillConfigMapper.deleteById(id);
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.bargain;
|
||||
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityBaseVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.help.AppBargainHelpCreateReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.app.bargain.vo.record.AppBargainRecordCreateReqVO;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class BargainRequestValidationContractTest {
|
||||
|
||||
@Test
|
||||
void activityRequestsRequireBoundedNameAndPositiveValues() throws Exception {
|
||||
assertThat(BargainActivityBaseVO.class.getDeclaredField("name")
|
||||
.isAnnotationPresent(NotBlank.class)).isTrue();
|
||||
assertThat(BargainActivityBaseVO.class.getDeclaredField("name")
|
||||
.getAnnotation(Size.class).max()).isEqualTo(255);
|
||||
for (String field : new String[]{"spuId", "skuId", "bargainFirstPrice", "bargainMinPrice",
|
||||
"stock", "totalLimitCount", "helpMaxCount", "bargainCount", "randomMinPrice", "randomMaxPrice"}) {
|
||||
assertThat(BargainActivityBaseVO.class.getDeclaredField(field)
|
||||
.isAnnotationPresent(Positive.class)).as(field).isTrue();
|
||||
}
|
||||
assertThat(BargainActivityUpdateReqVO.class.getDeclaredField("id")
|
||||
.isAnnotationPresent(Positive.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void activityRequestRejectsReversedTimesAndInvalidPriceRelationships() {
|
||||
BargainActivityBaseVO request = new BargainActivityBaseVO();
|
||||
request.setStartTime(LocalDateTime.of(2026, 1, 2, 0, 0));
|
||||
request.setEndTime(LocalDateTime.of(2026, 1, 1, 0, 0));
|
||||
request.setBargainFirstPrice(100);
|
||||
request.setBargainMinPrice(101);
|
||||
request.setRandomMinPrice(20);
|
||||
request.setRandomMaxPrice(19);
|
||||
|
||||
assertThat(request.isActivityTimeValid()).isFalse();
|
||||
assertThat(request.isBargainPriceValid()).isFalse();
|
||||
assertThat(request.isRandomPriceValid()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void appRequestsRequirePositiveIdentifiers() throws Exception {
|
||||
assertThat(AppBargainRecordCreateReqVO.class.getDeclaredField("activityId")
|
||||
.isAnnotationPresent(Positive.class)).isTrue();
|
||||
assertThat(AppBargainHelpCreateReqVO.class.getDeclaredField("recordId")
|
||||
.isAnnotationPresent(Positive.class)).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.combination;
|
||||
|
||||
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityBaseVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductBaseVO;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import jakarta.validation.constraints.PositiveOrZero;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.AnnotatedParameterizedType;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class CombinationRequestValidationContractTest {
|
||||
|
||||
@Test
|
||||
void activityRequestsRequireProductsPositiveIdentifiersAndAtLeastTwoMembers() throws Exception {
|
||||
assertThat(CombinationActivityCreateReqVO.class.getDeclaredField("products")
|
||||
.isAnnotationPresent(NotEmpty.class)).isTrue();
|
||||
AnnotatedParameterizedType productsType = (AnnotatedParameterizedType)
|
||||
CombinationActivityCreateReqVO.class.getDeclaredField("products").getAnnotatedType();
|
||||
assertThat(productsType.getAnnotatedActualTypeArguments()[0].isAnnotationPresent(Valid.class)).isTrue();
|
||||
assertThat(CombinationActivityUpdateReqVO.class.getDeclaredField("id")
|
||||
.isAnnotationPresent(Positive.class)).isTrue();
|
||||
assertThat(CombinationActivityBaseVO.class.getDeclaredField("spuId")
|
||||
.isAnnotationPresent(Positive.class)).isTrue();
|
||||
assertThat(CombinationActivityBaseVO.class.getDeclaredField("userSize")
|
||||
.getAnnotation(Min.class).value()).isEqualTo(2L);
|
||||
assertThat(CombinationProductBaseVO.class.getDeclaredField("skuId")
|
||||
.isAnnotationPresent(Positive.class)).isTrue();
|
||||
assertThat(CombinationProductBaseVO.class.getDeclaredField("combinationPrice")
|
||||
.isAnnotationPresent(Positive.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void activityRequestRejectsReversedTimesAndInvalidLimitRelationship() {
|
||||
CombinationActivityBaseVO request = new CombinationActivityBaseVO();
|
||||
request.setStartTime(LocalDateTime.of(2026, 1, 2, 0, 0));
|
||||
request.setEndTime(LocalDateTime.of(2026, 1, 1, 0, 0));
|
||||
request.setTotalLimitCount(2);
|
||||
request.setSingleLimitCount(3);
|
||||
|
||||
assertThat(request.isActivityTimeValid()).isFalse();
|
||||
assertThat(request.isLimitCountValid()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void recordCreationRequiresPositiveOrderUserProductAndNonNegativeHead() throws Exception {
|
||||
assertThat(CombinationRecordCreateReqDTO.class.getDeclaredField("activityId")
|
||||
.isAnnotationPresent(Positive.class)).isTrue();
|
||||
assertThat(CombinationRecordCreateReqDTO.class.getDeclaredField("orderId")
|
||||
.isAnnotationPresent(Positive.class)).isTrue();
|
||||
assertThat(CombinationRecordCreateReqDTO.class.getDeclaredField("userId")
|
||||
.isAnnotationPresent(Positive.class)).isTrue();
|
||||
assertThat(CombinationRecordCreateReqDTO.class.getDeclaredField("combinationPrice")
|
||||
.isAnnotationPresent(Positive.class)).isTrue();
|
||||
assertThat(CombinationRecordCreateReqDTO.class.getDeclaredField("headId")
|
||||
.isAnnotationPresent(PositiveOrZero.class)).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package cn.iocoder.yudao.module.promotion.controller.admin.seckill;
|
||||
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityCreateReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.SeckillConfigBaseVO;
|
||||
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.product.SeckillProductBaseVO;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.AnnotatedParameterizedType;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class SeckillRequestValidationContractTest {
|
||||
|
||||
@Test
|
||||
void activityRequestsRequirePositiveProductsAndIdentifiers() throws Exception {
|
||||
assertThat(SeckillActivityCreateReqVO.class.getDeclaredField("products")
|
||||
.isAnnotationPresent(NotEmpty.class)).isTrue();
|
||||
AnnotatedParameterizedType productsType = (AnnotatedParameterizedType)
|
||||
SeckillActivityCreateReqVO.class.getDeclaredField("products").getAnnotatedType();
|
||||
assertThat(productsType.getAnnotatedActualTypeArguments()[0].isAnnotationPresent(Valid.class)).isTrue();
|
||||
assertThat(SeckillActivityUpdateReqVO.class.getDeclaredField("id")
|
||||
.isAnnotationPresent(Positive.class)).isTrue();
|
||||
assertThat(SeckillProductBaseVO.class.getDeclaredField("seckillPrice")
|
||||
.isAnnotationPresent(Positive.class)).isTrue();
|
||||
assertThat(SeckillProductBaseVO.class.getDeclaredField("stock")
|
||||
.isAnnotationPresent(Positive.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void configRequestsRequireAtLeastOneSliderAndRejectReversedTimes() throws Exception {
|
||||
assertThat(SeckillConfigBaseVO.class.getDeclaredField("sliderPicUrls")
|
||||
.isAnnotationPresent(NotEmpty.class)).isTrue();
|
||||
SeckillConfigBaseVO request = new SeckillConfigBaseVO();
|
||||
request.setStartTime("12:00");
|
||||
request.setEndTime("11:00");
|
||||
assertThat(request.isValidEndTimeValid()).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package cn.iocoder.yudao.module.promotion.convert.combination;
|
||||
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class CombinationActivityConvertContractTest {
|
||||
|
||||
@Test
|
||||
void headCreationReturnsPersistedHeadRecordIdInsteadOfSentinel() {
|
||||
CombinationRecordDO head = new CombinationRecordDO().setId(123L).setActivityId(45L)
|
||||
.setHeadId(CombinationRecordDO.HEAD_ID_GROUP);
|
||||
|
||||
assertThat(CombinationActivityConvert.INSTANCE.convert4(head))
|
||||
.satisfies(response -> {
|
||||
assertThat(response.getCombinationActivityId()).isEqualTo(45L);
|
||||
assertThat(response.getCombinationRecordId()).isEqualTo(123L);
|
||||
assertThat(response.getCombinationHeadId()).isEqualTo(123L);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainHelpDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainRecordDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillConfigDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class PromotionActivityTenantContractTest {
|
||||
|
||||
@Test
|
||||
void checkoutPromotionRecordsParticipateInFrameworkTenantIsolation() {
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(DiscountActivityDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(DiscountProductDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(RewardActivityDO.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void seckillRecordsParticipateInFrameworkTenantIsolation() {
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(SeckillActivityDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(SeckillProductDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(SeckillConfigDO.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void combinationRecordsParticipateInFrameworkTenantIsolation() {
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(CombinationActivityDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(CombinationProductDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(CombinationRecordDO.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bargainRecordsParticipateInFrameworkTenantIsolation() {
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(BargainActivityDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(BargainRecordDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(BargainHelpDO.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package cn.iocoder.yudao.module.promotion.dal.dataobject.coupon;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class CouponTenantContractTest {
|
||||
|
||||
@Test
|
||||
void nativeCouponRecordsParticipateInFrameworkTenantIsolation() {
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(CouponTemplateDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(CouponDO.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package cn.iocoder.yudao.module.promotion.service.bargain;
|
||||
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainRecordDO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.mysql.bargain.BargainRecordMapper;
|
||||
import cn.iocoder.yudao.module.promotion.enums.bargain.BargainRecordStatusEnum;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class BargainServiceBehaviorContractTest {
|
||||
|
||||
@Test
|
||||
void calculateReducePriceUsesConfiguredRandomRange() {
|
||||
BargainHelpServiceImpl service = new BargainHelpServiceImpl();
|
||||
BargainActivityDO activity = new BargainActivityDO()
|
||||
.setRandomMinPrice(7)
|
||||
.setRandomMaxPrice(7)
|
||||
.setBargainMinPrice(10);
|
||||
BargainRecordDO record = new BargainRecordDO().setBargainPrice(100);
|
||||
|
||||
Integer reducePrice = ReflectionTestUtils.invokeMethod(service, "calculateReducePrice", activity, record);
|
||||
|
||||
assertThat(reducePrice).isEqualTo(7);
|
||||
}
|
||||
|
||||
@Test
|
||||
void successfulPriceUpdateStoresStatusAndEndTime() {
|
||||
BargainRecordMapper mapper = mock(BargainRecordMapper.class);
|
||||
BargainRecordServiceImpl service = new BargainRecordServiceImpl();
|
||||
ReflectionTestUtils.setField(service, "bargainRecordMapper", mapper);
|
||||
when(mapper.updateByIdAndBargainPrice(eq(1L), eq(100), org.mockito.ArgumentMatchers.any()))
|
||||
.thenReturn(1);
|
||||
|
||||
assertThat(service.updateBargainRecordBargainPrice(1L, 100, 20, true)).isTrue();
|
||||
|
||||
ArgumentCaptor<BargainRecordDO> updateCaptor = ArgumentCaptor.forClass(BargainRecordDO.class);
|
||||
verify(mapper).updateByIdAndBargainPrice(eq(1L), eq(100), updateCaptor.capture());
|
||||
assertThat(updateCaptor.getValue().getBargainPrice()).isEqualTo(80);
|
||||
assertThat(updateCaptor.getValue().getStatus()).isEqualTo(BargainRecordStatusEnum.SUCCESS.getStatus());
|
||||
assertThat(updateCaptor.getValue().getEndTime()).isNotNull();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -103,7 +103,7 @@ public class AfterSaleController {
|
||||
@PutMapping("/disagree")
|
||||
@Operation(summary = "拒绝售后")
|
||||
@PreAuthorize("@ss.hasPermission('trade:after-sale:disagree')")
|
||||
public CommonResult<Boolean> disagreeAfterSale(@RequestBody AfterSaleDisagreeReqVO confirmReqVO) {
|
||||
public CommonResult<Boolean> disagreeAfterSale(@RequestBody @Valid AfterSaleDisagreeReqVO confirmReqVO) {
|
||||
afterSaleService.disagreeAfterSale(getLoginUserId(), confirmReqVO);
|
||||
return success(true);
|
||||
}
|
||||
@@ -121,7 +121,7 @@ public class AfterSaleController {
|
||||
@Operation(summary = "拒绝收货")
|
||||
@Parameter(name = "id", description = "售后编号", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('trade:after-sale:receive')")
|
||||
public CommonResult<Boolean> refuseAfterSale(AfterSaleRefuseReqVO refuseReqVO) {
|
||||
public CommonResult<Boolean> refuseAfterSale(@Valid AfterSaleRefuseReqVO refuseReqVO) {
|
||||
afterSaleService.refuseAfterSale(getLoginUserId(), refuseReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderItemBas
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 售后订单的详情 Response VO")
|
||||
@@ -17,7 +18,8 @@ public class AfterSaleDetailRespVO extends AfterSaleBaseVO {
|
||||
@Schema(description = "售后编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
|
||||
@Schema(description = "申请时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 订单基本信息
|
||||
|
||||
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "管理后台 - 交易售后拒绝收货 Request VO")
|
||||
@@ -14,7 +15,7 @@ public class AfterSaleRefuseReqVO {
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "收货备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜")
|
||||
@NotNull(message = "收货备注不能为空")
|
||||
@NotBlank(message = "收货备注不能为空")
|
||||
private String refuseMemo;
|
||||
|
||||
}
|
||||
|
||||
@@ -85,6 +85,9 @@ public class BrokerageUserController {
|
||||
@PreAuthorize("@ss.hasPermission('trade:brokerage-user:query')")
|
||||
public CommonResult<BrokerageUserRespVO> getBrokerageUser(@RequestParam("id") Long id) {
|
||||
BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(id);
|
||||
if (brokerageUser == null) {
|
||||
return success(null);
|
||||
}
|
||||
// TODO @疯狂:是不是搞成一个统一的 convert?
|
||||
BrokerageUserRespVO respVO = BrokerageUserConvert.INSTANCE.convert(brokerageUser);
|
||||
return success(BrokerageUserConvert.INSTANCE.copyTo(memberUserApi.getUser(id), respVO));
|
||||
|
||||
@@ -4,7 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "管理后台 - 驳回申请 Request VO")
|
||||
@@ -17,7 +17,7 @@ public class BrokerageWithdrawRejectReqVO {
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "审核驳回原因", example = "不对")
|
||||
@NotEmpty(message = "审核驳回原因不能为空")
|
||||
@NotBlank(message = "审核驳回原因不能为空")
|
||||
private String auditReason;
|
||||
|
||||
}
|
||||
|
||||
@@ -6,24 +6,25 @@ import cn.iocoder.yudao.module.pay.enums.PayChannelEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.URL;
|
||||
|
||||
import jakarta.validation.Validator;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.PositiveOrZero;
|
||||
|
||||
@Schema(description = "用户 App - 分销提现创建 Request VO")
|
||||
@Data
|
||||
public class AppBrokerageWithdrawCreateReqVO {
|
||||
|
||||
@Schema(description = "提现方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "提现方式不能为空")
|
||||
@InEnum(value = BrokerageWithdrawTypeEnum.class, message = "提现方式必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "提现金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
|
||||
@PositiveOrZero(message = "提现金额不能小于 0")
|
||||
@Positive(message = "提现金额必须大于 0")
|
||||
@Min(value = 30, message = "微信提现金额不能小于 0.3", groups = {WechatApi.class})
|
||||
@NotNull(message = "提现金额不能为空")
|
||||
private Integer price;
|
||||
|
||||
@@ -44,6 +44,11 @@ public interface BrokerageRecordConvert {
|
||||
Integer status = brokerageFrozenDays > 0
|
||||
? BrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus()
|
||||
: BrokerageRecordStatusEnum.SETTLEMENT.getStatus();
|
||||
// 即时结算也要记录结算时刻。排行、汇总接口按 unfreeze_time 过滤,留空会导致
|
||||
// 无冻结期的正常佣金永远不出现在时间范围统计中。
|
||||
if (BrokerageRecordStatusEnum.SETTLEMENT.getStatus().equals(status) && unfreezeTime == null) {
|
||||
unfreezeTime = LocalDateTime.now();
|
||||
}
|
||||
return new BrokerageRecordDO().setUserId(user.getId())
|
||||
.setBizType(bizType.getType()).setBizId(bizId)
|
||||
.setPrice(brokeragePrice).setTotalPrice(user.getBrokeragePrice())
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.aftersale;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleStatusEnum;
|
||||
@@ -23,7 +23,7 @@ import java.util.List;
|
||||
@TableName(value = "trade_after_sale", autoResultMap = true)
|
||||
@KeySequence("trade_after_sale_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
public class AfterSaleDO extends BaseDO {
|
||||
public class AfterSaleDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 售后编号,主键自增
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.aftersale;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleOperateTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@@ -21,7 +21,7 @@ import lombok.*;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AfterSaleLogDO extends BaseDO {
|
||||
public class AfterSaleLogDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.brokerage;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
@@ -23,7 +23,7 @@ import java.time.LocalDateTime;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BrokerageRecordDO extends BaseDO {
|
||||
public class BrokerageRecordDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.brokerage;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
@@ -21,7 +21,7 @@ import java.time.LocalDateTime;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BrokerageUserDO extends BaseDO {
|
||||
public class BrokerageUserDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.brokerage;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
@@ -23,7 +23,7 @@ import java.time.LocalDateTime;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BrokerageWithdrawDO extends BaseDO {
|
||||
public class BrokerageWithdrawDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.cart;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
@@ -15,7 +15,7 @@ import lombok.Data;
|
||||
@TableName("trade_cart")
|
||||
@KeySequence("trade_cart_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
public class CartDO extends BaseDO {
|
||||
public class CartDO extends TenantBaseDO {
|
||||
|
||||
// ========= 基础字段 BEGIN =========
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.type.IntegerListTypeHandler;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageBindModeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageEnabledConditionEnum;
|
||||
@@ -27,7 +27,7 @@ import java.util.List;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TradeConfigDO extends BaseDO {
|
||||
public class TradeConfigDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 自增主键
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.delivery;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
@@ -15,7 +15,7 @@ import lombok.Data;
|
||||
@TableName(value ="trade_delivery_express")
|
||||
@KeySequence("trade_delivery_express_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
public class DeliveryExpressDO extends BaseDO {
|
||||
public class DeliveryExpressDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 编号,自增
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.delivery;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.type.IntegerListTypeHandler;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
@@ -18,7 +18,7 @@ import java.util.List;
|
||||
@TableName(value ="trade_delivery_express_template_charge", autoResultMap = true)
|
||||
@KeySequence("trade_delivery_express_template_charge_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
public class DeliveryExpressTemplateChargeDO extends BaseDO {
|
||||
public class DeliveryExpressTemplateChargeDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 编号,自增
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.delivery;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@@ -15,7 +15,7 @@ import lombok.*;
|
||||
@TableName("trade_delivery_express_template")
|
||||
@KeySequence("trade_delivery_express_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
public class DeliveryExpressTemplateDO extends BaseDO {
|
||||
public class DeliveryExpressTemplateDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 编号,自增
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.delivery;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.type.IntegerListTypeHandler;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
@@ -18,7 +18,7 @@ import java.util.List;
|
||||
@TableName(value ="trade_delivery_express_template_free", autoResultMap = true)
|
||||
@KeySequence("trade_delivery_express_template_free_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
public class DeliveryExpressTemplateFreeDO extends BaseDO {
|
||||
public class DeliveryExpressTemplateFreeDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.delivery;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
@@ -21,7 +21,7 @@ import java.util.List;
|
||||
@TableName(value ="trade_delivery_pick_up_store", autoResultMap = true)
|
||||
@KeySequence("trade_delivery_pick_up_store_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
public class DeliveryPickUpStoreDO extends BaseDO {
|
||||
public class DeliveryPickUpStoreDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.order;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
|
||||
@@ -35,7 +35,7 @@ import java.util.Map;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TradeOrderDO extends BaseDO {
|
||||
public class TradeOrderDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 发货物流公司编号 - 空(无需发货)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.order;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.CartDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
|
||||
@@ -21,7 +21,7 @@ import java.util.List;
|
||||
@TableName(value = "trade_order_item", autoResultMap = true)
|
||||
@KeySequence("trade_order_item_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
public class TradeOrderItemDO extends BaseDO {
|
||||
public class TradeOrderItemDO extends TenantBaseDO {
|
||||
|
||||
// ========== 订单项基本信息 ==========
|
||||
/**
|
||||
@@ -208,4 +208,3 @@ public class TradeOrderItemDO extends BaseDO {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject.order;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderOperateTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
@@ -21,7 +21,7 @@ import lombok.*;
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TradeOrderLogDO extends BaseDO {
|
||||
public class TradeOrderLogDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 用户类型 - 系统
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleLogDO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class AfterSaleTenantContractTest {
|
||||
|
||||
@Test
|
||||
void activatedAfterSaleRecordsParticipateInFrameworkTenantIsolation() {
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(AfterSaleDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(AfterSaleLogDO.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageRecordDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawRejectReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class BrokerageTenantContractTest {
|
||||
|
||||
@Test
|
||||
void nativeBrokerageRecordsParticipateInTenantIsolation() {
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(BrokerageUserDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(BrokerageRecordDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(BrokerageWithdrawDO.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withdrawalRequestsRejectMissingTypesNonPositiveAmountsAndBlankAuditReasons() throws Exception {
|
||||
assertThat(AppBrokerageWithdrawCreateReqVO.class.getDeclaredField("type")
|
||||
.isAnnotationPresent(NotNull.class)).isTrue();
|
||||
assertThat(AppBrokerageWithdrawCreateReqVO.class.getDeclaredField("price")
|
||||
.isAnnotationPresent(Positive.class)).isTrue();
|
||||
assertThat(BrokerageWithdrawRejectReqVO.class.getDeclaredField("auditReason")
|
||||
.isAnnotationPresent(NotBlank.class)).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateChargeDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressTemplateFreeDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class DeliveryTenantContractTest {
|
||||
|
||||
@Test
|
||||
void activatedDeliveryRecordsParticipateInFrameworkTenantIsolation() {
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(DeliveryExpressDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(DeliveryExpressTemplateDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(DeliveryExpressTemplateChargeDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(DeliveryExpressTemplateFreeDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(DeliveryPickUpStoreDO.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.yudao.module.trade.dal.dataobject;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.CartDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderLogDO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class TradeTenantContractTest {
|
||||
|
||||
@Test
|
||||
void activatedTradeRecordsParticipateInFrameworkTenantIsolation() {
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(TradeConfigDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(CartDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(TradeOrderDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(TradeOrderItemDO.class);
|
||||
assertThat(TenantBaseDO.class).isAssignableFrom(TradeOrderLogDO.class);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user